/* This notice must be untouched at all times.

wz_jsgraphics.js    v. 3.05
The latest version is available at
http://www.walterzorn.com
or http://www.devira.com
or http://www.walterzorn.de

Copyright (c) 2002-2009 Walter Zorn. All rights reserved.
Created 3. 11. 2002 by Walter Zorn (Web: http://www.walterzorn.com )
Last modified: 2. 2. 2009

Performance optimizations for Internet Explorer
by Thomas Frank and John Holdsworth.
fillPolygon method implemented by Matthieu Haller.

High Performance JavaScript Graphics Library.
Provides methods
- to draw lines, rectangles, ellipses, polygons
	with specifiable line thickness,
- to fill rectangles, polygons, ellipses and arcs
- to draw text.
NOTE: Operations, functions and branching have rather been optimized
to efficiency and speed than to shortness of source code.

LICENSE: LGPL

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License (LGPL) as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA,
or see http://www.gnu.org/copyleft/lesser.html
*/


var jg_ok, jg_ie, jg_fast, jg_dom, jg_moz;


function _chkDHTM(wnd, x, i)
// Under XUL, owner of 'document' must be specified explicitly
{
	x = wnd.document.body || null;
	jg_ie = x && typeof x.insertAdjacentHTML != "undefined" && wnd.document.createElement;
	jg_dom = (x && !jg_ie &&
		typeof x.appendChild != "undefined" &&
		typeof wnd.document.createRange != "undefined" &&
		typeof (i = wnd.document.createRange()).setStartBefore != "undefined" &&
		typeof i.createContextualFragment != "undefined");
	jg_fast = jg_ie && wnd.document.all && !wnd.opera;
	jg_moz = jg_dom && typeof x.style.MozOpacity != "undefined";
	jg_ok = !!(jg_ie || jg_dom);
}

function _pntCnvDom()
{
	var x = this.wnd.document.createRange();
	x.setStartBefore(this.cnv);
	x = x.createContextualFragment(jg_fast? this._htmRpc() : this.htm);
	if(this.cnv) this.cnv.appendChild(x);
	this.htm = "";
}

function _pntCnvIe()
{
	if(this.cnv) this.cnv.insertAdjacentHTML("BeforeEnd", jg_fast? this._htmRpc() : this.htm);
	this.htm = "";
}

function _pntDoc()
{
	this.wnd.document.write(jg_fast? this._htmRpc() : this.htm);
	this.htm = '';
}

function _pntN()
{
	;
}

function _mkDiv(x, y, w, h)
{
	this.htm += '<div style="position:absolute;'+
		'left:' + x + 'px;'+
		'top:' + y + 'px;'+
		'width:' + w + 'px;'+
		'height:' + h + 'px;'+
		'clip:rect(0,'+w+'px,'+h+'px,0);'+
		'background-color:' + this.color +
		(!jg_moz? ';overflow:hidden' : '')+
		';"><\/div>';
}

function _mkDivIe(x, y, w, h)
{
	this.htm += '%%'+this.color+';'+x+';'+y+';'+w+';'+h+';';
}

function _mkDivPrt(x, y, w, h)
{
	this.htm += '<div style="position:absolute;'+
		'border-left:' + w + 'px solid ' + this.color + ';'+
		'left:' + x + 'px;'+
		'top:' + y + 'px;'+
		'width:0px;'+
		'height:' + h + 'px;'+
		'clip:rect(0,'+w+'px,'+h+'px,0);'+
		'background-color:' + this.color +
		(!jg_moz? ';overflow:hidden' : '')+
		';"><\/div>';
}

var _regex =  /%%([^;]+);([^;]+);([^;]+);([^;]+);([^;]+);/g;
function _htmRpc()
{
	return this.htm.replace(
		_regex,
		'<div style="overflow:hidden;position:absolute;background-color:'+
		'$1;left:$2px;top:$3px;width:$4px;height:$5px"></div>\n');
}

function _htmPrtRpc()
{
	return this.htm.replace(
		_regex,
		'<div style="overflow:hidden;position:absolute;background-color:'+
		'$1;left:$2px;top:$3px;width:$4px;height:$5px;border-left:$4px solid $1"></div>\n');
}

function _mkLin(x1, y1, x2, y2)
{
	if(x1 > x2)
	{
		var _x2 = x2;
		var _y2 = y2;
		x2 = x1;
		y2 = y1;
		x1 = _x2;
		y1 = _y2;
	}
	var dx = x2-x1, dy = Math.abs(y2-y1),
	x = x1, y = y1,
	yIncr = (y1 > y2)? -1 : 1;

	if(dx >= dy)
	{
		var pr = dy<<1,
		pru = pr - (dx<<1),
		p = pr-dx,
		ox = x;
		while(dx > 0)
		{--dx;
			++x;
			if(p > 0)
			{
				this._mkDiv(ox, y, x-ox, 1);
				y += yIncr;
				p += pru;
				ox = x;
			}
			else p += pr;
		}
		this._mkDiv(ox, y, x2-ox+1, 1);
	}

	else
	{
		var pr = dx<<1,
		pru = pr - (dy<<1),
		p = pr-dy,
		oy = y;
		if(y2 <= y1)
		{
			while(dy > 0)
			{--dy;
				if(p > 0)
				{
					this._mkDiv(x++, y, 1, oy-y+1);
					y += yIncr;
					p += pru;
					oy = y;
				}
				else
				{
					y += yIncr;
					p += pr;
				}
			}
			this._mkDiv(x2, y2, 1, oy-y2+1);
		}
		else
		{
			while(dy > 0)
			{--dy;
				y += yIncr;
				if(p > 0)
				{
					this._mkDiv(x++, oy, 1, y-oy);
					p += pru;
					oy = y;
				}
				else p += pr;
			}
			this._mkDiv(x2, oy, 1, y2-oy+1);
		}
	}
}

function _mkLin2D(x1, y1, x2, y2)
{
	if(x1 > x2)
	{
		var _x2 = x2;
		var _y2 = y2;
		x2 = x1;
		y2 = y1;
		x1 = _x2;
		y1 = _y2;
	}
	var dx = x2-x1, dy = Math.abs(y2-y1),
	x = x1, y = y1,
	yIncr = (y1 > y2)? -1 : 1;

	var s = this.stroke;
	if(dx >= dy)
	{
		if(dx > 0 && s-3 > 0)
		{
			var _s = (s*dx*Math.sqrt(1+dy*dy/(dx*dx))-dx-(s>>1)*dy) / dx;
			_s = (!(s-4)? Math.ceil(_s) : Math.round(_s)) + 1;
		}
		else var _s = s;
		var ad = Math.ceil(s/2);

		var pr = dy<<1,
		pru = pr - (dx<<1),
		p = pr-dx,
		ox = x;
		while(dx > 0)
		{--dx;
			++x;
			if(p > 0)
			{
				this._mkDiv(ox, y, x-ox+ad, _s);
				y += yIncr;
				p += pru;
				ox = x;
			}
			else p += pr;
		}
		this._mkDiv(ox, y, x2-ox+ad+1, _s);
	}

	else
	{
		if(s-3 > 0)
		{
			var _s = (s*dy*Math.sqrt(1+dx*dx/(dy*dy))-(s>>1)*dx-dy) / dy;
			_s = (!(s-4)? Math.ceil(_s) : Math.round(_s)) + 1;
		}
		else var _s = s;
		var ad = Math.round(s/2);

		var pr = dx<<1,
		pru = pr - (dy<<1),
		p = pr-dy,
		oy = y;
		if(y2 <= y1)
		{
			++ad;
			while(dy > 0)
			{--dy;
				if(p > 0)
				{
					this._mkDiv(x++, y, _s, oy-y+ad);
					y += yIncr;
					p += pru;
					oy = y;
				}
				else
				{
					y += yIncr;
					p += pr;
				}
			}
			this._mkDiv(x2, y2, _s, oy-y2+ad);
		}
		else
		{
			while(dy > 0)
			{--dy;
				y += yIncr;
				if(p > 0)
				{
					this._mkDiv(x++, oy, _s, y-oy+ad);
					p += pru;
					oy = y;
				}
				else p += pr;
			}
			this._mkDiv(x2, oy, _s, y2-oy+ad+1);
		}
	}
}

function _mkLinDott(x1, y1, x2, y2)
{
	if(x1 > x2)
	{
		var _x2 = x2;
		var _y2 = y2;
		x2 = x1;
		y2 = y1;
		x1 = _x2;
		y1 = _y2;
	}
	var dx = x2-x1, dy = Math.abs(y2-y1),
	x = x1, y = y1,
	yIncr = (y1 > y2)? -1 : 1,
	drw = true;
	if(dx >= dy)
	{
		var pr = dy<<1,
		pru = pr - (dx<<1),
		p = pr-dx;
		while(dx > 0)
		{--dx;
			if(drw) this._mkDiv(x, y, 1, 1);
			drw = !drw;
			if(p > 0)
			{
				y += yIncr;
				p += pru;
			}
			else p += pr;
			++x;
		}
	}
	else
	{
		var pr = dx<<1,
		pru = pr - (dy<<1),
		p = pr-dy;
		while(dy > 0)
		{--dy;
			if(drw) this._mkDiv(x, y, 1, 1);
			drw = !drw;
			y += yIncr;
			if(p > 0)
			{
				++x;
				p += pru;
			}
			else p += pr;
		}
	}
	if(drw) this._mkDiv(x, y, 1, 1);
}

function _mkOv(left, top, width, height)
{
	var a = (++width)>>1, b = (++height)>>1,
	wod = width&1, hod = height&1,
	cx = left+a, cy = top+b,
	x = 0, y = b,
	ox = 0, oy = b,
	aa2 = (a*a)<<1, aa4 = aa2<<1, bb2 = (b*b)<<1, bb4 = bb2<<1,
	st = (aa2>>1)*(1-(b<<1)) + bb2,
	tt = (bb2>>1) - aa2*((b<<1)-1),
	w, h;
	while(y > 0)
	{
		if(st < 0)
		{
			st += bb2*((x<<1)+3);
			tt += bb4*(++x);
		}
		else if(tt < 0)
		{
			st += bb2*((x<<1)+3) - aa4*(y-1);
			tt += bb4*(++x) - aa2*(((y--)<<1)-3);
			w = x-ox;
			h = oy-y;
			if((w&2) && (h&2))
			{
				this._mkOvQds(cx, cy, x-2, y+2, 1, 1, wod, hod);
				this._mkOvQds(cx, cy, x-1, y+1, 1, 1, wod, hod);
			}
			else this._mkOvQds(cx, cy, x-1, oy, w, h, wod, hod);
			ox = x;
			oy = y;
		}
		else
		{
			tt -= aa2*((y<<1)-3);
			st -= aa4*(--y);
		}
	}
	w = a-ox+1;
	h = (oy<<1)+hod;
	y = cy-oy;
	this._mkDiv(cx-a, y, w, h);
	this._mkDiv(cx+ox+wod-1, y, w, h);
}

function _mkOv2D(left, top, width, height)
{
	var s = this.stroke;
	width += s+1;
	height += s+1;
	var a = width>>1, b = height>>1,
	wod = width&1, hod = height&1,
	cx = left+a, cy = top+b,
	x = 0, y = b,
	aa2 = (a*a)<<1, aa4 = aa2<<1, bb2 = (b*b)<<1, bb4 = bb2<<1,
	st = (aa2>>1)*(1-(b<<1)) + bb2,
	tt = (bb2>>1) - aa2*((b<<1)-1);

	if(s-4 < 0 && (!(s-2) || width-51 > 0 && height-51 > 0))
	{
		var ox = 0, oy = b,
		w, h,
		pxw;
		while(y > 0)
		{
			if(st < 0)
			{
				st += bb2*((x<<1)+3);
				tt += bb4*(++x);
			}
			else if(tt < 0)
			{
				st += bb2*((x<<1)+3) - aa4*(y-1);
				tt += bb4*(++x) - aa2*(((y--)<<1)-3);
				w = x-ox;
				h = oy-y;

				if(w-1)
				{
					pxw = w+1+(s&1);
					h = s;
				}
				else if(h-1)
				{
					pxw = s;
					h += 1+(s&1);
				}
				else pxw = h = s;
				this._mkOvQds(cx, cy, x-1, oy, pxw, h, wod, hod);
				ox = x;
				oy = y;
			}
			else
			{
				tt -= aa2*((y<<1)-3);
				st -= aa4*(--y);
			}
		}
		this._mkDiv(cx-a, cy-oy, s, (oy<<1)+hod);
		this._mkDiv(cx+a+wod-s, cy-oy, s, (oy<<1)+hod);
	}

	else
	{
		var _a = (width-(s<<1))>>1,
		_b = (height-(s<<1))>>1,
		_x = 0, _y = _b,
		_aa2 = (_a*_a)<<1, _aa4 = _aa2<<1, _bb2 = (_b*_b)<<1, _bb4 = _bb2<<1,
		_st = (_aa2>>1)*(1-(_b<<1)) + _bb2,
		_tt = (_bb2>>1) - _aa2*((_b<<1)-1),

		pxl = new Array(),
		pxt = new Array(),
		_pxb = new Array();
		pxl[0] = 0;
		pxt[0] = b;
		_pxb[0] = _b-1;
		while(y > 0)
		{
			if(st < 0)
			{
				pxl[pxl.length] = x;
				pxt[pxt.length] = y;
				st += bb2*((x<<1)+3);
				tt += bb4*(++x);
			}
			else if(tt < 0)
			{
				pxl[pxl.length] = x;
				st += bb2*((x<<1)+3) - aa4*(y-1);
				tt += bb4*(++x) - aa2*(((y--)<<1)-3);
				pxt[pxt.length] = y;
			}
			else
			{
				tt -= aa2*((y<<1)-3);
				st -= aa4*(--y);
			}

			if(_y > 0)
			{
				if(_st < 0)
				{
					_st += _bb2*((_x<<1)+3);
					_tt += _bb4*(++_x);
					_pxb[_pxb.length] = _y-1;
				}
				else if(_tt < 0)
				{
					_st += _bb2*((_x<<1)+3) - _aa4*(_y-1);
					_tt += _bb4*(++_x) - _aa2*(((_y--)<<1)-3);
					_pxb[_pxb.length] = _y-1;
				}
				else
				{
					_tt -= _aa2*((_y<<1)-3);
					_st -= _aa4*(--_y);
					_pxb[_pxb.length-1]--;
				}
			}
		}

		var ox = -wod, oy = b,
		_oy = _pxb[0],
		l = pxl.length,
		w, h;
		for(var i = 0; i < l; i++)
		{
			if(typeof _pxb[i] != "undefined")
			{
				if(_pxb[i] < _oy || pxt[i] < oy)
				{
					x = pxl[i];
					this._mkOvQds(cx, cy, x, oy, x-ox, oy-_oy, wod, hod);
					ox = x;
					oy = pxt[i];
					_oy = _pxb[i];
				}
			}
			else
			{
				x = pxl[i];
				this._mkDiv(cx-x, cy-oy, 1, (oy<<1)+hod);
				this._mkDiv(cx+ox+wod, cy-oy, 1, (oy<<1)+hod);
				ox = x;
				oy = pxt[i];
			}
		}
		this._mkDiv(cx-a, cy-oy, 1, (oy<<1)+hod);
		this._mkDiv(cx+ox+wod, cy-oy, 1, (oy<<1)+hod);
	}
}

function _mkOvDott(left, top, width, height)
{
	var a = (++width)>>1, b = (++height)>>1,
	wod = width&1, hod = height&1, hodu = hod^1,
	cx = left+a, cy = top+b,
	x = 0, y = b,
	aa2 = (a*a)<<1, aa4 = aa2<<1, bb2 = (b*b)<<1, bb4 = bb2<<1,
	st = (aa2>>1)*(1-(b<<1)) + bb2,
	tt = (bb2>>1) - aa2*((b<<1)-1),
	drw = true;
	while(y > 0)
	{
		if(st < 0)
		{
			st += bb2*((x<<1)+3);
			tt += bb4*(++x);
		}
		else if(tt < 0)
		{
			st += bb2*((x<<1)+3) - aa4*(y-1);
			tt += bb4*(++x) - aa2*(((y--)<<1)-3);
		}
		else
		{
			tt -= aa2*((y<<1)-3);
			st -= aa4*(--y);
		}
		if(drw && y >= hodu) this._mkOvQds(cx, cy, x, y, 1, 1, wod, hod);
		drw = !drw;
	}
}

function _mkRect(x, y, w, h)
{
	var s = this.stroke;
	this._mkDiv(x, y, w, s);
	this._mkDiv(x+w, y, s, h);
	this._mkDiv(x, y+h, w+s, s);
	this._mkDiv(x, y+s, s, h-s);
}

function _mkRectDott(x, y, w, h)
{
	this.drawLine(x, y, x+w, y);
	this.drawLine(x+w, y, x+w, y+h);
	this.drawLine(x, y+h, x+w, y+h);
	this.drawLine(x, y, x, y+h);
}

function jsgFont()
{
	this.PLAIN = 'font-weight:normal;';
	this.BOLD = 'font-weight:bold;';
	this.ITALIC = 'font-style:italic;';
	this.ITALIC_BOLD = this.ITALIC + this.BOLD;
	this.BOLD_ITALIC = this.ITALIC_BOLD;
}
var Font = new jsgFont();

function jsgStroke()
{
	this.DOTTED = -1;
}
var Stroke = new jsgStroke();

function JSGraphics(cnv, wnd)
{
	this.setColor = function(x)
	{
		this.color = x.toLowerCase();
	};

	this.setStroke = function(x)
	{
		this.stroke = x;
		if(!(x+1))
		{
			this.drawLine = _mkLinDott;
			this._mkOv = _mkOvDott;
			this.drawRect = _mkRectDott;
		}
		else if(x-1 > 0)
		{
			this.drawLine = _mkLin2D;
			this._mkOv = _mkOv2D;
			this.drawRect = _mkRect;
		}
		else
		{
			this.drawLine = _mkLin;
			this._mkOv = _mkOv;
			this.drawRect = _mkRect;
		}
	};

	this.setPrintable = function(arg)
	{
		this.printable = arg;
		if(jg_fast)
		{
			this._mkDiv = _mkDivIe;
			this._htmRpc = arg? _htmPrtRpc : _htmRpc;
		}
		else this._mkDiv = arg? _mkDivPrt : _mkDiv;
	};

	this.setFont = function(fam, sz, sty)
	{
		this.ftFam = fam;
		this.ftSz = sz;
		this.ftSty = sty || Font.PLAIN;
	};

	this.drawPolyline = this.drawPolyLine = function(x, y)
	{
		for (var i=x.length - 1; i;)
		{--i;
			this.drawLine(x[i], y[i], x[i+1], y[i+1]);
		}
	};

	this.fillRect = function(x, y, w, h)
	{
		this._mkDiv(x, y, w, h);
	};

	this.drawPolygon = function(x, y)
	{
		this.drawPolyline(x, y);
		this.drawLine(x[x.length-1], y[x.length-1], x[0], y[0]);
	};

	this.drawEllipse = this.drawOval = function(x, y, w, h)
	{
		this._mkOv(x, y, w, h);
	};

	this.fillEllipse = this.fillOval = function(left, top, w, h)
	{
		var a = w>>1, b = h>>1,
		wod = w&1, hod = h&1,
		cx = left+a, cy = top+b,
		x = 0, y = b, oy = b,
		aa2 = (a*a)<<1, aa4 = aa2<<1, bb2 = (b*b)<<1, bb4 = bb2<<1,
		st = (aa2>>1)*(1-(b<<1)) + bb2,
		tt = (bb2>>1) - aa2*((b<<1)-1),
		xl, dw, dh;
		if(w) while(y > 0)
		{
			if(st < 0)
			{
				st += bb2*((x<<1)+3);
				tt += bb4*(++x);
			}
			else if(tt < 0)
			{
				st += bb2*((x<<1)+3) - aa4*(y-1);
				xl = cx-x;
				dw = (x<<1)+wod;
				tt += bb4*(++x) - aa2*(((y--)<<1)-3);
				dh = oy-y;
				this._mkDiv(xl, cy-oy, dw, dh);
				this._mkDiv(xl, cy+y+hod, dw, dh);
				oy = y;
			}
			else
			{
				tt -= aa2*((y<<1)-3);
				st -= aa4*(--y);
			}
		}
		this._mkDiv(cx-a, cy-oy, w, (oy<<1)+hod);
	};

	this.fillArc = function(iL, iT, iW, iH, fAngA, fAngZ)
	{
		var a = iW>>1, b = iH>>1,
		iOdds = (iW&1) | ((iH&1) << 16),
		cx = iL+a, cy = iT+b,
		x = 0, y = b, ox = x, oy = y,
		aa2 = (a*a)<<1, aa4 = aa2<<1, bb2 = (b*b)<<1, bb4 = bb2<<1,
		st = (aa2>>1)*(1-(b<<1)) + bb2,
		tt = (bb2>>1) - aa2*((b<<1)-1),
		// Vars for radial boundary lines
		xEndA, yEndA, xEndZ, yEndZ,
		iSects = (1 << (Math.floor((fAngA %= 360.0)/180.0) << 3))
				| (2 << (Math.floor((fAngZ %= 360.0)/180.0) << 3))
				| ((fAngA >= fAngZ) << 16),
		aBndA = new Array(b+1), aBndZ = new Array(b+1);
		
		// Set up radial boundary lines
		fAngA *= Math.PI/180.0;
		fAngZ *= Math.PI/180.0;
		xEndA = cx+Math.round(a*Math.cos(fAngA));
		yEndA = cy+Math.round(-b*Math.sin(fAngA));
		_mkLinVirt(aBndA, cx, cy, xEndA, yEndA);
		xEndZ = cx+Math.round(a*Math.cos(fAngZ));
		yEndZ = cy+Math.round(-b*Math.sin(fAngZ));
		_mkLinVirt(aBndZ, cx, cy, xEndZ, yEndZ);

		while(y > 0)
		{
			if(st < 0) // Advance x
			{
				st += bb2*((x<<1)+3);
				tt += bb4*(++x);
			}
			else if(tt < 0) // Advance x and y
			{
				st += bb2*((x<<1)+3) - aa4*(y-1);
				ox = x;
				tt += bb4*(++x) - aa2*(((y--)<<1)-3);
				this._mkArcDiv(ox, y, oy, cx, cy, iOdds, aBndA, aBndZ, iSects);
				oy = y;
			}
			else // Advance y
			{
				tt -= aa2*((y<<1)-3);
				st -= aa4*(--y);
				if(y && (aBndA[y] != aBndA[y-1] || aBndZ[y] != aBndZ[y-1]))
				{
					this._mkArcDiv(x, y, oy, cx, cy, iOdds, aBndA, aBndZ, iSects);
					ox = x;
					oy = y;
				}
			}
		}
		this._mkArcDiv(x, 0, oy, cx, cy, iOdds, aBndA, aBndZ, iSects);
		if(iOdds >> 16) // Odd height
		{
			if(iSects >> 16) // Start-angle > end-angle
			{
				var xl = (yEndA <= cy || yEndZ > cy)? (cx - x) : cx;
				this._mkDiv(xl, cy, x + cx - xl + (iOdds & 0xffff), 1);
			}
			else if((iSects & 0x01) && yEndZ > cy)
				this._mkDiv(cx - x, cy, x, 1);
		}
	};

/* fillPolygon method, implemented by Matthieu Haller.
This javascript function is an adaptation of the gdImageFilledPolygon for Walter Zorn lib.
C source of GD 1.8.4 found at http://www.boutell.com/gd/

THANKS to Kirsten Schulz for the polygon fixes!

The intersection finding technique of this code could be improved
by remembering the previous intertersection, and by using the slope.
That could help to adjust intersections to produce a nice
interior_extrema. */
	this.fillPolygon = function(array_x, array_y)
	{
		var i;
		var y;
		var miny, maxy;
		var x1, y1;
		var x2, y2;
		var ind1, ind2;
		var ints;

		var n = array_x.length;
		if(!n) return;

		miny = array_y[0];
		maxy = array_y[0];
		for(i = 1; i < n; i++)
		{
			if(array_y[i] < miny)
				miny = array_y[i];

			if(array_y[i] > maxy)
				maxy = array_y[i];
		}
		for(y = miny; y <= maxy; y++)
		{
			var polyInts = new Array();
			ints = 0;
			for(i = 0; i < n; i++)
			{
				if(!i)
				{
					ind1 = n-1;
					ind2 = 0;
				}
				else
				{
					ind1 = i-1;
					ind2 = i;
				}
				y1 = array_y[ind1];
				y2 = array_y[ind2];
				if(y1 < y2)
				{
					x1 = array_x[ind1];
					x2 = array_x[ind2];
				}
				else if(y1 > y2)
				{
					y2 = array_y[ind1];
					y1 = array_y[ind2];
					x2 = array_x[ind1];
					x1 = array_x[ind2];
				}
				else continue;

				 //  Modified 11. 2. 2004 Walter Zorn
				if((y >= y1) && (y < y2))
					polyInts[ints++] = Math.round((y-y1) * (x2-x1) / (y2-y1) + x1);

				else if((y == maxy) && (y > y1) && (y <= y2))
					polyInts[ints++] = Math.round((y-y1) * (x2-x1) / (y2-y1) + x1);
			}
			polyInts.sort(_CompInt);
			for(i = 0; i < ints; i+=2)
				this._mkDiv(polyInts[i], y, polyInts[i+1]-polyInts[i]+1, 1);
		}
	};

	this.drawString = function(txt, x, y)
	{
		this.htm += '<div style="position:absolute;white-space:nowrap;'+
			'left:' + x + 'px;'+
			'top:' + y + 'px;'+
			'font-family:' +  this.ftFam + ';'+
			'font-size:' + this.ftSz + ';'+
			'color:' + this.color + ';' + this.ftSty + '">'+
			txt +
			'<\/div>';
	};

/* drawStringRect() added by Rick Blommers.
Allows to specify the size of the text rectangle and to align the
text both horizontally (e.g. right) and vertically within that rectangle */
	this.drawStringRect = function(txt, x, y, width, halign)
	{
		this.htm += '<div style="position:absolute;overflow:hidden;'+
			'left:' + x + 'px;'+
			'top:' + y + 'px;'+
			'width:'+width +'px;'+
			'text-align:'+halign+';'+
			'font-family:' +  this.ftFam + ';'+
			'font-size:' + this.ftSz + ';'+
			'color:' + this.color + ';' + this.ftSty + '">'+
			txt +
			'<\/div>';
	};

	this.drawImage = function(imgSrc, x, y, w, h, a)
	{
		this.htm += '<div style="position:absolute;'+
			'left:' + x + 'px;'+
			'top:' + y + 'px;'+
			// w (width) and h (height) arguments are now optional.
			// Added by Mahmut Keygubatli, 14.1.2008
			(w? ('width:' +  w + 'px;') : '') +
			(h? ('height:' + h + 'px;'):'')+'">'+
			'<img src="' + imgSrc +'"'+ (w ? (' width="' + w + '"'):'')+ (h ? (' height="' + h + '"'):'') + (a? (' '+a) : '') + '>'+
			'<\/div>';
	};

	this.clear = function()
	{
		this.htm = "";
		if(this.cnv) this.cnv.innerHTML = "";
	};

	this._mkOvQds = function(cx, cy, x, y, w, h, wod, hod)
	{
		var xl = cx - x, xr = cx + x + wod - w, yt = cy - y, yb = cy + y + hod - h;
		if(xr > xl+w)
		{
			this._mkDiv(xr, yt, w, h);
			this._mkDiv(xr, yb, w, h);
		}
		else
			w = xr - xl + w;
		this._mkDiv(xl, yt, w, h);
		this._mkDiv(xl, yb, w, h);
	};
	
	this._mkArcDiv = function(x, y, oy, cx, cy, iOdds, aBndA, aBndZ, iSects)
	{
		var xrDef = cx + x + (iOdds & 0xffff), y2, h = oy - y, xl, xr, w;

		if(!h) h = 1;
		x = cx - x;

		if(iSects & 0xff0000) // Start-angle > end-angle
		{
			y2 = cy - y - h;
			if(iSects & 0x00ff)
			{
				if(iSects & 0x02)
				{
					xl = Math.max(x, aBndZ[y]);
					w = xrDef - xl;
					if(w > 0) this._mkDiv(xl, y2, w, h);
				}
				if(iSects & 0x01)
				{
					xr = Math.min(xrDef, aBndA[y]);
					w = xr - x;
					if(w > 0) this._mkDiv(x, y2, w, h);
				}
			}
			else
				this._mkDiv(x, y2, xrDef - x, h);
			y2 = cy + y + (iOdds >> 16);
			if(iSects & 0xff00)
			{
				if(iSects & 0x0100)
				{
					xl = Math.max(x, aBndA[y]);
					w = xrDef - xl;
					if(w > 0) this._mkDiv(xl, y2, w, h);
				}
				if(iSects & 0x0200)
				{
					xr = Math.min(xrDef, aBndZ[y]);
					w = xr - x;
					if(w > 0) this._mkDiv(x, y2, w, h);
				}
			}
			else
				this._mkDiv(x, y2, xrDef - x, h);
		}
		else
		{
			if(iSects & 0x00ff)
			{
				if(iSects & 0x02)
					xl = Math.max(x, aBndZ[y]);
				else
					xl = x;
				if(iSects & 0x01)
					xr = Math.min(xrDef, aBndA[y]);
				else
					xr = xrDef;
				y2 = cy - y - h;
				w = xr - xl;
				if(w > 0) this._mkDiv(xl, y2, w, h);
			}
			if(iSects & 0xff00)
			{
				if(iSects & 0x0100)
					xl = Math.max(x, aBndA[y]);
				else
					xl = x;
				if(iSects & 0x0200)
					xr = Math.min(xrDef, aBndZ[y]);
				else
					xr = xrDef;
				y2 = cy + y + (iOdds >> 16);
				w = xr - xl;
				if(w > 0) this._mkDiv(xl, y2, w, h);
			}
		}
	};

	this.setStroke(1);
	this.setFont("verdana,geneva,helvetica,sans-serif", "12px", Font.PLAIN);
	this.color = "#000000";
	this.htm = "";
	this.wnd = wnd || window;

	if(!jg_ok) _chkDHTM(this.wnd);
    if(jg_ok)
	{
		if(cnv)
		{
			if(typeof(cnv) == "string")
				this.cont = document.all? (this.wnd.document.all[cnv] || null)
					: document.getElementById? (this.wnd.document.getElementById(cnv) || null)
					: null;
			else if(cnv == window.document)
				this.cont = document.getElementsByTagName("body")[0];
			// If cnv is a direct reference to a canvas DOM node
			// (option suggested by Andreas Luleich)
			else this.cont = cnv;
			// Create new canvas inside container DIV. Thus the drawing and clearing
			// methods won't interfere with the container's inner html.
			// Solution suggested by Vladimir.
			this.cnv = this.wnd.document.createElement("div");
			this.cnv.style.fontSize=0;
			this.cont.appendChild(this.cnv);
			this.paint = jg_dom? _pntCnvDom : _pntCnvIe;
		}
		else
			this.paint = _pntDoc;
	}
	else
		this.paint = _pntN;

	this.setPrintable(false);
}

function _mkLinVirt(aLin, x1, y1, x2, y2)
{
	var dx = Math.abs(x2-x1), dy = Math.abs(y2-y1),
	x = x1, y = y1,
	xIncr = (x1 > x2)? -1 : 1,
	yIncr = (y1 > y2)? -1 : 1,
	p,
	i = 0;
	if(dx >= dy)
	{
		var pr = dy<<1,
		pru = pr - (dx<<1);
		p = pr-dx;
		while(dx > 0)
		{--dx;
			if(p > 0)    //  Increment y
			{
				aLin[i++] = x;
				y += yIncr;
				p += pru;
			}
			else p += pr;
			x += xIncr;
		}
	}
	else
	{
		var pr = dx<<1,
		pru = pr - (dy<<1);
		p = pr-dy;
		while(dy > 0)
		{--dy;
			y += yIncr;
			aLin[i++] = x;
			if(p > 0)    //  Increment x
			{
				x += xIncr;
				p += pru;
			}
			else p += pr;
		}
	}
	for(var len = aLin.length, i = len-i; i;)
		aLin[len-(i--)] = x;
};

function _CompInt(x, y)
{
	return(x - y);
}


/*! jQuery v1.10.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license*/
(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.2",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=st(),k=st(),E=st(),S=!1,A=function(e,t){return e===t?(S=!0,0):0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=mt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+yt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,n,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function lt(e){return e[b]=!0,e}function ut(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t){var n=e.split("|"),r=e.length;while(r--)o.attrHandle[n[r]]=t}function pt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function dt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return lt(function(t){return t=+t,lt(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.defaultView;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.attachEvent&&i!==i.top&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),r.getElementsByTagName=ut(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ut(function(e){return e.innerHTML="<div class='a'></div><div class='a i'></div>",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ut(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=K.test(n.querySelectorAll))&&(ut(function(e){e.innerHTML="<select><option selected=''></option></select>",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ut(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=K.test(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ut(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=K.test(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return pt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?pt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:lt,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=mt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?lt(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:lt(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?lt(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:lt(function(e){return function(t){return at(e,t).length>0}}),contains:lt(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:lt(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},o.pseudos.nth=o.pseudos.eq;for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=ft(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=dt(n);function gt(){}gt.prototype=o.filters=o.pseudos,o.setFilters=new gt;function mt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function yt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function vt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function bt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function wt(e,t,n,r,i,o){return r&&!r[b]&&(r=wt(r)),i&&!i[b]&&(i=wt(i,o)),lt(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||Nt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:xt(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=xt(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=xt(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function Tt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=vt(function(e){return e===t},s,!0),p=vt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[vt(bt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return wt(l>1&&bt(f),l>1&&yt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&Tt(e.slice(l,r)),i>r&&Tt(e=e.slice(r)),i>r&&yt(e))}f.push(n)}return bt(f)}function Ct(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=xt(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?lt(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=mt(e)),n=t.length;while(n--)o=Tt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Ct(i,r))}return o};function Nt(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function kt(e,t,n,i){var a,s,u,c,p,f=mt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&yt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}r.sortStable=b.split("").sort(A).join("")===b,r.detectDuplicates=S,p(),r.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(f.createElement("div"))}),ut(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||ct("type|href|height|width",function(e,n,r){return r?t:e.getAttribute(n,"type"===n.toLowerCase()?1:2)}),r.attributes&&ut(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||ct("value",function(e,n,r){return r||"input"!==e.nodeName.toLowerCase()?t:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||ct(B,function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&i.specified?i.value:e[n]===!0?n.toLowerCase():null}),x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!l||i&&!u||(t=t||[],t=[e,t.slice?t.slice():t],n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav></:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="<table><tr><td></td><td>t</td></tr></table>",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="<div></div>",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t
}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,r=0,o=x(this),a=e.match(T)||[];while(t=a[r++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/<tbody/i,wt=/<|&#?\w+;/,Tt=/<(?:script|style|link)/i,Ct=/^(?:checkbox|radio)$/i,Nt=/checked\s*(?:[^=]|=\s*.checked.)/i,kt=/^$|\/(?:java|ecma)script/i,Et=/^true\/(.*)/,St=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,At={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:x.support.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1></$2>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1></$2>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?"<table>"!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle);
u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){nn(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("<iframe frameborder='0' width='0' height='0'/>").css("cssText","display:block !important")).appendTo(t.documentElement),t=(Pt[0].contentWindow||Pt[0].contentDocument).document,t.write("<!doctype html><html><body>"),t.close(),n=un(e,t),Pt.detach()),Gt[e]=n),n}function un(e,t){var n=x(t.createElement(e)).appendTo(t.body),r=x.css(n[0],"display");return n.remove(),r}x.each(["height","width"],function(e,n){x.cssHooks[n]={get:function(e,r,i){return r?0===e.offsetWidth&&Xt.test(x.css(e,"display"))?x.swap(e,Qt,function(){return sn(e,n,i)}):sn(e,n,i):t},set:function(e,t,r){var i=r&&Rt(e);return on(e,t,r?an(e,n,r,x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,i),i):0)}}}),x.support.opacity||(x.cssHooks.opacity={get:function(e,t){return It.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=x.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===x.trim(o.replace($t,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=$t.test(o)?o.replace($t,i):o+" "+i)}}),x(function(){x.support.reliableMarginRight||(x.cssHooks.marginRight={get:function(e,n){return n?x.swap(e,{display:"inline-block"},Wt,[e,"marginRight"]):t}}),!x.support.pixelPosition&&x.fn.position&&x.each(["top","left"],function(e,n){x.cssHooks[n]={get:function(e,r){return r?(r=Wt(e,n),Yt.test(r)?x(e).position()[n]+"px":r):t}}})}),x.expr&&x.expr.filters&&(x.expr.filters.hidden=function(e){return 0>=e.offsetWidth&&0>=e.offsetHeight||!x.support.reliableHiddenOffsets&&"none"===(e.style&&e.style.display||x.css(e,"display"))},x.expr.filters.visible=function(e){return!x.expr.filters.hidden(e)}),x.each({margin:"",padding:"",border:"Width"},function(e,t){x.cssHooks[e+t]={expand:function(n){var r=0,i={},o="string"==typeof n?n.split(" "):[n];for(;4>r;r++)i[e+Zt[r]+t]=o[r]||o[r-2]||o[0];return i}},Ut.test(e)||(x.cssHooks[e+t].set=on)});var cn=/%20/g,pn=/\[\]$/,fn=/\r?\n/g,dn=/^(?:submit|button|image|reset|file)$/i,hn=/^(?:input|select|textarea|keygen)/i;x.fn.extend({serialize:function(){return x.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=x.prop(this,"elements");return e?x.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!x(this).is(":disabled")&&hn.test(this.nodeName)&&!dn.test(e)&&(this.checked||!Ct.test(e))}).map(function(e,t){var n=x(this).val();return null==n?null:x.isArray(n)?x.map(n,function(e){return{name:t.name,value:e.replace(fn,"\r\n")}}):{name:t.name,value:n.replace(fn,"\r\n")}}).get()}}),x.param=function(e,n){var r,i=[],o=function(e,t){t=x.isFunction(t)?t():null==t?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(n===t&&(n=x.ajaxSettings&&x.ajaxSettings.traditional),x.isArray(e)||e.jquery&&!x.isPlainObject(e))x.each(e,function(){o(this.name,this.value)});else for(r in e)gn(r,e[r],n,o);return i.join("&").replace(cn,"+")};function gn(e,t,n,r){var i;if(x.isArray(t))x.each(t,function(t,i){n||pn.test(e)?r(e,i):gn(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==x.type(t))r(e,t);else for(i in t)gn(e+"["+i+"]",t[i],n,r)}x.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){x.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),x.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}});var mn,yn,vn=x.now(),bn=/\?/,xn=/#.*$/,wn=/([?&])_=[^&]*/,Tn=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Cn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Nn=/^(?:GET|HEAD)$/,kn=/^\/\//,En=/^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,Sn=x.fn.load,An={},jn={},Dn="*/".concat("*");try{yn=o.href}catch(Ln){yn=a.createElement("a"),yn.href="",yn=yn.href}mn=En.exec(yn.toLowerCase())||[];function Hn(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(T)||[];if(x.isFunction(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function qn(e,n,r,i){var o={},a=e===jn;function s(l){var u;return o[l]=!0,x.each(e[l]||[],function(e,l){var c=l(n,r,i);return"string"!=typeof c||a||o[c]?a?!(u=c):t:(n.dataTypes.unshift(c),s(c),!1)}),u}return s(n.dataTypes[0])||!o["*"]&&s("*")}function _n(e,n){var r,i,o=x.ajaxSettings.flatOptions||{};for(i in n)n[i]!==t&&((o[i]?e:r||(r={}))[i]=n[i]);return r&&x.extend(!0,e,r),e}x.fn.load=function(e,n,r){if("string"!=typeof e&&Sn)return Sn.apply(this,arguments);var i,o,a,s=this,l=e.indexOf(" ");return l>=0&&(i=e.slice(l,e.length),e=e.slice(0,l)),x.isFunction(n)?(r=n,n=t):n&&"object"==typeof n&&(a="POST"),s.length>0&&x.ajax({url:e,type:a,dataType:"html",data:n}).done(function(e){o=arguments,s.html(i?x("<div>").append(x.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},x.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){x.fn[t]=function(e){return this.on(t,e)}}),x.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:yn,type:"GET",isLocal:Cn.test(mn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Dn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":x.parseJSON,"text xml":x.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?_n(_n(e,x.ajaxSettings),t):_n(x.ajaxSettings,e)},ajaxPrefilter:Hn(An),ajaxTransport:Hn(jn),ajax:function(e,n){"object"==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,l,u,c,p=x.ajaxSetup({},n),f=p.context||p,d=p.context&&(f.nodeType||f.jquery)?x(f):x.event,h=x.Deferred(),g=x.Callbacks("once memory"),m=p.statusCode||{},y={},v={},b=0,w="canceled",C={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!c){c={};while(t=Tn.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return b||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>b)for(t in e)m[t]=[m[t],e[t]];else C.always(e[C.status]);return this},abort:function(e){var t=e||w;return u&&u.abort(t),k(0,t),this}};if(h.promise(C).complete=g.add,C.success=C.done,C.error=C.fail,p.url=((e||p.url||yn)+"").replace(xn,"").replace(kn,mn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=x.trim(p.dataType||"*").toLowerCase().match(T)||[""],null==p.crossDomain&&(r=En.exec(p.url.toLowerCase()),p.crossDomain=!(!r||r[1]===mn[1]&&r[2]===mn[2]&&(r[3]||("http:"===r[1]?"80":"443"))===(mn[3]||("http:"===mn[1]?"80":"443")))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=x.param(p.data,p.traditional)),qn(An,p,n,C),2===b)return C;l=p.global,l&&0===x.active++&&x.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!Nn.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(bn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=wn.test(o)?o.replace(wn,"$1_="+vn++):o+(bn.test(o)?"&":"?")+"_="+vn++)),p.ifModified&&(x.lastModified[o]&&C.setRequestHeader("If-Modified-Since",x.lastModified[o]),x.etag[o]&&C.setRequestHeader("If-None-Match",x.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&C.setRequestHeader("Content-Type",p.contentType),C.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+Dn+"; q=0.01":""):p.accepts["*"]);for(i in p.headers)C.setRequestHeader(i,p.headers[i]);if(p.beforeSend&&(p.beforeSend.call(f,C,p)===!1||2===b))return C.abort();w="abort";for(i in{success:1,error:1,complete:1})C[i](p[i]);if(u=qn(jn,p,n,C)){C.readyState=1,l&&d.trigger("ajaxSend",[C,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){C.abort("timeout")},p.timeout));try{b=1,u.send(y,k)}catch(N){if(!(2>b))throw N;k(-1,N)}}else k(-1,"No Transport");function k(e,n,r,i){var c,y,v,w,T,N=n;2!==b&&(b=2,s&&clearTimeout(s),u=t,a=i||"",C.readyState=e>0?4:0,c=e>=200&&300>e||304===e,r&&(w=Mn(p,C,r)),w=On(p,w,C,c),c?(p.ifModified&&(T=C.getResponseHeader("Last-Modified"),T&&(x.lastModified[o]=T),T=C.getResponseHeader("etag"),T&&(x.etag[o]=T)),204===e||"HEAD"===p.type?N="nocontent":304===e?N="notmodified":(N=w.state,y=w.data,v=w.error,c=!v)):(v=N,(e||!N)&&(N="error",0>e&&(e=0))),C.status=e,C.statusText=(n||N)+"",c?h.resolveWith(f,[y,N,C]):h.rejectWith(f,[C,N,v]),C.statusCode(m),m=t,l&&d.trigger(c?"ajaxSuccess":"ajaxError",[C,p,c?y:v]),g.fireWith(f,[C,N]),l&&(d.trigger("ajaxComplete",[C,p]),--x.active||x.event.trigger("ajaxStop")))}return C},getJSON:function(e,t,n){return x.get(e,t,n,"json")},getScript:function(e,n){return x.get(e,t,n,"script")}}),x.each(["get","post"],function(e,n){x[n]=function(e,r,i,o){return x.isFunction(r)&&(o=o||i,i=r,r=t),x.ajax({url:e,type:n,dataType:o,data:r,success:i})}});function Mn(e,n,r){var i,o,a,s,l=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),o===t&&(o=e.mimeType||n.getResponseHeader("Content-Type"));if(o)for(s in l)if(l[s]&&l[s].test(o)){u.unshift(s);break}if(u[0]in r)a=u[0];else{for(s in r){if(!u[0]||e.converters[s+" "+u[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==u[0]&&u.unshift(a),r[a]):t}function On(e,t,n,r){var i,o,a,s,l,u={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)u[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!l&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),l=o,o=c.shift())if("*"===o)o=l;else if("*"!==l&&l!==o){if(a=u[l+" "+o]||u["* "+o],!a)for(i in u)if(s=i.split(" "),s[1]===o&&(a=u[l+" "+s[0]]||u["* "+s[0]])){a===!0?a=u[i]:u[i]!==!0&&(o=s[0],c.unshift(s[1]));break}if(a!==!0)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(p){return{state:"parsererror",error:a?p:"No conversion from "+l+" to "+o}}}return{state:"success",data:t}}x.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return x.globalEval(e),e}}}),x.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),x.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=a.head||x("head")[0]||a.documentElement;return{send:function(t,i){n=a.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var Fn=[],Bn=/(=)\?(?=&|$)|\?\?/;x.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Fn.pop()||x.expando+"_"+vn++;return this[e]=!0,e}}),x.ajaxPrefilter("json jsonp",function(n,r,i){var o,a,s,l=n.jsonp!==!1&&(Bn.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Bn.test(n.data)&&"data");return l||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=x.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,l?n[l]=n[l].replace(Bn,"$1"+o):n.jsonp!==!1&&(n.url+=(bn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||x.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,Fn.push(o)),s&&x.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Pn,Rn,Wn=0,$n=e.ActiveXObject&&function(){var e;for(e in Pn)Pn[e](t,!0)};function In(){try{return new e.XMLHttpRequest}catch(t){}}function zn(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}x.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&In()||zn()}:In,Rn=x.ajaxSettings.xhr(),x.support.cors=!!Rn&&"withCredentials"in Rn,Rn=x.support.ajax=!!Rn,Rn&&x.ajaxTransport(function(n){if(!n.crossDomain||x.support.cors){var r;return{send:function(i,o){var a,s,l=n.xhr();if(n.username?l.open(n.type,n.url,n.async,n.username,n.password):l.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)l[s]=n.xhrFields[s];n.mimeType&&l.overrideMimeType&&l.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)l.setRequestHeader(s,i[s])}catch(u){}l.send(n.hasContent&&n.data||null),r=function(e,i){var s,u,c,p;try{if(r&&(i||4===l.readyState))if(r=t,a&&(l.onreadystatechange=x.noop,$n&&delete Pn[a]),i)4!==l.readyState&&l.abort();else{p={},s=l.status,u=l.getAllResponseHeaders(),"string"==typeof l.responseText&&(p.text=l.responseText);try{c=l.statusText}catch(f){c=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(d){i||o(-1,d)}p&&o(s,c,p,u)},n.async?4===l.readyState?setTimeout(r):(a=++Wn,$n&&(Pn||(Pn={},x(e).unload($n)),Pn[a]=r),l.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Xn,Un,Vn=/^(?:toggle|show|hide)$/,Yn=RegExp("^(?:([+-])=|)("+w+")([a-z%]*)$","i"),Jn=/queueHooks$/,Gn=[nr],Qn={"*":[function(e,t){var n=this.createTween(e,t),r=n.cur(),i=Yn.exec(t),o=i&&i[3]||(x.cssNumber[e]?"":"px"),a=(x.cssNumber[e]||"px"!==o&&+r)&&Yn.exec(x.css(n.elem,e)),s=1,l=20;if(a&&a[3]!==o){o=o||a[3],i=i||[],a=+r||1;do s=s||".5",a/=s,x.style(n.elem,e,a+o);while(s!==(s=n.cur()/r)&&1!==s&&--l)}return i&&(a=n.start=+a||+r||0,n.unit=o,n.end=i[1]?a+(i[1]+1)*i[2]:+i[2]),n}]};function Kn(){return setTimeout(function(){Xn=t}),Xn=x.now()}function Zn(e,t,n){var r,i=(Qn[t]||[]).concat(Qn["*"]),o=0,a=i.length;for(;a>o;o++)if(r=i[o].call(n,t,e))return r}function er(e,t,n){var r,i,o=0,a=Gn.length,s=x.Deferred().always(function(){delete l.elem}),l=function(){if(i)return!1;var t=Xn||Kn(),n=Math.max(0,u.startTime+u.duration-t),r=n/u.duration||0,o=1-r,a=0,l=u.tweens.length;for(;l>a;a++)u.tweens[a].run(o);return s.notifyWith(e,[u,o,n]),1>o&&l?n:(s.resolveWith(e,[u]),!1)},u=s.promise({elem:e,props:x.extend({},t),opts:x.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Xn||Kn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=x.Tween(e,u.opts,t,n,u.opts.specialEasing[t]||u.opts.easing);return u.tweens.push(r),r},stop:function(t){var n=0,r=t?u.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)u.tweens[n].run(1);return t?s.resolveWith(e,[u,t]):s.rejectWith(e,[u,t]),this}}),c=u.props;for(tr(c,u.opts.specialEasing);a>o;o++)if(r=Gn[o].call(u,e,c,u.opts))return r;return x.map(c,Zn,u),x.isFunction(u.opts.start)&&u.opts.start.call(e,u),x.fx.timer(x.extend(l,{elem:e,anim:u,queue:u.opts.queue})),u.progress(u.opts.progress).done(u.opts.done,u.opts.complete).fail(u.opts.fail).always(u.opts.always)}function tr(e,t){var n,r,i,o,a;for(n in e)if(r=x.camelCase(n),i=t[r],o=e[n],x.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),a=x.cssHooks[r],a&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}x.Animation=x.extend(er,{tweener:function(e,t){x.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Qn[n]=Qn[n]||[],Qn[n].unshift(t)},prefilter:function(e,t){t?Gn.unshift(e):Gn.push(e)}});function nr(e,t,n){var r,i,o,a,s,l,u=this,c={},p=e.style,f=e.nodeType&&nn(e),d=x._data(e,"fxshow");n.queue||(s=x._queueHooks(e,"fx"),null==s.unqueued&&(s.unqueued=0,l=s.empty.fire,s.empty.fire=function(){s.unqueued||l()}),s.unqueued++,u.always(function(){u.always(function(){s.unqueued--,x.queue(e,"fx").length||s.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[p.overflow,p.overflowX,p.overflowY],"inline"===x.css(e,"display")&&"none"===x.css(e,"float")&&(x.support.inlineBlockNeedsLayout&&"inline"!==ln(e.nodeName)?p.zoom=1:p.display="inline-block")),n.overflow&&(p.overflow="hidden",x.support.shrinkWrapBlocks||u.always(function(){p.overflow=n.overflow[0],p.overflowX=n.overflow[1],p.overflowY=n.overflow[2]}));for(r in t)if(i=t[r],Vn.exec(i)){if(delete t[r],o=o||"toggle"===i,i===(f?"hide":"show"))continue;c[r]=d&&d[r]||x.style(e,r)}if(!x.isEmptyObject(c)){d?"hidden"in d&&(f=d.hidden):d=x._data(e,"fxshow",{}),o&&(d.hidden=!f),f?x(e).show():u.done(function(){x(e).hide()}),u.done(function(){var t;x._removeData(e,"fxshow");for(t in c)x.style(e,t,c[t])});for(r in c)a=Zn(f?d[r]:0,r,u),r in d||(d[r]=a.start,f&&(a.end=a.start,a.start="width"===r||"height"===r?1:0))}}function rr(e,t,n,r,i){return new rr.prototype.init(e,t,n,r,i)}x.Tween=rr,rr.prototype={constructor:rr,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(x.cssNumber[n]?"":"px")},cur:function(){var e=rr.propHooks[this.prop];return e&&e.get?e.get(this):rr.propHooks._default.get(this)},run:function(e){var t,n=rr.propHooks[this.prop];return this.pos=t=this.options.duration?x.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):rr.propHooks._default.set(this),this}},rr.prototype.init.prototype=rr.prototype,rr.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=x.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){x.fx.step[e.prop]?x.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[x.cssProps[e.prop]]||x.cssHooks[e.prop])?x.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},rr.propHooks.scrollTop=rr.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},x.each(["toggle","show","hide"],function(e,t){var n=x.fn[t];x.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(ir(t,!0),e,r,i)}}),x.fn.extend({fadeTo:function(e,t,n,r){return this.filter(nn).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=x.isEmptyObject(e),o=x.speed(t,n,r),a=function(){var t=er(this,x.extend({},e),o);(i||x._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=x.timers,a=x._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&Jn.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&x.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=x._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=x.timers,a=r?r.length:0;for(n.finish=!0,x.queue(this,e,[]),i&&i.stop&&i.stop.call(this,!0),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function ir(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=Zt[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}x.each({slideDown:ir("show"),slideUp:ir("hide"),slideToggle:ir("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){x.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),x.speed=function(e,t,n){var r=e&&"object"==typeof e?x.extend({},e):{complete:n||!n&&t||x.isFunction(e)&&e,duration:e,easing:n&&t||t&&!x.isFunction(t)&&t};return r.duration=x.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in x.fx.speeds?x.fx.speeds[r.duration]:x.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){x.isFunction(r.old)&&r.old.call(this),r.queue&&x.dequeue(this,r.queue)},r},x.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},x.timers=[],x.fx=rr.prototype.init,x.fx.tick=function(){var e,n=x.timers,r=0;for(Xn=x.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||x.fx.stop(),Xn=t},x.fx.timer=function(e){e()&&x.timers.push(e)&&x.fx.start()},x.fx.interval=13,x.fx.start=function(){Un||(Un=setInterval(x.fx.tick,x.fx.interval))},x.fx.stop=function(){clearInterval(Un),Un=null},x.fx.speeds={slow:600,fast:200,_default:400},x.fx.step={},x.expr&&x.expr.filters&&(x.expr.filters.animated=function(e){return x.grep(x.timers,function(t){return e===t.elem}).length}),x.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){x.offset.setOffset(this,e,t)});var n,r,o={top:0,left:0},a=this[0],s=a&&a.ownerDocument;if(s)return n=s.documentElement,x.contains(n,a)?(typeof a.getBoundingClientRect!==i&&(o=a.getBoundingClientRect()),r=or(s),{top:o.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:o.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):o},x.offset={setOffset:function(e,t,n){var r=x.css(e,"position");"static"===r&&(e.style.position="relative");var i=x(e),o=i.offset(),a=x.css(e,"top"),s=x.css(e,"left"),l=("absolute"===r||"fixed"===r)&&x.inArray("auto",[a,s])>-1,u={},c={},p,f;l?(c=i.position(),p=c.top,f=c.left):(p=parseFloat(a)||0,f=parseFloat(s)||0),x.isFunction(t)&&(t=t.call(e,n,o)),null!=t.top&&(u.top=t.top-o.top+p),null!=t.left&&(u.left=t.left-o.left+f),"using"in t?t.using.call(e,u):i.css(u)}},x.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===x.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),x.nodeName(e[0],"html")||(n=e.offset()),n.top+=x.css(e[0],"borderTopWidth",!0),n.left+=x.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-x.css(r,"marginTop",!0),left:t.left-n.left-x.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||s;while(e&&!x.nodeName(e,"html")&&"static"===x.css(e,"position"))e=e.offsetParent;return e||s})}}),x.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);x.fn[e]=function(i){return x.access(this,function(e,i,o){var a=or(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?x(a).scrollLeft():o,r?o:x(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}});function or(e){return x.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}x.each({Height:"height",Width:"width"},function(e,n){x.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){x.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return x.access(this,function(n,r,i){var o;return x.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?x.css(n,r,s):x.style(n,r,i,s)},n,a?i:t,a,null)}})}),x.fn.size=function(){return this.length},x.fn.andSelf=x.fn.addBack,"object"==typeof module&&module&&"object"==typeof module.exports?module.exports=x:(e.jQuery=e.$=x,"function"==typeof define&&define.amd&&define("jquery",[],function(){return x}))})(window);

//     Underscore.js 1.5.2
//     http://underscorejs.org
//     (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
//     Underscore may be freely distributed under the MIT license.
(function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,g=e.filter,d=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,w=Object.keys,_=i.bind,j=function(n){return n instanceof j?n:this instanceof j?(this._wrapped=n,void 0):new j(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=j),exports._=j):n._=j,j.VERSION="1.5.2";var A=j.each=j.forEach=function(n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a=j.keys(n),u=0,i=a.length;i>u;u++)if(t.call(e,n[a[u]],a[u],n)===r)return};j.map=j.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e.push(t.call(r,n,u,i))}),e)};var E="Reduce of empty array with no initial value";j.reduce=j.foldl=j.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=j.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(E);return r},j.reduceRight=j.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=j.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=j.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(E);return r},j.find=j.detect=function(n,t,r){var e;return O(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},j.filter=j.select=function(n,t,r){var e=[];return null==n?e:g&&n.filter===g?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&e.push(n)}),e)},j.reject=function(n,t,r){return j.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},j.every=j.all=function(n,t,e){t||(t=j.identity);var u=!0;return null==n?u:d&&n.every===d?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var O=j.some=j.any=function(n,t,e){t||(t=j.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};j.contains=j.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:O(n,function(n){return n===t})},j.invoke=function(n,t){var r=o.call(arguments,2),e=j.isFunction(t);return j.map(n,function(n){return(e?t:n[t]).apply(n,r)})},j.pluck=function(n,t){return j.map(n,function(n){return n[t]})},j.where=function(n,t,r){return j.isEmpty(t)?r?void 0:[]:j[r?"find":"filter"](n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},j.findWhere=function(n,t){return j.where(n,t,!0)},j.max=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.max.apply(Math,n);if(!t&&j.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>e.computed&&(e={value:n,computed:a})}),e.value},j.min=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.min.apply(Math,n);if(!t&&j.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a<e.computed&&(e={value:n,computed:a})}),e.value},j.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=j.random(r++),e[r-1]=e[t],e[t]=n}),e},j.sample=function(n,t,r){return arguments.length<2||r?n[j.random(n.length-1)]:j.shuffle(n).slice(0,Math.max(0,t))};var k=function(n){return j.isFunction(n)?n:function(t){return t[n]}};j.sortBy=function(n,t,r){var e=k(t);return j.pluck(j.map(n,function(n,t,u){return{value:n,index:t,criteria:e.call(r,n,t,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var F=function(n){return function(t,r,e){var u={},i=null==r?j.identity:k(r);return A(t,function(r,a){var o=i.call(e,r,a,t);n(u,o,r)}),u}};j.groupBy=F(function(n,t,r){(j.has(n,t)?n[t]:n[t]=[]).push(r)}),j.indexBy=F(function(n,t,r){n[t]=r}),j.countBy=F(function(n,t){j.has(n,t)?n[t]++:n[t]=1}),j.sortedIndex=function(n,t,r,e){r=null==r?j.identity:k(r);for(var u=r.call(e,t),i=0,a=n.length;a>i;){var o=i+a>>>1;r.call(e,n[o])<u?i=o+1:a=o}return i},j.toArray=function(n){return n?j.isArray(n)?o.call(n):n.length===+n.length?j.map(n,j.identity):j.values(n):[]},j.size=function(n){return null==n?0:n.length===+n.length?n.length:j.keys(n).length},j.first=j.head=j.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:o.call(n,0,t)},j.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},j.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},j.rest=j.tail=j.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},j.compact=function(n){return j.filter(n,j.identity)};var M=function(n,t,r){return t&&j.every(n,j.isArray)?c.apply(r,n):(A(n,function(n){j.isArray(n)||j.isArguments(n)?t?a.apply(r,n):M(n,t,r):r.push(n)}),r)};j.flatten=function(n,t){return M(n,t,[])},j.without=function(n){return j.difference(n,o.call(arguments,1))},j.uniq=j.unique=function(n,t,r,e){j.isFunction(t)&&(e=r,r=t,t=!1);var u=r?j.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:j.contains(a,r))||(a.push(r),i.push(n[e]))}),i},j.union=function(){return j.uniq(j.flatten(arguments,!0))},j.intersection=function(n){var t=o.call(arguments,1);return j.filter(j.uniq(n),function(n){return j.every(t,function(t){return j.indexOf(t,n)>=0})})},j.difference=function(n){var t=c.apply(e,o.call(arguments,1));return j.filter(n,function(n){return!j.contains(t,n)})},j.zip=function(){for(var n=j.max(j.pluck(arguments,"length").concat(0)),t=new Array(n),r=0;n>r;r++)t[r]=j.pluck(arguments,""+r);return t},j.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},j.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=j.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},j.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},j.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=new Array(e);e>u;)i[u++]=n,n+=r;return i};var R=function(){};j.bind=function(n,t){var r,e;if(_&&n.bind===_)return _.apply(n,o.call(arguments,1));if(!j.isFunction(n))throw new TypeError;return r=o.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(o.call(arguments)));R.prototype=n.prototype;var u=new R;R.prototype=null;var i=n.apply(u,r.concat(o.call(arguments)));return Object(i)===i?i:u}},j.partial=function(n){var t=o.call(arguments,1);return function(){return n.apply(this,t.concat(o.call(arguments)))}},j.bindAll=function(n){var t=o.call(arguments,1);if(0===t.length)throw new Error("bindAll must be passed function names");return A(t,function(t){n[t]=j.bind(n[t],n)}),n},j.memoize=function(n,t){var r={};return t||(t=j.identity),function(){var e=t.apply(this,arguments);return j.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},j.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},j.defer=function(n){return j.delay.apply(j,[n,1].concat(o.call(arguments,1)))},j.throttle=function(n,t,r){var e,u,i,a=null,o=0;r||(r={});var c=function(){o=r.leading===!1?0:new Date,a=null,i=n.apply(e,u)};return function(){var l=new Date;o||r.leading!==!1||(o=l);var f=t-(l-o);return e=this,u=arguments,0>=f?(clearTimeout(a),a=null,o=l,i=n.apply(e,u)):a||r.trailing===!1||(a=setTimeout(c,f)),i}},j.debounce=function(n,t,r){var e,u,i,a,o;return function(){i=this,u=arguments,a=new Date;var c=function(){var l=new Date-a;t>l?e=setTimeout(c,t-l):(e=null,r||(o=n.apply(i,u)))},l=r&&!e;return e||(e=setTimeout(c,t)),l&&(o=n.apply(i,u)),o}},j.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},j.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},j.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},j.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},j.keys=w||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)j.has(n,r)&&t.push(r);return t},j.values=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},j.pairs=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},j.invert=function(n){for(var t={},r=j.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},j.functions=j.methods=function(n){var t=[];for(var r in n)j.isFunction(n[r])&&t.push(r);return t.sort()},j.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},j.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},j.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)j.contains(r,u)||(t[u]=n[u]);return t},j.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]===void 0&&(n[r]=t[r])}),n},j.clone=function(n){return j.isObject(n)?j.isArray(n)?n.slice():j.extend({},n):n},j.tap=function(n,t){return t(n),n};var S=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof j&&(n=n._wrapped),t instanceof j&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==String(t);case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;var a=n.constructor,o=t.constructor;if(a!==o&&!(j.isFunction(a)&&a instanceof a&&j.isFunction(o)&&o instanceof o))return!1;r.push(n),e.push(t);var c=0,f=!0;if("[object Array]"==u){if(c=n.length,f=c==t.length)for(;c--&&(f=S(n[c],t[c],r,e)););}else{for(var s in n)if(j.has(n,s)&&(c++,!(f=j.has(t,s)&&S(n[s],t[s],r,e))))break;if(f){for(s in t)if(j.has(t,s)&&!c--)break;f=!c}}return r.pop(),e.pop(),f};j.isEqual=function(n,t){return S(n,t,[],[])},j.isEmpty=function(n){if(null==n)return!0;if(j.isArray(n)||j.isString(n))return 0===n.length;for(var t in n)if(j.has(n,t))return!1;return!0},j.isElement=function(n){return!(!n||1!==n.nodeType)},j.isArray=x||function(n){return"[object Array]"==l.call(n)},j.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){j["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),j.isArguments(arguments)||(j.isArguments=function(n){return!(!n||!j.has(n,"callee"))}),"function"!=typeof/./&&(j.isFunction=function(n){return"function"==typeof n}),j.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},j.isNaN=function(n){return j.isNumber(n)&&n!=+n},j.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},j.isNull=function(n){return null===n},j.isUndefined=function(n){return n===void 0},j.has=function(n,t){return f.call(n,t)},j.noConflict=function(){return n._=t,this},j.identity=function(n){return n},j.times=function(n,t,r){for(var e=Array(Math.max(0,n)),u=0;n>u;u++)e[u]=t.call(r,u);return e},j.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))};var I={escape:{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;"}};I.unescape=j.invert(I.escape);var T={escape:new RegExp("["+j.keys(I.escape).join("")+"]","g"),unescape:new RegExp("("+j.keys(I.unescape).join("|")+")","g")};j.each(["escape","unescape"],function(n){j[n]=function(t){return null==t?"":(""+t).replace(T[n],function(t){return I[n][t]})}}),j.result=function(n,t){if(null==n)return void 0;var r=n[t];return j.isFunction(r)?r.call(n):r},j.mixin=function(n){A(j.functions(n),function(t){var r=j[t]=n[t];j.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),z.call(this,r.apply(j,n))}})};var N=0;j.uniqueId=function(n){var t=++N+"";return n?n+t:t},j.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var q=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n","	":"t","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\t|\u2028|\u2029/g;j.template=function(n,t,r){var e;r=j.defaults({},r,j.templateSettings);var u=new RegExp([(r.escape||q).source,(r.interpolate||q).source,(r.evaluate||q).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(D,function(n){return"\\"+B[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=new Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,j);var c=function(n){return e.call(this,n,j)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},j.chain=function(n){return j(n).chain()};var z=function(n){return this._chain?j(n).chain():n};j.mixin(j),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];j.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],z.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];j.prototype[n]=function(){return z.call(this,t.apply(this._wrapped,arguments))}}),j.extend(j.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this);

/*! jQuery UI - v1.13.0 - 2021-10-07
* http://jqueryui.com
* Includes: widget.js, position.js, data.js, disable-selection.js, effect.js, effects/effect-blind.js, effects/effect-bounce.js, effects/effect-clip.js, effects/effect-drop.js, effects/effect-explode.js, effects/effect-fade.js, effects/effect-fold.js, effects/effect-highlight.js, effects/effect-puff.js, effects/effect-pulsate.js, effects/effect-scale.js, effects/effect-shake.js, effects/effect-size.js, effects/effect-slide.js, effects/effect-transfer.js, focusable.js, form-reset-mixin.js, jquery-patch.js, keycode.js, labels.js, scroll-parent.js, tabbable.js, unique-id.js, widgets/accordion.js, widgets/autocomplete.js, widgets/button.js, widgets/checkboxradio.js, widgets/controlgroup.js, widgets/datepicker.js, widgets/dialog.js, widgets/draggable.js, widgets/droppable.js, widgets/menu.js, widgets/mouse.js, widgets/progressbar.js, widgets/resizable.js, widgets/selectable.js, widgets/selectmenu.js, widgets/slider.js, widgets/sortable.js, widgets/spinner.js, widgets/tabs.js, widgets/tooltip.js
* Copyright jQuery Foundation and other contributors; Licensed MIT */

!function(t){"use strict";"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)}(function(V){"use strict";V.ui=V.ui||{};V.ui.version="1.13.0";var n,i=0,a=Array.prototype.hasOwnProperty,r=Array.prototype.slice;V.cleanData=(n=V.cleanData,function(t){for(var e,i,s=0;null!=(i=t[s]);s++)(e=V._data(i,"events"))&&e.remove&&V(i).triggerHandler("remove");n(t)}),V.widget=function(t,i,e){var s,n,o,a={},r=t.split(".")[0],l=r+"-"+(t=t.split(".")[1]);return e||(e=i,i=V.Widget),Array.isArray(e)&&(e=V.extend.apply(null,[{}].concat(e))),V.expr.pseudos[l.toLowerCase()]=function(t){return!!V.data(t,l)},V[r]=V[r]||{},s=V[r][t],n=V[r][t]=function(t,e){if(!this._createWidget)return new n(t,e);arguments.length&&this._createWidget(t,e)},V.extend(n,s,{version:e.version,_proto:V.extend({},e),_childConstructors:[]}),(o=new i).options=V.widget.extend({},o.options),V.each(e,function(e,s){function n(){return i.prototype[e].apply(this,arguments)}function o(t){return i.prototype[e].apply(this,t)}a[e]="function"==typeof s?function(){var t,e=this._super,i=this._superApply;return this._super=n,this._superApply=o,t=s.apply(this,arguments),this._super=e,this._superApply=i,t}:s}),n.prototype=V.widget.extend(o,{widgetEventPrefix:s&&o.widgetEventPrefix||t},a,{constructor:n,namespace:r,widgetName:t,widgetFullName:l}),s?(V.each(s._childConstructors,function(t,e){var i=e.prototype;V.widget(i.namespace+"."+i.widgetName,n,e._proto)}),delete s._childConstructors):i._childConstructors.push(n),V.widget.bridge(t,n),n},V.widget.extend=function(t){for(var e,i,s=r.call(arguments,1),n=0,o=s.length;n<o;n++)for(e in s[n])i=s[n][e],a.call(s[n],e)&&void 0!==i&&(V.isPlainObject(i)?t[e]=V.isPlainObject(t[e])?V.widget.extend({},t[e],i):V.widget.extend({},i):t[e]=i);return t},V.widget.bridge=function(o,e){var a=e.prototype.widgetFullName||o;V.fn[o]=function(i){var t="string"==typeof i,s=r.call(arguments,1),n=this;return t?this.length||"instance"!==i?this.each(function(){var t,e=V.data(this,a);return"instance"===i?(n=e,!1):e?"function"!=typeof e[i]||"_"===i.charAt(0)?V.error("no such method '"+i+"' for "+o+" widget instance"):(t=e[i].apply(e,s))!==e&&void 0!==t?(n=t&&t.jquery?n.pushStack(t.get()):t,!1):void 0:V.error("cannot call methods on "+o+" prior to initialization; attempted to call method '"+i+"'")}):n=void 0:(s.length&&(i=V.widget.extend.apply(null,[i].concat(s))),this.each(function(){var t=V.data(this,a);t?(t.option(i||{}),t._init&&t._init()):V.data(this,a,new e(i,this))})),n}},V.Widget=function(){},V.Widget._childConstructors=[],V.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{classes:{},disabled:!1,create:null},_createWidget:function(t,e){e=V(e||this.defaultElement||this)[0],this.element=V(e),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=V(),this.hoverable=V(),this.focusable=V(),this.classesElementLookup={},e!==this&&(V.data(e,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===e&&this.destroy()}}),this.document=V(e.style?e.ownerDocument:e.document||e),this.window=V(this.document[0].defaultView||this.document[0].parentWindow)),this.options=V.widget.extend({},this.options,this._getCreateOptions(),t),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:V.noop,_create:V.noop,_init:V.noop,destroy:function(){var i=this;this._destroy(),V.each(this.classesElementLookup,function(t,e){i._removeClass(e,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:V.noop,widget:function(){return this.element},option:function(t,e){var i,s,n,o=t;if(0===arguments.length)return V.widget.extend({},this.options);if("string"==typeof t)if(o={},t=(i=t.split(".")).shift(),i.length){for(s=o[t]=V.widget.extend({},this.options[t]),n=0;n<i.length-1;n++)s[i[n]]=s[i[n]]||{},s=s[i[n]];if(t=i.pop(),1===arguments.length)return void 0===s[t]?null:s[t];s[t]=e}else{if(1===arguments.length)return void 0===this.options[t]?null:this.options[t];o[t]=e}return this._setOptions(o),this},_setOptions:function(t){for(var e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(t){var e,i,s;for(e in t)s=this.classesElementLookup[e],t[e]!==this.options.classes[e]&&s&&s.length&&(i=V(s.get()),this._removeClass(s,e),i.addClass(this._classes({element:i,keys:e,classes:t,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(n){var o=[],a=this;function t(t,e){for(var i,s=0;s<t.length;s++)i=a.classesElementLookup[t[s]]||V(),i=n.add?(n.element.each(function(t,e){V.map(a.classesElementLookup,function(t){return t}).some(function(t){return t.is(e)})||a._on(V(e),{remove:"_untrackClassesElement"})}),V(V.uniqueSort(i.get().concat(n.element.get())))):V(i.not(n.element).get()),a.classesElementLookup[t[s]]=i,o.push(t[s]),e&&n.classes[t[s]]&&o.push(n.classes[t[s]])}return(n=V.extend({element:this.element,classes:this.options.classes||{}},n)).keys&&t(n.keys.match(/\S+/g)||[],!0),n.extra&&t(n.extra.match(/\S+/g)||[]),o.join(" ")},_untrackClassesElement:function(i){var s=this;V.each(s.classesElementLookup,function(t,e){-1!==V.inArray(i.target,e)&&(s.classesElementLookup[t]=V(e.not(i.target).get()))}),this._off(V(i.target))},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){var n="string"==typeof t||null===t,i={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:s="boolean"==typeof s?s:i};return i.element.toggleClass(this._classes(i),s),this},_on:function(n,o,t){var a,r=this;"boolean"!=typeof n&&(t=o,o=n,n=!1),t?(o=a=V(o),this.bindings=this.bindings.add(o)):(t=o,o=this.element,a=this.widget()),V.each(t,function(t,e){function i(){if(n||!0!==r.options.disabled&&!V(this).hasClass("ui-state-disabled"))return("string"==typeof e?r[e]:e).apply(r,arguments)}"string"!=typeof e&&(i.guid=e.guid=e.guid||i.guid||V.guid++);var s=t.match(/^([\w:-]*)\s*(.*)$/),t=s[1]+r.eventNamespace,s=s[2];s?a.on(t,s,i):o.on(t,i)})},_off:function(t,e){e=(e||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,t.off(e),this.bindings=V(this.bindings.not(t).get()),this.focusable=V(this.focusable.not(t).get()),this.hoverable=V(this.hoverable.not(t).get())},_delay:function(t,e){var i=this;return setTimeout(function(){return("string"==typeof t?i[t]:t).apply(i,arguments)},e||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){this._addClass(V(t.currentTarget),null,"ui-state-hover")},mouseleave:function(t){this._removeClass(V(t.currentTarget),null,"ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){this._addClass(V(t.currentTarget),null,"ui-state-focus")},focusout:function(t){this._removeClass(V(t.currentTarget),null,"ui-state-focus")}})},_trigger:function(t,e,i){var s,n,o=this.options[t];if(i=i||{},(e=V.Event(e)).type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),e.target=this.element[0],n=e.originalEvent)for(s in n)s in e||(e[s]=n[s]);return this.element.trigger(e,i),!("function"==typeof o&&!1===o.apply(this.element[0],[e].concat(i))||e.isDefaultPrevented())}},V.each({show:"fadeIn",hide:"fadeOut"},function(o,a){V.Widget.prototype["_"+o]=function(e,t,i){var s,n=(t="string"==typeof t?{effect:t}:t)?!0!==t&&"number"!=typeof t&&t.effect||a:o;"number"==typeof(t=t||{})?t={duration:t}:!0===t&&(t={}),s=!V.isEmptyObject(t),t.complete=i,t.delay&&e.delay(t.delay),s&&V.effects&&V.effects.effect[n]?e[o](t):n!==o&&e[n]?e[n](t.duration,t.easing,i):e.queue(function(t){V(this)[o](),i&&i.call(e[0]),t()})}});var s,x,k,o,l,h,c,u,C;V.widget;function D(t,e,i){return[parseFloat(t[0])*(u.test(t[0])?e/100:1),parseFloat(t[1])*(u.test(t[1])?i/100:1)]}function I(t,e){return parseInt(V.css(t,e),10)||0}function T(t){return null!=t&&t===t.window}x=Math.max,k=Math.abs,o=/left|center|right/,l=/top|center|bottom/,h=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,u=/%$/,C=V.fn.position,V.position={scrollbarWidth:function(){if(void 0!==s)return s;var t,e=V("<div style='display:block;position:absolute;width:200px;height:200px;overflow:hidden;'><div style='height:300px;width:auto;'></div></div>"),i=e.children()[0];return V("body").append(e),t=i.offsetWidth,e.css("overflow","scroll"),t===(i=i.offsetWidth)&&(i=e[0].clientWidth),e.remove(),s=t-i},getScrollInfo:function(t){var e=t.isWindow||t.isDocument?"":t.element.css("overflow-x"),i=t.isWindow||t.isDocument?"":t.element.css("overflow-y"),e="scroll"===e||"auto"===e&&t.width<t.element[0].scrollWidth;return{width:"scroll"===i||"auto"===i&&t.height<t.element[0].scrollHeight?V.position.scrollbarWidth():0,height:e?V.position.scrollbarWidth():0}},getWithinInfo:function(t){var e=V(t||window),i=T(e[0]),s=!!e[0]&&9===e[0].nodeType;return{element:e,isWindow:i,isDocument:s,offset:!i&&!s?V(t).offset():{left:0,top:0},scrollLeft:e.scrollLeft(),scrollTop:e.scrollTop(),width:e.outerWidth(),height:e.outerHeight()}}},V.fn.position=function(u){if(!u||!u.of)return C.apply(this,arguments);var d,p,f,g,m,t,_="string"==typeof(u=V.extend({},u)).of?V(document).find(u.of):V(u.of),v=V.position.getWithinInfo(u.within),b=V.position.getScrollInfo(v),y=(u.collision||"flip").split(" "),w={},e=9===(t=(e=_)[0]).nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:T(t)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:t.preventDefault?{width:0,height:0,offset:{top:t.pageY,left:t.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()};return _[0].preventDefault&&(u.at="left top"),p=e.width,f=e.height,m=V.extend({},g=e.offset),V.each(["my","at"],function(){var t,e,i=(u[this]||"").split(" ");(i=1===i.length?o.test(i[0])?i.concat(["center"]):l.test(i[0])?["center"].concat(i):["center","center"]:i)[0]=o.test(i[0])?i[0]:"center",i[1]=l.test(i[1])?i[1]:"center",t=h.exec(i[0]),e=h.exec(i[1]),w[this]=[t?t[0]:0,e?e[0]:0],u[this]=[c.exec(i[0])[0],c.exec(i[1])[0]]}),1===y.length&&(y[1]=y[0]),"right"===u.at[0]?m.left+=p:"center"===u.at[0]&&(m.left+=p/2),"bottom"===u.at[1]?m.top+=f:"center"===u.at[1]&&(m.top+=f/2),d=D(w.at,p,f),m.left+=d[0],m.top+=d[1],this.each(function(){var i,t,a=V(this),r=a.outerWidth(),l=a.outerHeight(),e=I(this,"marginLeft"),s=I(this,"marginTop"),n=r+e+I(this,"marginRight")+b.width,o=l+s+I(this,"marginBottom")+b.height,h=V.extend({},m),c=D(w.my,a.outerWidth(),a.outerHeight());"right"===u.my[0]?h.left-=r:"center"===u.my[0]&&(h.left-=r/2),"bottom"===u.my[1]?h.top-=l:"center"===u.my[1]&&(h.top-=l/2),h.left+=c[0],h.top+=c[1],i={marginLeft:e,marginTop:s},V.each(["left","top"],function(t,e){V.ui.position[y[t]]&&V.ui.position[y[t]][e](h,{targetWidth:p,targetHeight:f,elemWidth:r,elemHeight:l,collisionPosition:i,collisionWidth:n,collisionHeight:o,offset:[d[0]+c[0],d[1]+c[1]],my:u.my,at:u.at,within:v,elem:a})}),u.using&&(t=function(t){var e=g.left-h.left,i=e+p-r,s=g.top-h.top,n=s+f-l,o={target:{element:_,left:g.left,top:g.top,width:p,height:f},element:{element:a,left:h.left,top:h.top,width:r,height:l},horizontal:i<0?"left":0<e?"right":"center",vertical:n<0?"top":0<s?"bottom":"middle"};p<r&&k(e+i)<p&&(o.horizontal="center"),f<l&&k(s+n)<f&&(o.vertical="middle"),x(k(e),k(i))>x(k(s),k(n))?o.important="horizontal":o.important="vertical",u.using.call(this,t,o)}),a.offset(V.extend(h,{using:t}))})},V.ui.position={fit:{left:function(t,e){var i=e.within,s=i.isWindow?i.scrollLeft:i.offset.left,n=i.width,o=t.left-e.collisionPosition.marginLeft,a=s-o,r=o+e.collisionWidth-n-s;e.collisionWidth>n?0<a&&r<=0?(i=t.left+a+e.collisionWidth-n-s,t.left+=a-i):t.left=!(0<r&&a<=0)&&r<a?s+n-e.collisionWidth:s:0<a?t.left+=a:0<r?t.left-=r:t.left=x(t.left-o,t.left)},top:function(t,e){var i=e.within,s=i.isWindow?i.scrollTop:i.offset.top,n=e.within.height,o=t.top-e.collisionPosition.marginTop,a=s-o,r=o+e.collisionHeight-n-s;e.collisionHeight>n?0<a&&r<=0?(i=t.top+a+e.collisionHeight-n-s,t.top+=a-i):t.top=!(0<r&&a<=0)&&r<a?s+n-e.collisionHeight:s:0<a?t.top+=a:0<r?t.top-=r:t.top=x(t.top-o,t.top)}},flip:{left:function(t,e){var i=e.within,s=i.offset.left+i.scrollLeft,n=i.width,o=i.isWindow?i.scrollLeft:i.offset.left,a=t.left-e.collisionPosition.marginLeft,r=a-o,l=a+e.collisionWidth-n-o,h="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,i="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,a=-2*e.offset[0];r<0?((s=t.left+h+i+a+e.collisionWidth-n-s)<0||s<k(r))&&(t.left+=h+i+a):0<l&&(0<(o=t.left-e.collisionPosition.marginLeft+h+i+a-o)||k(o)<l)&&(t.left+=h+i+a)},top:function(t,e){var i=e.within,s=i.offset.top+i.scrollTop,n=i.height,o=i.isWindow?i.scrollTop:i.offset.top,a=t.top-e.collisionPosition.marginTop,r=a-o,l=a+e.collisionHeight-n-o,h="top"===e.my[1]?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,i="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,a=-2*e.offset[1];r<0?((s=t.top+h+i+a+e.collisionHeight-n-s)<0||s<k(r))&&(t.top+=h+i+a):0<l&&(0<(o=t.top-e.collisionPosition.marginTop+h+i+a-o)||k(o)<l)&&(t.top+=h+i+a)}},flipfit:{left:function(){V.ui.position.flip.left.apply(this,arguments),V.ui.position.fit.left.apply(this,arguments)},top:function(){V.ui.position.flip.top.apply(this,arguments),V.ui.position.fit.top.apply(this,arguments)}}};V.ui.position,V.extend(V.expr.pseudos,{data:V.expr.createPseudo?V.expr.createPseudo(function(e){return function(t){return!!V.data(t,e)}}):function(t,e,i){return!!V.data(t,i[3])}}),V.fn.extend({disableSelection:(t="onselectstart"in document.createElement("div")?"selectstart":"mousedown",function(){return this.on(t+".ui-disableSelection",function(t){t.preventDefault()})}),enableSelection:function(){return this.off(".ui-disableSelection")}});var t,d=V,p={},e=p.toString,f=/^([\-+])=\s*(\d+\.?\d*)/,g=[{re:/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(t){return[t[1],t[2],t[3],t[4]]}},{re:/rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(t){return[2.55*t[1],2.55*t[2],2.55*t[3],t[4]]}},{re:/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})?/,parse:function(t){return[parseInt(t[1],16),parseInt(t[2],16),parseInt(t[3],16),t[4]?(parseInt(t[4],16)/255).toFixed(2):1]}},{re:/#([a-f0-9])([a-f0-9])([a-f0-9])([a-f0-9])?/,parse:function(t){return[parseInt(t[1]+t[1],16),parseInt(t[2]+t[2],16),parseInt(t[3]+t[3],16),t[4]?(parseInt(t[4]+t[4],16)/255).toFixed(2):1]}},{re:/hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,space:"hsla",parse:function(t){return[t[1],t[2]/100,t[3]/100,t[4]]}}],m=d.Color=function(t,e,i,s){return new d.Color.fn.parse(t,e,i,s)},_={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"}}}},v={byte:{floor:!0,max:255},percent:{max:1},degrees:{mod:360,floor:!0}},b=m.support={},y=d("<p>")[0],w=d.each;function P(t){return null==t?t+"":"object"==typeof t?p[e.call(t)]||"object":typeof t}function M(t,e,i){var s=v[e.type]||{};return null==t?i||!e.def?null:e.def:(t=s.floor?~~t:parseFloat(t),isNaN(t)?e.def:s.mod?(t+s.mod)%s.mod:Math.min(s.max,Math.max(0,t)))}function S(s){var n=m(),o=n._rgba=[];return s=s.toLowerCase(),w(g,function(t,e){var i=e.re.exec(s),i=i&&e.parse(i),e=e.space||"rgba";if(i)return i=n[e](i),n[_[e].cache]=i[_[e].cache],o=n._rgba=i._rgba,!1}),o.length?("0,0,0,0"===o.join()&&d.extend(o,B.transparent),n):B[s]}function H(t,e,i){return 6*(i=(i+1)%1)<1?t+(e-t)*i*6:2*i<1?e:3*i<2?t+(e-t)*(2/3-i)*6:t}y.style.cssText="background-color:rgba(1,1,1,.5)",b.rgba=-1<y.style.backgroundColor.indexOf("rgba"),w(_,function(t,e){e.cache="_"+t,e.props.alpha={idx:3,type:"percent",def:1}}),d.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(t,e){p["[object "+e+"]"]=e.toLowerCase()}),(m.fn=d.extend(m.prototype,{parse:function(n,t,e,i){if(void 0===n)return this._rgba=[null,null,null,null],this;(n.jquery||n.nodeType)&&(n=d(n).css(t),t=void 0);var o=this,s=P(n),a=this._rgba=[];return void 0!==t&&(n=[n,t,e,i],s="array"),"string"===s?this.parse(S(n)||B._default):"array"===s?(w(_.rgba.props,function(t,e){a[e.idx]=M(n[e.idx],e)}),this):"object"===s?(w(_,n instanceof m?function(t,e){n[e.cache]&&(o[e.cache]=n[e.cache].slice())}:function(t,i){var s=i.cache;w(i.props,function(t,e){if(!o[s]&&i.to){if("alpha"===t||null==n[t])return;o[s]=i.to(o._rgba)}o[s][e.idx]=M(n[t],e,!0)}),o[s]&&d.inArray(null,o[s].slice(0,3))<0&&(null==o[s][3]&&(o[s][3]=1),i.from&&(o._rgba=i.from(o[s])))}),this):void 0},is:function(t){var n=m(t),o=!0,a=this;return w(_,function(t,e){var i,s=n[e.cache];return s&&(i=a[e.cache]||e.to&&e.to(a._rgba)||[],w(e.props,function(t,e){if(null!=s[e.idx])return o=s[e.idx]===i[e.idx]})),o}),o},_space:function(){var i=[],s=this;return w(_,function(t,e){s[e.cache]&&i.push(t)}),i.pop()},transition:function(t,a){var e=(h=m(t))._space(),i=_[e],t=0===this.alpha()?m("transparent"):this,r=t[i.cache]||i.to(t._rgba),l=r.slice(),h=h[i.cache];return w(i.props,function(t,e){var i=e.idx,s=r[i],n=h[i],o=v[e.type]||{};null!==n&&(null===s?l[i]=n:(o.mod&&(n-s>o.mod/2?s+=o.mod:s-n>o.mod/2&&(s-=o.mod)),l[i]=M((n-s)*a+s,e)))}),this[e](l)},blend:function(t){if(1===this._rgba[3])return this;var e=this._rgba.slice(),i=e.pop(),s=m(t)._rgba;return m(d.map(e,function(t,e){return(1-i)*s[e]+i*t}))},toRgbaString:function(){var t="rgba(",e=d.map(this._rgba,function(t,e){return null!=t?t:2<e?1:0});return 1===e[3]&&(e.pop(),t="rgb("),t+e.join()+")"},toHslaString:function(){var t="hsla(",e=d.map(this.hsla(),function(t,e){return null==t&&(t=2<e?1:0),t=e&&e<3?Math.round(100*t)+"%":t});return 1===e[3]&&(e.pop(),t="hsl("),t+e.join()+")"},toHexString:function(t){var e=this._rgba.slice(),i=e.pop();return t&&e.push(~~(255*i)),"#"+d.map(e,function(t){return 1===(t=(t||0).toString(16)).length?"0"+t:t}).join("")},toString:function(){return 0===this._rgba[3]?"transparent":this.toRgbaString()}})).parse.prototype=m.fn,_.hsla.to=function(t){if(null==t[0]||null==t[1]||null==t[2])return[null,null,null,t[3]];var e=t[0]/255,i=t[1]/255,s=t[2]/255,n=t[3],o=Math.max(e,i,s),a=Math.min(e,i,s),r=o-a,l=o+a,t=.5*l,i=a===o?0:e===o?60*(i-s)/r+360:i===o?60*(s-e)/r+120:60*(e-i)/r+240,l=0==r?0:t<=.5?r/l:r/(2-l);return[Math.round(i)%360,l,t,null==n?1:n]},_.hsla.from=function(t){if(null==t[0]||null==t[1]||null==t[2])return[null,null,null,t[3]];var e=t[0]/360,i=t[1],s=t[2],t=t[3],i=s<=.5?s*(1+i):s+i-s*i,s=2*s-i;return[Math.round(255*H(s,i,e+1/3)),Math.round(255*H(s,i,e)),Math.round(255*H(s,i,e-1/3)),t]},w(_,function(l,t){var e=t.props,o=t.cache,a=t.to,r=t.from;m.fn[l]=function(t){if(a&&!this[o]&&(this[o]=a(this._rgba)),void 0===t)return this[o].slice();var i=P(t),s="array"===i||"object"===i?t:arguments,n=this[o].slice();return w(e,function(t,e){t=s["object"===i?t:e.idx];null==t&&(t=n[e.idx]),n[e.idx]=M(t,e)}),r?((t=m(r(n)))[o]=n,t):m(n)},w(e,function(a,r){m.fn[a]||(m.fn[a]=function(t){var e,i=P(t),s="alpha"===a?this._hsla?"hsla":"rgba":l,n=this[s](),o=n[r.idx];return"undefined"===i?o:("function"===i&&(i=P(t=t.call(this,o))),null==t&&r.empty?this:("string"===i&&(e=f.exec(t))&&(t=o+parseFloat(e[2])*("+"===e[1]?1:-1)),n[r.idx]=t,this[s](n)))})})}),(m.hook=function(t){t=t.split(" ");w(t,function(t,o){d.cssHooks[o]={set:function(t,e){var i,s,n="";if("transparent"!==e&&("string"!==P(e)||(i=S(e)))){if(e=m(i||e),!b.rgba&&1!==e._rgba[3]){for(s="backgroundColor"===o?t.parentNode:t;(""===n||"transparent"===n)&&s&&s.style;)try{n=d.css(s,"backgroundColor"),s=s.parentNode}catch(t){}e=e.blend(n&&"transparent"!==n?n:"_default")}e=e.toRgbaString()}try{t.style[o]=e}catch(t){}}},d.fx.step[o]=function(t){t.colorInit||(t.start=m(t.elem,o),t.end=m(t.end),t.colorInit=!0),d.cssHooks[o].set(t.elem,t.start.transition(t.end,t.pos))}})})("backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor"),d.cssHooks.borderColor={expand:function(i){var s={};return w(["Top","Right","Bottom","Left"],function(t,e){s["border"+e+"Color"]=i}),s}};var z,A,O,N,E,W,F,L,R,Y,B=d.Color.names={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",transparent:[null,null,null,0],_default:"#ffffff"},j="ui-effects-",q="ui-effects-style",K="ui-effects-animated";function U(t){var e,i,s=t.ownerDocument.defaultView?t.ownerDocument.defaultView.getComputedStyle(t,null):t.currentStyle,n={};if(s&&s.length&&s[0]&&s[s[0]])for(i=s.length;i--;)"string"==typeof s[e=s[i]]&&(n[e.replace(/-([\da-z])/gi,function(t,e){return e.toUpperCase()})]=s[e]);else for(e in s)"string"==typeof s[e]&&(n[e]=s[e]);return n}function X(t,e,i,s){return t={effect:t=V.isPlainObject(t)?(e=t).effect:t},"function"==typeof(e=null==e?{}:e)&&(s=e,i=null,e={}),"number"!=typeof e&&!V.fx.speeds[e]||(s=i,i=e,e={}),"function"==typeof i&&(s=i,i=null),e&&V.extend(t,e),i=i||e.duration,t.duration=V.fx.off?0:"number"==typeof i?i:i in V.fx.speeds?V.fx.speeds[i]:V.fx.speeds._default,t.complete=s||e.complete,t}function $(t){return!t||"number"==typeof t||V.fx.speeds[t]||("string"==typeof t&&!V.effects.effect[t]||("function"==typeof t||"object"==typeof t&&!t.effect))}function G(t,e){var i=e.outerWidth(),e=e.outerHeight(),t=/^rect\((-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto)\)$/.exec(t)||["",0,i,e,0];return{top:parseFloat(t[1])||0,right:"auto"===t[2]?i:parseFloat(t[2]),bottom:"auto"===t[3]?e:parseFloat(t[3]),left:parseFloat(t[4])||0}}V.effects={effect:{}},N=["add","remove","toggle"],E={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1},V.each(["borderLeftStyle","borderRightStyle","borderBottomStyle","borderTopStyle"],function(t,e){V.fx.step[e]=function(t){("none"!==t.end&&!t.setAttr||1===t.pos&&!t.setAttr)&&(d.style(t.elem,e,t.end),t.setAttr=!0)}}),V.fn.addBack||(V.fn.addBack=function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}),V.effects.animateClass=function(n,t,e,i){var o=V.speed(t,e,i);return this.queue(function(){var i=V(this),t=i.attr("class")||"",e=(e=o.children?i.find("*").addBack():i).map(function(){return{el:V(this),start:U(this)}}),s=function(){V.each(N,function(t,e){n[e]&&i[e+"Class"](n[e])})};s(),e=e.map(function(){return this.end=U(this.el[0]),this.diff=function(t,e){var i,s,n={};for(i in e)s=e[i],t[i]!==s&&(E[i]||!V.fx.step[i]&&isNaN(parseFloat(s))||(n[i]=s));return n}(this.start,this.end),this}),i.attr("class",t),e=e.map(function(){var t=this,e=V.Deferred(),i=V.extend({},o,{queue:!1,complete:function(){e.resolve(t)}});return this.el.animate(this.diff,i),e.promise()}),V.when.apply(V,e.get()).done(function(){s(),V.each(arguments,function(){var e=this.el;V.each(this.diff,function(t){e.css(t,"")})}),o.complete.call(i[0])})})},V.fn.extend({addClass:(O=V.fn.addClass,function(t,e,i,s){return e?V.effects.animateClass.call(this,{add:t},e,i,s):O.apply(this,arguments)}),removeClass:(A=V.fn.removeClass,function(t,e,i,s){return 1<arguments.length?V.effects.animateClass.call(this,{remove:t},e,i,s):A.apply(this,arguments)}),toggleClass:(z=V.fn.toggleClass,function(t,e,i,s,n){return"boolean"==typeof e||void 0===e?i?V.effects.animateClass.call(this,e?{add:t}:{remove:t},i,s,n):z.apply(this,arguments):V.effects.animateClass.call(this,{toggle:t},e,i,s)}),switchClass:function(t,e,i,s,n){return V.effects.animateClass.call(this,{add:e,remove:t},i,s,n)}}),V.expr&&V.expr.pseudos&&V.expr.pseudos.animated&&(V.expr.pseudos.animated=(W=V.expr.pseudos.animated,function(t){return!!V(t).data(K)||W(t)})),!1!==V.uiBackCompat&&V.extend(V.effects,{save:function(t,e){for(var i=0,s=e.length;i<s;i++)null!==e[i]&&t.data(j+e[i],t[0].style[e[i]])},restore:function(t,e){for(var i,s=0,n=e.length;s<n;s++)null!==e[s]&&(i=t.data(j+e[s]),t.css(e[s],i))},setMode:function(t,e){return e="toggle"===e?t.is(":hidden")?"show":"hide":e},createWrapper:function(i){if(i.parent().is(".ui-effects-wrapper"))return i.parent();var s={width:i.outerWidth(!0),height:i.outerHeight(!0),float:i.css("float")},t=V("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),e={width:i.width(),height:i.height()},n=document.activeElement;try{n.id}catch(t){n=document.body}return i.wrap(t),i[0]!==n&&!V.contains(i[0],n)||V(n).trigger("focus"),t=i.parent(),"static"===i.css("position")?(t.css({position:"relative"}),i.css({position:"relative"})):(V.extend(s,{position:i.css("position"),zIndex:i.css("z-index")}),V.each(["top","left","bottom","right"],function(t,e){s[e]=i.css(e),isNaN(parseInt(s[e],10))&&(s[e]="auto")}),i.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),i.css(e),t.css(s).show()},removeWrapper:function(t){var e=document.activeElement;return t.parent().is(".ui-effects-wrapper")&&(t.parent().replaceWith(t),t[0]!==e&&!V.contains(t[0],e)||V(e).trigger("focus")),t}}),V.extend(V.effects,{version:"1.13.0",define:function(t,e,i){return i||(i=e,e="effect"),V.effects.effect[t]=i,V.effects.effect[t].mode=e,i},scaledDimensions:function(t,e,i){if(0===e)return{height:0,width:0,outerHeight:0,outerWidth:0};var s="horizontal"!==i?(e||100)/100:1,e="vertical"!==i?(e||100)/100:1;return{height:t.height()*e,width:t.width()*s,outerHeight:t.outerHeight()*e,outerWidth:t.outerWidth()*s}},clipToBox:function(t){return{width:t.clip.right-t.clip.left,height:t.clip.bottom-t.clip.top,left:t.clip.left,top:t.clip.top}},unshift:function(t,e,i){var s=t.queue();1<e&&s.splice.apply(s,[1,0].concat(s.splice(e,i))),t.dequeue()},saveStyle:function(t){t.data(q,t[0].style.cssText)},restoreStyle:function(t){t[0].style.cssText=t.data(q)||"",t.removeData(q)},mode:function(t,e){t=t.is(":hidden");return"toggle"===e&&(e=t?"show":"hide"),e=(t?"hide"===e:"show"===e)?"none":e},getBaseline:function(t,e){var i,s;switch(t[0]){case"top":i=0;break;case"middle":i=.5;break;case"bottom":i=1;break;default:i=t[0]/e.height}switch(t[1]){case"left":s=0;break;case"center":s=.5;break;case"right":s=1;break;default:s=t[1]/e.width}return{x:s,y:i}},createPlaceholder:function(t){var e,i=t.css("position"),s=t.position();return t.css({marginTop:t.css("marginTop"),marginBottom:t.css("marginBottom"),marginLeft:t.css("marginLeft"),marginRight:t.css("marginRight")}).outerWidth(t.outerWidth()).outerHeight(t.outerHeight()),/^(static|relative)/.test(i)&&(i="absolute",e=V("<"+t[0].nodeName+">").insertAfter(t).css({display:/^(inline|ruby)/.test(t.css("display"))?"inline-block":"block",visibility:"hidden",marginTop:t.css("marginTop"),marginBottom:t.css("marginBottom"),marginLeft:t.css("marginLeft"),marginRight:t.css("marginRight"),float:t.css("float")}).outerWidth(t.outerWidth()).outerHeight(t.outerHeight()).addClass("ui-effects-placeholder"),t.data(j+"placeholder",e)),t.css({position:i,left:s.left,top:s.top}),e},removePlaceholder:function(t){var e=j+"placeholder",i=t.data(e);i&&(i.remove(),t.removeData(e))},cleanUp:function(t){V.effects.restoreStyle(t),V.effects.removePlaceholder(t)},setTransition:function(s,t,n,o){return o=o||{},V.each(t,function(t,e){var i=s.cssUnit(e);0<i[0]&&(o[e]=i[0]*n+i[1])}),o}}),V.fn.extend({effect:function(){function t(t){var e=V(this),i=V.effects.mode(e,r)||o;e.data(K,!0),l.push(i),o&&("show"===i||i===o&&"hide"===i)&&e.show(),o&&"none"===i||V.effects.saveStyle(e),"function"==typeof t&&t()}var s=X.apply(this,arguments),n=V.effects.effect[s.effect],o=n.mode,e=s.queue,i=e||"fx",a=s.complete,r=s.mode,l=[];return V.fx.off||!n?r?this[r](s.duration,a):this.each(function(){a&&a.call(this)}):!1===e?this.each(t).each(h):this.queue(i,t).queue(i,h);function h(t){var e=V(this);function i(){"function"==typeof a&&a.call(e[0]),"function"==typeof t&&t()}s.mode=l.shift(),!1===V.uiBackCompat||o?"none"===s.mode?(e[r](),i()):n.call(e[0],s,function(){e.removeData(K),V.effects.cleanUp(e),"hide"===s.mode&&e.hide(),i()}):(e.is(":hidden")?"hide"===r:"show"===r)?(e[r](),i()):n.call(e[0],s,i)}},show:(R=V.fn.show,function(t){if($(t))return R.apply(this,arguments);t=X.apply(this,arguments);return t.mode="show",this.effect.call(this,t)}),hide:(L=V.fn.hide,function(t){if($(t))return L.apply(this,arguments);t=X.apply(this,arguments);return t.mode="hide",this.effect.call(this,t)}),toggle:(F=V.fn.toggle,function(t){if($(t)||"boolean"==typeof t)return F.apply(this,arguments);t=X.apply(this,arguments);return t.mode="toggle",this.effect.call(this,t)}),cssUnit:function(t){var i=this.css(t),s=[];return V.each(["em","px","%","pt"],function(t,e){0<i.indexOf(e)&&(s=[parseFloat(i),e])}),s},cssClip:function(t){return t?this.css("clip","rect("+t.top+"px "+t.right+"px "+t.bottom+"px "+t.left+"px)"):G(this.css("clip"),this)},transfer:function(t,e){var i=V(this),s=V(t.to),n="fixed"===s.css("position"),o=V("body"),a=n?o.scrollTop():0,r=n?o.scrollLeft():0,o=s.offset(),o={top:o.top-a,left:o.left-r,height:s.innerHeight(),width:s.innerWidth()},s=i.offset(),l=V("<div class='ui-effects-transfer'></div>");l.appendTo("body").addClass(t.className).css({top:s.top-a,left:s.left-r,height:i.innerHeight(),width:i.innerWidth(),position:n?"fixed":"absolute"}).animate(o,t.duration,t.easing,function(){l.remove(),"function"==typeof e&&e()})}}),V.fx.step.clip=function(t){t.clipInit||(t.start=V(t.elem).cssClip(),"string"==typeof t.end&&(t.end=G(t.end,t.elem)),t.clipInit=!0),V(t.elem).cssClip({top:t.pos*(t.end.top-t.start.top)+t.start.top,right:t.pos*(t.end.right-t.start.right)+t.start.right,bottom:t.pos*(t.end.bottom-t.start.bottom)+t.start.bottom,left:t.pos*(t.end.left-t.start.left)+t.start.left})},Y={},V.each(["Quad","Cubic","Quart","Quint","Expo"],function(e,t){Y[t]=function(t){return Math.pow(t,e+2)}}),V.extend(Y,{Sine:function(t){return 1-Math.cos(t*Math.PI/2)},Circ:function(t){return 1-Math.sqrt(1-t*t)},Elastic:function(t){return 0===t||1===t?t:-Math.pow(2,8*(t-1))*Math.sin((80*(t-1)-7.5)*Math.PI/15)},Back:function(t){return t*t*(3*t-2)},Bounce:function(t){for(var e,i=4;t<((e=Math.pow(2,--i))-1)/11;);return 1/Math.pow(4,3-i)-7.5625*Math.pow((3*e-2)/22-t,2)}}),V.each(Y,function(t,e){V.easing["easeIn"+t]=e,V.easing["easeOut"+t]=function(t){return 1-e(1-t)},V.easing["easeInOut"+t]=function(t){return t<.5?e(2*t)/2:1-e(-2*t+2)/2}});y=V.effects,V.effects.define("blind","hide",function(t,e){var i={up:["bottom","top"],vertical:["bottom","top"],down:["top","bottom"],left:["right","left"],horizontal:["right","left"],right:["left","right"]},s=V(this),n=t.direction||"up",o=s.cssClip(),a={clip:V.extend({},o)},r=V.effects.createPlaceholder(s);a.clip[i[n][0]]=a.clip[i[n][1]],"show"===t.mode&&(s.cssClip(a.clip),r&&r.css(V.effects.clipToBox(a)),a.clip=o),r&&r.animate(V.effects.clipToBox(a),t.duration,t.easing),s.animate(a,{queue:!1,duration:t.duration,easing:t.easing,complete:e})}),V.effects.define("bounce",function(t,e){var i,s,n=V(this),o=t.mode,a="hide"===o,r="show"===o,l=t.direction||"up",h=t.distance,c=t.times||5,o=2*c+(r||a?1:0),u=t.duration/o,d=t.easing,p="up"===l||"down"===l?"top":"left",f="up"===l||"left"===l,g=0,t=n.queue().length;for(V.effects.createPlaceholder(n),l=n.css(p),h=h||n["top"==p?"outerHeight":"outerWidth"]()/3,r&&((s={opacity:1})[p]=l,n.css("opacity",0).css(p,f?2*-h:2*h).animate(s,u,d)),a&&(h/=Math.pow(2,c-1)),(s={})[p]=l;g<c;g++)(i={})[p]=(f?"-=":"+=")+h,n.animate(i,u,d).animate(s,u,d),h=a?2*h:h/2;a&&((i={opacity:0})[p]=(f?"-=":"+=")+h,n.animate(i,u,d)),n.queue(e),V.effects.unshift(n,t,1+o)}),V.effects.define("clip","hide",function(t,e){var i={},s=V(this),n=t.direction||"vertical",o="both"===n,a=o||"horizontal"===n,o=o||"vertical"===n,n=s.cssClip();i.clip={top:o?(n.bottom-n.top)/2:n.top,right:a?(n.right-n.left)/2:n.right,bottom:o?(n.bottom-n.top)/2:n.bottom,left:a?(n.right-n.left)/2:n.left},V.effects.createPlaceholder(s),"show"===t.mode&&(s.cssClip(i.clip),i.clip=n),s.animate(i,{queue:!1,duration:t.duration,easing:t.easing,complete:e})}),V.effects.define("drop","hide",function(t,e){var i=V(this),s="show"===t.mode,n=t.direction||"left",o="up"===n||"down"===n?"top":"left",a="up"===n||"left"===n?"-=":"+=",r="+="==a?"-=":"+=",l={opacity:0};V.effects.createPlaceholder(i),n=t.distance||i["top"==o?"outerHeight":"outerWidth"](!0)/2,l[o]=a+n,s&&(i.css(l),l[o]=r+n,l.opacity=1),i.animate(l,{queue:!1,duration:t.duration,easing:t.easing,complete:e})}),V.effects.define("explode","hide",function(t,e){var i,s,n,o,a,r,l=t.pieces?Math.round(Math.sqrt(t.pieces)):3,h=l,c=V(this),u="show"===t.mode,d=c.show().css("visibility","hidden").offset(),p=Math.ceil(c.outerWidth()/h),f=Math.ceil(c.outerHeight()/l),g=[];function m(){g.push(this),g.length===l*h&&(c.css({visibility:"visible"}),V(g).remove(),e())}for(i=0;i<l;i++)for(o=d.top+i*f,r=i-(l-1)/2,s=0;s<h;s++)n=d.left+s*p,a=s-(h-1)/2,c.clone().appendTo("body").wrap("<div></div>").css({position:"absolute",visibility:"visible",left:-s*p,top:-i*f}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:p,height:f,left:n+(u?a*p:0),top:o+(u?r*f:0),opacity:u?0:1}).animate({left:n+(u?0:a*p),top:o+(u?0:r*f),opacity:u?1:0},t.duration||500,t.easing,m)}),V.effects.define("fade","toggle",function(t,e){var i="show"===t.mode;V(this).css("opacity",i?0:1).animate({opacity:i?1:0},{queue:!1,duration:t.duration,easing:t.easing,complete:e})}),V.effects.define("fold","hide",function(e,t){var i=V(this),s=e.mode,n="show"===s,o="hide"===s,a=e.size||15,r=/([0-9]+)%/.exec(a),l=!!e.horizFirst?["right","bottom"]:["bottom","right"],h=e.duration/2,c=V.effects.createPlaceholder(i),u=i.cssClip(),d={clip:V.extend({},u)},p={clip:V.extend({},u)},f=[u[l[0]],u[l[1]]],s=i.queue().length;r&&(a=parseInt(r[1],10)/100*f[o?0:1]),d.clip[l[0]]=a,p.clip[l[0]]=a,p.clip[l[1]]=0,n&&(i.cssClip(p.clip),c&&c.css(V.effects.clipToBox(p)),p.clip=u),i.queue(function(t){c&&c.animate(V.effects.clipToBox(d),h,e.easing).animate(V.effects.clipToBox(p),h,e.easing),t()}).animate(d,h,e.easing).animate(p,h,e.easing).queue(t),V.effects.unshift(i,s,4)}),V.effects.define("highlight","show",function(t,e){var i=V(this),s={backgroundColor:i.css("backgroundColor")};"hide"===t.mode&&(s.opacity=0),V.effects.saveStyle(i),i.css({backgroundImage:"none",backgroundColor:t.color||"#ffff99"}).animate(s,{queue:!1,duration:t.duration,easing:t.easing,complete:e})}),V.effects.define("size",function(s,e){var n,i=V(this),t=["fontSize"],o=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],a=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],r=s.mode,l="effect"!==r,h=s.scale||"both",c=s.origin||["middle","center"],u=i.css("position"),d=i.position(),p=V.effects.scaledDimensions(i),f=s.from||p,g=s.to||V.effects.scaledDimensions(i,0);V.effects.createPlaceholder(i),"show"===r&&(r=f,f=g,g=r),n={from:{y:f.height/p.height,x:f.width/p.width},to:{y:g.height/p.height,x:g.width/p.width}},"box"!==h&&"both"!==h||(n.from.y!==n.to.y&&(f=V.effects.setTransition(i,o,n.from.y,f),g=V.effects.setTransition(i,o,n.to.y,g)),n.from.x!==n.to.x&&(f=V.effects.setTransition(i,a,n.from.x,f),g=V.effects.setTransition(i,a,n.to.x,g))),"content"!==h&&"both"!==h||n.from.y!==n.to.y&&(f=V.effects.setTransition(i,t,n.from.y,f),g=V.effects.setTransition(i,t,n.to.y,g)),c&&(c=V.effects.getBaseline(c,p),f.top=(p.outerHeight-f.outerHeight)*c.y+d.top,f.left=(p.outerWidth-f.outerWidth)*c.x+d.left,g.top=(p.outerHeight-g.outerHeight)*c.y+d.top,g.left=(p.outerWidth-g.outerWidth)*c.x+d.left),delete f.outerHeight,delete f.outerWidth,i.css(f),"content"!==h&&"both"!==h||(o=o.concat(["marginTop","marginBottom"]).concat(t),a=a.concat(["marginLeft","marginRight"]),i.find("*[width]").each(function(){var t=V(this),e=V.effects.scaledDimensions(t),i={height:e.height*n.from.y,width:e.width*n.from.x,outerHeight:e.outerHeight*n.from.y,outerWidth:e.outerWidth*n.from.x},e={height:e.height*n.to.y,width:e.width*n.to.x,outerHeight:e.height*n.to.y,outerWidth:e.width*n.to.x};n.from.y!==n.to.y&&(i=V.effects.setTransition(t,o,n.from.y,i),e=V.effects.setTransition(t,o,n.to.y,e)),n.from.x!==n.to.x&&(i=V.effects.setTransition(t,a,n.from.x,i),e=V.effects.setTransition(t,a,n.to.x,e)),l&&V.effects.saveStyle(t),t.css(i),t.animate(e,s.duration,s.easing,function(){l&&V.effects.restoreStyle(t)})})),i.animate(g,{queue:!1,duration:s.duration,easing:s.easing,complete:function(){var t=i.offset();0===g.opacity&&i.css("opacity",f.opacity),l||(i.css("position","static"===u?"relative":u).offset(t),V.effects.saveStyle(i)),e()}})}),V.effects.define("scale",function(t,e){var i=V(this),s=t.mode,s=parseInt(t.percent,10)||(0===parseInt(t.percent,10)||"effect"!==s?0:100),s=V.extend(!0,{from:V.effects.scaledDimensions(i),to:V.effects.scaledDimensions(i,s,t.direction||"both"),origin:t.origin||["middle","center"]},t);t.fade&&(s.from.opacity=1,s.to.opacity=0),V.effects.effect.size.call(this,s,e)}),V.effects.define("puff","hide",function(t,e){t=V.extend(!0,{},t,{fade:!0,percent:parseInt(t.percent,10)||150});V.effects.effect.scale.call(this,t,e)}),V.effects.define("pulsate","show",function(t,e){var i=V(this),s=t.mode,n="show"===s,o=2*(t.times||5)+(n||"hide"===s?1:0),a=t.duration/o,r=0,l=1,s=i.queue().length;for(!n&&i.is(":visible")||(i.css("opacity",0).show(),r=1);l<o;l++)i.animate({opacity:r},a,t.easing),r=1-r;i.animate({opacity:r},a,t.easing),i.queue(e),V.effects.unshift(i,s,1+o)}),V.effects.define("shake",function(t,e){var i=1,s=V(this),n=t.direction||"left",o=t.distance||20,a=t.times||3,r=2*a+1,l=Math.round(t.duration/r),h="up"===n||"down"===n?"top":"left",c="up"===n||"left"===n,u={},d={},p={},n=s.queue().length;for(V.effects.createPlaceholder(s),u[h]=(c?"-=":"+=")+o,d[h]=(c?"+=":"-=")+2*o,p[h]=(c?"-=":"+=")+2*o,s.animate(u,l,t.easing);i<a;i++)s.animate(d,l,t.easing).animate(p,l,t.easing);s.animate(d,l,t.easing).animate(u,l/2,t.easing).queue(e),V.effects.unshift(s,n,1+r)}),V.effects.define("slide","show",function(t,e){var i,s,n=V(this),o={up:["bottom","top"],down:["top","bottom"],left:["right","left"],right:["left","right"]},a=t.mode,r=t.direction||"left",l="up"===r||"down"===r?"top":"left",h="up"===r||"left"===r,c=t.distance||n["top"==l?"outerHeight":"outerWidth"](!0),u={};V.effects.createPlaceholder(n),i=n.cssClip(),s=n.position()[l],u[l]=(h?-1:1)*c+s,u.clip=n.cssClip(),u.clip[o[r][1]]=u.clip[o[r][0]],"show"===a&&(n.cssClip(u.clip),n.css(l,u[l]),u.clip=i,u[l]=s),n.animate(u,{queue:!1,duration:t.duration,easing:t.easing,complete:e})}),y=!1!==V.uiBackCompat?V.effects.define("transfer",function(t,e){V(this).transfer(t,e)}):y;V.ui.focusable=function(t,e){var i,s,n,o,a=t.nodeName.toLowerCase();return"area"===a?(s=(i=t.parentNode).name,!(!t.href||!s||"map"!==i.nodeName.toLowerCase())&&(0<(s=V("img[usemap='#"+s+"']")).length&&s.is(":visible"))):(/^(input|select|textarea|button|object)$/.test(a)?(n=!t.disabled)&&(o=V(t).closest("fieldset")[0])&&(n=!o.disabled):n="a"===a&&t.href||e,n&&V(t).is(":visible")&&function(t){var e=t.css("visibility");for(;"inherit"===e;)t=t.parent(),e=t.css("visibility");return"visible"===e}(V(t)))},V.extend(V.expr.pseudos,{focusable:function(t){return V.ui.focusable(t,null!=V.attr(t,"tabindex"))}});var Q,J;V.ui.focusable,V.fn._form=function(){return"string"==typeof this[0].form?this.closest("form"):V(this[0].form)},V.ui.formResetMixin={_formResetHandler:function(){var e=V(this);setTimeout(function(){var t=e.data("ui-form-reset-instances");V.each(t,function(){this.refresh()})})},_bindFormResetHandler:function(){var t;this.form=this.element._form(),this.form.length&&((t=this.form.data("ui-form-reset-instances")||[]).length||this.form.on("reset.ui-form-reset",this._formResetHandler),t.push(this),this.form.data("ui-form-reset-instances",t))},_unbindFormResetHandler:function(){var t;this.form.length&&((t=this.form.data("ui-form-reset-instances")).splice(V.inArray(this,t),1),t.length?this.form.data("ui-form-reset-instances",t):this.form.removeData("ui-form-reset-instances").off("reset.ui-form-reset"))}};V.expr.pseudos||(V.expr.pseudos=V.expr[":"]),V.uniqueSort||(V.uniqueSort=V.unique),V.escapeSelector||(Q=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g,J=function(t,e){return e?"\0"===t?"�":t.slice(0,-1)+"\\"+t.charCodeAt(t.length-1).toString(16)+" ":"\\"+t},V.escapeSelector=function(t){return(t+"").replace(Q,J)}),V.fn.even&&V.fn.odd||V.fn.extend({even:function(){return this.filter(function(t){return t%2==0})},odd:function(){return this.filter(function(t){return t%2==1})}});var Z;V.ui.keyCode={BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38},V.fn.labels=function(){var t,e,i;return this.length?this[0].labels&&this[0].labels.length?this.pushStack(this[0].labels):(e=this.eq(0).parents("label"),(t=this.attr("id"))&&(i=(i=this.eq(0).parents().last()).add((i.length?i:this).siblings()),t="label[for='"+V.escapeSelector(t)+"']",e=e.add(i.find(t).addBack(t))),this.pushStack(e)):this.pushStack([])},V.fn.scrollParent=function(t){var e=this.css("position"),i="absolute"===e,s=t?/(auto|scroll|hidden)/:/(auto|scroll)/,t=this.parents().filter(function(){var t=V(this);return(!i||"static"!==t.css("position"))&&s.test(t.css("overflow")+t.css("overflow-y")+t.css("overflow-x"))}).eq(0);return"fixed"!==e&&t.length?t:V(this[0].ownerDocument||document)},V.extend(V.expr.pseudos,{tabbable:function(t){var e=V.attr(t,"tabindex"),i=null!=e;return(!i||0<=e)&&V.ui.focusable(t,i)}}),V.fn.extend({uniqueId:(Z=0,function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++Z)})}),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&V(this).removeAttr("id")})}}),V.widget("ui.accordion",{version:"1.13.0",options:{active:0,animate:{},classes:{"ui-accordion-header":"ui-corner-top","ui-accordion-header-collapsed":"ui-corner-all","ui-accordion-content":"ui-corner-bottom"},collapsible:!1,event:"click",header:function(t){return t.find("> li > :first-child").add(t.find("> :not(li)").even())},heightStyle:"auto",icons:{activeHeader:"ui-icon-triangle-1-s",header:"ui-icon-triangle-1-e"},activate:null,beforeActivate:null},hideProps:{borderTopWidth:"hide",borderBottomWidth:"hide",paddingTop:"hide",paddingBottom:"hide",height:"hide"},showProps:{borderTopWidth:"show",borderBottomWidth:"show",paddingTop:"show",paddingBottom:"show",height:"show"},_create:function(){var t=this.options;this.prevShow=this.prevHide=V(),this._addClass("ui-accordion","ui-widget ui-helper-reset"),this.element.attr("role","tablist"),t.collapsible||!1!==t.active&&null!=t.active||(t.active=0),this._processPanels(),t.active<0&&(t.active+=this.headers.length),this._refresh()},_getCreateEventData:function(){return{header:this.active,panel:this.active.length?this.active.next():V()}},_createIcons:function(){var t,e=this.options.icons;e&&(t=V("<span>"),this._addClass(t,"ui-accordion-header-icon","ui-icon "+e.header),t.prependTo(this.headers),t=this.active.children(".ui-accordion-header-icon"),this._removeClass(t,e.header)._addClass(t,null,e.activeHeader)._addClass(this.headers,"ui-accordion-icons"))},_destroyIcons:function(){this._removeClass(this.headers,"ui-accordion-icons"),this.headers.children(".ui-accordion-header-icon").remove()},_destroy:function(){var t;this.element.removeAttr("role"),this.headers.removeAttr("role aria-expanded aria-selected aria-controls tabIndex").removeUniqueId(),this._destroyIcons(),t=this.headers.next().css("display","").removeAttr("role aria-hidden aria-labelledby").removeUniqueId(),"content"!==this.options.heightStyle&&t.css("height","")},_setOption:function(t,e){"active"!==t?("event"===t&&(this.options.event&&this._off(this.headers,this.options.event),this._setupEvents(e)),this._super(t,e),"collapsible"!==t||e||!1!==this.options.active||this._activate(0),"icons"===t&&(this._destroyIcons(),e&&this._createIcons())):this._activate(e)},_setOptionDisabled:function(t){this._super(t),this.element.attr("aria-disabled",t),this._toggleClass(null,"ui-state-disabled",!!t),this._toggleClass(this.headers.add(this.headers.next()),null,"ui-state-disabled",!!t)},_keydown:function(t){if(!t.altKey&&!t.ctrlKey){var e=V.ui.keyCode,i=this.headers.length,s=this.headers.index(t.target),n=!1;switch(t.keyCode){case e.RIGHT:case e.DOWN:n=this.headers[(s+1)%i];break;case e.LEFT:case e.UP:n=this.headers[(s-1+i)%i];break;case e.SPACE:case e.ENTER:this._eventHandler(t);break;case e.HOME:n=this.headers[0];break;case e.END:n=this.headers[i-1]}n&&(V(t.target).attr("tabIndex",-1),V(n).attr("tabIndex",0),V(n).trigger("focus"),t.preventDefault())}},_panelKeyDown:function(t){t.keyCode===V.ui.keyCode.UP&&t.ctrlKey&&V(t.currentTarget).prev().trigger("focus")},refresh:function(){var t=this.options;this._processPanels(),!1===t.active&&!0===t.collapsible||!this.headers.length?(t.active=!1,this.active=V()):!1===t.active?this._activate(0):this.active.length&&!V.contains(this.element[0],this.active[0])?this.headers.length===this.headers.find(".ui-state-disabled").length?(t.active=!1,this.active=V()):this._activate(Math.max(0,t.active-1)):t.active=this.headers.index(this.active),this._destroyIcons(),this._refresh()},_processPanels:function(){var t=this.headers,e=this.panels;"function"==typeof this.options.header?this.headers=this.options.header(this.element):this.headers=this.element.find(this.options.header),this._addClass(this.headers,"ui-accordion-header ui-accordion-header-collapsed","ui-state-default"),this.panels=this.headers.next().filter(":not(.ui-accordion-content-active)").hide(),this._addClass(this.panels,"ui-accordion-content","ui-helper-reset ui-widget-content"),e&&(this._off(t.not(this.headers)),this._off(e.not(this.panels)))},_refresh:function(){var i,t=this.options,e=t.heightStyle,s=this.element.parent();this.active=this._findActive(t.active),this._addClass(this.active,"ui-accordion-header-active","ui-state-active")._removeClass(this.active,"ui-accordion-header-collapsed"),this._addClass(this.active.next(),"ui-accordion-content-active"),this.active.next().show(),this.headers.attr("role","tab").each(function(){var t=V(this),e=t.uniqueId().attr("id"),i=t.next(),s=i.uniqueId().attr("id");t.attr("aria-controls",s),i.attr("aria-labelledby",e)}).next().attr("role","tabpanel"),this.headers.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}).next().attr({"aria-hidden":"true"}).hide(),this.active.length?this.active.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}).next().attr({"aria-hidden":"false"}):this.headers.eq(0).attr("tabIndex",0),this._createIcons(),this._setupEvents(t.event),"fill"===e?(i=s.height(),this.element.siblings(":visible").each(function(){var t=V(this),e=t.css("position");"absolute"!==e&&"fixed"!==e&&(i-=t.outerHeight(!0))}),this.headers.each(function(){i-=V(this).outerHeight(!0)}),this.headers.next().each(function(){V(this).height(Math.max(0,i-V(this).innerHeight()+V(this).height()))}).css("overflow","auto")):"auto"===e&&(i=0,this.headers.next().each(function(){var t=V(this).is(":visible");t||V(this).show(),i=Math.max(i,V(this).css("height","").height()),t||V(this).hide()}).height(i))},_activate:function(t){t=this._findActive(t)[0];t!==this.active[0]&&(t=t||this.active[0],this._eventHandler({target:t,currentTarget:t,preventDefault:V.noop}))},_findActive:function(t){return"number"==typeof t?this.headers.eq(t):V()},_setupEvents:function(t){var i={keydown:"_keydown"};t&&V.each(t.split(" "),function(t,e){i[e]="_eventHandler"}),this._off(this.headers.add(this.headers.next())),this._on(this.headers,i),this._on(this.headers.next(),{keydown:"_panelKeyDown"}),this._hoverable(this.headers),this._focusable(this.headers)},_eventHandler:function(t){var e=this.options,i=this.active,s=V(t.currentTarget),n=s[0]===i[0],o=n&&e.collapsible,a=o?V():s.next(),r=i.next(),a={oldHeader:i,oldPanel:r,newHeader:o?V():s,newPanel:a};t.preventDefault(),n&&!e.collapsible||!1===this._trigger("beforeActivate",t,a)||(e.active=!o&&this.headers.index(s),this.active=n?V():s,this._toggle(a),this._removeClass(i,"ui-accordion-header-active","ui-state-active"),e.icons&&(i=i.children(".ui-accordion-header-icon"),this._removeClass(i,null,e.icons.activeHeader)._addClass(i,null,e.icons.header)),n||(this._removeClass(s,"ui-accordion-header-collapsed")._addClass(s,"ui-accordion-header-active","ui-state-active"),e.icons&&(n=s.children(".ui-accordion-header-icon"),this._removeClass(n,null,e.icons.header)._addClass(n,null,e.icons.activeHeader)),this._addClass(s.next(),"ui-accordion-content-active")))},_toggle:function(t){var e=t.newPanel,i=this.prevShow.length?this.prevShow:t.oldPanel;this.prevShow.add(this.prevHide).stop(!0,!0),this.prevShow=e,this.prevHide=i,this.options.animate?this._animate(e,i,t):(i.hide(),e.show(),this._toggleComplete(t)),i.attr({"aria-hidden":"true"}),i.prev().attr({"aria-selected":"false","aria-expanded":"false"}),e.length&&i.length?i.prev().attr({tabIndex:-1,"aria-expanded":"false"}):e.length&&this.headers.filter(function(){return 0===parseInt(V(this).attr("tabIndex"),10)}).attr("tabIndex",-1),e.attr("aria-hidden","false").prev().attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0})},_animate:function(t,i,e){var s,n,o,a=this,r=0,l=t.css("box-sizing"),h=t.length&&(!i.length||t.index()<i.index()),c=this.options.animate||{},u=h&&c.down||c,h=function(){a._toggleComplete(e)};return n=(n="string"==typeof u?u:n)||u.easing||c.easing,o=(o="number"==typeof u?u:o)||u.duration||c.duration,i.length?t.length?(s=t.show().outerHeight(),i.animate(this.hideProps,{duration:o,easing:n,step:function(t,e){e.now=Math.round(t)}}),void t.hide().animate(this.showProps,{duration:o,easing:n,complete:h,step:function(t,e){e.now=Math.round(t),"height"!==e.prop?"content-box"===l&&(r+=e.now):"content"!==a.options.heightStyle&&(e.now=Math.round(s-i.outerHeight()-r),r=0)}})):i.animate(this.hideProps,o,n,h):t.animate(this.showProps,o,n,h)},_toggleComplete:function(t){var e=t.oldPanel,i=e.prev();this._removeClass(e,"ui-accordion-content-active"),this._removeClass(i,"ui-accordion-header-active")._addClass(i,"ui-accordion-header-collapsed"),e.length&&(e.parent()[0].className=e.parent()[0].className),this._trigger("activate",null,t)}}),V.ui.safeActiveElement=function(e){var i;try{i=e.activeElement}catch(t){i=e.body}return i=!(i=i||e.body).nodeName?e.body:i},V.widget("ui.menu",{version:"1.13.0",defaultElement:"<ul>",delay:300,options:{icons:{submenu:"ui-icon-caret-1-e"},items:"> *",menus:"ul",position:{my:"left top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.lastMousePosition={x:null,y:null},this.element.uniqueId().attr({role:this.options.role,tabIndex:0}),this._addClass("ui-menu","ui-widget ui-widget-content"),this._on({"mousedown .ui-menu-item":function(t){t.preventDefault(),this._activateItem(t)},"click .ui-menu-item":function(t){var e=V(t.target),i=V(V.ui.safeActiveElement(this.document[0]));!this.mouseHandled&&e.not(".ui-state-disabled").length&&(this.select(t),t.isPropagationStopped()||(this.mouseHandled=!0),e.has(".ui-menu").length?this.expand(t):!this.element.is(":focus")&&i.closest(".ui-menu").length&&(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":"_activateItem","mousemove .ui-menu-item":"_activateItem",mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(t,e){var i=this.active||this._menuItems().first();e||this.focus(t,i)},blur:function(t){this._delay(function(){V.contains(this.element[0],V.ui.safeActiveElement(this.document[0]))||this.collapseAll(t)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(t){this._closeOnDocumentClick(t)&&this.collapseAll(t,!0),this.mouseHandled=!1}})},_activateItem:function(t){var e,i;this.previousFilter||t.clientX===this.lastMousePosition.x&&t.clientY===this.lastMousePosition.y||(this.lastMousePosition={x:t.clientX,y:t.clientY},e=V(t.target).closest(".ui-menu-item"),i=V(t.currentTarget),e[0]===i[0]&&(i.is(".ui-state-active")||(this._removeClass(i.siblings().children(".ui-state-active"),null,"ui-state-active"),this.focus(t,i))))},_destroy:function(){var t=this.element.find(".ui-menu-item").removeAttr("role aria-disabled").children(".ui-menu-item-wrapper").removeUniqueId().removeAttr("tabIndex role aria-haspopup");this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeAttr("role aria-labelledby aria-expanded aria-hidden aria-disabled tabIndex").removeUniqueId().show(),t.children().each(function(){var t=V(this);t.data("ui-menu-submenu-caret")&&t.remove()})},_keydown:function(t){var e,i,s,n=!0;switch(t.keyCode){case V.ui.keyCode.PAGE_UP:this.previousPage(t);break;case V.ui.keyCode.PAGE_DOWN:this.nextPage(t);break;case V.ui.keyCode.HOME:this._move("first","first",t);break;case V.ui.keyCode.END:this._move("last","last",t);break;case V.ui.keyCode.UP:this.previous(t);break;case V.ui.keyCode.DOWN:this.next(t);break;case V.ui.keyCode.LEFT:this.collapse(t);break;case V.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(t);break;case V.ui.keyCode.ENTER:case V.ui.keyCode.SPACE:this._activate(t);break;case V.ui.keyCode.ESCAPE:this.collapse(t);break;default:e=this.previousFilter||"",s=n=!1,i=96<=t.keyCode&&t.keyCode<=105?(t.keyCode-96).toString():String.fromCharCode(t.keyCode),clearTimeout(this.filterTimer),i===e?s=!0:i=e+i,e=this._filterMenuItems(i),(e=s&&-1!==e.index(this.active.next())?this.active.nextAll(".ui-menu-item"):e).length||(i=String.fromCharCode(t.keyCode),e=this._filterMenuItems(i)),e.length?(this.focus(t,e),this.previousFilter=i,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter}n&&t.preventDefault()},_activate:function(t){this.active&&!this.active.is(".ui-state-disabled")&&(this.active.children("[aria-haspopup='true']").length?this.expand(t):this.select(t))},refresh:function(){var t,e,s=this,n=this.options.icons.submenu,i=this.element.find(this.options.menus);this._toggleClass("ui-menu-icons",null,!!this.element.find(".ui-icon").length),e=i.filter(":not(.ui-menu)").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var t=V(this),e=t.prev(),i=V("<span>").data("ui-menu-submenu-caret",!0);s._addClass(i,"ui-menu-icon","ui-icon "+n),e.attr("aria-haspopup","true").prepend(i),t.attr("aria-labelledby",e.attr("id"))}),this._addClass(e,"ui-menu","ui-widget ui-widget-content ui-front"),(t=i.add(this.element).find(this.options.items)).not(".ui-menu-item").each(function(){var t=V(this);s._isDivider(t)&&s._addClass(t,"ui-menu-divider","ui-widget-content")}),i=(e=t.not(".ui-menu-item, .ui-menu-divider")).children().not(".ui-menu").uniqueId().attr({tabIndex:-1,role:this._itemRole()}),this._addClass(e,"ui-menu-item")._addClass(i,"ui-menu-item-wrapper"),t.filter(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!V.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(t,e){var i;"icons"===t&&(i=this.element.find(".ui-menu-icon"),this._removeClass(i,null,this.options.icons.submenu)._addClass(i,null,e.submenu)),this._super(t,e)},_setOptionDisabled:function(t){this._super(t),this.element.attr("aria-disabled",String(t)),this._toggleClass(null,"ui-state-disabled",!!t)},focus:function(t,e){var i;this.blur(t,t&&"focus"===t.type),this._scrollIntoView(e),this.active=e.first(),i=this.active.children(".ui-menu-item-wrapper"),this._addClass(i,null,"ui-state-active"),this.options.role&&this.element.attr("aria-activedescendant",i.attr("id")),i=this.active.parent().closest(".ui-menu-item").children(".ui-menu-item-wrapper"),this._addClass(i,null,"ui-state-active"),t&&"keydown"===t.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),(i=e.children(".ui-menu")).length&&t&&/^mouse/.test(t.type)&&this._startOpening(i),this.activeMenu=e.parent(),this._trigger("focus",t,{item:e})},_scrollIntoView:function(t){var e,i,s;this._hasScroll()&&(i=parseFloat(V.css(this.activeMenu[0],"borderTopWidth"))||0,s=parseFloat(V.css(this.activeMenu[0],"paddingTop"))||0,e=t.offset().top-this.activeMenu.offset().top-i-s,i=this.activeMenu.scrollTop(),s=this.activeMenu.height(),t=t.outerHeight(),e<0?this.activeMenu.scrollTop(i+e):s<e+t&&this.activeMenu.scrollTop(i+e-s+t))},blur:function(t,e){e||clearTimeout(this.timer),this.active&&(this._removeClass(this.active.children(".ui-menu-item-wrapper"),null,"ui-state-active"),this._trigger("blur",t,{item:this.active}),this.active=null)},_startOpening:function(t){clearTimeout(this.timer),"true"===t.attr("aria-hidden")&&(this.timer=this._delay(function(){this._close(),this._open(t)},this.delay))},_open:function(t){var e=V.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(t.parents(".ui-menu")).hide().attr("aria-hidden","true"),t.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(e)},collapseAll:function(e,i){clearTimeout(this.timer),this.timer=this._delay(function(){var t=i?this.element:V(e&&e.target).closest(this.element.find(".ui-menu"));t.length||(t=this.element),this._close(t),this.blur(e),this._removeClass(t.find(".ui-state-active"),null,"ui-state-active"),this.activeMenu=t},i?0:this.delay)},_close:function(t){(t=t||(this.active?this.active.parent():this.element)).find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false")},_closeOnDocumentClick:function(t){return!V(t.target).closest(".ui-menu").length},_isDivider:function(t){return!/[^\-\u2014\u2013\s]/.test(t.text())},collapse:function(t){var e=this.active&&this.active.parent().closest(".ui-menu-item",this.element);e&&e.length&&(this._close(),this.focus(t,e))},expand:function(t){var e=this.active&&this._menuItems(this.active.children(".ui-menu")).first();e&&e.length&&(this._open(e.parent()),this._delay(function(){this.focus(t,e)}))},next:function(t){this._move("next","first",t)},previous:function(t){this._move("prev","last",t)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_menuItems:function(t){return(t||this.element).find(this.options.items).filter(".ui-menu-item")},_move:function(t,e,i){var s;(s=this.active?"first"===t||"last"===t?this.active["first"===t?"prevAll":"nextAll"](".ui-menu-item").last():this.active[t+"All"](".ui-menu-item").first():s)&&s.length&&this.active||(s=this._menuItems(this.activeMenu)[e]()),this.focus(i,s)},nextPage:function(t){var e,i,s;this.active?this.isLastItem()||(this._hasScroll()?(i=this.active.offset().top,s=this.element.innerHeight(),0===V.fn.jquery.indexOf("3.2.")&&(s+=this.element[0].offsetHeight-this.element.outerHeight()),this.active.nextAll(".ui-menu-item").each(function(){return(e=V(this)).offset().top-i-s<0}),this.focus(t,e)):this.focus(t,this._menuItems(this.activeMenu)[this.active?"last":"first"]())):this.next(t)},previousPage:function(t){var e,i,s;this.active?this.isFirstItem()||(this._hasScroll()?(i=this.active.offset().top,s=this.element.innerHeight(),0===V.fn.jquery.indexOf("3.2.")&&(s+=this.element[0].offsetHeight-this.element.outerHeight()),this.active.prevAll(".ui-menu-item").each(function(){return 0<(e=V(this)).offset().top-i+s}),this.focus(t,e)):this.focus(t,this._menuItems(this.activeMenu).first())):this.next(t)},_hasScroll:function(){return this.element.outerHeight()<this.element.prop("scrollHeight")},select:function(t){this.active=this.active||V(t.target).closest(".ui-menu-item");var e={item:this.active};this.active.has(".ui-menu").length||this.collapseAll(t,!0),this._trigger("select",t,e)},_filterMenuItems:function(t){var t=t.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&"),e=new RegExp("^"+t,"i");return this.activeMenu.find(this.options.items).filter(".ui-menu-item").filter(function(){return e.test(String.prototype.trim.call(V(this).children(".ui-menu-item-wrapper").text()))})}});V.widget("ui.autocomplete",{version:"1.13.0",defaultElement:"<input>",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},requestIndex:0,pending:0,_create:function(){var i,s,n,t=this.element[0].nodeName.toLowerCase(),e="textarea"===t,t="input"===t;this.isMultiLine=e||!t&&this._isContentEditable(this.element),this.valueMethod=this.element[e||t?"val":"text"],this.isNewMenu=!0,this._addClass("ui-autocomplete-input"),this.element.attr("autocomplete","off"),this._on(this.element,{keydown:function(t){if(this.element.prop("readOnly"))s=n=i=!0;else{s=n=i=!1;var e=V.ui.keyCode;switch(t.keyCode){case e.PAGE_UP:i=!0,this._move("previousPage",t);break;case e.PAGE_DOWN:i=!0,this._move("nextPage",t);break;case e.UP:i=!0,this._keyEvent("previous",t);break;case e.DOWN:i=!0,this._keyEvent("next",t);break;case e.ENTER:this.menu.active&&(i=!0,t.preventDefault(),this.menu.select(t));break;case e.TAB:this.menu.active&&this.menu.select(t);break;case e.ESCAPE:this.menu.element.is(":visible")&&(this.isMultiLine||this._value(this.term),this.close(t),t.preventDefault());break;default:s=!0,this._searchTimeout(t)}}},keypress:function(t){if(i)return i=!1,void(this.isMultiLine&&!this.menu.element.is(":visible")||t.preventDefault());if(!s){var e=V.ui.keyCode;switch(t.keyCode){case e.PAGE_UP:this._move("previousPage",t);break;case e.PAGE_DOWN:this._move("nextPage",t);break;case e.UP:this._keyEvent("previous",t);break;case e.DOWN:this._keyEvent("next",t)}}},input:function(t){if(n)return n=!1,void t.preventDefault();this._searchTimeout(t)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(t){clearTimeout(this.searching),this.close(t),this._change(t)}}),this._initSource(),this.menu=V("<ul>").appendTo(this._appendTo()).menu({role:null}).hide().attr({unselectable:"on"}).menu("instance"),this._addClass(this.menu.element,"ui-autocomplete","ui-front"),this._on(this.menu.element,{mousedown:function(t){t.preventDefault()},menufocus:function(t,e){var i;if(this.isNewMenu&&(this.isNewMenu=!1,t.originalEvent&&/^mouse/.test(t.originalEvent.type)))return this.menu.blur(),void this.document.one("mousemove",function(){V(t.target).trigger(t.originalEvent)});i=e.item.data("ui-autocomplete-item"),!1!==this._trigger("focus",t,{item:i})&&t.originalEvent&&/^key/.test(t.originalEvent.type)&&this._value(i.value),(i=e.item.attr("aria-label")||i.value)&&String.prototype.trim.call(i).length&&(this.liveRegion.children().hide(),V("<div>").text(i).appendTo(this.liveRegion))},menuselect:function(t,e){var i=e.item.data("ui-autocomplete-item"),s=this.previous;this.element[0]!==V.ui.safeActiveElement(this.document[0])&&(this.element.trigger("focus"),this.previous=s,this._delay(function(){this.previous=s,this.selectedItem=i})),!1!==this._trigger("select",t,{item:i})&&this._value(i.value),this.term=this._value(),this.close(t),this.selectedItem=i}}),this.liveRegion=V("<div>",{role:"status","aria-live":"assertive","aria-relevant":"additions"}).appendTo(this.document[0].body),this._addClass(this.liveRegion,null,"ui-helper-hidden-accessible"),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_destroy:function(){clearTimeout(this.searching),this.element.removeAttr("autocomplete"),this.menu.element.remove(),this.liveRegion.remove()},_setOption:function(t,e){this._super(t,e),"source"===t&&this._initSource(),"appendTo"===t&&this.menu.element.appendTo(this._appendTo()),"disabled"===t&&e&&this.xhr&&this.xhr.abort()},_isEventTargetInWidget:function(t){var e=this.menu.element[0];return t.target===this.element[0]||t.target===e||V.contains(e,t.target)},_closeOnClickOutside:function(t){this._isEventTargetInWidget(t)||this.close()},_appendTo:function(){var t=this.options.appendTo;return t=!(t=!(t=t&&(t.jquery||t.nodeType?V(t):this.document.find(t).eq(0)))||!t[0]?this.element.closest(".ui-front, dialog"):t).length?this.document[0].body:t},_initSource:function(){var i,s,n=this;Array.isArray(this.options.source)?(i=this.options.source,this.source=function(t,e){e(V.ui.autocomplete.filter(i,t.term))}):"string"==typeof this.options.source?(s=this.options.source,this.source=function(t,e){n.xhr&&n.xhr.abort(),n.xhr=V.ajax({url:s,data:t,dataType:"json",success:function(t){e(t)},error:function(){e([])}})}):this.source=this.options.source},_searchTimeout:function(s){clearTimeout(this.searching),this.searching=this._delay(function(){var t=this.term===this._value(),e=this.menu.element.is(":visible"),i=s.altKey||s.ctrlKey||s.metaKey||s.shiftKey;t&&(e||i)||(this.selectedItem=null,this.search(null,s))},this.options.delay)},search:function(t,e){return t=null!=t?t:this._value(),this.term=this._value(),t.length<this.options.minLength?this.close(e):!1!==this._trigger("search",e)?this._search(t):void 0},_search:function(t){this.pending++,this._addClass("ui-autocomplete-loading"),this.cancelSearch=!1,this.source({term:t},this._response())},_response:function(){var e=++this.requestIndex;return function(t){e===this.requestIndex&&this.__response(t),this.pending--,this.pending||this._removeClass("ui-autocomplete-loading")}.bind(this)},__response:function(t){t=t&&this._normalize(t),this._trigger("response",null,{content:t}),!this.options.disabled&&t&&t.length&&!this.cancelSearch?(this._suggest(t),this._trigger("open")):this._close()},close:function(t){this.cancelSearch=!0,this._close(t)},_close:function(t){this._off(this.document,"mousedown"),this.menu.element.is(":visible")&&(this.menu.element.hide(),this.menu.blur(),this.isNewMenu=!0,this._trigger("close",t))},_change:function(t){this.previous!==this._value()&&this._trigger("change",t,{item:this.selectedItem})},_normalize:function(t){return t.length&&t[0].label&&t[0].value?t:V.map(t,function(t){return"string"==typeof t?{label:t,value:t}:V.extend({},t,{label:t.label||t.value,value:t.value||t.label})})},_suggest:function(t){var e=this.menu.element.empty();this._renderMenu(e,t),this.isNewMenu=!0,this.menu.refresh(),e.show(),this._resizeMenu(),e.position(V.extend({of:this.element},this.options.position)),this.options.autoFocus&&this.menu.next(),this._on(this.document,{mousedown:"_closeOnClickOutside"})},_resizeMenu:function(){var t=this.menu.element;t.outerWidth(Math.max(t.width("").outerWidth()+1,this.element.outerWidth()))},_renderMenu:function(i,t){var s=this;V.each(t,function(t,e){s._renderItemData(i,e)})},_renderItemData:function(t,e){return this._renderItem(t,e).data("ui-autocomplete-item",e)},_renderItem:function(t,e){return V("<li>").append(V("<div>").text(e.label)).appendTo(t)},_move:function(t,e){if(this.menu.element.is(":visible"))return this.menu.isFirstItem()&&/^previous/.test(t)||this.menu.isLastItem()&&/^next/.test(t)?(this.isMultiLine||this._value(this.term),void this.menu.blur()):void this.menu[t](e);this.search(null,e)},widget:function(){return this.menu.element},_value:function(){return this.valueMethod.apply(this.element,arguments)},_keyEvent:function(t,e){this.isMultiLine&&!this.menu.element.is(":visible")||(this._move(t,e),e.preventDefault())},_isContentEditable:function(t){if(!t.length)return!1;var e=t.prop("contentEditable");return"inherit"===e?this._isContentEditable(t.parent()):"true"===e}}),V.extend(V.ui.autocomplete,{escapeRegex:function(t){return t.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")},filter:function(t,e){var i=new RegExp(V.ui.autocomplete.escapeRegex(e),"i");return V.grep(t,function(t){return i.test(t.label||t.value||t)})}}),V.widget("ui.autocomplete",V.ui.autocomplete,{options:{messages:{noResults:"No search results.",results:function(t){return t+(1<t?" results are":" result is")+" available, use up and down arrow keys to navigate."}}},__response:function(t){this._superApply(arguments),this.options.disabled||this.cancelSearch||(t=t&&t.length?this.options.messages.results(t.length):this.options.messages.noResults,this.liveRegion.children().hide(),V("<div>").text(t).appendTo(this.liveRegion))}});V.ui.autocomplete;var tt=/ui-corner-([a-z]){2,6}/g;V.widget("ui.controlgroup",{version:"1.13.0",defaultElement:"<div>",options:{direction:"horizontal",disabled:null,onlyVisible:!0,items:{button:"input[type=button], input[type=submit], input[type=reset], button, a",controlgroupLabel:".ui-controlgroup-label",checkboxradio:"input[type='checkbox'], input[type='radio']",selectmenu:"select",spinner:".ui-spinner-input"}},_create:function(){this._enhance()},_enhance:function(){this.element.attr("role","toolbar"),this.refresh()},_destroy:function(){this._callChildMethod("destroy"),this.childWidgets.removeData("ui-controlgroup-data"),this.element.removeAttr("role"),this.options.items.controlgroupLabel&&this.element.find(this.options.items.controlgroupLabel).find(".ui-controlgroup-label-contents").contents().unwrap()},_initWidgets:function(){var o=this,a=[];V.each(this.options.items,function(s,t){var e,n={};if(t)return"controlgroupLabel"===s?((e=o.element.find(t)).each(function(){var t=V(this);t.children(".ui-controlgroup-label-contents").length||t.contents().wrapAll("<span class='ui-controlgroup-label-contents'></span>")}),o._addClass(e,null,"ui-widget ui-widget-content ui-state-default"),void(a=a.concat(e.get()))):void(V.fn[s]&&(n=o["_"+s+"Options"]?o["_"+s+"Options"]("middle"):{classes:{}},o.element.find(t).each(function(){var t=V(this),e=t[s]("instance"),i=V.widget.extend({},n);"button"===s&&t.parent(".ui-spinner").length||((e=e||t[s]()[s]("instance"))&&(i.classes=o._resolveClassesValues(i.classes,e)),t[s](i),i=t[s]("widget"),V.data(i[0],"ui-controlgroup-data",e||t[s]("instance")),a.push(i[0]))})))}),this.childWidgets=V(V.uniqueSort(a)),this._addClass(this.childWidgets,"ui-controlgroup-item")},_callChildMethod:function(e){this.childWidgets.each(function(){var t=V(this).data("ui-controlgroup-data");t&&t[e]&&t[e]()})},_updateCornerClass:function(t,e){e=this._buildSimpleOptions(e,"label").classes.label;this._removeClass(t,null,"ui-corner-top ui-corner-bottom ui-corner-left ui-corner-right ui-corner-all"),this._addClass(t,null,e)},_buildSimpleOptions:function(t,e){var i="vertical"===this.options.direction,s={classes:{}};return s.classes[e]={middle:"",first:"ui-corner-"+(i?"top":"left"),last:"ui-corner-"+(i?"bottom":"right"),only:"ui-corner-all"}[t],s},_spinnerOptions:function(t){t=this._buildSimpleOptions(t,"ui-spinner");return t.classes["ui-spinner-up"]="",t.classes["ui-spinner-down"]="",t},_buttonOptions:function(t){return this._buildSimpleOptions(t,"ui-button")},_checkboxradioOptions:function(t){return this._buildSimpleOptions(t,"ui-checkboxradio-label")},_selectmenuOptions:function(t){var e="vertical"===this.options.direction;return{width:e&&"auto",classes:{middle:{"ui-selectmenu-button-open":"","ui-selectmenu-button-closed":""},first:{"ui-selectmenu-button-open":"ui-corner-"+(e?"top":"tl"),"ui-selectmenu-button-closed":"ui-corner-"+(e?"top":"left")},last:{"ui-selectmenu-button-open":e?"":"ui-corner-tr","ui-selectmenu-button-closed":"ui-corner-"+(e?"bottom":"right")},only:{"ui-selectmenu-button-open":"ui-corner-top","ui-selectmenu-button-closed":"ui-corner-all"}}[t]}},_resolveClassesValues:function(i,s){var n={};return V.each(i,function(t){var e=s.options.classes[t]||"",e=String.prototype.trim.call(e.replace(tt,""));n[t]=(e+" "+i[t]).replace(/\s+/g," ")}),n},_setOption:function(t,e){"direction"===t&&this._removeClass("ui-controlgroup-"+this.options.direction),this._super(t,e),"disabled"!==t?this.refresh():this._callChildMethod(e?"disable":"enable")},refresh:function(){var n,o=this;this._addClass("ui-controlgroup ui-controlgroup-"+this.options.direction),"horizontal"===this.options.direction&&this._addClass(null,"ui-helper-clearfix"),this._initWidgets(),n=this.childWidgets,(n=this.options.onlyVisible?n.filter(":visible"):n).length&&(V.each(["first","last"],function(t,e){var i,s=n[e]().data("ui-controlgroup-data");s&&o["_"+s.widgetName+"Options"]?((i=o["_"+s.widgetName+"Options"](1===n.length?"only":e)).classes=o._resolveClassesValues(i.classes,s),s.element[s.widgetName](i)):o._updateCornerClass(n[e](),e)}),this._callChildMethod("refresh"))}});V.widget("ui.checkboxradio",[V.ui.formResetMixin,{version:"1.13.0",options:{disabled:null,label:null,icon:!0,classes:{"ui-checkboxradio-label":"ui-corner-all","ui-checkboxradio-icon":"ui-corner-all"}},_getCreateOptions:function(){var t,e=this,i=this._super()||{};return this._readType(),t=this.element.labels(),this.label=V(t[t.length-1]),this.label.length||V.error("No label found for checkboxradio widget"),this.originalLabel="",this.label.contents().not(this.element[0]).each(function(){e.originalLabel+=3===this.nodeType?V(this).text():this.outerHTML}),this.originalLabel&&(i.label=this.originalLabel),null!=(t=this.element[0].disabled)&&(i.disabled=t),i},_create:function(){var t=this.element[0].checked;this._bindFormResetHandler(),null==this.options.disabled&&(this.options.disabled=this.element[0].disabled),this._setOption("disabled",this.options.disabled),this._addClass("ui-checkboxradio","ui-helper-hidden-accessible"),this._addClass(this.label,"ui-checkboxradio-label","ui-button ui-widget"),"radio"===this.type&&this._addClass(this.label,"ui-checkboxradio-radio-label"),this.options.label&&this.options.label!==this.originalLabel?this._updateLabel():this.originalLabel&&(this.options.label=this.originalLabel),this._enhance(),t&&this._addClass(this.label,"ui-checkboxradio-checked","ui-state-active"),this._on({change:"_toggleClasses",focus:function(){this._addClass(this.label,null,"ui-state-focus ui-visual-focus")},blur:function(){this._removeClass(this.label,null,"ui-state-focus ui-visual-focus")}})},_readType:function(){var t=this.element[0].nodeName.toLowerCase();this.type=this.element[0].type,"input"===t&&/radio|checkbox/.test(this.type)||V.error("Can't create checkboxradio on element.nodeName="+t+" and element.type="+this.type)},_enhance:function(){this._updateIcon(this.element[0].checked)},widget:function(){return this.label},_getRadioGroup:function(){var t=this.element[0].name,e="input[name='"+V.escapeSelector(t)+"']";return t?(this.form.length?V(this.form[0].elements).filter(e):V(e).filter(function(){return 0===V(this)._form().length})).not(this.element):V([])},_toggleClasses:function(){var t=this.element[0].checked;this._toggleClass(this.label,"ui-checkboxradio-checked","ui-state-active",t),this.options.icon&&"checkbox"===this.type&&this._toggleClass(this.icon,null,"ui-icon-check ui-state-checked",t)._toggleClass(this.icon,null,"ui-icon-blank",!t),"radio"===this.type&&this._getRadioGroup().each(function(){var t=V(this).checkboxradio("instance");t&&t._removeClass(t.label,"ui-checkboxradio-checked","ui-state-active")})},_destroy:function(){this._unbindFormResetHandler(),this.icon&&(this.icon.remove(),this.iconSpace.remove())},_setOption:function(t,e){if("label"!==t||e){if(this._super(t,e),"disabled"===t)return this._toggleClass(this.label,null,"ui-state-disabled",e),void(this.element[0].disabled=e);this.refresh()}},_updateIcon:function(t){var e="ui-icon ui-icon-background ";this.options.icon?(this.icon||(this.icon=V("<span>"),this.iconSpace=V("<span> </span>"),this._addClass(this.iconSpace,"ui-checkboxradio-icon-space")),"checkbox"===this.type?(e+=t?"ui-icon-check ui-state-checked":"ui-icon-blank",this._removeClass(this.icon,null,t?"ui-icon-blank":"ui-icon-check")):e+="ui-icon-blank",this._addClass(this.icon,"ui-checkboxradio-icon",e),t||this._removeClass(this.icon,null,"ui-icon-check ui-state-checked"),this.icon.prependTo(this.label).after(this.iconSpace)):void 0!==this.icon&&(this.icon.remove(),this.iconSpace.remove(),delete this.icon)},_updateLabel:function(){var t=this.label.contents().not(this.element[0]);this.icon&&(t=t.not(this.icon[0])),(t=this.iconSpace?t.not(this.iconSpace[0]):t).remove(),this.label.append(this.options.label)},refresh:function(){var t=this.element[0].checked,e=this.element[0].disabled;this._updateIcon(t),this._toggleClass(this.label,"ui-checkboxradio-checked","ui-state-active",t),null!==this.options.label&&this._updateLabel(),e!==this.options.disabled&&this._setOptions({disabled:e})}}]);var et;V.ui.checkboxradio;V.widget("ui.button",{version:"1.13.0",defaultElement:"<button>",options:{classes:{"ui-button":"ui-corner-all"},disabled:null,icon:null,iconPosition:"beginning",label:null,showLabel:!0},_getCreateOptions:function(){var t,e=this._super()||{};return this.isInput=this.element.is("input"),null!=(t=this.element[0].disabled)&&(e.disabled=t),this.originalLabel=this.isInput?this.element.val():this.element.html(),this.originalLabel&&(e.label=this.originalLabel),e},_create:function(){!this.option.showLabel&!this.options.icon&&(this.options.showLabel=!0),null==this.options.disabled&&(this.options.disabled=this.element[0].disabled||!1),this.hasTitle=!!this.element.attr("title"),this.options.label&&this.options.label!==this.originalLabel&&(this.isInput?this.element.val(this.options.label):this.element.html(this.options.label)),this._addClass("ui-button","ui-widget"),this._setOption("disabled",this.options.disabled),this._enhance(),this.element.is("a")&&this._on({keyup:function(t){t.keyCode===V.ui.keyCode.SPACE&&(t.preventDefault(),this.element[0].click?this.element[0].click():this.element.trigger("click"))}})},_enhance:function(){this.element.is("button")||this.element.attr("role","button"),this.options.icon&&(this._updateIcon("icon",this.options.icon),this._updateTooltip())},_updateTooltip:function(){this.title=this.element.attr("title"),this.options.showLabel||this.title||this.element.attr("title",this.options.label)},_updateIcon:function(t,e){var i="iconPosition"!==t,s=i?this.options.iconPosition:e,t="top"===s||"bottom"===s;this.icon?i&&this._removeClass(this.icon,null,this.options.icon):(this.icon=V("<span>"),this._addClass(this.icon,"ui-button-icon","ui-icon"),this.options.showLabel||this._addClass("ui-button-icon-only")),i&&this._addClass(this.icon,null,e),this._attachIcon(s),t?(this._addClass(this.icon,null,"ui-widget-icon-block"),this.iconSpace&&this.iconSpace.remove()):(this.iconSpace||(this.iconSpace=V("<span> </span>"),this._addClass(this.iconSpace,"ui-button-icon-space")),this._removeClass(this.icon,null,"ui-wiget-icon-block"),this._attachIconSpace(s))},_destroy:function(){this.element.removeAttr("role"),this.icon&&this.icon.remove(),this.iconSpace&&this.iconSpace.remove(),this.hasTitle||this.element.removeAttr("title")},_attachIconSpace:function(t){this.icon[/^(?:end|bottom)/.test(t)?"before":"after"](this.iconSpace)},_attachIcon:function(t){this.element[/^(?:end|bottom)/.test(t)?"append":"prepend"](this.icon)},_setOptions:function(t){var e=(void 0===t.showLabel?this.options:t).showLabel,i=(void 0===t.icon?this.options:t).icon;e||i||(t.showLabel=!0),this._super(t)},_setOption:function(t,e){"icon"===t&&(e?this._updateIcon(t,e):this.icon&&(this.icon.remove(),this.iconSpace&&this.iconSpace.remove())),"iconPosition"===t&&this._updateIcon(t,e),"showLabel"===t&&(this._toggleClass("ui-button-icon-only",null,!e),this._updateTooltip()),"label"===t&&(this.isInput?this.element.val(e):(this.element.html(e),this.icon&&(this._attachIcon(this.options.iconPosition),this._attachIconSpace(this.options.iconPosition)))),this._super(t,e),"disabled"===t&&(this._toggleClass(null,"ui-state-disabled",e),(this.element[0].disabled=e)&&this.element.trigger("blur"))},refresh:function(){var t=this.element.is("input, button")?this.element[0].disabled:this.element.hasClass("ui-button-disabled");t!==this.options.disabled&&this._setOptions({disabled:t}),this._updateTooltip()}}),!1!==V.uiBackCompat&&(V.widget("ui.button",V.ui.button,{options:{text:!0,icons:{primary:null,secondary:null}},_create:function(){this.options.showLabel&&!this.options.text&&(this.options.showLabel=this.options.text),!this.options.showLabel&&this.options.text&&(this.options.text=this.options.showLabel),this.options.icon||!this.options.icons.primary&&!this.options.icons.secondary?this.options.icon&&(this.options.icons.primary=this.options.icon):this.options.icons.primary?this.options.icon=this.options.icons.primary:(this.options.icon=this.options.icons.secondary,this.options.iconPosition="end"),this._super()},_setOption:function(t,e){"text"!==t?("showLabel"===t&&(this.options.text=e),"icon"===t&&(this.options.icons.primary=e),"icons"===t&&(e.primary?(this._super("icon",e.primary),this._super("iconPosition","beginning")):e.secondary&&(this._super("icon",e.secondary),this._super("iconPosition","end"))),this._superApply(arguments)):this._super("showLabel",e)}}),V.fn.button=(et=V.fn.button,function(i){var t="string"==typeof i,s=Array.prototype.slice.call(arguments,1),n=this;return t?this.length||"instance"!==i?this.each(function(){var t=V(this).attr("type"),e=V.data(this,"ui-"+("checkbox"!==t&&"radio"!==t?"button":"checkboxradio"));return"instance"===i?(n=e,!1):e?"function"!=typeof e[i]||"_"===i.charAt(0)?V.error("no such method '"+i+"' for button widget instance"):(t=e[i].apply(e,s))!==e&&void 0!==t?(n=t&&t.jquery?n.pushStack(t.get()):t,!1):void 0:V.error("cannot call methods on button prior to initialization; attempted to call method '"+i+"'")}):n=void 0:(s.length&&(i=V.widget.extend.apply(null,[i].concat(s))),this.each(function(){var t=V(this).attr("type"),e="checkbox"!==t&&"radio"!==t?"button":"checkboxradio",t=V.data(this,"ui-"+e);t?(t.option(i||{}),t._init&&t._init()):"button"!=e?V(this).checkboxradio(V.extend({icon:!1},i)):et.call(V(this),i)})),n}),V.fn.buttonset=function(){return V.ui.controlgroup||V.error("Controlgroup widget missing"),"option"===arguments[0]&&"items"===arguments[1]&&arguments[2]?this.controlgroup.apply(this,[arguments[0],"items.button",arguments[2]]):"option"===arguments[0]&&"items"===arguments[1]?this.controlgroup.apply(this,[arguments[0],"items.button"]):("object"==typeof arguments[0]&&arguments[0].items&&(arguments[0].items={button:arguments[0].items}),this.controlgroup.apply(this,arguments))});var it;V.ui.button;function st(){this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:"",selectMonthLabel:"Select month",selectYearLabel:"Select year"},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,onUpdateDatepicker:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},V.extend(this._defaults,this.regional[""]),this.regional.en=V.extend(!0,{},this.regional[""]),this.regional["en-US"]=V.extend(!0,{},this.regional.en),this.dpDiv=nt(V("<div id='"+this._mainDivId+"' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"))}function nt(t){var e="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return t.on("mouseout",e,function(){V(this).removeClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&V(this).removeClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&V(this).removeClass("ui-datepicker-next-hover")}).on("mouseover",e,ot)}function ot(){V.datepicker._isDisabledDatepicker((it.inline?it.dpDiv.parent():it.input)[0])||(V(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),V(this).addClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&V(this).addClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&V(this).addClass("ui-datepicker-next-hover"))}function at(t,e){for(var i in V.extend(t,e),e)null==e[i]&&(t[i]=e[i]);return t}V.extend(V.ui,{datepicker:{version:"1.13.0"}}),V.extend(st.prototype,{markerClassName:"hasDatepicker",maxRows:4,_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(t){return at(this._defaults,t||{}),this},_attachDatepicker:function(t,e){var i,s=t.nodeName.toLowerCase(),n="div"===s||"span"===s;t.id||(this.uuid+=1,t.id="dp"+this.uuid),(i=this._newInst(V(t),n)).settings=V.extend({},e||{}),"input"===s?this._connectDatepicker(t,i):n&&this._inlineDatepicker(t,i)},_newInst:function(t,e){return{id:t[0].id.replace(/([^A-Za-z0-9_\-])/g,"\\\\$1"),input:t,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:e,dpDiv:e?nt(V("<div class='"+this._inlineClass+" ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")):this.dpDiv}},_connectDatepicker:function(t,e){var i=V(t);e.append=V([]),e.trigger=V([]),i.hasClass(this.markerClassName)||(this._attachments(i,e),i.addClass(this.markerClassName).on("keydown",this._doKeyDown).on("keypress",this._doKeyPress).on("keyup",this._doKeyUp),this._autoSize(e),V.data(t,"datepicker",e),e.settings.disabled&&this._disableDatepicker(t))},_attachments:function(t,e){var i,s=this._get(e,"appendText"),n=this._get(e,"isRTL");e.append&&e.append.remove(),s&&(e.append=V("<span>").addClass(this._appendClass).text(s),t[n?"before":"after"](e.append)),t.off("focus",this._showDatepicker),e.trigger&&e.trigger.remove(),"focus"!==(i=this._get(e,"showOn"))&&"both"!==i||t.on("focus",this._showDatepicker),"button"!==i&&"both"!==i||(s=this._get(e,"buttonText"),i=this._get(e,"buttonImage"),this._get(e,"buttonImageOnly")?e.trigger=V("<img>").addClass(this._triggerClass).attr({src:i,alt:s,title:s}):(e.trigger=V("<button type='button'>").addClass(this._triggerClass),i?e.trigger.html(V("<img>").attr({src:i,alt:s,title:s})):e.trigger.text(s)),t[n?"before":"after"](e.trigger),e.trigger.on("click",function(){return V.datepicker._datepickerShowing&&V.datepicker._lastInput===t[0]?V.datepicker._hideDatepicker():(V.datepicker._datepickerShowing&&V.datepicker._lastInput!==t[0]&&V.datepicker._hideDatepicker(),V.datepicker._showDatepicker(t[0])),!1}))},_autoSize:function(t){var e,i,s,n,o,a;this._get(t,"autoSize")&&!t.inline&&(o=new Date(2009,11,20),(a=this._get(t,"dateFormat")).match(/[DM]/)&&(e=function(t){for(n=s=i=0;n<t.length;n++)t[n].length>i&&(i=t[n].length,s=n);return s},o.setMonth(e(this._get(t,a.match(/MM/)?"monthNames":"monthNamesShort"))),o.setDate(e(this._get(t,a.match(/DD/)?"dayNames":"dayNamesShort"))+20-o.getDay())),t.input.attr("size",this._formatDate(t,o).length))},_inlineDatepicker:function(t,e){var i=V(t);i.hasClass(this.markerClassName)||(i.addClass(this.markerClassName).append(e.dpDiv),V.data(t,"datepicker",e),this._setDate(e,this._getDefaultDate(e),!0),this._updateDatepicker(e),this._updateAlternate(e),e.settings.disabled&&this._disableDatepicker(t),e.dpDiv.css("display","block"))},_dialogDatepicker:function(t,e,i,s,n){var o,a=this._dialogInst;return a||(this.uuid+=1,o="dp"+this.uuid,this._dialogInput=V("<input type='text' id='"+o+"' style='position: absolute; top: -100px; width: 0px;'/>"),this._dialogInput.on("keydown",this._doKeyDown),V("body").append(this._dialogInput),(a=this._dialogInst=this._newInst(this._dialogInput,!1)).settings={},V.data(this._dialogInput[0],"datepicker",a)),at(a.settings,s||{}),e=e&&e.constructor===Date?this._formatDate(a,e):e,this._dialogInput.val(e),this._pos=n?n.length?n:[n.pageX,n.pageY]:null,this._pos||(o=document.documentElement.clientWidth,s=document.documentElement.clientHeight,e=document.documentElement.scrollLeft||document.body.scrollLeft,n=document.documentElement.scrollTop||document.body.scrollTop,this._pos=[o/2-100+e,s/2-150+n]),this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),a.settings.onSelect=i,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),V.blockUI&&V.blockUI(this.dpDiv),V.data(this._dialogInput[0],"datepicker",a),this},_destroyDatepicker:function(t){var e,i=V(t),s=V.data(t,"datepicker");i.hasClass(this.markerClassName)&&(e=t.nodeName.toLowerCase(),V.removeData(t,"datepicker"),"input"===e?(s.append.remove(),s.trigger.remove(),i.removeClass(this.markerClassName).off("focus",this._showDatepicker).off("keydown",this._doKeyDown).off("keypress",this._doKeyPress).off("keyup",this._doKeyUp)):"div"!==e&&"span"!==e||i.removeClass(this.markerClassName).empty(),it===s&&(it=null,this._curInst=null))},_enableDatepicker:function(e){var t,i=V(e),s=V.data(e,"datepicker");i.hasClass(this.markerClassName)&&("input"===(t=e.nodeName.toLowerCase())?(e.disabled=!1,s.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""})):"div"!==t&&"span"!==t||((i=i.children("."+this._inlineClass)).children().removeClass("ui-state-disabled"),i.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!1)),this._disabledInputs=V.map(this._disabledInputs,function(t){return t===e?null:t}))},_disableDatepicker:function(e){var t,i=V(e),s=V.data(e,"datepicker");i.hasClass(this.markerClassName)&&("input"===(t=e.nodeName.toLowerCase())?(e.disabled=!0,s.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"})):"div"!==t&&"span"!==t||((i=i.children("."+this._inlineClass)).children().addClass("ui-state-disabled"),i.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!0)),this._disabledInputs=V.map(this._disabledInputs,function(t){return t===e?null:t}),this._disabledInputs[this._disabledInputs.length]=e)},_isDisabledDatepicker:function(t){if(!t)return!1;for(var e=0;e<this._disabledInputs.length;e++)if(this._disabledInputs[e]===t)return!0;return!1},_getInst:function(t){try{return V.data(t,"datepicker")}catch(t){throw"Missing instance data for this datepicker"}},_optionDatepicker:function(t,e,i){var s,n,o=this._getInst(t);if(2===arguments.length&&"string"==typeof e)return"defaults"===e?V.extend({},V.datepicker._defaults):o?"all"===e?V.extend({},o.settings):this._get(o,e):null;s=e||{},"string"==typeof e&&((s={})[e]=i),o&&(this._curInst===o&&this._hideDatepicker(),n=this._getDateDatepicker(t,!0),e=this._getMinMaxDate(o,"min"),i=this._getMinMaxDate(o,"max"),at(o.settings,s),null!==e&&void 0!==s.dateFormat&&void 0===s.minDate&&(o.settings.minDate=this._formatDate(o,e)),null!==i&&void 0!==s.dateFormat&&void 0===s.maxDate&&(o.settings.maxDate=this._formatDate(o,i)),"disabled"in s&&(s.disabled?this._disableDatepicker(t):this._enableDatepicker(t)),this._attachments(V(t),o),this._autoSize(o),this._setDate(o,n),this._updateAlternate(o),this._updateDatepicker(o))},_changeDatepicker:function(t,e,i){this._optionDatepicker(t,e,i)},_refreshDatepicker:function(t){t=this._getInst(t);t&&this._updateDatepicker(t)},_setDateDatepicker:function(t,e){t=this._getInst(t);t&&(this._setDate(t,e),this._updateDatepicker(t),this._updateAlternate(t))},_getDateDatepicker:function(t,e){t=this._getInst(t);return t&&!t.inline&&this._setDateFromField(t,e),t?this._getDate(t):null},_doKeyDown:function(t){var e,i,s=V.datepicker._getInst(t.target),n=!0,o=s.dpDiv.is(".ui-datepicker-rtl");if(s._keyEvent=!0,V.datepicker._datepickerShowing)switch(t.keyCode){case 9:V.datepicker._hideDatepicker(),n=!1;break;case 13:return(i=V("td."+V.datepicker._dayOverClass+":not(."+V.datepicker._currentClass+")",s.dpDiv))[0]&&V.datepicker._selectDay(t.target,s.selectedMonth,s.selectedYear,i[0]),(e=V.datepicker._get(s,"onSelect"))?(i=V.datepicker._formatDate(s),e.apply(s.input?s.input[0]:null,[i,s])):V.datepicker._hideDatepicker(),!1;case 27:V.datepicker._hideDatepicker();break;case 33:V.datepicker._adjustDate(t.target,t.ctrlKey?-V.datepicker._get(s,"stepBigMonths"):-V.datepicker._get(s,"stepMonths"),"M");break;case 34:V.datepicker._adjustDate(t.target,t.ctrlKey?+V.datepicker._get(s,"stepBigMonths"):+V.datepicker._get(s,"stepMonths"),"M");break;case 35:(t.ctrlKey||t.metaKey)&&V.datepicker._clearDate(t.target),n=t.ctrlKey||t.metaKey;break;case 36:(t.ctrlKey||t.metaKey)&&V.datepicker._gotoToday(t.target),n=t.ctrlKey||t.metaKey;break;case 37:(t.ctrlKey||t.metaKey)&&V.datepicker._adjustDate(t.target,o?1:-1,"D"),n=t.ctrlKey||t.metaKey,t.originalEvent.altKey&&V.datepicker._adjustDate(t.target,t.ctrlKey?-V.datepicker._get(s,"stepBigMonths"):-V.datepicker._get(s,"stepMonths"),"M");break;case 38:(t.ctrlKey||t.metaKey)&&V.datepicker._adjustDate(t.target,-7,"D"),n=t.ctrlKey||t.metaKey;break;case 39:(t.ctrlKey||t.metaKey)&&V.datepicker._adjustDate(t.target,o?-1:1,"D"),n=t.ctrlKey||t.metaKey,t.originalEvent.altKey&&V.datepicker._adjustDate(t.target,t.ctrlKey?+V.datepicker._get(s,"stepBigMonths"):+V.datepicker._get(s,"stepMonths"),"M");break;case 40:(t.ctrlKey||t.metaKey)&&V.datepicker._adjustDate(t.target,7,"D"),n=t.ctrlKey||t.metaKey;break;default:n=!1}else 36===t.keyCode&&t.ctrlKey?V.datepicker._showDatepicker(this):n=!1;n&&(t.preventDefault(),t.stopPropagation())},_doKeyPress:function(t){var e,i=V.datepicker._getInst(t.target);if(V.datepicker._get(i,"constrainInput"))return e=V.datepicker._possibleChars(V.datepicker._get(i,"dateFormat")),i=String.fromCharCode(null==t.charCode?t.keyCode:t.charCode),t.ctrlKey||t.metaKey||i<" "||!e||-1<e.indexOf(i)},_doKeyUp:function(t){t=V.datepicker._getInst(t.target);if(t.input.val()!==t.lastVal)try{V.datepicker.parseDate(V.datepicker._get(t,"dateFormat"),t.input?t.input.val():null,V.datepicker._getFormatConfig(t))&&(V.datepicker._setDateFromField(t),V.datepicker._updateAlternate(t),V.datepicker._updateDatepicker(t))}catch(t){}return!0},_showDatepicker:function(t){var e,i,s,n;"input"!==(t=t.target||t).nodeName.toLowerCase()&&(t=V("input",t.parentNode)[0]),V.datepicker._isDisabledDatepicker(t)||V.datepicker._lastInput===t||(n=V.datepicker._getInst(t),V.datepicker._curInst&&V.datepicker._curInst!==n&&(V.datepicker._curInst.dpDiv.stop(!0,!0),n&&V.datepicker._datepickerShowing&&V.datepicker._hideDatepicker(V.datepicker._curInst.input[0])),!1!==(i=(s=V.datepicker._get(n,"beforeShow"))?s.apply(t,[t,n]):{})&&(at(n.settings,i),n.lastVal=null,V.datepicker._lastInput=t,V.datepicker._setDateFromField(n),V.datepicker._inDialog&&(t.value=""),V.datepicker._pos||(V.datepicker._pos=V.datepicker._findPos(t),V.datepicker._pos[1]+=t.offsetHeight),e=!1,V(t).parents().each(function(){return!(e|="fixed"===V(this).css("position"))}),s={left:V.datepicker._pos[0],top:V.datepicker._pos[1]},V.datepicker._pos=null,n.dpDiv.empty(),n.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),V.datepicker._updateDatepicker(n),s=V.datepicker._checkOffset(n,s,e),n.dpDiv.css({position:V.datepicker._inDialog&&V.blockUI?"static":e?"fixed":"absolute",display:"none",left:s.left+"px",top:s.top+"px"}),n.inline||(i=V.datepicker._get(n,"showAnim"),s=V.datepicker._get(n,"duration"),n.dpDiv.css("z-index",function(t){for(var e,i;t.length&&t[0]!==document;){if(("absolute"===(e=t.css("position"))||"relative"===e||"fixed"===e)&&(i=parseInt(t.css("zIndex"),10),!isNaN(i)&&0!==i))return i;t=t.parent()}return 0}(V(t))+1),V.datepicker._datepickerShowing=!0,V.effects&&V.effects.effect[i]?n.dpDiv.show(i,V.datepicker._get(n,"showOptions"),s):n.dpDiv[i||"show"](i?s:null),V.datepicker._shouldFocusInput(n)&&n.input.trigger("focus"),V.datepicker._curInst=n)))},_updateDatepicker:function(t){this.maxRows=4,(it=t).dpDiv.empty().append(this._generateHTML(t)),this._attachHandlers(t);var e,i=this._getNumberOfMonths(t),s=i[1],n=t.dpDiv.find("."+this._dayOverClass+" a"),o=V.datepicker._get(t,"onUpdateDatepicker");0<n.length&&ot.apply(n.get(0)),t.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),1<s&&t.dpDiv.addClass("ui-datepicker-multi-"+s).css("width",17*s+"em"),t.dpDiv[(1!==i[0]||1!==i[1]?"add":"remove")+"Class"]("ui-datepicker-multi"),t.dpDiv[(this._get(t,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),t===V.datepicker._curInst&&V.datepicker._datepickerShowing&&V.datepicker._shouldFocusInput(t)&&t.input.trigger("focus"),t.yearshtml&&(e=t.yearshtml,setTimeout(function(){e===t.yearshtml&&t.yearshtml&&t.dpDiv.find("select.ui-datepicker-year").first().replaceWith(t.yearshtml),e=t.yearshtml=null},0)),o&&o.apply(t.input?t.input[0]:null,[t])},_shouldFocusInput:function(t){return t.input&&t.input.is(":visible")&&!t.input.is(":disabled")&&!t.input.is(":focus")},_checkOffset:function(t,e,i){var s=t.dpDiv.outerWidth(),n=t.dpDiv.outerHeight(),o=t.input?t.input.outerWidth():0,a=t.input?t.input.outerHeight():0,r=document.documentElement.clientWidth+(i?0:V(document).scrollLeft()),l=document.documentElement.clientHeight+(i?0:V(document).scrollTop());return e.left-=this._get(t,"isRTL")?s-o:0,e.left-=i&&e.left===t.input.offset().left?V(document).scrollLeft():0,e.top-=i&&e.top===t.input.offset().top+a?V(document).scrollTop():0,e.left-=Math.min(e.left,e.left+s>r&&s<r?Math.abs(e.left+s-r):0),e.top-=Math.min(e.top,e.top+n>l&&n<l?Math.abs(n+a):0),e},_findPos:function(t){for(var e=this._getInst(t),i=this._get(e,"isRTL");t&&("hidden"===t.type||1!==t.nodeType||V.expr.pseudos.hidden(t));)t=t[i?"previousSibling":"nextSibling"];return[(e=V(t).offset()).left,e.top]},_hideDatepicker:function(t){var e,i,s=this._curInst;!s||t&&s!==V.data(t,"datepicker")||this._datepickerShowing&&(e=this._get(s,"showAnim"),i=this._get(s,"duration"),t=function(){V.datepicker._tidyDialog(s)},V.effects&&(V.effects.effect[e]||V.effects[e])?s.dpDiv.hide(e,V.datepicker._get(s,"showOptions"),i,t):s.dpDiv["slideDown"===e?"slideUp":"fadeIn"===e?"fadeOut":"hide"](e?i:null,t),e||t(),this._datepickerShowing=!1,(t=this._get(s,"onClose"))&&t.apply(s.input?s.input[0]:null,[s.input?s.input.val():"",s]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),V.blockUI&&(V.unblockUI(),V("body").append(this.dpDiv))),this._inDialog=!1)},_tidyDialog:function(t){t.dpDiv.removeClass(this._dialogClass).off(".ui-datepicker-calendar")},_checkExternalClick:function(t){var e;V.datepicker._curInst&&(e=V(t.target),t=V.datepicker._getInst(e[0]),(e[0].id===V.datepicker._mainDivId||0!==e.parents("#"+V.datepicker._mainDivId).length||e.hasClass(V.datepicker.markerClassName)||e.closest("."+V.datepicker._triggerClass).length||!V.datepicker._datepickerShowing||V.datepicker._inDialog&&V.blockUI)&&(!e.hasClass(V.datepicker.markerClassName)||V.datepicker._curInst===t)||V.datepicker._hideDatepicker())},_adjustDate:function(t,e,i){var s=V(t),t=this._getInst(s[0]);this._isDisabledDatepicker(s[0])||(this._adjustInstDate(t,e,i),this._updateDatepicker(t))},_gotoToday:function(t){var e=V(t),i=this._getInst(e[0]);this._get(i,"gotoCurrent")&&i.currentDay?(i.selectedDay=i.currentDay,i.drawMonth=i.selectedMonth=i.currentMonth,i.drawYear=i.selectedYear=i.currentYear):(t=new Date,i.selectedDay=t.getDate(),i.drawMonth=i.selectedMonth=t.getMonth(),i.drawYear=i.selectedYear=t.getFullYear()),this._notifyChange(i),this._adjustDate(e)},_selectMonthYear:function(t,e,i){var s=V(t),t=this._getInst(s[0]);t["selected"+("M"===i?"Month":"Year")]=t["draw"+("M"===i?"Month":"Year")]=parseInt(e.options[e.selectedIndex].value,10),this._notifyChange(t),this._adjustDate(s)},_selectDay:function(t,e,i,s){var n=V(t);V(s).hasClass(this._unselectableClass)||this._isDisabledDatepicker(n[0])||((n=this._getInst(n[0])).selectedDay=n.currentDay=parseInt(V("a",s).attr("data-date")),n.selectedMonth=n.currentMonth=e,n.selectedYear=n.currentYear=i,this._selectDate(t,this._formatDate(n,n.currentDay,n.currentMonth,n.currentYear)))},_clearDate:function(t){t=V(t);this._selectDate(t,"")},_selectDate:function(t,e){var i=V(t),t=this._getInst(i[0]);e=null!=e?e:this._formatDate(t),t.input&&t.input.val(e),this._updateAlternate(t),(i=this._get(t,"onSelect"))?i.apply(t.input?t.input[0]:null,[e,t]):t.input&&t.input.trigger("change"),t.inline?this._updateDatepicker(t):(this._hideDatepicker(),this._lastInput=t.input[0],"object"!=typeof t.input[0]&&t.input.trigger("focus"),this._lastInput=null)},_updateAlternate:function(t){var e,i,s=this._get(t,"altField");s&&(e=this._get(t,"altFormat")||this._get(t,"dateFormat"),i=this._getDate(t),t=this.formatDate(e,i,this._getFormatConfig(t)),V(document).find(s).val(t))},noWeekends:function(t){t=t.getDay();return[0<t&&t<6,""]},iso8601Week:function(t){var e=new Date(t.getTime());return e.setDate(e.getDate()+4-(e.getDay()||7)),t=e.getTime(),e.setMonth(0),e.setDate(1),Math.floor(Math.round((t-e)/864e5)/7)+1},parseDate:function(e,n,t){if(null==e||null==n)throw"Invalid arguments";if(""===(n="object"==typeof n?n.toString():n+""))return null;for(var i,s,o,a=0,r=(t?t.shortYearCutoff:null)||this._defaults.shortYearCutoff,r="string"!=typeof r?r:(new Date).getFullYear()%100+parseInt(r,10),l=(t?t.dayNamesShort:null)||this._defaults.dayNamesShort,h=(t?t.dayNames:null)||this._defaults.dayNames,c=(t?t.monthNamesShort:null)||this._defaults.monthNamesShort,u=(t?t.monthNames:null)||this._defaults.monthNames,d=-1,p=-1,f=-1,g=-1,m=!1,_=function(t){t=w+1<e.length&&e.charAt(w+1)===t;return t&&w++,t},v=function(t){var e=_(t),e="@"===t?14:"!"===t?20:"y"===t&&e?4:"o"===t?3:2,e=new RegExp("^\\d{"+("y"===t?e:1)+","+e+"}"),e=n.substring(a).match(e);if(!e)throw"Missing number at position "+a;return a+=e[0].length,parseInt(e[0],10)},b=function(t,e,i){var s=-1,e=V.map(_(t)?i:e,function(t,e){return[[e,t]]}).sort(function(t,e){return-(t[1].length-e[1].length)});if(V.each(e,function(t,e){var i=e[1];if(n.substr(a,i.length).toLowerCase()===i.toLowerCase())return s=e[0],a+=i.length,!1}),-1!==s)return s+1;throw"Unknown name at position "+a},y=function(){if(n.charAt(a)!==e.charAt(w))throw"Unexpected literal at position "+a;a++},w=0;w<e.length;w++)if(m)"'"!==e.charAt(w)||_("'")?y():m=!1;else switch(e.charAt(w)){case"d":f=v("d");break;case"D":b("D",l,h);break;case"o":g=v("o");break;case"m":p=v("m");break;case"M":p=b("M",c,u);break;case"y":d=v("y");break;case"@":d=(o=new Date(v("@"))).getFullYear(),p=o.getMonth()+1,f=o.getDate();break;case"!":d=(o=new Date((v("!")-this._ticksTo1970)/1e4)).getFullYear(),p=o.getMonth()+1,f=o.getDate();break;case"'":_("'")?y():m=!0;break;default:y()}if(a<n.length&&(s=n.substr(a),!/^\s+/.test(s)))throw"Extra/unparsed characters found in date: "+s;if(-1===d?d=(new Date).getFullYear():d<100&&(d+=(new Date).getFullYear()-(new Date).getFullYear()%100+(d<=r?0:-100)),-1<g)for(p=1,f=g;;){if(f<=(i=this._getDaysInMonth(d,p-1)))break;p++,f-=i}if((o=this._daylightSavingAdjust(new Date(d,p-1,f))).getFullYear()!==d||o.getMonth()+1!==p||o.getDate()!==f)throw"Invalid date";return o},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:24*(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*60*60*1e7,formatDate:function(e,t,i){if(!t)return"";function s(t,e,i){var s=""+e;if(c(t))for(;s.length<i;)s="0"+s;return s}function n(t,e,i,s){return(c(t)?s:i)[e]}var o,a=(i?i.dayNamesShort:null)||this._defaults.dayNamesShort,r=(i?i.dayNames:null)||this._defaults.dayNames,l=(i?i.monthNamesShort:null)||this._defaults.monthNamesShort,h=(i?i.monthNames:null)||this._defaults.monthNames,c=function(t){t=o+1<e.length&&e.charAt(o+1)===t;return t&&o++,t},u="",d=!1;if(t)for(o=0;o<e.length;o++)if(d)"'"!==e.charAt(o)||c("'")?u+=e.charAt(o):d=!1;else switch(e.charAt(o)){case"d":u+=s("d",t.getDate(),2);break;case"D":u+=n("D",t.getDay(),a,r);break;case"o":u+=s("o",Math.round((new Date(t.getFullYear(),t.getMonth(),t.getDate()).getTime()-new Date(t.getFullYear(),0,0).getTime())/864e5),3);break;case"m":u+=s("m",t.getMonth()+1,2);break;case"M":u+=n("M",t.getMonth(),l,h);break;case"y":u+=c("y")?t.getFullYear():(t.getFullYear()%100<10?"0":"")+t.getFullYear()%100;break;case"@":u+=t.getTime();break;case"!":u+=1e4*t.getTime()+this._ticksTo1970;break;case"'":c("'")?u+="'":d=!0;break;default:u+=e.charAt(o)}return u},_possibleChars:function(e){for(var t="",i=!1,s=function(t){t=n+1<e.length&&e.charAt(n+1)===t;return t&&n++,t},n=0;n<e.length;n++)if(i)"'"!==e.charAt(n)||s("'")?t+=e.charAt(n):i=!1;else switch(e.charAt(n)){case"d":case"m":case"y":case"@":t+="0123456789";break;case"D":case"M":return null;case"'":s("'")?t+="'":i=!0;break;default:t+=e.charAt(n)}return t},_get:function(t,e){return(void 0!==t.settings[e]?t.settings:this._defaults)[e]},_setDateFromField:function(t,e){if(t.input.val()!==t.lastVal){var i=this._get(t,"dateFormat"),s=t.lastVal=t.input?t.input.val():null,n=this._getDefaultDate(t),o=n,a=this._getFormatConfig(t);try{o=this.parseDate(i,s,a)||n}catch(t){s=e?"":s}t.selectedDay=o.getDate(),t.drawMonth=t.selectedMonth=o.getMonth(),t.drawYear=t.selectedYear=o.getFullYear(),t.currentDay=s?o.getDate():0,t.currentMonth=s?o.getMonth():0,t.currentYear=s?o.getFullYear():0,this._adjustInstDate(t)}},_getDefaultDate:function(t){return this._restrictMinMax(t,this._determineDate(t,this._get(t,"defaultDate"),new Date))},_determineDate:function(r,t,e){var i,s,t=null==t||""===t?e:"string"==typeof t?function(t){try{return V.datepicker.parseDate(V.datepicker._get(r,"dateFormat"),t,V.datepicker._getFormatConfig(r))}catch(t){}for(var e=(t.toLowerCase().match(/^c/)?V.datepicker._getDate(r):null)||new Date,i=e.getFullYear(),s=e.getMonth(),n=e.getDate(),o=/([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,a=o.exec(t);a;){switch(a[2]||"d"){case"d":case"D":n+=parseInt(a[1],10);break;case"w":case"W":n+=7*parseInt(a[1],10);break;case"m":case"M":s+=parseInt(a[1],10),n=Math.min(n,V.datepicker._getDaysInMonth(i,s));break;case"y":case"Y":i+=parseInt(a[1],10),n=Math.min(n,V.datepicker._getDaysInMonth(i,s))}a=o.exec(t)}return new Date(i,s,n)}(t):"number"==typeof t?isNaN(t)?e:(i=t,(s=new Date).setDate(s.getDate()+i),s):new Date(t.getTime());return(t=t&&"Invalid Date"===t.toString()?e:t)&&(t.setHours(0),t.setMinutes(0),t.setSeconds(0),t.setMilliseconds(0)),this._daylightSavingAdjust(t)},_daylightSavingAdjust:function(t){return t?(t.setHours(12<t.getHours()?t.getHours()+2:0),t):null},_setDate:function(t,e,i){var s=!e,n=t.selectedMonth,o=t.selectedYear,e=this._restrictMinMax(t,this._determineDate(t,e,new Date));t.selectedDay=t.currentDay=e.getDate(),t.drawMonth=t.selectedMonth=t.currentMonth=e.getMonth(),t.drawYear=t.selectedYear=t.currentYear=e.getFullYear(),n===t.selectedMonth&&o===t.selectedYear||i||this._notifyChange(t),this._adjustInstDate(t),t.input&&t.input.val(s?"":this._formatDate(t))},_getDate:function(t){return!t.currentYear||t.input&&""===t.input.val()?null:this._daylightSavingAdjust(new Date(t.currentYear,t.currentMonth,t.currentDay))},_attachHandlers:function(t){var e=this._get(t,"stepMonths"),i="#"+t.id.replace(/\\\\/g,"\\");t.dpDiv.find("[data-handler]").map(function(){var t={prev:function(){V.datepicker._adjustDate(i,-e,"M")},next:function(){V.datepicker._adjustDate(i,+e,"M")},hide:function(){V.datepicker._hideDatepicker()},today:function(){V.datepicker._gotoToday(i)},selectDay:function(){return V.datepicker._selectDay(i,+this.getAttribute("data-month"),+this.getAttribute("data-year"),this),!1},selectMonth:function(){return V.datepicker._selectMonthYear(i,this,"M"),!1},selectYear:function(){return V.datepicker._selectMonthYear(i,this,"Y"),!1}};V(this).on(this.getAttribute("data-event"),t[this.getAttribute("data-handler")])})},_generateHTML:function(t){var e,i,s,n,o,a,r,l,h,c,u,d,p,f,g,m,_,v,b,y,w,x,k,C,D,I,T,P,M,S,H,z,A=new Date,O=this._daylightSavingAdjust(new Date(A.getFullYear(),A.getMonth(),A.getDate())),N=this._get(t,"isRTL"),E=this._get(t,"showButtonPanel"),W=this._get(t,"hideIfNoPrevNext"),F=this._get(t,"navigationAsDateFormat"),L=this._getNumberOfMonths(t),R=this._get(t,"showCurrentAtPos"),A=this._get(t,"stepMonths"),Y=1!==L[0]||1!==L[1],B=this._daylightSavingAdjust(t.currentDay?new Date(t.currentYear,t.currentMonth,t.currentDay):new Date(9999,9,9)),j=this._getMinMaxDate(t,"min"),q=this._getMinMaxDate(t,"max"),K=t.drawMonth-R,U=t.drawYear;if(K<0&&(K+=12,U--),q)for(e=this._daylightSavingAdjust(new Date(q.getFullYear(),q.getMonth()-L[0]*L[1]+1,q.getDate())),e=j&&e<j?j:e;this._daylightSavingAdjust(new Date(U,K,1))>e;)--K<0&&(K=11,U--);for(t.drawMonth=K,t.drawYear=U,R=this._get(t,"prevText"),R=F?this.formatDate(R,this._daylightSavingAdjust(new Date(U,K-A,1)),this._getFormatConfig(t)):R,i=this._canAdjustMonth(t,-1,U,K)?V("<a>").attr({class:"ui-datepicker-prev ui-corner-all","data-handler":"prev","data-event":"click",title:R}).append(V("<span>").addClass("ui-icon ui-icon-circle-triangle-"+(N?"e":"w")).text(R))[0].outerHTML:W?"":V("<a>").attr({class:"ui-datepicker-prev ui-corner-all ui-state-disabled",title:R}).append(V("<span>").addClass("ui-icon ui-icon-circle-triangle-"+(N?"e":"w")).text(R))[0].outerHTML,R=this._get(t,"nextText"),R=F?this.formatDate(R,this._daylightSavingAdjust(new Date(U,K+A,1)),this._getFormatConfig(t)):R,s=this._canAdjustMonth(t,1,U,K)?V("<a>").attr({class:"ui-datepicker-next ui-corner-all","data-handler":"next","data-event":"click",title:R}).append(V("<span>").addClass("ui-icon ui-icon-circle-triangle-"+(N?"w":"e")).text(R))[0].outerHTML:W?"":V("<a>").attr({class:"ui-datepicker-next ui-corner-all ui-state-disabled",title:R}).append(V("<span>").attr("class","ui-icon ui-icon-circle-triangle-"+(N?"w":"e")).text(R))[0].outerHTML,A=this._get(t,"currentText"),W=this._get(t,"gotoCurrent")&&t.currentDay?B:O,A=F?this.formatDate(A,W,this._getFormatConfig(t)):A,R="",t.inline||(R=V("<button>").attr({type:"button",class:"ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all","data-handler":"hide","data-event":"click"}).text(this._get(t,"closeText"))[0].outerHTML),F="",E&&(F=V("<div class='ui-datepicker-buttonpane ui-widget-content'>").append(N?R:"").append(this._isInRange(t,W)?V("<button>").attr({type:"button",class:"ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all","data-handler":"today","data-event":"click"}).text(A):"").append(N?"":R)[0].outerHTML),n=parseInt(this._get(t,"firstDay"),10),n=isNaN(n)?0:n,o=this._get(t,"showWeek"),a=this._get(t,"dayNames"),r=this._get(t,"dayNamesMin"),l=this._get(t,"monthNames"),h=this._get(t,"monthNamesShort"),c=this._get(t,"beforeShowDay"),u=this._get(t,"showOtherMonths"),d=this._get(t,"selectOtherMonths"),p=this._getDefaultDate(t),f="",m=0;m<L[0];m++){for(_="",this.maxRows=4,v=0;v<L[1];v++){if(b=this._daylightSavingAdjust(new Date(U,K,t.selectedDay)),y=" ui-corner-all",w="",Y){if(w+="<div class='ui-datepicker-group",1<L[1])switch(v){case 0:w+=" ui-datepicker-group-first",y=" ui-corner-"+(N?"right":"left");break;case L[1]-1:w+=" ui-datepicker-group-last",y=" ui-corner-"+(N?"left":"right");break;default:w+=" ui-datepicker-group-middle",y=""}w+="'>"}for(w+="<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix"+y+"'>"+(/all|left/.test(y)&&0===m?N?s:i:"")+(/all|right/.test(y)&&0===m?N?i:s:"")+this._generateMonthYearHeader(t,K,U,j,q,0<m||0<v,l,h)+"</div><table class='ui-datepicker-calendar'><thead><tr>",x=o?"<th class='ui-datepicker-week-col'>"+this._get(t,"weekHeader")+"</th>":"",g=0;g<7;g++)x+="<th scope='col'"+(5<=(g+n+6)%7?" class='ui-datepicker-week-end'":"")+"><span title='"+a[k=(g+n)%7]+"'>"+r[k]+"</span></th>";for(w+=x+"</tr></thead><tbody>",D=this._getDaysInMonth(U,K),U===t.selectedYear&&K===t.selectedMonth&&(t.selectedDay=Math.min(t.selectedDay,D)),C=(this._getFirstDayOfMonth(U,K)-n+7)%7,D=Math.ceil((C+D)/7),I=Y&&this.maxRows>D?this.maxRows:D,this.maxRows=I,T=this._daylightSavingAdjust(new Date(U,K,1-C)),P=0;P<I;P++){for(w+="<tr>",M=o?"<td class='ui-datepicker-week-col'>"+this._get(t,"calculateWeek")(T)+"</td>":"",g=0;g<7;g++)S=c?c.apply(t.input?t.input[0]:null,[T]):[!0,""],z=(H=T.getMonth()!==K)&&!d||!S[0]||j&&T<j||q&&q<T,M+="<td class='"+(5<=(g+n+6)%7?" ui-datepicker-week-end":"")+(H?" ui-datepicker-other-month":"")+(T.getTime()===b.getTime()&&K===t.selectedMonth&&t._keyEvent||p.getTime()===T.getTime()&&p.getTime()===b.getTime()?" "+this._dayOverClass:"")+(z?" "+this._unselectableClass+" ui-state-disabled":"")+(H&&!u?"":" "+S[1]+(T.getTime()===B.getTime()?" "+this._currentClass:"")+(T.getTime()===O.getTime()?" ui-datepicker-today":""))+"'"+(H&&!u||!S[2]?"":" title='"+S[2].replace(/'/g,"&#39;")+"'")+(z?"":" data-handler='selectDay' data-event='click' data-month='"+T.getMonth()+"' data-year='"+T.getFullYear()+"'")+">"+(H&&!u?"&#xa0;":z?"<span class='ui-state-default'>"+T.getDate()+"</span>":"<a class='ui-state-default"+(T.getTime()===O.getTime()?" ui-state-highlight":"")+(T.getTime()===B.getTime()?" ui-state-active":"")+(H?" ui-priority-secondary":"")+"' href='#' aria-current='"+(T.getTime()===B.getTime()?"true":"false")+"' data-date='"+T.getDate()+"'>"+T.getDate()+"</a>")+"</td>",T.setDate(T.getDate()+1),T=this._daylightSavingAdjust(T);w+=M+"</tr>"}11<++K&&(K=0,U++),_+=w+="</tbody></table>"+(Y?"</div>"+(0<L[0]&&v===L[1]-1?"<div class='ui-datepicker-row-break'></div>":""):"")}f+=_}return f+=F,t._keyEvent=!1,f},_generateMonthYearHeader:function(t,e,i,s,n,o,a,r){var l,h,c,u,d,p,f=this._get(t,"changeMonth"),g=this._get(t,"changeYear"),m=this._get(t,"showMonthAfterYear"),_=this._get(t,"selectMonthLabel"),v=this._get(t,"selectYearLabel"),b="<div class='ui-datepicker-title'>",y="";if(o||!f)y+="<span class='ui-datepicker-month'>"+a[e]+"</span>";else{for(l=s&&s.getFullYear()===i,h=n&&n.getFullYear()===i,y+="<select class='ui-datepicker-month' aria-label='"+_+"' data-handler='selectMonth' data-event='change'>",c=0;c<12;c++)(!l||c>=s.getMonth())&&(!h||c<=n.getMonth())&&(y+="<option value='"+c+"'"+(c===e?" selected='selected'":"")+">"+r[c]+"</option>");y+="</select>"}if(m||(b+=y+(!o&&f&&g?"":"&#xa0;")),!t.yearshtml)if(t.yearshtml="",o||!g)b+="<span class='ui-datepicker-year'>"+i+"</span>";else{for(a=this._get(t,"yearRange").split(":"),u=(new Date).getFullYear(),d=(_=function(t){t=t.match(/c[+\-].*/)?i+parseInt(t.substring(1),10):t.match(/[+\-].*/)?u+parseInt(t,10):parseInt(t,10);return isNaN(t)?u:t})(a[0]),p=Math.max(d,_(a[1]||"")),d=s?Math.max(d,s.getFullYear()):d,p=n?Math.min(p,n.getFullYear()):p,t.yearshtml+="<select class='ui-datepicker-year' aria-label='"+v+"' data-handler='selectYear' data-event='change'>";d<=p;d++)t.yearshtml+="<option value='"+d+"'"+(d===i?" selected='selected'":"")+">"+d+"</option>";t.yearshtml+="</select>",b+=t.yearshtml,t.yearshtml=null}return b+=this._get(t,"yearSuffix"),m&&(b+=(!o&&f&&g?"":"&#xa0;")+y),b+="</div>"},_adjustInstDate:function(t,e,i){var s=t.selectedYear+("Y"===i?e:0),n=t.selectedMonth+("M"===i?e:0),e=Math.min(t.selectedDay,this._getDaysInMonth(s,n))+("D"===i?e:0),e=this._restrictMinMax(t,this._daylightSavingAdjust(new Date(s,n,e)));t.selectedDay=e.getDate(),t.drawMonth=t.selectedMonth=e.getMonth(),t.drawYear=t.selectedYear=e.getFullYear(),"M"!==i&&"Y"!==i||this._notifyChange(t)},_restrictMinMax:function(t,e){var i=this._getMinMaxDate(t,"min"),t=this._getMinMaxDate(t,"max"),e=i&&e<i?i:e;return t&&t<e?t:e},_notifyChange:function(t){var e=this._get(t,"onChangeMonthYear");e&&e.apply(t.input?t.input[0]:null,[t.selectedYear,t.selectedMonth+1,t])},_getNumberOfMonths:function(t){t=this._get(t,"numberOfMonths");return null==t?[1,1]:"number"==typeof t?[1,t]:t},_getMinMaxDate:function(t,e){return this._determineDate(t,this._get(t,e+"Date"),null)},_getDaysInMonth:function(t,e){return 32-this._daylightSavingAdjust(new Date(t,e,32)).getDate()},_getFirstDayOfMonth:function(t,e){return new Date(t,e,1).getDay()},_canAdjustMonth:function(t,e,i,s){var n=this._getNumberOfMonths(t),n=this._daylightSavingAdjust(new Date(i,s+(e<0?e:n[0]*n[1]),1));return e<0&&n.setDate(this._getDaysInMonth(n.getFullYear(),n.getMonth())),this._isInRange(t,n)},_isInRange:function(t,e){var i=this._getMinMaxDate(t,"min"),s=this._getMinMaxDate(t,"max"),n=null,o=null,a=this._get(t,"yearRange");return a&&(t=a.split(":"),a=(new Date).getFullYear(),n=parseInt(t[0],10),o=parseInt(t[1],10),t[0].match(/[+\-].*/)&&(n+=a),t[1].match(/[+\-].*/)&&(o+=a)),(!i||e.getTime()>=i.getTime())&&(!s||e.getTime()<=s.getTime())&&(!n||e.getFullYear()>=n)&&(!o||e.getFullYear()<=o)},_getFormatConfig:function(t){var e=this._get(t,"shortYearCutoff");return{shortYearCutoff:e="string"!=typeof e?e:(new Date).getFullYear()%100+parseInt(e,10),dayNamesShort:this._get(t,"dayNamesShort"),dayNames:this._get(t,"dayNames"),monthNamesShort:this._get(t,"monthNamesShort"),monthNames:this._get(t,"monthNames")}},_formatDate:function(t,e,i,s){e||(t.currentDay=t.selectedDay,t.currentMonth=t.selectedMonth,t.currentYear=t.selectedYear);e=e?"object"==typeof e?e:this._daylightSavingAdjust(new Date(s,i,e)):this._daylightSavingAdjust(new Date(t.currentYear,t.currentMonth,t.currentDay));return this.formatDate(this._get(t,"dateFormat"),e,this._getFormatConfig(t))}}),V.fn.datepicker=function(t){if(!this.length)return this;V.datepicker.initialized||(V(document).on("mousedown",V.datepicker._checkExternalClick),V.datepicker.initialized=!0),0===V("#"+V.datepicker._mainDivId).length&&V("body").append(V.datepicker.dpDiv);var e=Array.prototype.slice.call(arguments,1);return"string"==typeof t&&("isDisabled"===t||"getDate"===t||"widget"===t)||"option"===t&&2===arguments.length&&"string"==typeof arguments[1]?V.datepicker["_"+t+"Datepicker"].apply(V.datepicker,[this[0]].concat(e)):this.each(function(){"string"==typeof t?V.datepicker["_"+t+"Datepicker"].apply(V.datepicker,[this].concat(e)):V.datepicker._attachDatepicker(this,t)})},V.datepicker=new st,V.datepicker.initialized=!1,V.datepicker.uuid=(new Date).getTime(),V.datepicker.version="1.13.0";V.datepicker,V.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase());var rt=!1;V(document).on("mouseup",function(){rt=!1});V.widget("ui.mouse",{version:"1.13.0",options:{cancel:"input, textarea, button, select, option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.on("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).on("click."+this.widgetName,function(t){if(!0===V.data(t.target,e.widgetName+".preventClickEvent"))return V.removeData(t.target,e.widgetName+".preventClickEvent"),t.stopImmediatePropagation(),!1}),this.started=!1},_mouseDestroy:function(){this.element.off("."+this.widgetName),this._mouseMoveDelegate&&this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(t){if(!rt){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(t),this._mouseDownEvent=t;var e=this,i=1===t.which,s=!("string"!=typeof this.options.cancel||!t.target.nodeName)&&V(t.target).closest(this.options.cancel).length;return i&&!s&&this._mouseCapture(t)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){e.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=!1!==this._mouseStart(t),!this._mouseStarted)?(t.preventDefault(),!0):(!0===V.data(t.target,this.widgetName+".preventClickEvent")&&V.removeData(t.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return e._mouseMove(t)},this._mouseUpDelegate=function(t){return e._mouseUp(t)},this.document.on("mousemove."+this.widgetName,this._mouseMoveDelegate).on("mouseup."+this.widgetName,this._mouseUpDelegate),t.preventDefault(),rt=!0)):!0}},_mouseMove:function(t){if(this._mouseMoved){if(V.ui.ie&&(!document.documentMode||document.documentMode<9)&&!t.button)return this._mouseUp(t);if(!t.which)if(t.originalEvent.altKey||t.originalEvent.ctrlKey||t.originalEvent.metaKey||t.originalEvent.shiftKey)this.ignoreMissingWhich=!0;else if(!this.ignoreMissingWhich)return this._mouseUp(t)}return(t.which||t.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(t),t.preventDefault()):(this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=!1!==this._mouseStart(this._mouseDownEvent,t),this._mouseStarted?this._mouseDrag(t):this._mouseUp(t)),!this._mouseStarted)},_mouseUp:function(t){this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,t.target===this._mouseDownEvent.target&&V.data(t.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(t)),this._mouseDelayTimer&&(clearTimeout(this._mouseDelayTimer),delete this._mouseDelayTimer),this.ignoreMissingWhich=!1,rt=!1,t.preventDefault()},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),V.ui.plugin={add:function(t,e,i){var s,n=V.ui[t].prototype;for(s in i)n.plugins[s]=n.plugins[s]||[],n.plugins[s].push([e,i[s]])},call:function(t,e,i,s){var n,o=t.plugins[e];if(o&&(s||t.element[0].parentNode&&11!==t.element[0].parentNode.nodeType))for(n=0;n<o.length;n++)t.options[o[n][0]]&&o[n][1].apply(t.element,i)}},V.ui.safeBlur=function(t){t&&"body"!==t.nodeName.toLowerCase()&&V(t).trigger("blur")};V.widget("ui.draggable",V.ui.mouse,{version:"1.13.0",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"===this.options.helper&&this._setPositionRelative(),this.options.addClasses&&this._addClass("ui-draggable"),this._setHandleClassName(),this._mouseInit()},_setOption:function(t,e){this._super(t,e),"handle"===t&&(this._removeHandleClassName(),this._setHandleClassName())},_destroy:function(){(this.helper||this.element).is(".ui-draggable-dragging")?this.destroyOnClear=!0:(this._removeHandleClassName(),this._mouseDestroy())},_mouseCapture:function(t){var e=this.options;return!(this.helper||e.disabled||0<V(t.target).closest(".ui-resizable-handle").length)&&(this.handle=this._getHandle(t),!!this.handle&&(this._blurActiveElement(t),this._blockFrames(!0===e.iframeFix?"iframe":e.iframeFix),!0))},_blockFrames:function(t){this.iframeBlocks=this.document.find(t).map(function(){var t=V(this);return V("<div>").css("position","absolute").appendTo(t.parent()).outerWidth(t.outerWidth()).outerHeight(t.outerHeight()).offset(t.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_blurActiveElement:function(t){var e=V.ui.safeActiveElement(this.document[0]);V(t.target).closest(e).length||V.ui.safeBlur(e)},_mouseStart:function(t){var e=this.options;return this.helper=this._createHelper(t),this._addClass(this.helper,"ui-draggable-dragging"),this._cacheHelperProportions(),V.ui.ddmanager&&(V.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(!0),this.offsetParent=this.helper.offsetParent(),this.hasFixedAncestor=0<this.helper.parents().filter(function(){return"fixed"===V(this).css("position")}).length,this.positionAbs=this.element.offset(),this._refreshOffsets(t),this.originalPosition=this.position=this._generatePosition(t,!1),this.originalPageX=t.pageX,this.originalPageY=t.pageY,e.cursorAt&&this._adjustOffsetFromHelper(e.cursorAt),this._setContainment(),!1===this._trigger("start",t)?(this._clear(),!1):(this._cacheHelperProportions(),V.ui.ddmanager&&!e.dropBehaviour&&V.ui.ddmanager.prepareOffsets(this,t),this._mouseDrag(t,!0),V.ui.ddmanager&&V.ui.ddmanager.dragStart(this,t),!0)},_refreshOffsets:function(t){this.offset={top:this.positionAbs.top-this.margins.top,left:this.positionAbs.left-this.margins.left,scroll:!1,parent:this._getParentOffset(),relative:this._getRelativeOffset()},this.offset.click={left:t.pageX-this.offset.left,top:t.pageY-this.offset.top}},_mouseDrag:function(t,e){if(this.hasFixedAncestor&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(t,!0),this.positionAbs=this._convertPositionTo("absolute"),!e){e=this._uiHash();if(!1===this._trigger("drag",t,e))return this._mouseUp(new V.Event("mouseup",t)),!1;this.position=e.position}return this.helper[0].style.left=this.position.left+"px",this.helper[0].style.top=this.position.top+"px",V.ui.ddmanager&&V.ui.ddmanager.drag(this,t),!1},_mouseStop:function(t){var e=this,i=!1;return V.ui.ddmanager&&!this.options.dropBehaviour&&(i=V.ui.ddmanager.drop(this,t)),this.dropped&&(i=this.dropped,this.dropped=!1),"invalid"===this.options.revert&&!i||"valid"===this.options.revert&&i||!0===this.options.revert||"function"==typeof this.options.revert&&this.options.revert.call(this.element,i)?V(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){!1!==e._trigger("stop",t)&&e._clear()}):!1!==this._trigger("stop",t)&&this._clear(),!1},_mouseUp:function(t){return this._unblockFrames(),V.ui.ddmanager&&V.ui.ddmanager.dragStop(this,t),this.handleElement.is(t.target)&&this.element.trigger("focus"),V.ui.mouse.prototype._mouseUp.call(this,t)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp(new V.Event("mouseup",{target:this.element[0]})):this._clear(),this},_getHandle:function(t){return!this.options.handle||!!V(t.target).closest(this.element.find(this.options.handle)).length},_setHandleClassName:function(){this.handleElement=this.options.handle?this.element.find(this.options.handle):this.element,this._addClass(this.handleElement,"ui-draggable-handle")},_removeHandleClassName:function(){this._removeClass(this.handleElement,"ui-draggable-handle")},_createHelper:function(t){var e=this.options,i="function"==typeof e.helper,t=i?V(e.helper.apply(this.element[0],[t])):"clone"===e.helper?this.element.clone().removeAttr("id"):this.element;return t.parents("body").length||t.appendTo("parent"===e.appendTo?this.element[0].parentNode:e.appendTo),i&&t[0]===this.element[0]&&this._setPositionRelative(),t[0]===this.element[0]||/(fixed|absolute)/.test(t.css("position"))||t.css("position","absolute"),t},_setPositionRelative:function(){/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative")},_adjustOffsetFromHelper:function(t){"string"==typeof t&&(t=t.split(" ")),"left"in(t=Array.isArray(t)?{left:+t[0],top:+t[1]||0}:t)&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_isRootNode:function(t){return/(html|body)/i.test(t.tagName)||t===this.document[0]},_getParentOffset:function(){var t=this.offsetParent.offset(),e=this.document[0];return"absolute"===this.cssPosition&&this.scrollParent[0]!==e&&V.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop()),{top:(t=this._isRootNode(this.offsetParent[0])?{top:0,left:0}:t).top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"!==this.cssPosition)return{top:0,left:0};var t=this.element.position(),e=this._isRootNode(this.scrollParent[0]);return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+(e?0:this.scrollParent.scrollTop()),left:t.left-(parseInt(this.helper.css("left"),10)||0)+(e?0:this.scrollParent.scrollLeft())}},_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 t,e,i,s=this.options,n=this.document[0];this.relativeContainer=null,s.containment?"window"!==s.containment?"document"!==s.containment?s.containment.constructor!==Array?("parent"===s.containment&&(s.containment=this.helper[0].parentNode),(i=(e=V(s.containment))[0])&&(t=/(scroll|auto)/.test(e.css("overflow")),this.containment=[(parseInt(e.css("borderLeftWidth"),10)||0)+(parseInt(e.css("paddingLeft"),10)||0),(parseInt(e.css("borderTopWidth"),10)||0)+(parseInt(e.css("paddingTop"),10)||0),(t?Math.max(i.scrollWidth,i.offsetWidth):i.offsetWidth)-(parseInt(e.css("borderRightWidth"),10)||0)-(parseInt(e.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(t?Math.max(i.scrollHeight,i.offsetHeight):i.offsetHeight)-(parseInt(e.css("borderBottomWidth"),10)||0)-(parseInt(e.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relativeContainer=e)):this.containment=s.containment:this.containment=[0,0,V(n).width()-this.helperProportions.width-this.margins.left,(V(n).height()||n.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]:this.containment=[V(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,V(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,V(window).scrollLeft()+V(window).width()-this.helperProportions.width-this.margins.left,V(window).scrollTop()+(V(window).height()||n.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]:this.containment=null},_convertPositionTo:function(t,e){e=e||this.position;var i="absolute"===t?1:-1,t=this._isRootNode(this.scrollParent[0]);return{top:e.top+this.offset.relative.top*i+this.offset.parent.top*i-("fixed"===this.cssPosition?-this.offset.scroll.top:t?0:this.offset.scroll.top)*i,left:e.left+this.offset.relative.left*i+this.offset.parent.left*i-("fixed"===this.cssPosition?-this.offset.scroll.left:t?0:this.offset.scroll.left)*i}},_generatePosition:function(t,e){var i,s=this.options,n=this._isRootNode(this.scrollParent[0]),o=t.pageX,a=t.pageY;return n&&this.offset.scroll||(this.offset.scroll={top:this.scrollParent.scrollTop(),left:this.scrollParent.scrollLeft()}),e&&(this.containment&&(i=this.relativeContainer?(i=this.relativeContainer.offset(),[this.containment[0]+i.left,this.containment[1]+i.top,this.containment[2]+i.left,this.containment[3]+i.top]):this.containment,t.pageX-this.offset.click.left<i[0]&&(o=i[0]+this.offset.click.left),t.pageY-this.offset.click.top<i[1]&&(a=i[1]+this.offset.click.top),t.pageX-this.offset.click.left>i[2]&&(o=i[2]+this.offset.click.left),t.pageY-this.offset.click.top>i[3]&&(a=i[3]+this.offset.click.top)),s.grid&&(t=s.grid[1]?this.originalPageY+Math.round((a-this.originalPageY)/s.grid[1])*s.grid[1]:this.originalPageY,a=!i||t-this.offset.click.top>=i[1]||t-this.offset.click.top>i[3]?t:t-this.offset.click.top>=i[1]?t-s.grid[1]:t+s.grid[1],t=s.grid[0]?this.originalPageX+Math.round((o-this.originalPageX)/s.grid[0])*s.grid[0]:this.originalPageX,o=!i||t-this.offset.click.left>=i[0]||t-this.offset.click.left>i[2]?t:t-this.offset.click.left>=i[0]?t-s.grid[0]:t+s.grid[0]),"y"===s.axis&&(o=this.originalPageX),"x"===s.axis&&(a=this.originalPageY)),{top:a-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.offset.scroll.top:n?0:this.offset.scroll.top),left:o-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.offset.scroll.left:n?0:this.offset.scroll.left)}},_clear:function(){this._removeClass(this.helper,"ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1,this.destroyOnClear&&this.destroy()},_trigger:function(t,e,i){return i=i||this._uiHash(),V.ui.plugin.call(this,t,[e,i,this],!0),/^(drag|start|stop)/.test(t)&&(this.positionAbs=this._convertPositionTo("absolute"),i.offset=this.positionAbs),V.Widget.prototype._trigger.call(this,t,e,i)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),V.ui.plugin.add("draggable","connectToSortable",{start:function(e,t,i){var s=V.extend({},t,{item:i.element});i.sortables=[],V(i.options.connectToSortable).each(function(){var t=V(this).sortable("instance");t&&!t.options.disabled&&(i.sortables.push(t),t.refreshPositions(),t._trigger("activate",e,s))})},stop:function(e,t,i){var s=V.extend({},t,{item:i.element});i.cancelHelperRemoval=!1,V.each(i.sortables,function(){var t=this;t.isOver?(t.isOver=0,i.cancelHelperRemoval=!0,t.cancelHelperRemoval=!1,t._storedCSS={position:t.placeholder.css("position"),top:t.placeholder.css("top"),left:t.placeholder.css("left")},t._mouseStop(e),t.options.helper=t.options._helper):(t.cancelHelperRemoval=!0,t._trigger("deactivate",e,s))})},drag:function(i,s,n){V.each(n.sortables,function(){var t=!1,e=this;e.positionAbs=n.positionAbs,e.helperProportions=n.helperProportions,e.offset.click=n.offset.click,e._intersectsWith(e.containerCache)&&(t=!0,V.each(n.sortables,function(){return this.positionAbs=n.positionAbs,this.helperProportions=n.helperProportions,this.offset.click=n.offset.click,t=this!==e&&this._intersectsWith(this.containerCache)&&V.contains(e.element[0],this.element[0])?!1:t})),t?(e.isOver||(e.isOver=1,n._parent=s.helper.parent(),e.currentItem=s.helper.appendTo(e.element).data("ui-sortable-item",!0),e.options._helper=e.options.helper,e.options.helper=function(){return s.helper[0]},i.target=e.currentItem[0],e._mouseCapture(i,!0),e._mouseStart(i,!0,!0),e.offset.click.top=n.offset.click.top,e.offset.click.left=n.offset.click.left,e.offset.parent.left-=n.offset.parent.left-e.offset.parent.left,e.offset.parent.top-=n.offset.parent.top-e.offset.parent.top,n._trigger("toSortable",i),n.dropped=e.element,V.each(n.sortables,function(){this.refreshPositions()}),n.currentItem=n.element,e.fromOutside=n),e.currentItem&&(e._mouseDrag(i),s.position=e.position)):e.isOver&&(e.isOver=0,e.cancelHelperRemoval=!0,e.options._revert=e.options.revert,e.options.revert=!1,e._trigger("out",i,e._uiHash(e)),e._mouseStop(i,!0),e.options.revert=e.options._revert,e.options.helper=e.options._helper,e.placeholder&&e.placeholder.remove(),s.helper.appendTo(n._parent),n._refreshOffsets(i),s.position=n._generatePosition(i,!0),n._trigger("fromSortable",i),n.dropped=!1,V.each(n.sortables,function(){this.refreshPositions()}))})}}),V.ui.plugin.add("draggable","cursor",{start:function(t,e,i){var s=V("body"),i=i.options;s.css("cursor")&&(i._cursor=s.css("cursor")),s.css("cursor",i.cursor)},stop:function(t,e,i){i=i.options;i._cursor&&V("body").css("cursor",i._cursor)}}),V.ui.plugin.add("draggable","opacity",{start:function(t,e,i){e=V(e.helper),i=i.options;e.css("opacity")&&(i._opacity=e.css("opacity")),e.css("opacity",i.opacity)},stop:function(t,e,i){i=i.options;i._opacity&&V(e.helper).css("opacity",i._opacity)}}),V.ui.plugin.add("draggable","scroll",{start:function(t,e,i){i.scrollParentNotHidden||(i.scrollParentNotHidden=i.helper.scrollParent(!1)),i.scrollParentNotHidden[0]!==i.document[0]&&"HTML"!==i.scrollParentNotHidden[0].tagName&&(i.overflowOffset=i.scrollParentNotHidden.offset())},drag:function(t,e,i){var s=i.options,n=!1,o=i.scrollParentNotHidden[0],a=i.document[0];o!==a&&"HTML"!==o.tagName?(s.axis&&"x"===s.axis||(i.overflowOffset.top+o.offsetHeight-t.pageY<s.scrollSensitivity?o.scrollTop=n=o.scrollTop+s.scrollSpeed:t.pageY-i.overflowOffset.top<s.scrollSensitivity&&(o.scrollTop=n=o.scrollTop-s.scrollSpeed)),s.axis&&"y"===s.axis||(i.overflowOffset.left+o.offsetWidth-t.pageX<s.scrollSensitivity?o.scrollLeft=n=o.scrollLeft+s.scrollSpeed:t.pageX-i.overflowOffset.left<s.scrollSensitivity&&(o.scrollLeft=n=o.scrollLeft-s.scrollSpeed))):(s.axis&&"x"===s.axis||(t.pageY-V(a).scrollTop()<s.scrollSensitivity?n=V(a).scrollTop(V(a).scrollTop()-s.scrollSpeed):V(window).height()-(t.pageY-V(a).scrollTop())<s.scrollSensitivity&&(n=V(a).scrollTop(V(a).scrollTop()+s.scrollSpeed))),s.axis&&"y"===s.axis||(t.pageX-V(a).scrollLeft()<s.scrollSensitivity?n=V(a).scrollLeft(V(a).scrollLeft()-s.scrollSpeed):V(window).width()-(t.pageX-V(a).scrollLeft())<s.scrollSensitivity&&(n=V(a).scrollLeft(V(a).scrollLeft()+s.scrollSpeed)))),!1!==n&&V.ui.ddmanager&&!s.dropBehaviour&&V.ui.ddmanager.prepareOffsets(i,t)}}),V.ui.plugin.add("draggable","snap",{start:function(t,e,i){var s=i.options;i.snapElements=[],V(s.snap.constructor!==String?s.snap.items||":data(ui-draggable)":s.snap).each(function(){var t=V(this),e=t.offset();this!==i.element[0]&&i.snapElements.push({item:this,width:t.outerWidth(),height:t.outerHeight(),top:e.top,left:e.left})})},drag:function(t,e,i){for(var s,n,o,a,r,l,h,c,u,d=i.options,p=d.snapTolerance,f=e.offset.left,g=f+i.helperProportions.width,m=e.offset.top,_=m+i.helperProportions.height,v=i.snapElements.length-1;0<=v;v--)l=(r=i.snapElements[v].left-i.margins.left)+i.snapElements[v].width,c=(h=i.snapElements[v].top-i.margins.top)+i.snapElements[v].height,g<r-p||l+p<f||_<h-p||c+p<m||!V.contains(i.snapElements[v].item.ownerDocument,i.snapElements[v].item)?(i.snapElements[v].snapping&&i.options.snap.release&&i.options.snap.release.call(i.element,t,V.extend(i._uiHash(),{snapItem:i.snapElements[v].item})),i.snapElements[v].snapping=!1):("inner"!==d.snapMode&&(s=Math.abs(h-_)<=p,n=Math.abs(c-m)<=p,o=Math.abs(r-g)<=p,a=Math.abs(l-f)<=p,s&&(e.position.top=i._convertPositionTo("relative",{top:h-i.helperProportions.height,left:0}).top),n&&(e.position.top=i._convertPositionTo("relative",{top:c,left:0}).top),o&&(e.position.left=i._convertPositionTo("relative",{top:0,left:r-i.helperProportions.width}).left),a&&(e.position.left=i._convertPositionTo("relative",{top:0,left:l}).left)),u=s||n||o||a,"outer"!==d.snapMode&&(s=Math.abs(h-m)<=p,n=Math.abs(c-_)<=p,o=Math.abs(r-f)<=p,a=Math.abs(l-g)<=p,s&&(e.position.top=i._convertPositionTo("relative",{top:h,left:0}).top),n&&(e.position.top=i._convertPositionTo("relative",{top:c-i.helperProportions.height,left:0}).top),o&&(e.position.left=i._convertPositionTo("relative",{top:0,left:r}).left),a&&(e.position.left=i._convertPositionTo("relative",{top:0,left:l-i.helperProportions.width}).left)),!i.snapElements[v].snapping&&(s||n||o||a||u)&&i.options.snap.snap&&i.options.snap.snap.call(i.element,t,V.extend(i._uiHash(),{snapItem:i.snapElements[v].item})),i.snapElements[v].snapping=s||n||o||a||u)}}),V.ui.plugin.add("draggable","stack",{start:function(t,e,i){var s,i=i.options,i=V.makeArray(V(i.stack)).sort(function(t,e){return(parseInt(V(t).css("zIndex"),10)||0)-(parseInt(V(e).css("zIndex"),10)||0)});i.length&&(s=parseInt(V(i[0]).css("zIndex"),10)||0,V(i).each(function(t){V(this).css("zIndex",s+t)}),this.css("zIndex",s+i.length))}}),V.ui.plugin.add("draggable","zIndex",{start:function(t,e,i){e=V(e.helper),i=i.options;e.css("zIndex")&&(i._zIndex=e.css("zIndex")),e.css("zIndex",i.zIndex)},stop:function(t,e,i){i=i.options;i._zIndex&&V(e.helper).css("zIndex",i._zIndex)}});V.ui.draggable;V.widget("ui.resizable",V.ui.mouse,{version:"1.13.0",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,classes:{"ui-resizable-se":"ui-icon ui-icon-gripsmall-diagonal-se"},containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_num:function(t){return parseFloat(t)||0},_isNumber:function(t){return!isNaN(parseFloat(t))},_hasScroll:function(t,e){if("hidden"===V(t).css("overflow"))return!1;var i=e&&"left"===e?"scrollLeft":"scrollTop",e=!1;if(0<t[i])return!0;try{t[i]=1,e=0<t[i],t[i]=0}catch(t){}return e},_create:function(){var t,e=this.options,i=this;this._addClass("ui-resizable"),V.extend(this,{_aspectRatio:!!e.aspectRatio,aspectRatio:e.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:e.helper||e.ghost||e.animate?e.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/^(canvas|textarea|input|select|button|img)$/i)&&(this.element.wrap(V("<div class='ui-wrapper'></div>").css({overflow:"hidden",position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.resizable("instance")),this.elementIsWrapper=!0,t={marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom"),marginLeft:this.originalElement.css("marginLeft")},this.element.css(t),this.originalElement.css("margin",0),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css(t),this._proportionallyResize()),this._setupHandles(),e.autoHide&&V(this.element).on("mouseenter",function(){e.disabled||(i._removeClass("ui-resizable-autohide"),i._handles.show())}).on("mouseleave",function(){e.disabled||i.resizing||(i._addClass("ui-resizable-autohide"),i._handles.hide())}),this._mouseInit()},_destroy:function(){this._mouseDestroy(),this._addedHandles.remove();function t(t){V(t).removeData("resizable").removeData("ui-resizable").off(".resizable")}var e;return this.elementIsWrapper&&(t(this.element),e=this.element,this.originalElement.css({position:e.css("position"),width:e.outerWidth(),height:e.outerHeight(),top:e.css("top"),left:e.css("left")}).insertAfter(e),e.remove()),this.originalElement.css("resize",this.originalResizeStyle),t(this.originalElement),this},_setOption:function(t,e){switch(this._super(t,e),t){case"handles":this._removeHandles(),this._setupHandles();break;case"aspectRatio":this._aspectRatio=!!e}},_setupHandles:function(){var t,e,i,s,n,o=this.options,a=this;if(this.handles=o.handles||(V(".ui-resizable-handle",this.element).length?{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"}:"e,s,se"),this._handles=V(),this._addedHandles=V(),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),i=this.handles.split(","),this.handles={},e=0;e<i.length;e++)s="ui-resizable-"+(t=String.prototype.trim.call(i[e])),n=V("<div>"),this._addClass(n,"ui-resizable-handle "+s),n.css({zIndex:o.zIndex}),this.handles[t]=".ui-resizable-"+t,this.element.children(this.handles[t]).length||(this.element.append(n),this._addedHandles=this._addedHandles.add(n));this._renderAxis=function(t){var e,i,s;for(e in t=t||this.element,this.handles)this.handles[e].constructor===String?this.handles[e]=this.element.children(this.handles[e]).first().show():(this.handles[e].jquery||this.handles[e].nodeType)&&(this.handles[e]=V(this.handles[e]),this._on(this.handles[e],{mousedown:a._mouseDown})),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/^(textarea|input|select|button)$/i)&&(i=V(this.handles[e],this.element),s=/sw|ne|nw|se|n|s/.test(e)?i.outerHeight():i.outerWidth(),i=["padding",/ne|nw|n/.test(e)?"Top":/se|sw|s/.test(e)?"Bottom":/^e$/.test(e)?"Right":"Left"].join(""),t.css(i,s),this._proportionallyResize()),this._handles=this._handles.add(this.handles[e])},this._renderAxis(this.element),this._handles=this._handles.add(this.element.find(".ui-resizable-handle")),this._handles.disableSelection(),this._handles.on("mouseover",function(){a.resizing||(this.className&&(n=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),a.axis=n&&n[1]?n[1]:"se")}),o.autoHide&&(this._handles.hide(),this._addClass("ui-resizable-autohide"))},_removeHandles:function(){this._addedHandles.remove()},_mouseCapture:function(t){var e,i,s=!1;for(e in this.handles)(i=V(this.handles[e])[0])!==t.target&&!V.contains(i,t.target)||(s=!0);return!this.options.disabled&&s},_mouseStart:function(t){var e,i,s=this.options,n=this.element;return this.resizing=!0,this._renderProxy(),e=this._num(this.helper.css("left")),i=this._num(this.helper.css("top")),s.containment&&(e+=V(s.containment).scrollLeft()||0,i+=V(s.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:e,top:i},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:n.width(),height:n.height()},this.originalSize=this._helper?{width:n.outerWidth(),height:n.outerHeight()}:{width:n.width(),height:n.height()},this.sizeDiff={width:n.outerWidth()-n.width(),height:n.outerHeight()-n.height()},this.originalPosition={left:e,top:i},this.originalMousePosition={left:t.pageX,top:t.pageY},this.aspectRatio="number"==typeof s.aspectRatio?s.aspectRatio:this.originalSize.width/this.originalSize.height||1,s=V(".ui-resizable-"+this.axis).css("cursor"),V("body").css("cursor","auto"===s?this.axis+"-resize":s),this._addClass("ui-resizable-resizing"),this._propagate("start",t),!0},_mouseDrag:function(t){var e=this.originalMousePosition,i=this.axis,s=t.pageX-e.left||0,e=t.pageY-e.top||0,i=this._change[i];return this._updatePrevProperties(),i&&(e=i.apply(this,[t,s,e]),this._updateVirtualBoundaries(t.shiftKey),(this._aspectRatio||t.shiftKey)&&(e=this._updateRatio(e,t)),e=this._respectSize(e,t),this._updateCache(e),this._propagate("resize",t),e=this._applyChanges(),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),V.isEmptyObject(e)||(this._updatePrevProperties(),this._trigger("resize",t,this.ui()),this._applyChanges())),!1},_mouseStop:function(t){this.resizing=!1;var e,i,s,n=this.options,o=this;return this._helper&&(s=(e=(i=this._proportionallyResizeElements).length&&/textarea/i.test(i[0].nodeName))&&this._hasScroll(i[0],"left")?0:o.sizeDiff.height,i=e?0:o.sizeDiff.width,e={width:o.helper.width()-i,height:o.helper.height()-s},i=parseFloat(o.element.css("left"))+(o.position.left-o.originalPosition.left)||null,s=parseFloat(o.element.css("top"))+(o.position.top-o.originalPosition.top)||null,n.animate||this.element.css(V.extend(e,{top:s,left:i})),o.helper.height(o.size.height),o.helper.width(o.size.width),this._helper&&!n.animate&&this._proportionallyResize()),V("body").css("cursor","auto"),this._removeClass("ui-resizable-resizing"),this._propagate("stop",t),this._helper&&this.helper.remove(),!1},_updatePrevProperties:function(){this.prevPosition={top:this.position.top,left:this.position.left},this.prevSize={width:this.size.width,height:this.size.height}},_applyChanges:function(){var t={};return this.position.top!==this.prevPosition.top&&(t.top=this.position.top+"px"),this.position.left!==this.prevPosition.left&&(t.left=this.position.left+"px"),this.size.width!==this.prevSize.width&&(t.width=this.size.width+"px"),this.size.height!==this.prevSize.height&&(t.height=this.size.height+"px"),this.helper.css(t),t},_updateVirtualBoundaries:function(t){var e,i,s=this.options,n={minWidth:this._isNumber(s.minWidth)?s.minWidth:0,maxWidth:this._isNumber(s.maxWidth)?s.maxWidth:1/0,minHeight:this._isNumber(s.minHeight)?s.minHeight:0,maxHeight:this._isNumber(s.maxHeight)?s.maxHeight:1/0};(this._aspectRatio||t)&&(e=n.minHeight*this.aspectRatio,i=n.minWidth/this.aspectRatio,s=n.maxHeight*this.aspectRatio,t=n.maxWidth/this.aspectRatio,e>n.minWidth&&(n.minWidth=e),i>n.minHeight&&(n.minHeight=i),s<n.maxWidth&&(n.maxWidth=s),t<n.maxHeight&&(n.maxHeight=t)),this._vBoundaries=n},_updateCache:function(t){this.offset=this.helper.offset(),this._isNumber(t.left)&&(this.position.left=t.left),this._isNumber(t.top)&&(this.position.top=t.top),this._isNumber(t.height)&&(this.size.height=t.height),this._isNumber(t.width)&&(this.size.width=t.width)},_updateRatio:function(t){var e=this.position,i=this.size,s=this.axis;return this._isNumber(t.height)?t.width=t.height*this.aspectRatio:this._isNumber(t.width)&&(t.height=t.width/this.aspectRatio),"sw"===s&&(t.left=e.left+(i.width-t.width),t.top=null),"nw"===s&&(t.top=e.top+(i.height-t.height),t.left=e.left+(i.width-t.width)),t},_respectSize:function(t){var e=this._vBoundaries,i=this.axis,s=this._isNumber(t.width)&&e.maxWidth&&e.maxWidth<t.width,n=this._isNumber(t.height)&&e.maxHeight&&e.maxHeight<t.height,o=this._isNumber(t.width)&&e.minWidth&&e.minWidth>t.width,a=this._isNumber(t.height)&&e.minHeight&&e.minHeight>t.height,r=this.originalPosition.left+this.originalSize.width,l=this.originalPosition.top+this.originalSize.height,h=/sw|nw|w/.test(i),i=/nw|ne|n/.test(i);return o&&(t.width=e.minWidth),a&&(t.height=e.minHeight),s&&(t.width=e.maxWidth),n&&(t.height=e.maxHeight),o&&h&&(t.left=r-e.minWidth),s&&h&&(t.left=r-e.maxWidth),a&&i&&(t.top=l-e.minHeight),n&&i&&(t.top=l-e.maxHeight),t.width||t.height||t.left||!t.top?t.width||t.height||t.top||!t.left||(t.left=null):t.top=null,t},_getPaddingPlusBorderDimensions:function(t){for(var e=0,i=[],s=[t.css("borderTopWidth"),t.css("borderRightWidth"),t.css("borderBottomWidth"),t.css("borderLeftWidth")],n=[t.css("paddingTop"),t.css("paddingRight"),t.css("paddingBottom"),t.css("paddingLeft")];e<4;e++)i[e]=parseFloat(s[e])||0,i[e]+=parseFloat(n[e])||0;return{height:i[0]+i[2],width:i[1]+i[3]}},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var t,e=0,i=this.helper||this.element;e<this._proportionallyResizeElements.length;e++)t=this._proportionallyResizeElements[e],this.outerDimensions||(this.outerDimensions=this._getPaddingPlusBorderDimensions(t)),t.css({height:i.height()-this.outerDimensions.height||0,width:i.width()-this.outerDimensions.width||0})},_renderProxy:function(){var t=this.element,e=this.options;this.elementOffset=t.offset(),this._helper?(this.helper=this.helper||V("<div></div>").css({overflow:"hidden"}),this._addClass(this.helper,this._helper),this.helper.css({width:this.element.outerWidth(),height:this.element.outerHeight(),position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++e.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(t,e){return{width:this.originalSize.width+e}},w:function(t,e){var i=this.originalSize;return{left:this.originalPosition.left+e,width:i.width-e}},n:function(t,e,i){var s=this.originalSize;return{top:this.originalPosition.top+i,height:s.height-i}},s:function(t,e,i){return{height:this.originalSize.height+i}},se:function(t,e,i){return V.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[t,e,i]))},sw:function(t,e,i){return V.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[t,e,i]))},ne:function(t,e,i){return V.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[t,e,i]))},nw:function(t,e,i){return V.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[t,e,i]))}},_propagate:function(t,e){V.ui.plugin.call(this,t,[e,this.ui()]),"resize"!==t&&this._trigger(t,e,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}}}),V.ui.plugin.add("resizable","animate",{stop:function(e){var i=V(this).resizable("instance"),t=i.options,s=i._proportionallyResizeElements,n=s.length&&/textarea/i.test(s[0].nodeName),o=n&&i._hasScroll(s[0],"left")?0:i.sizeDiff.height,a=n?0:i.sizeDiff.width,n={width:i.size.width-a,height:i.size.height-o},a=parseFloat(i.element.css("left"))+(i.position.left-i.originalPosition.left)||null,o=parseFloat(i.element.css("top"))+(i.position.top-i.originalPosition.top)||null;i.element.animate(V.extend(n,o&&a?{top:o,left:a}:{}),{duration:t.animateDuration,easing:t.animateEasing,step:function(){var t={width:parseFloat(i.element.css("width")),height:parseFloat(i.element.css("height")),top:parseFloat(i.element.css("top")),left:parseFloat(i.element.css("left"))};s&&s.length&&V(s[0]).css({width:t.width,height:t.height}),i._updateCache(t),i._propagate("resize",e)}})}}),V.ui.plugin.add("resizable","containment",{start:function(){var i,s,n=V(this).resizable("instance"),t=n.options,e=n.element,o=t.containment,a=o instanceof V?o.get(0):/parent/.test(o)?e.parent().get(0):o;a&&(n.containerElement=V(a),/document/.test(o)||o===document?(n.containerOffset={left:0,top:0},n.containerPosition={left:0,top:0},n.parentData={element:V(document),left:0,top:0,width:V(document).width(),height:V(document).height()||document.body.parentNode.scrollHeight}):(i=V(a),s=[],V(["Top","Right","Left","Bottom"]).each(function(t,e){s[t]=n._num(i.css("padding"+e))}),n.containerOffset=i.offset(),n.containerPosition=i.position(),n.containerSize={height:i.innerHeight()-s[3],width:i.innerWidth()-s[1]},t=n.containerOffset,e=n.containerSize.height,o=n.containerSize.width,o=n._hasScroll(a,"left")?a.scrollWidth:o,e=n._hasScroll(a)?a.scrollHeight:e,n.parentData={element:a,left:t.left,top:t.top,width:o,height:e}))},resize:function(t){var e=V(this).resizable("instance"),i=e.options,s=e.containerOffset,n=e.position,o=e._aspectRatio||t.shiftKey,a={top:0,left:0},r=e.containerElement,t=!0;r[0]!==document&&/static/.test(r.css("position"))&&(a=s),n.left<(e._helper?s.left:0)&&(e.size.width=e.size.width+(e._helper?e.position.left-s.left:e.position.left-a.left),o&&(e.size.height=e.size.width/e.aspectRatio,t=!1),e.position.left=i.helper?s.left:0),n.top<(e._helper?s.top:0)&&(e.size.height=e.size.height+(e._helper?e.position.top-s.top:e.position.top),o&&(e.size.width=e.size.height*e.aspectRatio,t=!1),e.position.top=e._helper?s.top:0),i=e.containerElement.get(0)===e.element.parent().get(0),n=/relative|absolute/.test(e.containerElement.css("position")),i&&n?(e.offset.left=e.parentData.left+e.position.left,e.offset.top=e.parentData.top+e.position.top):(e.offset.left=e.element.offset().left,e.offset.top=e.element.offset().top),n=Math.abs(e.sizeDiff.width+(e._helper?e.offset.left-a.left:e.offset.left-s.left)),s=Math.abs(e.sizeDiff.height+(e._helper?e.offset.top-a.top:e.offset.top-s.top)),n+e.size.width>=e.parentData.width&&(e.size.width=e.parentData.width-n,o&&(e.size.height=e.size.width/e.aspectRatio,t=!1)),s+e.size.height>=e.parentData.height&&(e.size.height=e.parentData.height-s,o&&(e.size.width=e.size.height*e.aspectRatio,t=!1)),t||(e.position.left=e.prevPosition.left,e.position.top=e.prevPosition.top,e.size.width=e.prevSize.width,e.size.height=e.prevSize.height)},stop:function(){var t=V(this).resizable("instance"),e=t.options,i=t.containerOffset,s=t.containerPosition,n=t.containerElement,o=V(t.helper),a=o.offset(),r=o.outerWidth()-t.sizeDiff.width,o=o.outerHeight()-t.sizeDiff.height;t._helper&&!e.animate&&/relative/.test(n.css("position"))&&V(this).css({left:a.left-s.left-i.left,width:r,height:o}),t._helper&&!e.animate&&/static/.test(n.css("position"))&&V(this).css({left:a.left-s.left-i.left,width:r,height:o})}}),V.ui.plugin.add("resizable","alsoResize",{start:function(){var t=V(this).resizable("instance").options;V(t.alsoResize).each(function(){var t=V(this);t.data("ui-resizable-alsoresize",{width:parseFloat(t.width()),height:parseFloat(t.height()),left:parseFloat(t.css("left")),top:parseFloat(t.css("top"))})})},resize:function(t,i){var e=V(this).resizable("instance"),s=e.options,n=e.originalSize,o=e.originalPosition,a={height:e.size.height-n.height||0,width:e.size.width-n.width||0,top:e.position.top-o.top||0,left:e.position.left-o.left||0};V(s.alsoResize).each(function(){var t=V(this),s=V(this).data("ui-resizable-alsoresize"),n={},e=t.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];V.each(e,function(t,e){var i=(s[e]||0)+(a[e]||0);i&&0<=i&&(n[e]=i||null)}),t.css(n)})},stop:function(){V(this).removeData("ui-resizable-alsoresize")}}),V.ui.plugin.add("resizable","ghost",{start:function(){var t=V(this).resizable("instance"),e=t.size;t.ghost=t.originalElement.clone(),t.ghost.css({opacity:.25,display:"block",position:"relative",height:e.height,width:e.width,margin:0,left:0,top:0}),t._addClass(t.ghost,"ui-resizable-ghost"),!1!==V.uiBackCompat&&"string"==typeof t.options.ghost&&t.ghost.addClass(this.options.ghost),t.ghost.appendTo(t.helper)},resize:function(){var t=V(this).resizable("instance");t.ghost&&t.ghost.css({position:"relative",height:t.size.height,width:t.size.width})},stop:function(){var t=V(this).resizable("instance");t.ghost&&t.helper&&t.helper.get(0).removeChild(t.ghost.get(0))}}),V.ui.plugin.add("resizable","grid",{resize:function(){var t,e=V(this).resizable("instance"),i=e.options,s=e.size,n=e.originalSize,o=e.originalPosition,a=e.axis,r="number"==typeof i.grid?[i.grid,i.grid]:i.grid,l=r[0]||1,h=r[1]||1,c=Math.round((s.width-n.width)/l)*l,u=Math.round((s.height-n.height)/h)*h,d=n.width+c,p=n.height+u,f=i.maxWidth&&i.maxWidth<d,g=i.maxHeight&&i.maxHeight<p,m=i.minWidth&&i.minWidth>d,s=i.minHeight&&i.minHeight>p;i.grid=r,m&&(d+=l),s&&(p+=h),f&&(d-=l),g&&(p-=h),/^(se|s|e)$/.test(a)?(e.size.width=d,e.size.height=p):/^(ne)$/.test(a)?(e.size.width=d,e.size.height=p,e.position.top=o.top-u):/^(sw)$/.test(a)?(e.size.width=d,e.size.height=p,e.position.left=o.left-c):((p-h<=0||d-l<=0)&&(t=e._getPaddingPlusBorderDimensions(this)),0<p-h?(e.size.height=p,e.position.top=o.top-u):(p=h-t.height,e.size.height=p,e.position.top=o.top+n.height-p),0<d-l?(e.size.width=d,e.position.left=o.left-c):(d=l-t.width,e.size.width=d,e.position.left=o.left+n.width-d))}});V.ui.resizable;V.widget("ui.dialog",{version:"1.13.0",options:{appendTo:"body",autoOpen:!0,buttons:[],classes:{"ui-dialog":"ui-corner-all","ui-dialog-titlebar":"ui-corner-all"},closeOnEscape:!0,closeText:"Close",draggable:!0,hide:null,height:"auto",maxHeight:null,maxWidth:null,minHeight:150,minWidth:150,modal:!1,position:{my:"center",at:"center",of:window,collision:"fit",using:function(t){var e=V(this).css(t).offset().top;e<0&&V(this).css("top",t.top-e)}},resizable:!0,show:null,title:null,width:300,beforeClose:null,close:null,drag:null,dragStart:null,dragStop:null,focus:null,open:null,resize:null,resizeStart:null,resizeStop:null},sizeRelatedOptions:{buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},resizableRelatedOptions:{maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0},_create:function(){this.originalCss={display:this.element[0].style.display,width:this.element[0].style.width,minHeight:this.element[0].style.minHeight,maxHeight:this.element[0].style.maxHeight,height:this.element[0].style.height},this.originalPosition={parent:this.element.parent(),index:this.element.parent().children().index(this.element)},this.originalTitle=this.element.attr("title"),null==this.options.title&&null!=this.originalTitle&&(this.options.title=this.originalTitle),this.options.disabled&&(this.options.disabled=!1),this._createWrapper(),this.element.show().removeAttr("title").appendTo(this.uiDialog),this._addClass("ui-dialog-content","ui-widget-content"),this._createTitlebar(),this._createButtonPane(),this.options.draggable&&V.fn.draggable&&this._makeDraggable(),this.options.resizable&&V.fn.resizable&&this._makeResizable(),this._isOpen=!1,this._trackFocus()},_init:function(){this.options.autoOpen&&this.open()},_appendTo:function(){var t=this.options.appendTo;return t&&(t.jquery||t.nodeType)?V(t):this.document.find(t||"body").eq(0)},_destroy:function(){var t,e=this.originalPosition;this._untrackInstance(),this._destroyOverlay(),this.element.removeUniqueId().css(this.originalCss).detach(),this.uiDialog.remove(),this.originalTitle&&this.element.attr("title",this.originalTitle),(t=e.parent.children().eq(e.index)).length&&t[0]!==this.element[0]?t.before(this.element):e.parent.append(this.element)},widget:function(){return this.uiDialog},disable:V.noop,enable:V.noop,close:function(t){var e=this;this._isOpen&&!1!==this._trigger("beforeClose",t)&&(this._isOpen=!1,this._focusedElement=null,this._destroyOverlay(),this._untrackInstance(),this.opener.filter(":focusable").trigger("focus").length||V.ui.safeBlur(V.ui.safeActiveElement(this.document[0])),this._hide(this.uiDialog,this.options.hide,function(){e._trigger("close",t)}))},isOpen:function(){return this._isOpen},moveToTop:function(){this._moveToTop()},_moveToTop:function(t,e){var i=!1,s=this.uiDialog.siblings(".ui-front:visible").map(function(){return+V(this).css("z-index")}).get(),s=Math.max.apply(null,s);return s>=+this.uiDialog.css("z-index")&&(this.uiDialog.css("z-index",s+1),i=!0),i&&!e&&this._trigger("focus",t),i},open:function(){var t=this;this._isOpen?this._moveToTop()&&this._focusTabbable():(this._isOpen=!0,this.opener=V(V.ui.safeActiveElement(this.document[0])),this._size(),this._position(),this._createOverlay(),this._moveToTop(null,!0),this.overlay&&this.overlay.css("z-index",this.uiDialog.css("z-index")-1),this._show(this.uiDialog,this.options.show,function(){t._focusTabbable(),t._trigger("focus")}),this._makeFocusTarget(),this._trigger("open"))},_focusTabbable:function(){var t=this._focusedElement;(t=!(t=!(t=!(t=!(t=t||this.element.find("[autofocus]")).length?this.element.find(":tabbable"):t).length?this.uiDialogButtonPane.find(":tabbable"):t).length?this.uiDialogTitlebarClose.filter(":tabbable"):t).length?this.uiDialog:t).eq(0).trigger("focus")},_restoreTabbableFocus:function(){var t=V.ui.safeActiveElement(this.document[0]);this.uiDialog[0]===t||V.contains(this.uiDialog[0],t)||this._focusTabbable()},_keepFocus:function(t){t.preventDefault(),this._restoreTabbableFocus(),this._delay(this._restoreTabbableFocus)},_createWrapper:function(){this.uiDialog=V("<div>").hide().attr({tabIndex:-1,role:"dialog"}).appendTo(this._appendTo()),this._addClass(this.uiDialog,"ui-dialog","ui-widget ui-widget-content ui-front"),this._on(this.uiDialog,{keydown:function(t){if(this.options.closeOnEscape&&!t.isDefaultPrevented()&&t.keyCode&&t.keyCode===V.ui.keyCode.ESCAPE)return t.preventDefault(),void this.close(t);var e,i,s;t.keyCode!==V.ui.keyCode.TAB||t.isDefaultPrevented()||(e=this.uiDialog.find(":tabbable"),i=e.first(),s=e.last(),t.target!==s[0]&&t.target!==this.uiDialog[0]||t.shiftKey?t.target!==i[0]&&t.target!==this.uiDialog[0]||!t.shiftKey||(this._delay(function(){s.trigger("focus")}),t.preventDefault()):(this._delay(function(){i.trigger("focus")}),t.preventDefault()))},mousedown:function(t){this._moveToTop(t)&&this._focusTabbable()}}),this.element.find("[aria-describedby]").length||this.uiDialog.attr({"aria-describedby":this.element.uniqueId().attr("id")})},_createTitlebar:function(){var t;this.uiDialogTitlebar=V("<div>"),this._addClass(this.uiDialogTitlebar,"ui-dialog-titlebar","ui-widget-header ui-helper-clearfix"),this._on(this.uiDialogTitlebar,{mousedown:function(t){V(t.target).closest(".ui-dialog-titlebar-close")||this.uiDialog.trigger("focus")}}),this.uiDialogTitlebarClose=V("<button type='button'></button>").button({label:V("<a>").text(this.options.closeText).html(),icon:"ui-icon-closethick",showLabel:!1}).appendTo(this.uiDialogTitlebar),this._addClass(this.uiDialogTitlebarClose,"ui-dialog-titlebar-close"),this._on(this.uiDialogTitlebarClose,{click:function(t){t.preventDefault(),this.close(t)}}),t=V("<span>").uniqueId().prependTo(this.uiDialogTitlebar),this._addClass(t,"ui-dialog-title"),this._title(t),this.uiDialogTitlebar.prependTo(this.uiDialog),this.uiDialog.attr({"aria-labelledby":t.attr("id")})},_title:function(t){this.options.title?t.text(this.options.title):t.html("&#160;")},_createButtonPane:function(){this.uiDialogButtonPane=V("<div>"),this._addClass(this.uiDialogButtonPane,"ui-dialog-buttonpane","ui-widget-content ui-helper-clearfix"),this.uiButtonSet=V("<div>").appendTo(this.uiDialogButtonPane),this._addClass(this.uiButtonSet,"ui-dialog-buttonset"),this._createButtons()},_createButtons:function(){var s=this,t=this.options.buttons;this.uiDialogButtonPane.remove(),this.uiButtonSet.empty(),V.isEmptyObject(t)||Array.isArray(t)&&!t.length?this._removeClass(this.uiDialog,"ui-dialog-buttons"):(V.each(t,function(t,e){var i;e=V.extend({type:"button"},e="function"==typeof e?{click:e,text:t}:e),i=e.click,t={icon:e.icon,iconPosition:e.iconPosition,showLabel:e.showLabel,icons:e.icons,text:e.text},delete e.click,delete e.icon,delete e.iconPosition,delete e.showLabel,delete e.icons,"boolean"==typeof e.text&&delete e.text,V("<button></button>",e).button(t).appendTo(s.uiButtonSet).on("click",function(){i.apply(s.element[0],arguments)})}),this._addClass(this.uiDialog,"ui-dialog-buttons"),this.uiDialogButtonPane.appendTo(this.uiDialog))},_makeDraggable:function(){var n=this,o=this.options;function a(t){return{position:t.position,offset:t.offset}}this.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(t,e){n._addClass(V(this),"ui-dialog-dragging"),n._blockFrames(),n._trigger("dragStart",t,a(e))},drag:function(t,e){n._trigger("drag",t,a(e))},stop:function(t,e){var i=e.offset.left-n.document.scrollLeft(),s=e.offset.top-n.document.scrollTop();o.position={my:"left top",at:"left"+(0<=i?"+":"")+i+" top"+(0<=s?"+":"")+s,of:n.window},n._removeClass(V(this),"ui-dialog-dragging"),n._unblockFrames(),n._trigger("dragStop",t,a(e))}})},_makeResizable:function(){var n=this,o=this.options,t=o.resizable,e=this.uiDialog.css("position"),t="string"==typeof t?t:"n,e,s,w,se,sw,ne,nw";function a(t){return{originalPosition:t.originalPosition,originalSize:t.originalSize,position:t.position,size:t.size}}this.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:this.element,maxWidth:o.maxWidth,maxHeight:o.maxHeight,minWidth:o.minWidth,minHeight:this._minHeight(),handles:t,start:function(t,e){n._addClass(V(this),"ui-dialog-resizing"),n._blockFrames(),n._trigger("resizeStart",t,a(e))},resize:function(t,e){n._trigger("resize",t,a(e))},stop:function(t,e){var i=n.uiDialog.offset(),s=i.left-n.document.scrollLeft(),i=i.top-n.document.scrollTop();o.height=n.uiDialog.height(),o.width=n.uiDialog.width(),o.position={my:"left top",at:"left"+(0<=s?"+":"")+s+" top"+(0<=i?"+":"")+i,of:n.window},n._removeClass(V(this),"ui-dialog-resizing"),n._unblockFrames(),n._trigger("resizeStop",t,a(e))}}).css("position",e)},_trackFocus:function(){this._on(this.widget(),{focusin:function(t){this._makeFocusTarget(),this._focusedElement=V(t.target)}})},_makeFocusTarget:function(){this._untrackInstance(),this._trackingInstances().unshift(this)},_untrackInstance:function(){var t=this._trackingInstances(),e=V.inArray(this,t);-1!==e&&t.splice(e,1)},_trackingInstances:function(){var t=this.document.data("ui-dialog-instances");return t||this.document.data("ui-dialog-instances",t=[]),t},_minHeight:function(){var t=this.options;return"auto"===t.height?t.minHeight:Math.min(t.minHeight,t.height)},_position:function(){var t=this.uiDialog.is(":visible");t||this.uiDialog.show(),this.uiDialog.position(this.options.position),t||this.uiDialog.hide()},_setOptions:function(t){var i=this,s=!1,n={};V.each(t,function(t,e){i._setOption(t,e),t in i.sizeRelatedOptions&&(s=!0),t in i.resizableRelatedOptions&&(n[t]=e)}),s&&(this._size(),this._position()),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option",n)},_setOption:function(t,e){var i,s=this.uiDialog;"disabled"!==t&&(this._super(t,e),"appendTo"===t&&this.uiDialog.appendTo(this._appendTo()),"buttons"===t&&this._createButtons(),"closeText"===t&&this.uiDialogTitlebarClose.button({label:V("<a>").text(""+this.options.closeText).html()}),"draggable"===t&&((i=s.is(":data(ui-draggable)"))&&!e&&s.draggable("destroy"),!i&&e&&this._makeDraggable()),"position"===t&&this._position(),"resizable"===t&&((i=s.is(":data(ui-resizable)"))&&!e&&s.resizable("destroy"),i&&"string"==typeof e&&s.resizable("option","handles",e),i||!1===e||this._makeResizable()),"title"===t&&this._title(this.uiDialogTitlebar.find(".ui-dialog-title")))},_size:function(){var t,e,i,s=this.options;this.element.show().css({width:"auto",minHeight:0,maxHeight:"none",height:0}),s.minWidth>s.width&&(s.width=s.minWidth),t=this.uiDialog.css({height:"auto",width:s.width}).outerHeight(),e=Math.max(0,s.minHeight-t),i="number"==typeof s.maxHeight?Math.max(0,s.maxHeight-t):"none","auto"===s.height?this.element.css({minHeight:e,maxHeight:i,height:"auto"}):this.element.height(Math.max(0,s.height-t)),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())},_blockFrames:function(){this.iframeBlocks=this.document.find("iframe").map(function(){var t=V(this);return V("<div>").css({position:"absolute",width:t.outerWidth(),height:t.outerHeight()}).appendTo(t.parent()).offset(t.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_allowInteraction:function(t){return!!V(t.target).closest(".ui-dialog").length||!!V(t.target).closest(".ui-datepicker").length},_createOverlay:function(){var i,s;this.options.modal&&(i=V.fn.jquery.substring(0,4),s=!0,this._delay(function(){s=!1}),this.document.data("ui-dialog-overlays")||this.document.on("focusin.ui-dialog",function(t){var e;s||((e=this._trackingInstances()[0])._allowInteraction(t)||(t.preventDefault(),e._focusTabbable(),"3.4."!==i&&"3.5."!==i||e._delay(e._restoreTabbableFocus)))}.bind(this)),this.overlay=V("<div>").appendTo(this._appendTo()),this._addClass(this.overlay,null,"ui-widget-overlay ui-front"),this._on(this.overlay,{mousedown:"_keepFocus"}),this.document.data("ui-dialog-overlays",(this.document.data("ui-dialog-overlays")||0)+1))},_destroyOverlay:function(){var t;this.options.modal&&this.overlay&&((t=this.document.data("ui-dialog-overlays")-1)?this.document.data("ui-dialog-overlays",t):(this.document.off("focusin.ui-dialog"),this.document.removeData("ui-dialog-overlays")),this.overlay.remove(),this.overlay=null)}}),!1!==V.uiBackCompat&&V.widget("ui.dialog",V.ui.dialog,{options:{dialogClass:""},_createWrapper:function(){this._super(),this.uiDialog.addClass(this.options.dialogClass)},_setOption:function(t,e){"dialogClass"===t&&this.uiDialog.removeClass(this.options.dialogClass).addClass(e),this._superApply(arguments)}});V.ui.dialog;function lt(t,e,i){return e<=t&&t<e+i}V.widget("ui.droppable",{version:"1.13.0",widgetEventPrefix:"drop",options:{accept:"*",addClasses:!0,greedy:!1,scope:"default",tolerance:"intersect",activate:null,deactivate:null,drop:null,out:null,over:null},_create:function(){var t,e=this.options,i=e.accept;this.isover=!1,this.isout=!0,this.accept="function"==typeof i?i:function(t){return t.is(i)},this.proportions=function(){if(!arguments.length)return t=t||{width:this.element[0].offsetWidth,height:this.element[0].offsetHeight};t=arguments[0]},this._addToManager(e.scope),e.addClasses&&this._addClass("ui-droppable")},_addToManager:function(t){V.ui.ddmanager.droppables[t]=V.ui.ddmanager.droppables[t]||[],V.ui.ddmanager.droppables[t].push(this)},_splice:function(t){for(var e=0;e<t.length;e++)t[e]===this&&t.splice(e,1)},_destroy:function(){var t=V.ui.ddmanager.droppables[this.options.scope];this._splice(t)},_setOption:function(t,e){var i;"accept"===t?this.accept="function"==typeof e?e:function(t){return t.is(e)}:"scope"===t&&(i=V.ui.ddmanager.droppables[this.options.scope],this._splice(i),this._addToManager(e)),this._super(t,e)},_activate:function(t){var e=V.ui.ddmanager.current;this._addActiveClass(),e&&this._trigger("activate",t,this.ui(e))},_deactivate:function(t){var e=V.ui.ddmanager.current;this._removeActiveClass(),e&&this._trigger("deactivate",t,this.ui(e))},_over:function(t){var e=V.ui.ddmanager.current;e&&(e.currentItem||e.element)[0]!==this.element[0]&&this.accept.call(this.element[0],e.currentItem||e.element)&&(this._addHoverClass(),this._trigger("over",t,this.ui(e)))},_out:function(t){var e=V.ui.ddmanager.current;e&&(e.currentItem||e.element)[0]!==this.element[0]&&this.accept.call(this.element[0],e.currentItem||e.element)&&(this._removeHoverClass(),this._trigger("out",t,this.ui(e)))},_drop:function(e,t){var i=t||V.ui.ddmanager.current,s=!1;return!(!i||(i.currentItem||i.element)[0]===this.element[0])&&(this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function(){var t=V(this).droppable("instance");if(t.options.greedy&&!t.options.disabled&&t.options.scope===i.options.scope&&t.accept.call(t.element[0],i.currentItem||i.element)&&V.ui.intersect(i,V.extend(t,{offset:t.element.offset()}),t.options.tolerance,e))return!(s=!0)}),!s&&(!!this.accept.call(this.element[0],i.currentItem||i.element)&&(this._removeActiveClass(),this._removeHoverClass(),this._trigger("drop",e,this.ui(i)),this.element)))},ui:function(t){return{draggable:t.currentItem||t.element,helper:t.helper,position:t.position,offset:t.positionAbs}},_addHoverClass:function(){this._addClass("ui-droppable-hover")},_removeHoverClass:function(){this._removeClass("ui-droppable-hover")},_addActiveClass:function(){this._addClass("ui-droppable-active")},_removeActiveClass:function(){this._removeClass("ui-droppable-active")}}),V.ui.intersect=function(t,e,i,s){if(!e.offset)return!1;var n=(t.positionAbs||t.position.absolute).left+t.margins.left,o=(t.positionAbs||t.position.absolute).top+t.margins.top,a=n+t.helperProportions.width,r=o+t.helperProportions.height,l=e.offset.left,h=e.offset.top,c=l+e.proportions().width,u=h+e.proportions().height;switch(i){case"fit":return l<=n&&a<=c&&h<=o&&r<=u;case"intersect":return l<n+t.helperProportions.width/2&&a-t.helperProportions.width/2<c&&h<o+t.helperProportions.height/2&&r-t.helperProportions.height/2<u;case"pointer":return lt(s.pageY,h,e.proportions().height)&&lt(s.pageX,l,e.proportions().width);case"touch":return(h<=o&&o<=u||h<=r&&r<=u||o<h&&u<r)&&(l<=n&&n<=c||l<=a&&a<=c||n<l&&c<a);default:return!1}},!(V.ui.ddmanager={current:null,droppables:{default:[]},prepareOffsets:function(t,e){var i,s,n=V.ui.ddmanager.droppables[t.options.scope]||[],o=e?e.type:null,a=(t.currentItem||t.element).find(":data(ui-droppable)").addBack();t:for(i=0;i<n.length;i++)if(!(n[i].options.disabled||t&&!n[i].accept.call(n[i].element[0],t.currentItem||t.element))){for(s=0;s<a.length;s++)if(a[s]===n[i].element[0]){n[i].proportions().height=0;continue t}n[i].visible="none"!==n[i].element.css("display"),n[i].visible&&("mousedown"===o&&n[i]._activate.call(n[i],e),n[i].offset=n[i].element.offset(),n[i].proportions({width:n[i].element[0].offsetWidth,height:n[i].element[0].offsetHeight}))}},drop:function(t,e){var i=!1;return V.each((V.ui.ddmanager.droppables[t.options.scope]||[]).slice(),function(){this.options&&(!this.options.disabled&&this.visible&&V.ui.intersect(t,this,this.options.tolerance,e)&&(i=this._drop.call(this,e)||i),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],t.currentItem||t.element)&&(this.isout=!0,this.isover=!1,this._deactivate.call(this,e)))}),i},dragStart:function(t,e){t.element.parentsUntil("body").on("scroll.droppable",function(){t.options.refreshPositions||V.ui.ddmanager.prepareOffsets(t,e)})},drag:function(n,o){n.options.refreshPositions&&V.ui.ddmanager.prepareOffsets(n,o),V.each(V.ui.ddmanager.droppables[n.options.scope]||[],function(){var t,e,i,s;this.options.disabled||this.greedyChild||!this.visible||(s=!(i=V.ui.intersect(n,this,this.options.tolerance,o))&&this.isover?"isout":i&&!this.isover?"isover":null)&&(this.options.greedy&&(e=this.options.scope,(i=this.element.parents(":data(ui-droppable)").filter(function(){return V(this).droppable("instance").options.scope===e})).length&&((t=V(i[0]).droppable("instance")).greedyChild="isover"===s)),t&&"isover"===s&&(t.isover=!1,t.isout=!0,t._out.call(t,o)),this[s]=!0,this["isout"===s?"isover":"isout"]=!1,this["isover"===s?"_over":"_out"].call(this,o),t&&"isout"===s&&(t.isout=!1,t.isover=!0,t._over.call(t,o)))})},dragStop:function(t,e){t.element.parentsUntil("body").off("scroll.droppable"),t.options.refreshPositions||V.ui.ddmanager.prepareOffsets(t,e)}})!==V.uiBackCompat&&V.widget("ui.droppable",V.ui.droppable,{options:{hoverClass:!1,activeClass:!1},_addActiveClass:function(){this._super(),this.options.activeClass&&this.element.addClass(this.options.activeClass)},_removeActiveClass:function(){this._super(),this.options.activeClass&&this.element.removeClass(this.options.activeClass)},_addHoverClass:function(){this._super(),this.options.hoverClass&&this.element.addClass(this.options.hoverClass)},_removeHoverClass:function(){this._super(),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass)}});V.ui.droppable,V.widget("ui.progressbar",{version:"1.13.0",options:{classes:{"ui-progressbar":"ui-corner-all","ui-progressbar-value":"ui-corner-left","ui-progressbar-complete":"ui-corner-right"},max:100,value:0,change:null,complete:null},min:0,_create:function(){this.oldValue=this.options.value=this._constrainedValue(),this.element.attr({role:"progressbar","aria-valuemin":this.min}),this._addClass("ui-progressbar","ui-widget ui-widget-content"),this.valueDiv=V("<div>").appendTo(this.element),this._addClass(this.valueDiv,"ui-progressbar-value","ui-widget-header"),this._refreshValue()},_destroy:function(){this.element.removeAttr("role aria-valuemin aria-valuemax aria-valuenow"),this.valueDiv.remove()},value:function(t){if(void 0===t)return this.options.value;this.options.value=this._constrainedValue(t),this._refreshValue()},_constrainedValue:function(t){return void 0===t&&(t=this.options.value),this.indeterminate=!1===t,"number"!=typeof t&&(t=0),!this.indeterminate&&Math.min(this.options.max,Math.max(this.min,t))},_setOptions:function(t){var e=t.value;delete t.value,this._super(t),this.options.value=this._constrainedValue(e),this._refreshValue()},_setOption:function(t,e){"max"===t&&(e=Math.max(this.min,e)),this._super(t,e)},_setOptionDisabled:function(t){this._super(t),this.element.attr("aria-disabled",t),this._toggleClass(null,"ui-state-disabled",!!t)},_percentage:function(){return this.indeterminate?100:100*(this.options.value-this.min)/(this.options.max-this.min)},_refreshValue:function(){var t=this.options.value,e=this._percentage();this.valueDiv.toggle(this.indeterminate||t>this.min).width(e.toFixed(0)+"%"),this._toggleClass(this.valueDiv,"ui-progressbar-complete",null,t===this.options.max)._toggleClass("ui-progressbar-indeterminate",null,this.indeterminate),this.indeterminate?(this.element.removeAttr("aria-valuenow"),this.overlayDiv||(this.overlayDiv=V("<div>").appendTo(this.valueDiv),this._addClass(this.overlayDiv,"ui-progressbar-overlay"))):(this.element.attr({"aria-valuemax":this.options.max,"aria-valuenow":t}),this.overlayDiv&&(this.overlayDiv.remove(),this.overlayDiv=null)),this.oldValue!==t&&(this.oldValue=t,this._trigger("change")),t===this.options.max&&this._trigger("complete")}}),V.widget("ui.selectable",V.ui.mouse,{version:"1.13.0",options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch",selected:null,selecting:null,start:null,stop:null,unselected:null,unselecting:null},_create:function(){var i=this;this._addClass("ui-selectable"),this.dragged=!1,this.refresh=function(){i.elementPos=V(i.element[0]).offset(),i.selectees=V(i.options.filter,i.element[0]),i._addClass(i.selectees,"ui-selectee"),i.selectees.each(function(){var t=V(this),e=t.offset(),e={left:e.left-i.elementPos.left,top:e.top-i.elementPos.top};V.data(this,"selectable-item",{element:this,$element:t,left:e.left,top:e.top,right:e.left+t.outerWidth(),bottom:e.top+t.outerHeight(),startselected:!1,selected:t.hasClass("ui-selected"),selecting:t.hasClass("ui-selecting"),unselecting:t.hasClass("ui-unselecting")})})},this.refresh(),this._mouseInit(),this.helper=V("<div>"),this._addClass(this.helper,"ui-selectable-helper")},_destroy:function(){this.selectees.removeData("selectable-item"),this._mouseDestroy()},_mouseStart:function(i){var s=this,t=this.options;this.opos=[i.pageX,i.pageY],this.elementPos=V(this.element[0]).offset(),this.options.disabled||(this.selectees=V(t.filter,this.element[0]),this._trigger("start",i),V(t.appendTo).append(this.helper),this.helper.css({left:i.pageX,top:i.pageY,width:0,height:0}),t.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var t=V.data(this,"selectable-item");t.startselected=!0,i.metaKey||i.ctrlKey||(s._removeClass(t.$element,"ui-selected"),t.selected=!1,s._addClass(t.$element,"ui-unselecting"),t.unselecting=!0,s._trigger("unselecting",i,{unselecting:t.element}))}),V(i.target).parents().addBack().each(function(){var t,e=V.data(this,"selectable-item");if(e)return t=!i.metaKey&&!i.ctrlKey||!e.$element.hasClass("ui-selected"),s._removeClass(e.$element,t?"ui-unselecting":"ui-selected")._addClass(e.$element,t?"ui-selecting":"ui-unselecting"),e.unselecting=!t,e.selecting=t,(e.selected=t)?s._trigger("selecting",i,{selecting:e.element}):s._trigger("unselecting",i,{unselecting:e.element}),!1}))},_mouseDrag:function(s){if(this.dragged=!0,!this.options.disabled){var t,n=this,o=this.options,a=this.opos[0],r=this.opos[1],l=s.pageX,h=s.pageY;return l<a&&(t=l,l=a,a=t),h<r&&(t=h,h=r,r=t),this.helper.css({left:a,top:r,width:l-a,height:h-r}),this.selectees.each(function(){var t=V.data(this,"selectable-item"),e=!1,i={};t&&t.element!==n.element[0]&&(i.left=t.left+n.elementPos.left,i.right=t.right+n.elementPos.left,i.top=t.top+n.elementPos.top,i.bottom=t.bottom+n.elementPos.top,"touch"===o.tolerance?e=!(i.left>l||i.right<a||i.top>h||i.bottom<r):"fit"===o.tolerance&&(e=i.left>a&&i.right<l&&i.top>r&&i.bottom<h),e?(t.selected&&(n._removeClass(t.$element,"ui-selected"),t.selected=!1),t.unselecting&&(n._removeClass(t.$element,"ui-unselecting"),t.unselecting=!1),t.selecting||(n._addClass(t.$element,"ui-selecting"),t.selecting=!0,n._trigger("selecting",s,{selecting:t.element}))):(t.selecting&&((s.metaKey||s.ctrlKey)&&t.startselected?(n._removeClass(t.$element,"ui-selecting"),t.selecting=!1,n._addClass(t.$element,"ui-selected"),t.selected=!0):(n._removeClass(t.$element,"ui-selecting"),t.selecting=!1,t.startselected&&(n._addClass(t.$element,"ui-unselecting"),t.unselecting=!0),n._trigger("unselecting",s,{unselecting:t.element}))),t.selected&&(s.metaKey||s.ctrlKey||t.startselected||(n._removeClass(t.$element,"ui-selected"),t.selected=!1,n._addClass(t.$element,"ui-unselecting"),t.unselecting=!0,n._trigger("unselecting",s,{unselecting:t.element})))))}),!1}},_mouseStop:function(e){var i=this;return this.dragged=!1,V(".ui-unselecting",this.element[0]).each(function(){var t=V.data(this,"selectable-item");i._removeClass(t.$element,"ui-unselecting"),t.unselecting=!1,t.startselected=!1,i._trigger("unselected",e,{unselected:t.element})}),V(".ui-selecting",this.element[0]).each(function(){var t=V.data(this,"selectable-item");i._removeClass(t.$element,"ui-selecting")._addClass(t.$element,"ui-selected"),t.selecting=!1,t.selected=!0,t.startselected=!0,i._trigger("selected",e,{selected:t.element})}),this._trigger("stop",e),this.helper.remove(),!1}}),V.widget("ui.selectmenu",[V.ui.formResetMixin,{version:"1.13.0",defaultElement:"<select>",options:{appendTo:null,classes:{"ui-selectmenu-button-open":"ui-corner-top","ui-selectmenu-button-closed":"ui-corner-all"},disabled:null,icons:{button:"ui-icon-triangle-1-s"},position:{my:"left top",at:"left bottom",collision:"none"},width:!1,change:null,close:null,focus:null,open:null,select:null},_create:function(){var t=this.element.uniqueId().attr("id");this.ids={element:t,button:t+"-button",menu:t+"-menu"},this._drawButton(),this._drawMenu(),this._bindFormResetHandler(),this._rendered=!1,this.menuItems=V()},_drawButton:function(){var t,e=this,i=this._parseOption(this.element.find("option:selected"),this.element[0].selectedIndex);this.labels=this.element.labels().attr("for",this.ids.button),this._on(this.labels,{click:function(t){this.button.trigger("focus"),t.preventDefault()}}),this.element.hide(),this.button=V("<span>",{tabindex:this.options.disabled?-1:0,id:this.ids.button,role:"combobox","aria-expanded":"false","aria-autocomplete":"list","aria-owns":this.ids.menu,"aria-haspopup":"true",title:this.element.attr("title")}).insertAfter(this.element),this._addClass(this.button,"ui-selectmenu-button ui-selectmenu-button-closed","ui-button ui-widget"),t=V("<span>").appendTo(this.button),this._addClass(t,"ui-selectmenu-icon","ui-icon "+this.options.icons.button),this.buttonItem=this._renderButtonItem(i).appendTo(this.button),!1!==this.options.width&&this._resizeButton(),this._on(this.button,this._buttonEvents),this.button.one("focusin",function(){e._rendered||e._refreshMenu()})},_drawMenu:function(){var i=this;this.menu=V("<ul>",{"aria-hidden":"true","aria-labelledby":this.ids.button,id:this.ids.menu}),this.menuWrap=V("<div>").append(this.menu),this._addClass(this.menuWrap,"ui-selectmenu-menu","ui-front"),this.menuWrap.appendTo(this._appendTo()),this.menuInstance=this.menu.menu({classes:{"ui-menu":"ui-corner-bottom"},role:"listbox",select:function(t,e){t.preventDefault(),i._setSelection(),i._select(e.item.data("ui-selectmenu-item"),t)},focus:function(t,e){e=e.item.data("ui-selectmenu-item");null!=i.focusIndex&&e.index!==i.focusIndex&&(i._trigger("focus",t,{item:e}),i.isOpen||i._select(e,t)),i.focusIndex=e.index,i.button.attr("aria-activedescendant",i.menuItems.eq(e.index).attr("id"))}}).menu("instance"),this.menuInstance._off(this.menu,"mouseleave"),this.menuInstance._closeOnDocumentClick=function(){return!1},this.menuInstance._isDivider=function(){return!1}},refresh:function(){this._refreshMenu(),this.buttonItem.replaceWith(this.buttonItem=this._renderButtonItem(this._getSelectedItem().data("ui-selectmenu-item")||{})),null===this.options.width&&this._resizeButton()},_refreshMenu:function(){var t=this.element.find("option");this.menu.empty(),this._parseOptions(t),this._renderMenu(this.menu,this.items),this.menuInstance.refresh(),this.menuItems=this.menu.find("li").not(".ui-selectmenu-optgroup").find(".ui-menu-item-wrapper"),this._rendered=!0,t.length&&(t=this._getSelectedItem(),this.menuInstance.focus(null,t),this._setAria(t.data("ui-selectmenu-item")),this._setOption("disabled",this.element.prop("disabled")))},open:function(t){this.options.disabled||(this._rendered?(this._removeClass(this.menu.find(".ui-state-active"),null,"ui-state-active"),this.menuInstance.focus(null,this._getSelectedItem())):this._refreshMenu(),this.menuItems.length&&(this.isOpen=!0,this._toggleAttr(),this._resizeMenu(),this._position(),this._on(this.document,this._documentClick),this._trigger("open",t)))},_position:function(){this.menuWrap.position(V.extend({of:this.button},this.options.position))},close:function(t){this.isOpen&&(this.isOpen=!1,this._toggleAttr(),this.range=null,this._off(this.document),this._trigger("close",t))},widget:function(){return this.button},menuWidget:function(){return this.menu},_renderButtonItem:function(t){var e=V("<span>");return this._setText(e,t.label),this._addClass(e,"ui-selectmenu-text"),e},_renderMenu:function(s,t){var n=this,o="";V.each(t,function(t,e){var i;e.optgroup!==o&&(i=V("<li>",{text:e.optgroup}),n._addClass(i,"ui-selectmenu-optgroup","ui-menu-divider"+(e.element.parent("optgroup").prop("disabled")?" ui-state-disabled":"")),i.appendTo(s),o=e.optgroup),n._renderItemData(s,e)})},_renderItemData:function(t,e){return this._renderItem(t,e).data("ui-selectmenu-item",e)},_renderItem:function(t,e){var i=V("<li>"),s=V("<div>",{title:e.element.attr("title")});return e.disabled&&this._addClass(i,null,"ui-state-disabled"),this._setText(s,e.label),i.append(s).appendTo(t)},_setText:function(t,e){e?t.text(e):t.html("&#160;")},_move:function(t,e){var i,s=".ui-menu-item";this.isOpen?i=this.menuItems.eq(this.focusIndex).parent("li"):(i=this.menuItems.eq(this.element[0].selectedIndex).parent("li"),s+=":not(.ui-state-disabled)"),(s="first"===t||"last"===t?i["first"===t?"prevAll":"nextAll"](s).eq(-1):i[t+"All"](s).eq(0)).length&&this.menuInstance.focus(e,s)},_getSelectedItem:function(){return this.menuItems.eq(this.element[0].selectedIndex).parent("li")},_toggle:function(t){this[this.isOpen?"close":"open"](t)},_setSelection:function(){var t;this.range&&(window.getSelection?((t=window.getSelection()).removeAllRanges(),t.addRange(this.range)):this.range.select(),this.button.focus())},_documentClick:{mousedown:function(t){this.isOpen&&(V(t.target).closest(".ui-selectmenu-menu, #"+V.escapeSelector(this.ids.button)).length||this.close(t))}},_buttonEvents:{mousedown:function(){var t;window.getSelection?(t=window.getSelection()).rangeCount&&(this.range=t.getRangeAt(0)):this.range=document.selection.createRange()},click:function(t){this._setSelection(),this._toggle(t)},keydown:function(t){var e=!0;switch(t.keyCode){case V.ui.keyCode.TAB:case V.ui.keyCode.ESCAPE:this.close(t),e=!1;break;case V.ui.keyCode.ENTER:this.isOpen&&this._selectFocusedItem(t);break;case V.ui.keyCode.UP:t.altKey?this._toggle(t):this._move("prev",t);break;case V.ui.keyCode.DOWN:t.altKey?this._toggle(t):this._move("next",t);break;case V.ui.keyCode.SPACE:this.isOpen?this._selectFocusedItem(t):this._toggle(t);break;case V.ui.keyCode.LEFT:this._move("prev",t);break;case V.ui.keyCode.RIGHT:this._move("next",t);break;case V.ui.keyCode.HOME:case V.ui.keyCode.PAGE_UP:this._move("first",t);break;case V.ui.keyCode.END:case V.ui.keyCode.PAGE_DOWN:this._move("last",t);break;default:this.menu.trigger(t),e=!1}e&&t.preventDefault()}},_selectFocusedItem:function(t){var e=this.menuItems.eq(this.focusIndex).parent("li");e.hasClass("ui-state-disabled")||this._select(e.data("ui-selectmenu-item"),t)},_select:function(t,e){var i=this.element[0].selectedIndex;this.element[0].selectedIndex=t.index,this.buttonItem.replaceWith(this.buttonItem=this._renderButtonItem(t)),this._setAria(t),this._trigger("select",e,{item:t}),t.index!==i&&this._trigger("change",e,{item:t}),this.close(e)},_setAria:function(t){t=this.menuItems.eq(t.index).attr("id");this.button.attr({"aria-labelledby":t,"aria-activedescendant":t}),this.menu.attr("aria-activedescendant",t)},_setOption:function(t,e){var i;"icons"===t&&(i=this.button.find("span.ui-icon"),this._removeClass(i,null,this.options.icons.button)._addClass(i,null,e.button)),this._super(t,e),"appendTo"===t&&this.menuWrap.appendTo(this._appendTo()),"width"===t&&this._resizeButton()},_setOptionDisabled:function(t){this._super(t),this.menuInstance.option("disabled",t),this.button.attr("aria-disabled",t),this._toggleClass(this.button,null,"ui-state-disabled",t),this.element.prop("disabled",t),t?(this.button.attr("tabindex",-1),this.close()):this.button.attr("tabindex",0)},_appendTo:function(){var t=this.options.appendTo;return t=!(t=!(t=t&&(t.jquery||t.nodeType?V(t):this.document.find(t).eq(0)))||!t[0]?this.element.closest(".ui-front, dialog"):t).length?this.document[0].body:t},_toggleAttr:function(){this.button.attr("aria-expanded",this.isOpen),this._removeClass(this.button,"ui-selectmenu-button-"+(this.isOpen?"closed":"open"))._addClass(this.button,"ui-selectmenu-button-"+(this.isOpen?"open":"closed"))._toggleClass(this.menuWrap,"ui-selectmenu-open",null,this.isOpen),this.menu.attr("aria-hidden",!this.isOpen)},_resizeButton:function(){var t=this.options.width;!1!==t?(null===t&&(t=this.element.show().outerWidth(),this.element.hide()),this.button.outerWidth(t)):this.button.css("width","")},_resizeMenu:function(){this.menu.outerWidth(Math.max(this.button.outerWidth(),this.menu.width("").outerWidth()+1))},_getCreateOptions:function(){var t=this._super();return t.disabled=this.element.prop("disabled"),t},_parseOptions:function(t){var i=this,s=[];t.each(function(t,e){e.hidden||s.push(i._parseOption(V(e),t))}),this.items=s},_parseOption:function(t,e){var i=t.parent("optgroup");return{element:t,index:e,value:t.val(),label:t.text(),optgroup:i.attr("label")||"",disabled:i.prop("disabled")||t.prop("disabled")}},_destroy:function(){this._unbindFormResetHandler(),this.menuWrap.remove(),this.button.remove(),this.element.show(),this.element.removeUniqueId(),this.labels.attr("for",this.ids.element)}}]),V.widget("ui.slider",V.ui.mouse,{version:"1.13.0",widgetEventPrefix:"slide",options:{animate:!1,classes:{"ui-slider":"ui-corner-all","ui-slider-handle":"ui-corner-all","ui-slider-range":"ui-corner-all ui-widget-header"},distance:0,max:100,min:0,orientation:"horizontal",range:!1,step:1,value:0,values:null,change:null,slide:null,start:null,stop:null},numPages:5,_create:function(){this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this._calculateNewMax(),this._addClass("ui-slider ui-slider-"+this.orientation,"ui-widget ui-widget-content"),this._refresh(),this._animateOff=!1},_refresh:function(){this._createRange(),this._createHandles(),this._setupEvents(),this._refreshValue()},_createHandles:function(){var t,e=this.options,i=this.element.find(".ui-slider-handle"),s=[],n=e.values&&e.values.length||1;for(i.length>n&&(i.slice(n).remove(),i=i.slice(0,n)),t=i.length;t<n;t++)s.push("<span tabindex='0'></span>");this.handles=i.add(V(s.join("")).appendTo(this.element)),this._addClass(this.handles,"ui-slider-handle","ui-state-default"),this.handle=this.handles.eq(0),this.handles.each(function(t){V(this).data("ui-slider-handle-index",t).attr("tabIndex",0)})},_createRange:function(){var t=this.options;t.range?(!0===t.range&&(t.values?t.values.length&&2!==t.values.length?t.values=[t.values[0],t.values[0]]:Array.isArray(t.values)&&(t.values=t.values.slice(0)):t.values=[this._valueMin(),this._valueMin()]),this.range&&this.range.length?(this._removeClass(this.range,"ui-slider-range-min ui-slider-range-max"),this.range.css({left:"",bottom:""})):(this.range=V("<div>").appendTo(this.element),this._addClass(this.range,"ui-slider-range")),"min"!==t.range&&"max"!==t.range||this._addClass(this.range,"ui-slider-range-"+t.range)):(this.range&&this.range.remove(),this.range=null)},_setupEvents:function(){this._off(this.handles),this._on(this.handles,this._handleEvents),this._hoverable(this.handles),this._focusable(this.handles)},_destroy:function(){this.handles.remove(),this.range&&this.range.remove(),this._mouseDestroy()},_mouseCapture:function(t){var i,s,n,o,e,a,r=this,l=this.options;return!l.disabled&&(this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()},this.elementOffset=this.element.offset(),a={x:t.pageX,y:t.pageY},i=this._normValueFromMouse(a),s=this._valueMax()-this._valueMin()+1,this.handles.each(function(t){var e=Math.abs(i-r.values(t));(e<s||s===e&&(t===r._lastChangedValue||r.values(t)===l.min))&&(s=e,n=V(this),o=t)}),!1!==this._start(t,o)&&(this._mouseSliding=!0,this._handleIndex=o,this._addClass(n,null,"ui-state-active"),n.trigger("focus"),e=n.offset(),a=!V(t.target).parents().addBack().is(".ui-slider-handle"),this._clickOffset=a?{left:0,top:0}:{left:t.pageX-e.left-n.width()/2,top:t.pageY-e.top-n.height()/2-(parseInt(n.css("borderTopWidth"),10)||0)-(parseInt(n.css("borderBottomWidth"),10)||0)+(parseInt(n.css("marginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(t,o,i),this._animateOff=!0))},_mouseStart:function(){return!0},_mouseDrag:function(t){var e={x:t.pageX,y:t.pageY},e=this._normValueFromMouse(e);return this._slide(t,this._handleIndex,e),!1},_mouseStop:function(t){return this._removeClass(this.handles,null,"ui-state-active"),this._mouseSliding=!1,this._stop(t,this._handleIndex),this._change(t,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1},_detectOrientation:function(){this.orientation="vertical"===this.options.orientation?"vertical":"horizontal"},_normValueFromMouse:function(t){var e,t="horizontal"===this.orientation?(e=this.elementSize.width,t.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(e=this.elementSize.height,t.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),t=t/e;return(t=1<t?1:t)<0&&(t=0),"vertical"===this.orientation&&(t=1-t),e=this._valueMax()-this._valueMin(),e=this._valueMin()+t*e,this._trimAlignValue(e)},_uiHash:function(t,e,i){var s={handle:this.handles[t],handleIndex:t,value:void 0!==e?e:this.value()};return this._hasMultipleValues()&&(s.value=void 0!==e?e:this.values(t),s.values=i||this.values()),s},_hasMultipleValues:function(){return this.options.values&&this.options.values.length},_start:function(t,e){return this._trigger("start",t,this._uiHash(e))},_slide:function(t,e,i){var s,n=this.value(),o=this.values();this._hasMultipleValues()&&(s=this.values(e?0:1),n=this.values(e),2===this.options.values.length&&!0===this.options.range&&(i=0===e?Math.min(s,i):Math.max(s,i)),o[e]=i),i!==n&&!1!==this._trigger("slide",t,this._uiHash(e,i,o))&&(this._hasMultipleValues()?this.values(e,i):this.value(i))},_stop:function(t,e){this._trigger("stop",t,this._uiHash(e))},_change:function(t,e){this._keySliding||this._mouseSliding||(this._lastChangedValue=e,this._trigger("change",t,this._uiHash(e)))},value:function(t){return arguments.length?(this.options.value=this._trimAlignValue(t),this._refreshValue(),void this._change(null,0)):this._value()},values:function(t,e){var i,s,n;if(1<arguments.length)return this.options.values[t]=this._trimAlignValue(e),this._refreshValue(),void this._change(null,t);if(!arguments.length)return this._values();if(!Array.isArray(t))return this._hasMultipleValues()?this._values(t):this.value();for(i=this.options.values,s=t,n=0;n<i.length;n+=1)i[n]=this._trimAlignValue(s[n]),this._change(null,n);this._refreshValue()},_setOption:function(t,e){var i,s=0;switch("range"===t&&!0===this.options.range&&("min"===e?(this.options.value=this._values(0),this.options.values=null):"max"===e&&(this.options.value=this._values(this.options.values.length-1),this.options.values=null)),Array.isArray(this.options.values)&&(s=this.options.values.length),this._super(t,e),t){case"orientation":this._detectOrientation(),this._removeClass("ui-slider-horizontal ui-slider-vertical")._addClass("ui-slider-"+this.orientation),this._refreshValue(),this.options.range&&this._refreshRange(e),this.handles.css("horizontal"===e?"bottom":"left","");break;case"value":this._animateOff=!0,this._refreshValue(),this._change(null,0),this._animateOff=!1;break;case"values":for(this._animateOff=!0,this._refreshValue(),i=s-1;0<=i;i--)this._change(null,i);this._animateOff=!1;break;case"step":case"min":case"max":this._animateOff=!0,this._calculateNewMax(),this._refreshValue(),this._animateOff=!1;break;case"range":this._animateOff=!0,this._refresh(),this._animateOff=!1}},_setOptionDisabled:function(t){this._super(t),this._toggleClass(null,"ui-state-disabled",!!t)},_value:function(){var t=this.options.value;return t=this._trimAlignValue(t)},_values:function(t){var e,i;if(arguments.length)return t=this.options.values[t],t=this._trimAlignValue(t);if(this._hasMultipleValues()){for(e=this.options.values.slice(),i=0;i<e.length;i+=1)e[i]=this._trimAlignValue(e[i]);return e}return[]},_trimAlignValue:function(t){if(t<=this._valueMin())return this._valueMin();if(t>=this._valueMax())return this._valueMax();var e=0<this.options.step?this.options.step:1,i=(t-this._valueMin())%e,t=t-i;return 2*Math.abs(i)>=e&&(t+=0<i?e:-e),parseFloat(t.toFixed(5))},_calculateNewMax:function(){var t=this.options.max,e=this._valueMin(),i=this.options.step;(t=Math.round((t-e)/i)*i+e)>this.options.max&&(t-=i),this.max=parseFloat(t.toFixed(this._precision()))},_precision:function(){var t=this._precisionOf(this.options.step);return t=null!==this.options.min?Math.max(t,this._precisionOf(this.options.min)):t},_precisionOf:function(t){var e=t.toString(),t=e.indexOf(".");return-1===t?0:e.length-t-1},_valueMin:function(){return this.options.min},_valueMax:function(){return this.max},_refreshRange:function(t){"vertical"===t&&this.range.css({width:"",left:""}),"horizontal"===t&&this.range.css({height:"",bottom:""})},_refreshValue:function(){var e,i,t,s,n,o=this.options.range,a=this.options,r=this,l=!this._animateOff&&a.animate,h={};this._hasMultipleValues()?this.handles.each(function(t){i=(r.values(t)-r._valueMin())/(r._valueMax()-r._valueMin())*100,h["horizontal"===r.orientation?"left":"bottom"]=i+"%",V(this).stop(1,1)[l?"animate":"css"](h,a.animate),!0===r.options.range&&("horizontal"===r.orientation?(0===t&&r.range.stop(1,1)[l?"animate":"css"]({left:i+"%"},a.animate),1===t&&r.range[l?"animate":"css"]({width:i-e+"%"},{queue:!1,duration:a.animate})):(0===t&&r.range.stop(1,1)[l?"animate":"css"]({bottom:i+"%"},a.animate),1===t&&r.range[l?"animate":"css"]({height:i-e+"%"},{queue:!1,duration:a.animate}))),e=i}):(t=this.value(),s=this._valueMin(),n=this._valueMax(),i=n!==s?(t-s)/(n-s)*100:0,h["horizontal"===this.orientation?"left":"bottom"]=i+"%",this.handle.stop(1,1)[l?"animate":"css"](h,a.animate),"min"===o&&"horizontal"===this.orientation&&this.range.stop(1,1)[l?"animate":"css"]({width:i+"%"},a.animate),"max"===o&&"horizontal"===this.orientation&&this.range.stop(1,1)[l?"animate":"css"]({width:100-i+"%"},a.animate),"min"===o&&"vertical"===this.orientation&&this.range.stop(1,1)[l?"animate":"css"]({height:i+"%"},a.animate),"max"===o&&"vertical"===this.orientation&&this.range.stop(1,1)[l?"animate":"css"]({height:100-i+"%"},a.animate))},_handleEvents:{keydown:function(t){var e,i,s,n=V(t.target).data("ui-slider-handle-index");switch(t.keyCode){case V.ui.keyCode.HOME:case V.ui.keyCode.END:case V.ui.keyCode.PAGE_UP:case V.ui.keyCode.PAGE_DOWN:case V.ui.keyCode.UP:case V.ui.keyCode.RIGHT:case V.ui.keyCode.DOWN:case V.ui.keyCode.LEFT:if(t.preventDefault(),!this._keySliding&&(this._keySliding=!0,this._addClass(V(t.target),null,"ui-state-active"),!1===this._start(t,n)))return}switch(s=this.options.step,e=i=this._hasMultipleValues()?this.values(n):this.value(),t.keyCode){case V.ui.keyCode.HOME:i=this._valueMin();break;case V.ui.keyCode.END:i=this._valueMax();break;case V.ui.keyCode.PAGE_UP:i=this._trimAlignValue(e+(this._valueMax()-this._valueMin())/this.numPages);break;case V.ui.keyCode.PAGE_DOWN:i=this._trimAlignValue(e-(this._valueMax()-this._valueMin())/this.numPages);break;case V.ui.keyCode.UP:case V.ui.keyCode.RIGHT:if(e===this._valueMax())return;i=this._trimAlignValue(e+s);break;case V.ui.keyCode.DOWN:case V.ui.keyCode.LEFT:if(e===this._valueMin())return;i=this._trimAlignValue(e-s)}this._slide(t,n,i)},keyup:function(t){var e=V(t.target).data("ui-slider-handle-index");this._keySliding&&(this._keySliding=!1,this._stop(t,e),this._change(t,e),this._removeClass(V(t.target),null,"ui-state-active"))}}}),V.widget("ui.sortable",V.ui.mouse,{version:"1.13.0",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_isOverAxis:function(t,e,i){return e<=t&&t<e+i},_isFloating:function(t){return/left|right/.test(t.css("float"))||/inline|table-cell/.test(t.css("display"))},_create:function(){this.containerCache={},this._addClass("ui-sortable"),this.refresh(),this.offset=this.element.offset(),this._mouseInit(),this._setHandleClassName(),this.ready=!0},_setOption:function(t,e){this._super(t,e),"handle"===t&&this._setHandleClassName()},_setHandleClassName:function(){var t=this;this._removeClass(this.element.find(".ui-sortable-handle"),"ui-sortable-handle"),V.each(this.items,function(){t._addClass(this.instance.options.handle?this.item.find(this.instance.options.handle):this.item,"ui-sortable-handle")})},_destroy:function(){this._mouseDestroy();for(var t=this.items.length-1;0<=t;t--)this.items[t].item.removeData(this.widgetName+"-item");return this},_mouseCapture:function(t,e){var i=null,s=!1,n=this;return!this.reverting&&(!this.options.disabled&&"static"!==this.options.type&&(this._refreshItems(t),V(t.target).parents().each(function(){if(V.data(this,n.widgetName+"-item")===n)return i=V(this),!1}),!!(i=V.data(t.target,n.widgetName+"-item")===n?V(t.target):i)&&(!(this.options.handle&&!e&&(V(this.options.handle,i).find("*").addBack().each(function(){this===t.target&&(s=!0)}),!s))&&(this.currentItem=i,this._removeCurrentsFromItems(),!0))))},_mouseStart:function(t,e,i){var s,n,o=this.options;if((this.currentContainer=this).refreshPositions(),this.appendTo=V("parent"!==o.appendTo?o.appendTo:this.currentItem.parent()),this.helper=this._createHelper(t),this._cacheHelperProportions(),this._cacheMargins(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},V.extend(this.offset,{click:{left:t.pageX-this.offset.left,top:t.pageY-this.offset.top},relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),o.cursorAt&&this._adjustOffsetFromHelper(o.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!==this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),this.scrollParent=this.placeholder.scrollParent(),V.extend(this.offset,{parent:this._getParentOffset()}),o.containment&&this._setContainment(),o.cursor&&"auto"!==o.cursor&&(n=this.document.find("body"),this.storedCursor=n.css("cursor"),n.css("cursor",o.cursor),this.storedStylesheet=V("<style>*{ cursor: "+o.cursor+" !important; }</style>").appendTo(n)),o.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",o.zIndex)),o.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",o.opacity)),this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",t,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!i)for(s=this.containers.length-1;0<=s;s--)this.containers[s]._trigger("activate",t,this._uiHash(this));return V.ui.ddmanager&&(V.ui.ddmanager.current=this),V.ui.ddmanager&&!o.dropBehaviour&&V.ui.ddmanager.prepareOffsets(this,t),this.dragging=!0,this._addClass(this.helper,"ui-sortable-helper"),this.helper.parent().is(this.appendTo)||(this.helper.detach().appendTo(this.appendTo),this.offset.parent=this._getParentOffset()),this.position=this.originalPosition=this._generatePosition(t),this.originalPageX=t.pageX,this.originalPageY=t.pageY,this.lastPositionAbs=this.positionAbs=this._convertPositionTo("absolute"),this._mouseDrag(t),!0},_scroll:function(t){var e=this.options,i=!1;return this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-t.pageY<e.scrollSensitivity?this.scrollParent[0].scrollTop=i=this.scrollParent[0].scrollTop+e.scrollSpeed:t.pageY-this.overflowOffset.top<e.scrollSensitivity&&(this.scrollParent[0].scrollTop=i=this.scrollParent[0].scrollTop-e.scrollSpeed),this.overflowOffset.left+this.scrollParent[0].offsetWidth-t.pageX<e.scrollSensitivity?this.scrollParent[0].scrollLeft=i=this.scrollParent[0].scrollLeft+e.scrollSpeed:t.pageX-this.overflowOffset.left<e.scrollSensitivity&&(this.scrollParent[0].scrollLeft=i=this.scrollParent[0].scrollLeft-e.scrollSpeed)):(t.pageY-this.document.scrollTop()<e.scrollSensitivity?i=this.document.scrollTop(this.document.scrollTop()-e.scrollSpeed):this.window.height()-(t.pageY-this.document.scrollTop())<e.scrollSensitivity&&(i=this.document.scrollTop(this.document.scrollTop()+e.scrollSpeed)),t.pageX-this.document.scrollLeft()<e.scrollSensitivity?i=this.document.scrollLeft(this.document.scrollLeft()-e.scrollSpeed):this.window.width()-(t.pageX-this.document.scrollLeft())<e.scrollSensitivity&&(i=this.document.scrollLeft(this.document.scrollLeft()+e.scrollSpeed))),i},_mouseDrag:function(t){var e,i,s,n,o=this.options;if(this.position=this._generatePosition(t),this.positionAbs=this._convertPositionTo("absolute"),this.options.axis&&"y"===this.options.axis||(this.helper[0].style.left=this.position.left+"px"),this.options.axis&&"x"===this.options.axis||(this.helper[0].style.top=this.position.top+"px"),this._contactContainers(t),null!==this.innermostContainer)for(o.scroll&&!1!==this._scroll(t)&&(this._refreshItemPositions(!0),V.ui.ddmanager&&!o.dropBehaviour&&V.ui.ddmanager.prepareOffsets(this,t)),this.dragDirection={vertical:this._getDragVerticalDirection(),horizontal:this._getDragHorizontalDirection()},e=this.items.length-1;0<=e;e--)if(s=(i=this.items[e]).item[0],(n=this._intersectsWithPointer(i))&&i.instance===this.currentContainer&&!(s===this.currentItem[0]||this.placeholder[1===n?"next":"prev"]()[0]===s||V.contains(this.placeholder[0],s)||"semi-dynamic"===this.options.type&&V.contains(this.element[0],s))){if(this.direction=1===n?"down":"up","pointer"!==this.options.tolerance&&!this._intersectsWithSides(i))break;this._rearrange(t,i),this._trigger("change",t,this._uiHash());break}return V.ui.ddmanager&&V.ui.ddmanager.drag(this,t),this._trigger("sort",t,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(t,e){var i,s,n,o;if(t)return V.ui.ddmanager&&!this.options.dropBehaviour&&V.ui.ddmanager.drop(this,t),this.options.revert?(s=(i=this).placeholder.offset(),o={},(n=this.options.axis)&&"x"!==n||(o.left=s.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===this.document[0].body?0:this.offsetParent[0].scrollLeft)),n&&"y"!==n||(o.top=s.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===this.document[0].body?0:this.offsetParent[0].scrollTop)),this.reverting=!0,V(this.helper).animate(o,parseInt(this.options.revert,10)||500,function(){i._clear(t)})):this._clear(t,e),!1},cancel:function(){if(this.dragging){this._mouseUp(new V.Event("mouseup",{target:null})),"original"===this.options.helper?(this.currentItem.css(this._storedCSS),this._removeClass(this.currentItem,"ui-sortable-helper")):this.currentItem.show();for(var t=this.containers.length-1;0<=t;t--)this.containers[t]._trigger("deactivate",null,this._uiHash(this)),this.containers[t].containerCache.over&&(this.containers[t]._trigger("out",null,this._uiHash(this)),this.containers[t].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),"original"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),V.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?V(this.domPosition.prev).after(this.currentItem):V(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(e){var t=this._getItemsAsjQuery(e&&e.connected),i=[];return e=e||{},V(t).each(function(){var t=(V(e.item||this).attr(e.attribute||"id")||"").match(e.expression||/(.+)[\-=_](.+)/);t&&i.push((e.key||t[1]+"[]")+"="+(e.key&&e.expression?t[1]:t[2]))}),!i.length&&e.key&&i.push(e.key+"="),i.join("&")},toArray:function(t){var e=this._getItemsAsjQuery(t&&t.connected),i=[];return t=t||{},e.each(function(){i.push(V(t.item||this).attr(t.attribute||"id")||"")}),i},_intersectsWith:function(t){var e=this.positionAbs.left,i=e+this.helperProportions.width,s=this.positionAbs.top,n=s+this.helperProportions.height,o=t.left,a=o+t.width,r=t.top,l=r+t.height,h=this.offset.click.top,c=this.offset.click.left,h="x"===this.options.axis||r<s+h&&s+h<l,c="y"===this.options.axis||o<e+c&&e+c<a;return"pointer"===this.options.tolerance||this.options.forcePointerForContainers||"pointer"!==this.options.tolerance&&this.helperProportions[this.floating?"width":"height"]>t[this.floating?"width":"height"]?h&&c:o<e+this.helperProportions.width/2&&i-this.helperProportions.width/2<a&&r<s+this.helperProportions.height/2&&n-this.helperProportions.height/2<l},_intersectsWithPointer:function(t){var e="x"===this.options.axis||this._isOverAxis(this.positionAbs.top+this.offset.click.top,t.top,t.height),t="y"===this.options.axis||this._isOverAxis(this.positionAbs.left+this.offset.click.left,t.left,t.width);return!(!e||!t)&&(e=this.dragDirection.vertical,t=this.dragDirection.horizontal,this.floating?"right"===t||"down"===e?2:1:e&&("down"===e?2:1))},_intersectsWithSides:function(t){var e=this._isOverAxis(this.positionAbs.top+this.offset.click.top,t.top+t.height/2,t.height),i=this._isOverAxis(this.positionAbs.left+this.offset.click.left,t.left+t.width/2,t.width),s=this.dragDirection.vertical,t=this.dragDirection.horizontal;return this.floating&&t?"right"===t&&i||"left"===t&&!i:s&&("down"===s&&e||"up"===s&&!e)},_getDragVerticalDirection:function(){var t=this.positionAbs.top-this.lastPositionAbs.top;return 0!=t&&(0<t?"down":"up")},_getDragHorizontalDirection:function(){var t=this.positionAbs.left-this.lastPositionAbs.left;return 0!=t&&(0<t?"right":"left")},refresh:function(t){return this._refreshItems(t),this._setHandleClassName(),this.refreshPositions(),this},_connectWith:function(){var t=this.options;return t.connectWith.constructor===String?[t.connectWith]:t.connectWith},_getItemsAsjQuery:function(t){var e,i,s,n,o=[],a=[],r=this._connectWith();if(r&&t)for(e=r.length-1;0<=e;e--)for(i=(s=V(r[e],this.document[0])).length-1;0<=i;i--)(n=V.data(s[i],this.widgetFullName))&&n!==this&&!n.options.disabled&&a.push(["function"==typeof n.options.items?n.options.items.call(n.element):V(n.options.items,n.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),n]);function l(){o.push(this)}for(a.push(["function"==typeof this.options.items?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):V(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]),e=a.length-1;0<=e;e--)a[e][0].each(l);return V(o)},_removeCurrentsFromItems:function(){var i=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=V.grep(this.items,function(t){for(var e=0;e<i.length;e++)if(i[e]===t.item[0])return!1;return!0})},_refreshItems:function(t){this.items=[],this.containers=[this];var e,i,s,n,o,a,r,l,h=this.items,c=[["function"==typeof this.options.items?this.options.items.call(this.element[0],t,{item:this.currentItem}):V(this.options.items,this.element),this]],u=this._connectWith();if(u&&this.ready)for(e=u.length-1;0<=e;e--)for(i=(s=V(u[e],this.document[0])).length-1;0<=i;i--)(n=V.data(s[i],this.widgetFullName))&&n!==this&&!n.options.disabled&&(c.push(["function"==typeof n.options.items?n.options.items.call(n.element[0],t,{item:this.currentItem}):V(n.options.items,n.element),n]),this.containers.push(n));for(e=c.length-1;0<=e;e--)for(o=c[e][1],l=(a=c[e][i=0]).length;i<l;i++)(r=V(a[i])).data(this.widgetName+"-item",o),h.push({item:r,instance:o,width:0,height:0,left:0,top:0})},_refreshItemPositions:function(t){for(var e,i,s=this.items.length-1;0<=s;s--)e=this.items[s],this.currentContainer&&e.instance!==this.currentContainer&&e.item[0]!==this.currentItem[0]||(i=this.options.toleranceElement?V(this.options.toleranceElement,e.item):e.item,t||(e.width=i.outerWidth(),e.height=i.outerHeight()),i=i.offset(),e.left=i.left,e.top=i.top)},refreshPositions:function(t){var e,i;if(this.floating=!!this.items.length&&("x"===this.options.axis||this._isFloating(this.items[0].item)),null!==this.innermostContainer&&this._refreshItemPositions(t),this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(e=this.containers.length-1;0<=e;e--)i=this.containers[e].element.offset(),this.containers[e].containerCache.left=i.left,this.containers[e].containerCache.top=i.top,this.containers[e].containerCache.width=this.containers[e].element.outerWidth(),this.containers[e].containerCache.height=this.containers[e].element.outerHeight();return this},_createPlaceholder:function(i){var s,n,o=(i=i||this).options;o.placeholder&&o.placeholder.constructor!==String||(s=o.placeholder,n=i.currentItem[0].nodeName.toLowerCase(),o.placeholder={element:function(){var t=V("<"+n+">",i.document[0]);return i._addClass(t,"ui-sortable-placeholder",s||i.currentItem[0].className)._removeClass(t,"ui-sortable-helper"),"tbody"===n?i._createTrPlaceholder(i.currentItem.find("tr").eq(0),V("<tr>",i.document[0]).appendTo(t)):"tr"===n?i._createTrPlaceholder(i.currentItem,t):"img"===n&&t.attr("src",i.currentItem.attr("src")),s||t.css("visibility","hidden"),t},update:function(t,e){s&&!o.forcePlaceholderSize||(e.height()&&(!o.forcePlaceholderSize||"tbody"!==n&&"tr"!==n)||e.height(i.currentItem.innerHeight()-parseInt(i.currentItem.css("paddingTop")||0,10)-parseInt(i.currentItem.css("paddingBottom")||0,10)),e.width()||e.width(i.currentItem.innerWidth()-parseInt(i.currentItem.css("paddingLeft")||0,10)-parseInt(i.currentItem.css("paddingRight")||0,10)))}}),i.placeholder=V(o.placeholder.element.call(i.element,i.currentItem)),i.currentItem.after(i.placeholder),o.placeholder.update(i,i.placeholder)},_createTrPlaceholder:function(t,e){var i=this;t.children().each(function(){V("<td>&#160;</td>",i.document[0]).attr("colspan",V(this).attr("colspan")||1).appendTo(e)})},_contactContainers:function(t){for(var e,i,s,n,o,a,r,l,h,c=null,u=null,d=this.containers.length-1;0<=d;d--)V.contains(this.currentItem[0],this.containers[d].element[0])||(this._intersectsWith(this.containers[d].containerCache)?c&&V.contains(this.containers[d].element[0],c.element[0])||(c=this.containers[d],u=d):this.containers[d].containerCache.over&&(this.containers[d]._trigger("out",t,this._uiHash(this)),this.containers[d].containerCache.over=0));if(this.innermostContainer=c)if(1===this.containers.length)this.containers[u].containerCache.over||(this.containers[u]._trigger("over",t,this._uiHash(this)),this.containers[u].containerCache.over=1);else{for(i=1e4,s=null,n=(l=c.floating||this._isFloating(this.currentItem))?"left":"top",o=l?"width":"height",h=l?"pageX":"pageY",e=this.items.length-1;0<=e;e--)V.contains(this.containers[u].element[0],this.items[e].item[0])&&this.items[e].item[0]!==this.currentItem[0]&&(a=this.items[e].item.offset()[n],r=!1,t[h]-a>this.items[e][o]/2&&(r=!0),Math.abs(t[h]-a)<i&&(i=Math.abs(t[h]-a),s=this.items[e],this.direction=r?"up":"down"));(s||this.options.dropOnEmpty)&&(this.currentContainer!==this.containers[u]?(s?this._rearrange(t,s,null,!0):this._rearrange(t,null,this.containers[u].element,!0),this._trigger("change",t,this._uiHash()),this.containers[u]._trigger("change",t,this._uiHash(this)),this.currentContainer=this.containers[u],this.options.placeholder.update(this.currentContainer,this.placeholder),this.scrollParent=this.placeholder.scrollParent(),this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this.containers[u]._trigger("over",t,this._uiHash(this)),this.containers[u].containerCache.over=1):this.currentContainer.containerCache.over||(this.containers[u]._trigger("over",t,this._uiHash()),this.currentContainer.containerCache.over=1))}},_createHelper:function(t){var e=this.options,t="function"==typeof e.helper?V(e.helper.apply(this.element[0],[t,this.currentItem])):"clone"===e.helper?this.currentItem.clone():this.currentItem;return t.parents("body").length||this.appendTo[0].appendChild(t[0]),t[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")}),t[0].style.width&&!e.forceHelperSize||t.width(this.currentItem.width()),t[0].style.height&&!e.forceHelperSize||t.height(this.currentItem.height()),t},_adjustOffsetFromHelper:function(t){"string"==typeof t&&(t=t.split(" ")),"left"in(t=Array.isArray(t)?{left:+t[0],top:+t[1]||0}:t)&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var t=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==this.document[0]&&V.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop()),{top:(t=this.offsetParent[0]===this.document[0].body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&V.ui.ie?{top:0,left:0}:t).top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"!==this.cssPosition)return{top:0,left:0};var t=this.currentItem.position();return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}},_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 t,e,i=this.options;"parent"===i.containment&&(i.containment=this.helper[0].parentNode),"document"!==i.containment&&"window"!==i.containment||(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,"document"===i.containment?this.document.width():this.window.width()-this.helperProportions.width-this.margins.left,("document"===i.containment?this.document.height()||document.body.parentNode.scrollHeight:this.window.height()||this.document[0].body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(i.containment)||(t=V(i.containment)[0],e=V(i.containment).offset(),i="hidden"!==V(t).css("overflow"),this.containment=[e.left+(parseInt(V(t).css("borderLeftWidth"),10)||0)+(parseInt(V(t).css("paddingLeft"),10)||0)-this.margins.left,e.top+(parseInt(V(t).css("borderTopWidth"),10)||0)+(parseInt(V(t).css("paddingTop"),10)||0)-this.margins.top,e.left+(i?Math.max(t.scrollWidth,t.offsetWidth):t.offsetWidth)-(parseInt(V(t).css("borderLeftWidth"),10)||0)-(parseInt(V(t).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,e.top+(i?Math.max(t.scrollHeight,t.offsetHeight):t.offsetHeight)-(parseInt(V(t).css("borderTopWidth"),10)||0)-(parseInt(V(t).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(t,e){e=e||this.position;var i="absolute"===t?1:-1,s="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&V.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,t=/(html|body)/i.test(s[0].tagName);return{top:e.top+this.offset.relative.top*i+this.offset.parent.top*i-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():t?0:s.scrollTop())*i,left:e.left+this.offset.relative.left*i+this.offset.parent.left*i-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():t?0:s.scrollLeft())*i}},_generatePosition:function(t){var e=this.options,i=t.pageX,s=t.pageY,n="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&V.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,o=/(html|body)/i.test(n[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(t.pageX-this.offset.click.left<this.containment[0]&&(i=this.containment[0]+this.offset.click.left),t.pageY-this.offset.click.top<this.containment[1]&&(s=this.containment[1]+this.offset.click.top),t.pageX-this.offset.click.left>this.containment[2]&&(i=this.containment[2]+this.offset.click.left),t.pageY-this.offset.click.top>this.containment[3]&&(s=this.containment[3]+this.offset.click.top)),e.grid&&(t=this.originalPageY+Math.round((s-this.originalPageY)/e.grid[1])*e.grid[1],s=!this.containment||t-this.offset.click.top>=this.containment[1]&&t-this.offset.click.top<=this.containment[3]?t:t-this.offset.click.top>=this.containment[1]?t-e.grid[1]:t+e.grid[1],t=this.originalPageX+Math.round((i-this.originalPageX)/e.grid[0])*e.grid[0],i=!this.containment||t-this.offset.click.left>=this.containment[0]&&t-this.offset.click.left<=this.containment[2]?t:t-this.offset.click.left>=this.containment[0]?t-e.grid[0]:t+e.grid[0])),{top:s-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():o?0:n.scrollTop()),left:i-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():o?0:n.scrollLeft())}},_rearrange:function(t,e,i,s){i?i[0].appendChild(this.placeholder[0]):e.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?e.item[0]:e.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter;this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(t,e){this.reverting=!1;var i,s=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(i in this._storedCSS)"auto"!==this._storedCSS[i]&&"static"!==this._storedCSS[i]||(this._storedCSS[i]="");this.currentItem.css(this._storedCSS),this._removeClass(this.currentItem,"ui-sortable-helper")}else this.currentItem.show();function n(e,i,s){return function(t){s._trigger(e,t,i._uiHash(i))}}for(this.fromOutside&&!e&&s.push(function(t){this._trigger("receive",t,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||e||s.push(function(t){this._trigger("update",t,this._uiHash())}),this!==this.currentContainer&&(e||(s.push(function(t){this._trigger("remove",t,this._uiHash())}),s.push(function(e){return function(t){e._trigger("receive",t,this._uiHash(this))}}.call(this,this.currentContainer)),s.push(function(e){return function(t){e._trigger("update",t,this._uiHash(this))}}.call(this,this.currentContainer)))),i=this.containers.length-1;0<=i;i--)e||s.push(n("deactivate",this,this.containers[i])),this.containers[i].containerCache.over&&(s.push(n("out",this,this.containers[i])),this.containers[i].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,e||this._trigger("beforeStop",t,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.cancelHelperRemoval||(this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null),!e){for(i=0;i<s.length;i++)s[i].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!this.cancelHelperRemoval},_trigger:function(){!1===V.Widget.prototype._trigger.apply(this,arguments)&&this.cancel()},_uiHash:function(t){var e=t||this;return{helper:e.helper,placeholder:e.placeholder||V([]),position:e.position,originalPosition:e.originalPosition,offset:e.positionAbs,item:e.currentItem,sender:t?t.element:null}}});function ht(e){return function(){var t=this.element.val();e.apply(this,arguments),this._refresh(),t!==this.element.val()&&this._trigger("change")}}V.widget("ui.spinner",{version:"1.13.0",defaultElement:"<input>",widgetEventPrefix:"spin",options:{classes:{"ui-spinner":"ui-corner-all","ui-spinner-down":"ui-corner-br","ui-spinner-up":"ui-corner-tr"},culture:null,icons:{down:"ui-icon-triangle-1-s",up:"ui-icon-triangle-1-n"},incremental:!0,max:null,min:null,numberFormat:null,page:10,step:1,change:null,spin:null,start:null,stop:null},_create:function(){this._setOption("max",this.options.max),this._setOption("min",this.options.min),this._setOption("step",this.options.step),""!==this.value()&&this._value(this.element.val(),!0),this._draw(),this._on(this._events),this._refresh(),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_getCreateOptions:function(){var s=this._super(),n=this.element;return V.each(["min","max","step"],function(t,e){var i=n.attr(e);null!=i&&i.length&&(s[e]=i)}),s},_events:{keydown:function(t){this._start(t)&&this._keydown(t)&&t.preventDefault()},keyup:"_stop",focus:function(){this.previous=this.element.val()},blur:function(t){this.cancelBlur?delete this.cancelBlur:(this._stop(),this._refresh(),this.previous!==this.element.val()&&this._trigger("change",t))},mousewheel:function(t,e){var i=V.ui.safeActiveElement(this.document[0]);if(this.element[0]===i&&e){if(!this.spinning&&!this._start(t))return!1;this._spin((0<e?1:-1)*this.options.step,t),clearTimeout(this.mousewheelTimer),this.mousewheelTimer=this._delay(function(){this.spinning&&this._stop(t)},100),t.preventDefault()}},"mousedown .ui-spinner-button":function(t){var e;function i(){this.element[0]===V.ui.safeActiveElement(this.document[0])||(this.element.trigger("focus"),this.previous=e,this._delay(function(){this.previous=e}))}e=this.element[0]===V.ui.safeActiveElement(this.document[0])?this.previous:this.element.val(),t.preventDefault(),i.call(this),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur,i.call(this)}),!1!==this._start(t)&&this._repeat(null,V(t.currentTarget).hasClass("ui-spinner-up")?1:-1,t)},"mouseup .ui-spinner-button":"_stop","mouseenter .ui-spinner-button":function(t){if(V(t.currentTarget).hasClass("ui-state-active"))return!1!==this._start(t)&&void this._repeat(null,V(t.currentTarget).hasClass("ui-spinner-up")?1:-1,t)},"mouseleave .ui-spinner-button":"_stop"},_enhance:function(){this.uiSpinner=this.element.attr("autocomplete","off").wrap("<span>").parent().append("<a></a><a></a>")},_draw:function(){this._enhance(),this._addClass(this.uiSpinner,"ui-spinner","ui-widget ui-widget-content"),this._addClass("ui-spinner-input"),this.element.attr("role","spinbutton"),this.buttons=this.uiSpinner.children("a").attr("tabIndex",-1).attr("aria-hidden",!0).button({classes:{"ui-button":""}}),this._removeClass(this.buttons,"ui-corner-all"),this._addClass(this.buttons.first(),"ui-spinner-button ui-spinner-up"),this._addClass(this.buttons.last(),"ui-spinner-button ui-spinner-down"),this.buttons.first().button({icon:this.options.icons.up,showLabel:!1}),this.buttons.last().button({icon:this.options.icons.down,showLabel:!1}),this.buttons.height()>Math.ceil(.5*this.uiSpinner.height())&&0<this.uiSpinner.height()&&this.uiSpinner.height(this.uiSpinner.height())},_keydown:function(t){var e=this.options,i=V.ui.keyCode;switch(t.keyCode){case i.UP:return this._repeat(null,1,t),!0;case i.DOWN:return this._repeat(null,-1,t),!0;case i.PAGE_UP:return this._repeat(null,e.page,t),!0;case i.PAGE_DOWN:return this._repeat(null,-e.page,t),!0}return!1},_start:function(t){return!(!this.spinning&&!1===this._trigger("start",t))&&(this.counter||(this.counter=1),this.spinning=!0)},_repeat:function(t,e,i){t=t||500,clearTimeout(this.timer),this.timer=this._delay(function(){this._repeat(40,e,i)},t),this._spin(e*this.options.step,i)},_spin:function(t,e){var i=this.value()||0;this.counter||(this.counter=1),i=this._adjustValue(i+t*this._increment(this.counter)),this.spinning&&!1===this._trigger("spin",e,{value:i})||(this._value(i),this.counter++)},_increment:function(t){var e=this.options.incremental;return e?"function"==typeof e?e(t):Math.floor(t*t*t/5e4-t*t/500+17*t/200+1):1},_precision:function(){var t=this._precisionOf(this.options.step);return t=null!==this.options.min?Math.max(t,this._precisionOf(this.options.min)):t},_precisionOf:function(t){var e=t.toString(),t=e.indexOf(".");return-1===t?0:e.length-t-1},_adjustValue:function(t){var e=this.options,i=null!==e.min?e.min:0,s=t-i;return t=i+Math.round(s/e.step)*e.step,t=parseFloat(t.toFixed(this._precision())),null!==e.max&&t>e.max?e.max:null!==e.min&&t<e.min?e.min:t},_stop:function(t){this.spinning&&(clearTimeout(this.timer),clearTimeout(this.mousewheelTimer),this.counter=0,this.spinning=!1,this._trigger("stop",t))},_setOption:function(t,e){var i;if("culture"===t||"numberFormat"===t)return i=this._parse(this.element.val()),this.options[t]=e,void this.element.val(this._format(i));"max"!==t&&"min"!==t&&"step"!==t||"string"==typeof e&&(e=this._parse(e)),"icons"===t&&(i=this.buttons.first().find(".ui-icon"),this._removeClass(i,null,this.options.icons.up),this._addClass(i,null,e.up),i=this.buttons.last().find(".ui-icon"),this._removeClass(i,null,this.options.icons.down),this._addClass(i,null,e.down)),this._super(t,e)},_setOptionDisabled:function(t){this._super(t),this._toggleClass(this.uiSpinner,null,"ui-state-disabled",!!t),this.element.prop("disabled",!!t),this.buttons.button(t?"disable":"enable")},_setOptions:ht(function(t){this._super(t)}),_parse:function(t){return""===(t="string"==typeof t&&""!==t?window.Globalize&&this.options.numberFormat?Globalize.parseFloat(t,10,this.options.culture):+t:t)||isNaN(t)?null:t},_format:function(t){return""===t?"":window.Globalize&&this.options.numberFormat?Globalize.format(t,this.options.numberFormat,this.options.culture):t},_refresh:function(){this.element.attr({"aria-valuemin":this.options.min,"aria-valuemax":this.options.max,"aria-valuenow":this._parse(this.element.val())})},isValid:function(){var t=this.value();return null!==t&&t===this._adjustValue(t)},_value:function(t,e){var i;""!==t&&null!==(i=this._parse(t))&&(e||(i=this._adjustValue(i)),t=this._format(i)),this.element.val(t),this._refresh()},_destroy:function(){this.element.prop("disabled",!1).removeAttr("autocomplete role aria-valuemin aria-valuemax aria-valuenow"),this.uiSpinner.replaceWith(this.element)},stepUp:ht(function(t){this._stepUp(t)}),_stepUp:function(t){this._start()&&(this._spin((t||1)*this.options.step),this._stop())},stepDown:ht(function(t){this._stepDown(t)}),_stepDown:function(t){this._start()&&(this._spin((t||1)*-this.options.step),this._stop())},pageUp:ht(function(t){this._stepUp((t||1)*this.options.page)}),pageDown:ht(function(t){this._stepDown((t||1)*this.options.page)}),value:function(t){if(!arguments.length)return this._parse(this.element.val());ht(this._value).call(this,t)},widget:function(){return this.uiSpinner}}),!1!==V.uiBackCompat&&V.widget("ui.spinner",V.ui.spinner,{_enhance:function(){this.uiSpinner=this.element.attr("autocomplete","off").wrap(this._uiSpinnerHtml()).parent().append(this._buttonHtml())},_uiSpinnerHtml:function(){return"<span>"},_buttonHtml:function(){return"<a></a><a></a>"}});var ct;V.ui.spinner;V.widget("ui.tabs",{version:"1.13.0",delay:300,options:{active:null,classes:{"ui-tabs":"ui-corner-all","ui-tabs-nav":"ui-corner-all","ui-tabs-panel":"ui-corner-bottom","ui-tabs-tab":"ui-corner-top"},collapsible:!1,event:"click",heightStyle:"content",hide:null,show:null,activate:null,beforeActivate:null,beforeLoad:null,load:null},_isLocal:(ct=/#.*$/,function(t){var e=t.href.replace(ct,""),i=location.href.replace(ct,"");try{e=decodeURIComponent(e)}catch(t){}try{i=decodeURIComponent(i)}catch(t){}return 1<t.hash.length&&e===i}),_create:function(){var e=this,t=this.options;this.running=!1,this._addClass("ui-tabs","ui-widget ui-widget-content"),this._toggleClass("ui-tabs-collapsible",null,t.collapsible),this._processTabs(),t.active=this._initialActive(),Array.isArray(t.disabled)&&(t.disabled=V.uniqueSort(t.disabled.concat(V.map(this.tabs.filter(".ui-state-disabled"),function(t){return e.tabs.index(t)}))).sort()),!1!==this.options.active&&this.anchors.length?this.active=this._findActive(t.active):this.active=V(),this._refresh(),this.active.length&&this.load(t.active)},_initialActive:function(){var i=this.options.active,t=this.options.collapsible,s=location.hash.substring(1);return null===i&&(s&&this.tabs.each(function(t,e){if(V(e).attr("aria-controls")===s)return i=t,!1}),null!==(i=null===i?this.tabs.index(this.tabs.filter(".ui-tabs-active")):i)&&-1!==i||(i=!!this.tabs.length&&0)),!1!==i&&-1===(i=this.tabs.index(this.tabs.eq(i)))&&(i=!t&&0),i=!t&&!1===i&&this.anchors.length?0:i},_getCreateEventData:function(){return{tab:this.active,panel:this.active.length?this._getPanelForTab(this.active):V()}},_tabKeydown:function(t){var e=V(V.ui.safeActiveElement(this.document[0])).closest("li"),i=this.tabs.index(e),s=!0;if(!this._handlePageNav(t)){switch(t.keyCode){case V.ui.keyCode.RIGHT:case V.ui.keyCode.DOWN:i++;break;case V.ui.keyCode.UP:case V.ui.keyCode.LEFT:s=!1,i--;break;case V.ui.keyCode.END:i=this.anchors.length-1;break;case V.ui.keyCode.HOME:i=0;break;case V.ui.keyCode.SPACE:return t.preventDefault(),clearTimeout(this.activating),void this._activate(i);case V.ui.keyCode.ENTER:return t.preventDefault(),clearTimeout(this.activating),void this._activate(i!==this.options.active&&i);default:return}t.preventDefault(),clearTimeout(this.activating),i=this._focusNextTab(i,s),t.ctrlKey||t.metaKey||(e.attr("aria-selected","false"),this.tabs.eq(i).attr("aria-selected","true"),this.activating=this._delay(function(){this.option("active",i)},this.delay))}},_panelKeydown:function(t){this._handlePageNav(t)||t.ctrlKey&&t.keyCode===V.ui.keyCode.UP&&(t.preventDefault(),this.active.trigger("focus"))},_handlePageNav:function(t){return t.altKey&&t.keyCode===V.ui.keyCode.PAGE_UP?(this._activate(this._focusNextTab(this.options.active-1,!1)),!0):t.altKey&&t.keyCode===V.ui.keyCode.PAGE_DOWN?(this._activate(this._focusNextTab(this.options.active+1,!0)),!0):void 0},_findNextTab:function(t,e){var i=this.tabs.length-1;for(;-1!==V.inArray(t=(t=i<t?0:t)<0?i:t,this.options.disabled);)t=e?t+1:t-1;return t},_focusNextTab:function(t,e){return t=this._findNextTab(t,e),this.tabs.eq(t).trigger("focus"),t},_setOption:function(t,e){"active"!==t?(this._super(t,e),"collapsible"===t&&(this._toggleClass("ui-tabs-collapsible",null,e),e||!1!==this.options.active||this._activate(0)),"event"===t&&this._setupEvents(e),"heightStyle"===t&&this._setupHeightStyle(e)):this._activate(e)},_sanitizeSelector:function(t){return t?t.replace(/[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g,"\\$&"):""},refresh:function(){var t=this.options,e=this.tablist.children(":has(a[href])");t.disabled=V.map(e.filter(".ui-state-disabled"),function(t){return e.index(t)}),this._processTabs(),!1!==t.active&&this.anchors.length?this.active.length&&!V.contains(this.tablist[0],this.active[0])?this.tabs.length===t.disabled.length?(t.active=!1,this.active=V()):this._activate(this._findNextTab(Math.max(0,t.active-1),!1)):t.active=this.tabs.index(this.active):(t.active=!1,this.active=V()),this._refresh()},_refresh:function(){this._setOptionDisabled(this.options.disabled),this._setupEvents(this.options.event),this._setupHeightStyle(this.options.heightStyle),this.tabs.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}),this.panels.not(this._getPanelForTab(this.active)).hide().attr({"aria-hidden":"true"}),this.active.length?(this.active.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}),this._addClass(this.active,"ui-tabs-active","ui-state-active"),this._getPanelForTab(this.active).show().attr({"aria-hidden":"false"})):this.tabs.eq(0).attr("tabIndex",0)},_processTabs:function(){var l=this,t=this.tabs,e=this.anchors,i=this.panels;this.tablist=this._getList().attr("role","tablist"),this._addClass(this.tablist,"ui-tabs-nav","ui-helper-reset ui-helper-clearfix ui-widget-header"),this.tablist.on("mousedown"+this.eventNamespace,"> li",function(t){V(this).is(".ui-state-disabled")&&t.preventDefault()}).on("focus"+this.eventNamespace,".ui-tabs-anchor",function(){V(this).closest("li").is(".ui-state-disabled")&&this.blur()}),this.tabs=this.tablist.find("> li:has(a[href])").attr({role:"tab",tabIndex:-1}),this._addClass(this.tabs,"ui-tabs-tab","ui-state-default"),this.anchors=this.tabs.map(function(){return V("a",this)[0]}).attr({tabIndex:-1}),this._addClass(this.anchors,"ui-tabs-anchor"),this.panels=V(),this.anchors.each(function(t,e){var i,s,n,o=V(e).uniqueId().attr("id"),a=V(e).closest("li"),r=a.attr("aria-controls");l._isLocal(e)?(n=(i=e.hash).substring(1),s=l.element.find(l._sanitizeSelector(i))):(n=a.attr("aria-controls")||V({}).uniqueId()[0].id,(s=l.element.find(i="#"+n)).length||(s=l._createPanel(n)).insertAfter(l.panels[t-1]||l.tablist),s.attr("aria-live","polite")),s.length&&(l.panels=l.panels.add(s)),r&&a.data("ui-tabs-aria-controls",r),a.attr({"aria-controls":n,"aria-labelledby":o}),s.attr("aria-labelledby",o)}),this.panels.attr("role","tabpanel"),this._addClass(this.panels,"ui-tabs-panel","ui-widget-content"),t&&(this._off(t.not(this.tabs)),this._off(e.not(this.anchors)),this._off(i.not(this.panels)))},_getList:function(){return this.tablist||this.element.find("ol, ul").eq(0)},_createPanel:function(t){return V("<div>").attr("id",t).data("ui-tabs-destroy",!0)},_setOptionDisabled:function(t){var e,i;for(Array.isArray(t)&&(t.length?t.length===this.anchors.length&&(t=!0):t=!1),i=0;e=this.tabs[i];i++)e=V(e),!0===t||-1!==V.inArray(i,t)?(e.attr("aria-disabled","true"),this._addClass(e,null,"ui-state-disabled")):(e.removeAttr("aria-disabled"),this._removeClass(e,null,"ui-state-disabled"));this.options.disabled=t,this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!0===t)},_setupEvents:function(t){var i={};t&&V.each(t.split(" "),function(t,e){i[e]="_eventHandler"}),this._off(this.anchors.add(this.tabs).add(this.panels)),this._on(!0,this.anchors,{click:function(t){t.preventDefault()}}),this._on(this.anchors,i),this._on(this.tabs,{keydown:"_tabKeydown"}),this._on(this.panels,{keydown:"_panelKeydown"}),this._focusable(this.tabs),this._hoverable(this.tabs)},_setupHeightStyle:function(t){var i,e=this.element.parent();"fill"===t?(i=e.height(),i-=this.element.outerHeight()-this.element.height(),this.element.siblings(":visible").each(function(){var t=V(this),e=t.css("position");"absolute"!==e&&"fixed"!==e&&(i-=t.outerHeight(!0))}),this.element.children().not(this.panels).each(function(){i-=V(this).outerHeight(!0)}),this.panels.each(function(){V(this).height(Math.max(0,i-V(this).innerHeight()+V(this).height()))}).css("overflow","auto")):"auto"===t&&(i=0,this.panels.each(function(){i=Math.max(i,V(this).height("").height())}).height(i))},_eventHandler:function(t){var e=this.options,i=this.active,s=V(t.currentTarget).closest("li"),n=s[0]===i[0],o=n&&e.collapsible,a=o?V():this._getPanelForTab(s),r=i.length?this._getPanelForTab(i):V(),i={oldTab:i,oldPanel:r,newTab:o?V():s,newPanel:a};t.preventDefault(),s.hasClass("ui-state-disabled")||s.hasClass("ui-tabs-loading")||this.running||n&&!e.collapsible||!1===this._trigger("beforeActivate",t,i)||(e.active=!o&&this.tabs.index(s),this.active=n?V():s,this.xhr&&this.xhr.abort(),r.length||a.length||V.error("jQuery UI Tabs: Mismatching fragment identifier."),a.length&&this.load(this.tabs.index(s),t),this._toggle(t,i))},_toggle:function(t,e){var i=this,s=e.newPanel,n=e.oldPanel;function o(){i.running=!1,i._trigger("activate",t,e)}function a(){i._addClass(e.newTab.closest("li"),"ui-tabs-active","ui-state-active"),s.length&&i.options.show?i._show(s,i.options.show,o):(s.show(),o())}this.running=!0,n.length&&this.options.hide?this._hide(n,this.options.hide,function(){i._removeClass(e.oldTab.closest("li"),"ui-tabs-active","ui-state-active"),a()}):(this._removeClass(e.oldTab.closest("li"),"ui-tabs-active","ui-state-active"),n.hide(),a()),n.attr("aria-hidden","true"),e.oldTab.attr({"aria-selected":"false","aria-expanded":"false"}),s.length&&n.length?e.oldTab.attr("tabIndex",-1):s.length&&this.tabs.filter(function(){return 0===V(this).attr("tabIndex")}).attr("tabIndex",-1),s.attr("aria-hidden","false"),e.newTab.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0})},_activate:function(t){var t=this._findActive(t);t[0]!==this.active[0]&&(t=(t=!t.length?this.active:t).find(".ui-tabs-anchor")[0],this._eventHandler({target:t,currentTarget:t,preventDefault:V.noop}))},_findActive:function(t){return!1===t?V():this.tabs.eq(t)},_getIndex:function(t){return t="string"==typeof t?this.anchors.index(this.anchors.filter("[href$='"+V.escapeSelector(t)+"']")):t},_destroy:function(){this.xhr&&this.xhr.abort(),this.tablist.removeAttr("role").off(this.eventNamespace),this.anchors.removeAttr("role tabIndex").removeUniqueId(),this.tabs.add(this.panels).each(function(){V.data(this,"ui-tabs-destroy")?V(this).remove():V(this).removeAttr("role tabIndex aria-live aria-busy aria-selected aria-labelledby aria-hidden aria-expanded")}),this.tabs.each(function(){var t=V(this),e=t.data("ui-tabs-aria-controls");e?t.attr("aria-controls",e).removeData("ui-tabs-aria-controls"):t.removeAttr("aria-controls")}),this.panels.show(),"content"!==this.options.heightStyle&&this.panels.css("height","")},enable:function(i){var t=this.options.disabled;!1!==t&&(t=void 0!==i&&(i=this._getIndex(i),Array.isArray(t)?V.map(t,function(t){return t!==i?t:null}):V.map(this.tabs,function(t,e){return e!==i?e:null})),this._setOptionDisabled(t))},disable:function(t){var e=this.options.disabled;if(!0!==e){if(void 0===t)e=!0;else{if(t=this._getIndex(t),-1!==V.inArray(t,e))return;e=Array.isArray(e)?V.merge([t],e).sort():[t]}this._setOptionDisabled(e)}},load:function(t,s){t=this._getIndex(t);function n(t,e){"abort"===e&&o.panels.stop(!1,!0),o._removeClass(i,"ui-tabs-loading"),a.removeAttr("aria-busy"),t===o.xhr&&delete o.xhr}var o=this,i=this.tabs.eq(t),t=i.find(".ui-tabs-anchor"),a=this._getPanelForTab(i),r={tab:i,panel:a};this._isLocal(t[0])||(this.xhr=V.ajax(this._ajaxSettings(t,s,r)),this.xhr&&"canceled"!==this.xhr.statusText&&(this._addClass(i,"ui-tabs-loading"),a.attr("aria-busy","true"),this.xhr.done(function(t,e,i){setTimeout(function(){a.html(t),o._trigger("load",s,r),n(i,e)},1)}).fail(function(t,e){setTimeout(function(){n(t,e)},1)})))},_ajaxSettings:function(t,i,s){var n=this;return{url:t.attr("href").replace(/#.*$/,""),beforeSend:function(t,e){return n._trigger("beforeLoad",i,V.extend({jqXHR:t,ajaxSettings:e},s))}}},_getPanelForTab:function(t){t=V(t).attr("aria-controls");return this.element.find(this._sanitizeSelector("#"+t))}}),!1!==V.uiBackCompat&&V.widget("ui.tabs",V.ui.tabs,{_processTabs:function(){this._superApply(arguments),this._addClass(this.tabs,"ui-tab")}});V.ui.tabs;V.widget("ui.tooltip",{version:"1.13.0",options:{classes:{"ui-tooltip":"ui-corner-all ui-widget-shadow"},content:function(){var t=V(this).attr("title");return V("<a>").text(t).html()},hide:!0,items:"[title]:not([disabled])",position:{my:"left top+15",at:"left bottom",collision:"flipfit flip"},show:!0,track:!1,close:null,open:null},_addDescribedBy:function(t,e){var i=(t.attr("aria-describedby")||"").split(/\s+/);i.push(e),t.data("ui-tooltip-id",e).attr("aria-describedby",String.prototype.trim.call(i.join(" ")))},_removeDescribedBy:function(t){var e=t.data("ui-tooltip-id"),i=(t.attr("aria-describedby")||"").split(/\s+/),e=V.inArray(e,i);-1!==e&&i.splice(e,1),t.removeData("ui-tooltip-id"),(i=String.prototype.trim.call(i.join(" ")))?t.attr("aria-describedby",i):t.removeAttr("aria-describedby")},_create:function(){this._on({mouseover:"open",focusin:"open"}),this.tooltips={},this.parents={},this.liveRegion=V("<div>").attr({role:"log","aria-live":"assertive","aria-relevant":"additions"}).appendTo(this.document[0].body),this._addClass(this.liveRegion,null,"ui-helper-hidden-accessible"),this.disabledTitles=V([])},_setOption:function(t,e){var i=this;this._super(t,e),"content"===t&&V.each(this.tooltips,function(t,e){i._updateContent(e.element)})},_setOptionDisabled:function(t){this[t?"_disable":"_enable"]()},_disable:function(){var s=this;V.each(this.tooltips,function(t,e){var i=V.Event("blur");i.target=i.currentTarget=e.element[0],s.close(i,!0)}),this.disabledTitles=this.disabledTitles.add(this.element.find(this.options.items).addBack().filter(function(){var t=V(this);if(t.is("[title]"))return t.data("ui-tooltip-title",t.attr("title")).removeAttr("title")}))},_enable:function(){this.disabledTitles.each(function(){var t=V(this);t.data("ui-tooltip-title")&&t.attr("title",t.data("ui-tooltip-title"))}),this.disabledTitles=V([])},open:function(t){var i=this,e=V(t?t.target:this.element).closest(this.options.items);e.length&&!e.data("ui-tooltip-id")&&(e.attr("title")&&e.data("ui-tooltip-title",e.attr("title")),e.data("ui-tooltip-open",!0),t&&"mouseover"===t.type&&e.parents().each(function(){var t,e=V(this);e.data("ui-tooltip-open")&&((t=V.Event("blur")).target=t.currentTarget=this,i.close(t,!0)),e.attr("title")&&(e.uniqueId(),i.parents[this.id]={element:this,title:e.attr("title")},e.attr("title",""))}),this._registerCloseHandlers(t,e),this._updateContent(e,t))},_updateContent:function(e,i){var t=this.options.content,s=this,n=i?i.type:null;if("string"==typeof t||t.nodeType||t.jquery)return this._open(i,e,t);(t=t.call(e[0],function(t){s._delay(function(){e.data("ui-tooltip-open")&&(i&&(i.type=n),this._open(i,e,t))})}))&&this._open(i,e,t)},_open:function(t,e,i){var s,n,o,a=V.extend({},this.options.position);function r(t){a.of=t,n.is(":hidden")||n.position(a)}i&&((s=this._find(e))?s.tooltip.find(".ui-tooltip-content").html(i):(e.is("[title]")&&(t&&"mouseover"===t.type?e.attr("title",""):e.removeAttr("title")),s=this._tooltip(e),n=s.tooltip,this._addDescribedBy(e,n.attr("id")),n.find(".ui-tooltip-content").html(i),this.liveRegion.children().hide(),(i=V("<div>").html(n.find(".ui-tooltip-content").html())).removeAttr("name").find("[name]").removeAttr("name"),i.removeAttr("id").find("[id]").removeAttr("id"),i.appendTo(this.liveRegion),this.options.track&&t&&/^mouse/.test(t.type)?(this._on(this.document,{mousemove:r}),r(t)):n.position(V.extend({of:e},this.options.position)),n.hide(),this._show(n,this.options.show),this.options.track&&this.options.show&&this.options.show.delay&&(o=this.delayedShow=setInterval(function(){n.is(":visible")&&(r(a.of),clearInterval(o))},13)),this._trigger("open",t,{tooltip:n})))},_registerCloseHandlers:function(t,e){var i={keyup:function(t){t.keyCode===V.ui.keyCode.ESCAPE&&((t=V.Event(t)).currentTarget=e[0],this.close(t,!0))}};e[0]!==this.element[0]&&(i.remove=function(){this._removeTooltip(this._find(e).tooltip)}),t&&"mouseover"!==t.type||(i.mouseleave="close"),t&&"focusin"!==t.type||(i.focusout="close"),this._on(!0,e,i)},close:function(t){var e,i=this,s=V(t?t.currentTarget:this.element),n=this._find(s);n?(e=n.tooltip,n.closing||(clearInterval(this.delayedShow),s.data("ui-tooltip-title")&&!s.attr("title")&&s.attr("title",s.data("ui-tooltip-title")),this._removeDescribedBy(s),n.hiding=!0,e.stop(!0),this._hide(e,this.options.hide,function(){i._removeTooltip(V(this))}),s.removeData("ui-tooltip-open"),this._off(s,"mouseleave focusout keyup"),s[0]!==this.element[0]&&this._off(s,"remove"),this._off(this.document,"mousemove"),t&&"mouseleave"===t.type&&V.each(this.parents,function(t,e){V(e.element).attr("title",e.title),delete i.parents[t]}),n.closing=!0,this._trigger("close",t,{tooltip:e}),n.hiding||(n.closing=!1))):s.removeData("ui-tooltip-open")},_tooltip:function(t){var e=V("<div>").attr("role","tooltip"),i=V("<div>").appendTo(e),s=e.uniqueId().attr("id");return this._addClass(i,"ui-tooltip-content"),this._addClass(e,"ui-tooltip","ui-widget ui-widget-content"),e.appendTo(this._appendTo(t)),this.tooltips[s]={element:t,tooltip:e}},_find:function(t){t=t.data("ui-tooltip-id");return t?this.tooltips[t]:null},_removeTooltip:function(t){clearInterval(this.delayedShow),t.remove(),delete this.tooltips[t.attr("id")]},_appendTo:function(t){t=t.closest(".ui-front, dialog");return t=!t.length?this.document[0].body:t},_destroy:function(){var s=this;V.each(this.tooltips,function(t,e){var i=V.Event("blur"),e=e.element;i.target=i.currentTarget=e[0],s.close(i,!0),V("#"+t).remove(),e.data("ui-tooltip-title")&&(e.attr("title")||e.attr("title",e.data("ui-tooltip-title")),e.removeData("ui-tooltip-title"))}),this.liveRegion.remove()}}),!1!==V.uiBackCompat&&V.widget("ui.tooltip",V.ui.tooltip,{options:{tooltipClass:null},_tooltip:function(){var t=this._superApply(arguments);return this.options.tooltipClass&&t.tooltip.addClass(this.options.tooltipClass),t}});V.ui.tooltip});
/*
 jquery.layout 1.3.0 - Release Candidate 30.79
 $Date: 2013-01-01 08:00:00 (Tue, 1 Jan 2013) $
 $Rev: 303007 $

 Copyright (c) 2013 Kevin Dalman (http://allpro.net)
 Based on work by Fabrizio Balliano (http://www.fabrizioballiano.net)

 Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
 and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.

 SEE: http://layout.jquery-dev.net/LICENSE.txt

 Changelog: http://layout.jquery-dev.net/changelog.cfm#1.3.0.rc30.79

 Docs: http://layout.jquery-dev.net/documentation.html
 Tips: http://layout.jquery-dev.net/tips.html
 Help: http://groups.google.com/group/jquery-ui-layout
*/
(function(b){var a=Math.min,d=Math.max,c=Math.floor,f=function(a){return"string"===b.type(a)},j=function(a,d){if(b.isArray(d))for(var c=0,j=d.length;c<j;c++){var h=d[c];try{f(h)&&(h=eval(h)),b.isFunction(h)&&h(a)}catch(k){}}};b.layout={version:"1.3.rc30.79",revision:0.033007,browser:{},effects:{slide:{all:{duration:"fast"},north:{direction:"up"},south:{direction:"down"},east:{direction:"right"},west:{direction:"left"}},drop:{all:{duration:"slow"},north:{direction:"up"},south:{direction:"down"},east:{direction:"right"},
west:{direction:"left"}},scale:{all:{duration:"fast"}},blind:{},clip:{},explode:{},fade:{},fold:{},puff:{},size:{all:{easing:"swing"}}},config:{optionRootKeys:"effects panes north south west east center".split(" "),allPanes:["north","south","west","east","center"],borderPanes:["north","south","west","east"],oppositeEdge:{north:"south",south:"north",east:"west",west:"east"},offscreenCSS:{left:"-99999px",right:"auto"},offscreenReset:"offscreenReset",hidden:{visibility:"hidden"},visible:{visibility:"visible"},
resizers:{cssReq:{position:"absolute",padding:0,margin:0,fontSize:"1px",textAlign:"left",overflow:"hidden"},cssDemo:{background:"#DDD",border:"none"}},togglers:{cssReq:{position:"absolute",display:"block",padding:0,margin:0,overflow:"hidden",textAlign:"center",fontSize:"1px",cursor:"pointer",zIndex:1},cssDemo:{background:"#AAA"}},content:{cssReq:{position:"relative"},cssDemo:{overflow:"auto",padding:"10px"},cssDemoPane:{overflow:"hidden",padding:0}},panes:{cssReq:{position:"absolute",margin:0},cssDemo:{padding:"10px",
background:"#FFF",border:"1px solid #BBB",overflow:"auto"}},north:{side:"top",sizeType:"Height",dir:"horz",cssReq:{top:0,bottom:"auto",left:0,right:0,width:"auto"}},south:{side:"bottom",sizeType:"Height",dir:"horz",cssReq:{top:"auto",bottom:0,left:0,right:0,width:"auto"}},east:{side:"right",sizeType:"Width",dir:"vert",cssReq:{left:"auto",right:0,top:"auto",bottom:"auto",height:"auto"}},west:{side:"left",sizeType:"Width",dir:"vert",cssReq:{left:0,right:"auto",top:"auto",bottom:"auto",height:"auto"}},
center:{dir:"center",cssReq:{left:"auto",right:"auto",top:"auto",bottom:"auto",height:"auto",width:"auto"}}},callbacks:{},getParentPaneElem:function(a){a=b(a);if(a=a.data("layout")||a.data("parentLayout")){a=a.container;if(a.data("layoutPane"))return a;a=a.closest("."+b.layout.defaults.panes.paneClass);if(a.data("layoutPane"))return a}return null},getParentPaneInstance:function(a){return(a=b.layout.getParentPaneElem(a))?a.data("layoutPane"):null},getParentLayoutInstance:function(a){return(a=b.layout.getParentPaneElem(a))?
a.data("parentLayout"):null},getEventObject:function(b){return"object"===typeof b&&b.stopPropagation?b:null},parsePaneName:function(a){var d=b.layout.getEventObject(a);d&&(d.stopPropagation(),a=b(this).data("layoutEdge"));a&&!/^(west|east|north|south|center)$/.test(a)&&(b.layout.msg('LAYOUT ERROR - Invalid pane-name: "'+a+'"'),a="error");return a},plugins:{draggable:!!b.fn.draggable,effects:{core:!!b.effects,slide:b.effects&&(b.effects.slide||b.effects.effect&&b.effects.effect.slide)}},onCreate:[],
onLoad:[],onReady:[],onDestroy:[],onUnload:[],afterOpen:[],afterClose:[],scrollbarWidth:function(){return window.scrollbarWidth||b.layout.getScrollbarSize("width")},scrollbarHeight:function(){return window.scrollbarHeight||b.layout.getScrollbarSize("height")},getScrollbarSize:function(a){var d=b('<div style="position: absolute; top: -10000px; left: -10000px; width: 100px; height: 100px; overflow: scroll;"></div>').appendTo("body"),c={width:d.css("width")-d[0].clientWidth,height:d.height()-d[0].clientHeight};
d.remove();window.scrollbarWidth=c.width;window.scrollbarHeight=c.height;return a.match(/^(width|height)$/)?c[a]:c},showInvisibly:function(b,a){if(b&&b.length&&(a||"none"===b.css("display"))){var d=b[0].style,d={display:d.display||"",visibility:d.visibility||""};b.css({display:"block",visibility:"hidden"});return d}return{}},getElementDimensions:function(a,c){var f={css:{},inset:{}},j=f.css,h={bottom:0},k=b.layout.cssNum,p=a.offset(),O,R,D;f.offsetLeft=p.left;f.offsetTop=p.top;c||(c={});b.each(["Left",
"Right","Top","Bottom"],function(d,k){O=j["border"+k]=b.layout.borderWidth(a,k);R=j["padding"+k]=b.layout.cssNum(a,"padding"+k);D=k.toLowerCase();f.inset[D]=0<=c[D]?c[D]:R;h[D]=f.inset[D]+O});j.width=a.width();j.height=a.height();j.top=k(a,"top",!0);j.bottom=k(a,"bottom",!0);j.left=k(a,"left",!0);j.right=k(a,"right",!0);f.outerWidth=a.outerWidth();f.outerHeight=a.outerHeight();f.innerWidth=d(0,f.outerWidth-h.left-h.right);f.innerHeight=d(0,f.outerHeight-h.top-h.bottom);f.layoutWidth=a.innerWidth();
f.layoutHeight=a.innerHeight();return f},getElementStyles:function(b,a){var d={},c=b[0].style,f=a.split(","),k=["Top","Bottom","Left","Right"],j=["Color","Style","Width"],h,p,D,x,A,r;for(x=0;x<f.length;x++)if(h=f[x],h.match(/(border|padding|margin)$/))for(A=0;4>A;A++)if(p=k[A],"border"===h)for(r=0;3>r;r++)D=j[r],d[h+p+D]=c[h+p+D];else d[h+p]=c[h+p];else d[h]=c[h];return d},cssWidth:function(a,c){if(0>=c)return 0;var f=!b.layout.browser.boxModel?"border-box":b.support.boxSizing?a.css("boxSizing"):
"content-box",j=b.layout.borderWidth,h=b.layout.cssNum,k=c;"border-box"!==f&&(k-=j(a,"Left")+j(a,"Right"));"content-box"===f&&(k-=h(a,"paddingLeft")+h(a,"paddingRight"));return d(0,k)},cssHeight:function(a,c){if(0>=c)return 0;var f=!b.layout.browser.boxModel?"border-box":b.support.boxSizing?a.css("boxSizing"):"content-box",j=b.layout.borderWidth,h=b.layout.cssNum,k=c;"border-box"!==f&&(k-=j(a,"Top")+j(a,"Bottom"));"content-box"===f&&(k-=h(a,"paddingTop")+h(a,"paddingBottom"));return d(0,k)},cssNum:function(a,
d,c){a.jquery||(a=b(a));var f=b.layout.showInvisibly(a);d=b.css(a[0],d,!0);c=c&&"auto"==d?d:Math.round(parseFloat(d)||0);a.css(f);return c},borderWidth:function(a,d){a.jquery&&(a=a[0]);var c="border"+d.substr(0,1).toUpperCase()+d.substr(1);return"none"===b.css(a,c+"Style",!0)?0:Math.round(parseFloat(b.css(a,c+"Width",!0))||0)},isMouseOverElem:function(a,d){var c=b(d||this),f=c.offset(),j=f.top,f=f.left,k=f+c.outerWidth(),c=j+c.outerHeight(),h=a.pageX,p=a.pageY;return b.layout.browser.msie&&0>h&&0>
p||h>=f&&h<=k&&p>=j&&p<=c},msg:function(a,d,c,f){b.isPlainObject(a)&&window.debugData?("string"===typeof d?(f=c,c=d):"object"===typeof c&&(f=c,c=null),c=c||"log( <object> )",f=b.extend({sort:!1,returnHTML:!1,display:!1},f),!0===d||f.display?debugData(a,c,f):window.console&&console.log(debugData(a,c,f))):d?alert(a):window.console?console.log(a):(d=b("#layoutLogger"),d.length||(d=b('<div id="layoutLogger" style="position: '+(b.support.fixedPosition?"fixed":"absolute")+'; top: 5px; z-index: 999999; max-width: 25%; overflow: hidden; border: 1px solid #000; border-radius: 5px; background: #FBFBFB; box-shadow: 0 2px 10px rgba(0,0,0,0.3);"><div style="font-size: 13px; font-weight: bold; padding: 5px 10px; background: #F6F6F6; border-radius: 5px 5px 0 0; cursor: move;"><span style="float: right; padding-left: 7px; cursor: pointer;" title="Remove Console" onclick="$(this).closest(\'#layoutLogger\').remove()">X</span>Layout console.log</div><ul style="font-size: 13px; font-weight: none; list-style: none; margin: 0; padding: 0 0 2px;"></ul></div>').appendTo("body"),
d.css("left",b(window).width()-d.outerWidth()-5),b.ui.draggable&&d.draggable({handle:":first-child"})),d.children("ul").append('<li style="padding: 4px 10px; margin: 0; border-top: 1px solid #CCC;">'+a.replace(/\</g,"&lt;").replace(/\>/g,"&gt;")+"</li>"))}};var h=navigator.userAgent.toLowerCase(),p=/(chrome)[ \/]([\w.]+)/.exec(h)||/(webkit)[ \/]([\w.]+)/.exec(h)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(h)||/(msie) ([\w.]+)/.exec(h)||0>h.indexOf("compatible")&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(h)||
[],h=p[1]||"",p=p[2]||0,x="msie"===h;b.layout.browser={version:p,safari:"webkit"===h,webkit:"chrome"===h,msie:x,isIE6:x&&6==p,boxModel:!x||!1!==b.support.boxModel};h&&(b.layout.browser[h]=!0);x&&b(function(){b.layout.browser.boxModel=b.support.boxModel});b.layout.defaults={name:"",containerClass:"ui-layout-container",inset:null,scrollToBookmarkOnLoad:!0,resizeWithWindow:!0,resizeWithWindowDelay:200,resizeWithWindowMaxDelay:0,maskPanesEarly:!1,onresizeall_start:null,onresizeall_end:null,onload_start:null,
onload_end:null,onunload_start:null,onunload_end:null,initPanes:!0,showErrorMessages:!0,showDebugMessages:!1,zIndex:null,zIndexes:{pane_normal:0,content_mask:1,resizer_normal:2,pane_sliding:100,pane_animate:1E3,resizer_drag:1E4},errors:{pane:"pane",selector:"selector",addButtonError:"Error Adding Button\nInvalid ",containerMissing:"UI Layout Initialization Error\nThe specified layout-container does not exist.",centerPaneMissing:"UI Layout Initialization Error\nThe center-pane element does not exist.\nThe center-pane is a required element.",
noContainerHeight:"UI Layout Initialization Warning\nThe layout-container \"CONTAINER\" has no height.\nTherefore the layout is 0-height and hence 'invisible'!",callbackError:"UI Layout Callback Error\nThe EVENT callback is not a valid function."},panes:{applyDemoStyles:!1,closable:!0,resizable:!0,slidable:!0,initClosed:!1,initHidden:!1,contentSelector:".ui-layout-content",contentIgnoreSelector:".ui-layout-ignore",findNestedContent:!1,paneClass:"ui-layout-pane",resizerClass:"ui-layout-resizer",togglerClass:"ui-layout-toggler",
buttonClass:"ui-layout-button",minSize:0,maxSize:0,spacing_open:6,spacing_closed:6,togglerLength_open:50,togglerLength_closed:50,togglerAlign_open:"center",togglerAlign_closed:"center",togglerContent_open:"",togglerContent_closed:"",resizerDblClickToggle:!0,autoResize:!0,autoReopen:!0,resizerDragOpacity:1,maskContents:!1,maskObjects:!1,maskZindex:null,resizingGrid:!1,livePaneResizing:!1,liveContentResizing:!1,liveResizingTolerance:1,sliderCursor:"pointer",slideTrigger_open:"click",slideTrigger_close:"mouseleave",
slideDelay_open:300,slideDelay_close:300,hideTogglerOnSlide:!1,preventQuickSlideClose:b.layout.browser.webkit,preventPrematureSlideClose:!1,tips:{Open:"Open",Close:"Close",Resize:"Resize",Slide:"Slide Open",Pin:"Pin",Unpin:"Un-Pin",noRoomToOpen:"Not enough room to show this panel.",minSizeWarning:"Panel has reached its minimum size",maxSizeWarning:"Panel has reached its maximum size"},showOverflowOnHover:!1,enableCursorHotkey:!0,customHotkeyModifier:"SHIFT",fxName:"slide",fxSpeed:null,fxSettings:{},
fxOpacityFix:!0,animatePaneSizing:!1,children:null,containerSelector:"",initChildren:!0,destroyChildren:!0,resizeChildren:!0,triggerEventsOnLoad:!1,triggerEventsDuringLiveResize:!0,onshow_start:null,onshow_end:null,onhide_start:null,onhide_end:null,onopen_start:null,onopen_end:null,onclose_start:null,onclose_end:null,onresize_start:null,onresize_end:null,onsizecontent_start:null,onsizecontent_end:null,onswap_start:null,onswap_end:null,ondrag_start:null,ondrag_end:null},north:{paneSelector:".ui-layout-north",
size:"auto",resizerCursor:"n-resize",customHotkey:""},south:{paneSelector:".ui-layout-south",size:"auto",resizerCursor:"s-resize",customHotkey:""},east:{paneSelector:".ui-layout-east",size:200,resizerCursor:"e-resize",customHotkey:""},west:{paneSelector:".ui-layout-west",size:200,resizerCursor:"w-resize",customHotkey:""},center:{paneSelector:".ui-layout-center",minWidth:0,minHeight:0}};b.layout.optionsMap={layout:"name instanceKey stateManagement effects inset zIndexes errors zIndex scrollToBookmarkOnLoad showErrorMessages maskPanesEarly outset resizeWithWindow resizeWithWindowDelay resizeWithWindowMaxDelay onresizeall onresizeall_start onresizeall_end onload onload_start onload_end onunload onunload_start onunload_end".split(" "),
center:"paneClass contentSelector contentIgnoreSelector findNestedContent applyDemoStyles triggerEventsOnLoad showOverflowOnHover maskContents maskObjects liveContentResizing containerSelector children initChildren resizeChildren destroyChildren onresize onresize_start onresize_end onsizecontent onsizecontent_start onsizecontent_end".split(" "),noDefault:["paneSelector","resizerCursor","customHotkey"]};b.layout.transformData=function(a,d){var c=d?{panes:{},center:{}}:{},f,j,k,h,p,x,D;if("object"!==
typeof a)return c;for(j in a){f=c;p=a[j];k=j.split("__");D=k.length-1;for(x=0;x<=D;x++)h=k[x],x===D?f[h]=b.isPlainObject(p)?b.layout.transformData(p):p:(f[h]||(f[h]={}),f=f[h])}return c};b.layout.backwardCompatibility={map:{applyDefaultStyles:"applyDemoStyles",childOptions:"children",initChildLayout:"initChildren",destroyChildLayout:"destroyChildren",resizeChildLayout:"resizeChildren",resizeNestedLayout:"resizeChildren",resizeWhileDragging:"livePaneResizing",resizeContentWhileDragging:"liveContentResizing",
triggerEventsWhileDragging:"triggerEventsDuringLiveResize",maskIframesOnResize:"maskContents",useStateCookie:"stateManagement.enabled","cookie.autoLoad":"stateManagement.autoLoad","cookie.autoSave":"stateManagement.autoSave","cookie.keys":"stateManagement.stateKeys","cookie.name":"stateManagement.cookie.name","cookie.domain":"stateManagement.cookie.domain","cookie.path":"stateManagement.cookie.path","cookie.expires":"stateManagement.cookie.expires","cookie.secure":"stateManagement.cookie.secure",
noRoomToOpenTip:"tips.noRoomToOpen",togglerTip_open:"tips.Close",togglerTip_closed:"tips.Open",resizerTip:"tips.Resize",sliderTip:"tips.Slide"},renameOptions:function(a){function d(b,c){for(var f=b.split("."),k=f.length-1,j={branch:a,key:f[k]},r=0,q;r<k;r++)q=f[r],j.branch=void 0==j.branch[q]?c?j.branch[q]={}:{}:j.branch[q];return j}var c=b.layout.backwardCompatibility.map,f,j,k,h;for(h in c)f=d(h),k=f.branch[f.key],void 0!==k&&(j=d(c[h],!0),j.branch[j.key]=k,delete f.branch[f.key])},renameAllOptions:function(a){var d=
b.layout.backwardCompatibility.renameOptions;d(a);a.defaults&&("object"!==typeof a.panes&&(a.panes={}),b.extend(!0,a.panes,a.defaults),delete a.defaults);a.panes&&d(a.panes);b.each(b.layout.config.allPanes,function(b,c){a[c]&&d(a[c])});return a}};b.fn.layout=function(h){function p(e){if(!e)return!0;var w=e.keyCode;if(33>w)return!0;var m={38:"north",40:"south",37:"west",39:"east"},a=e.shiftKey,g=e.ctrlKey,t,n,d,c;g&&(37<=w&&40>=w)&&r[m[w]].enableCursorHotkey?c=m[w]:(g||a)&&b.each(k.borderPanes,function(e,
b){t=r[b];n=t.customHotkey;d=t.customHotkeyModifier;if(a&&"SHIFT"==d||g&&"CTRL"==d||g&&a)if(n&&w===(isNaN(n)||9>=n?n.toUpperCase().charCodeAt(0):n))return c=b,!1});if(!c||!y[c]||!r[c].closable||q[c].isHidden)return!0;na(c);e.stopPropagation();return e.returnValue=!1}function x(e){if(H()){this&&this.tagName&&(e=this);var w;f(e)?w=y[e]:b(e).data("layoutRole")?w=b(e):b(e).parents().each(function(){if(b(this).data("layoutRole"))return w=b(this),!1});if(w&&w.length){var m=w.data("layoutEdge");e=q[m];e.cssSaved&&
X(m);if(e.isSliding||e.isResizing||e.isClosed)e.cssSaved=!1;else{var a={zIndex:r.zIndexes.resizer_normal+1},g={},t=w.css("overflow"),n=w.css("overflowX"),d=w.css("overflowY");"visible"!=t&&(g.overflow=t,a.overflow="visible");n&&!n.match(/(visible|auto)/)&&(g.overflowX=n,a.overflowX="visible");d&&!d.match(/(visible|auto)/)&&(g.overflowY=n,a.overflowY="visible");e.cssSaved=g;w.css(a);b.each(k.allPanes,function(e,b){b!=m&&X(b)})}}}}function X(e){if(H()){this&&this.tagName&&(e=this);var w;f(e)?w=y[e]:
b(e).data("layoutRole")?w=b(e):b(e).parents().each(function(){if(b(this).data("layoutRole"))return w=b(this),!1});if(w&&w.length){e=w.data("layoutEdge");e=q[e];var m=e.cssSaved||{};!e.isSliding&&!e.isResizing&&w.css("zIndex",r.zIndexes.pane_normal);w.css(m);e.cssSaved=!1}}}var G=b.layout.browser,k=b.layout.config,Q=b.layout.cssWidth,O=b.layout.cssHeight,R=b.layout.getElementDimensions,D=b.layout.getElementStyles,Ma=b.layout.getEventObject,A=b.layout.parsePaneName,r=b.extend(!0,{},b.layout.defaults);
r.effects=b.extend(!0,{},b.layout.effects);var q={id:"layout"+b.now(),initialized:!1,paneResizing:!1,panesSliding:{},container:{innerWidth:0,innerHeight:0,outerWidth:0,outerHeight:0,layoutWidth:0,layoutHeight:0},north:{childIdx:0},south:{childIdx:0},east:{childIdx:0},west:{childIdx:0},center:{childIdx:0}},ba={north:null,south:null,east:null,west:null,center:null},M={data:{},set:function(e,b,m){M.clear(e);M.data[e]=setTimeout(b,m)},clear:function(e){var b=M.data;b[e]&&(clearTimeout(b[e]),delete b[e])}},
ca=function(e,w,m){var a=r;(a.showErrorMessages&&!m||m&&a.showDebugMessages)&&b.layout.msg(a.name+" / "+e,!1!==w);return!1},C=function(e,w,m){var a=w&&f(w),g=a?q[w]:q,t=a?r[w]:r,n=r.name,d=e+(e.match(/_/)?"":"_end"),c=d.match(/_end$/)?d.substr(0,d.length-4):"",l=t[d]||t[c],h="NC",k=[];!a&&"boolean"===b.type(w)&&(m=w,w="");if(l)try{f(l)&&(l.match(/,/)?(k=l.split(","),l=eval(k[0])):l=eval(l)),b.isFunction(l)&&(h=k.length?l(k[1]):a?l(w,y[w],g,t,n):l(z,g,t,n))}catch(j){ca(r.errors.callbackError.replace(/EVENT/,
b.trim((w||"")+" "+d)),!1),"string"===b.type(j)&&string.length&&ca("Exception:  "+j,!1)}!m&&!1!==h&&(a?(m=y[w],t=r[w],g=q[w],m.triggerHandler("layoutpane"+d,[w,m,g,t,n]),c&&m.triggerHandler("layoutpane"+c,[w,m,g,t,n])):(u.triggerHandler("layout"+d,[z,g,t,n]),c&&u.triggerHandler("layout"+c,[z,g,t,n])));a&&"onresize_end"===e&&db(w+"",!0);return h},eb=function(e){if(!G.mozilla){var b=y[e];"IFRAME"===q[e].tagName?b.css(k.hidden).css(k.visible):b.find("IFRAME").css(k.hidden).css(k.visible)}},ya=function(e){var b=
y[e];e=k[e].dir;b={minWidth:1001-Q(b,1E3),minHeight:1001-O(b,1E3)};"horz"===e&&(b.minSize=b.minHeight);"vert"===e&&(b.minSize=b.minWidth);return b},fa=function(e,w,m){m||(m=k[e].dir);f(w)&&w.match(/%/)&&(w="100%"===w?-1:parseInt(w,10)/100);if(0===w)return 0;if(1<=w)return parseInt(w,10);var a=r,g=0;"horz"==m?g=v.innerHeight-(y.north?a.north.spacing_open:0)-(y.south?a.south.spacing_open:0):"vert"==m&&(g=v.innerWidth-(y.west?a.west.spacing_open:0)-(y.east?a.east.spacing_open:0));if(-1===w)return g;
if(0<w)return c(g*w);if("center"==e)return 0;m="horz"===m?"height":"width";a=y[e];e="height"===m?U[e]:!1;var g=b.layout.showInvisibly(a),t=a.css(m),n=e?e.css(m):0;a.css(m,"auto");e&&e.css(m,"auto");w="height"===m?a.outerHeight():a.outerWidth();a.css(m,t).css(g);e&&e.css(m,n);return w},ga=function(e,b){var a=y[e],E=r[e],g=q[e],t=b?E.spacing_open:0,E=b?E.spacing_closed:0;return!a||g.isHidden?0:g.isClosed||g.isSliding&&b?E:"horz"===k[e].dir?a.outerHeight()+t:a.outerWidth()+t},Y=function(e,b){if(H()){var m=
r[e],E=q[e],g=k[e],t=g.dir;g.sizeType.toLowerCase();var g=void 0!=b?b:E.isSliding,n=m.spacing_open,c=k.oppositeEdge[e],f=q[c],l=y[c],h=!l||!1===f.isVisible||f.isSliding?0:"horz"==t?l.outerHeight():l.outerWidth(),c=(!l||f.isHidden?0:r[c][!1!==f.isClosed?"spacing_closed":"spacing_open"])||0,f="horz"==t?v.innerHeight:v.innerWidth,l=ya("center"),l="horz"==t?d(r.center.minHeight,l.minHeight):d(r.center.minWidth,l.minWidth),g=f-n-(g?0:fa("center",l,t)+h+c),t=E.minSize=d(fa(e,m.minSize),ya(e).minSize),g=
E.maxSize=a(m.maxSize?fa(e,m.maxSize):1E5,g),E=E.resizerPosition={},n=v.inset.top,h=v.inset.left,c=v.innerWidth,f=v.innerHeight,m=m.spacing_open;switch(e){case "north":E.min=n+t;E.max=n+g;break;case "west":E.min=h+t;E.max=h+g;break;case "south":E.min=n+f-g-m;E.max=n+f-t-m;break;case "east":E.min=h+c-g-m,E.max=h+c-t-m}}},Na=function(e,a){var m=b(e),E=m.data("layoutRole"),g=m.data("layoutEdge"),t=r[g][E+"Class"],g="-"+g,n=m.hasClass(t+"-closed")?"-closed":"-open",d="-closed"===n?"-open":"-closed",n=
t+"-hover "+(t+g+"-hover ")+(t+n+"-hover ")+(t+g+n+"-hover ");a&&(n+=t+d+"-hover "+(t+g+d+"-hover "));"resizer"==E&&m.hasClass(t+"-sliding")&&(n+=t+"-sliding-hover "+(t+g+"-sliding-hover "));return b.trim(n)},Oa=function(e,a){var m=b(a||this);e&&"toggler"===m.data("layoutRole")&&e.stopPropagation();m.addClass(Na(m))},da=function(e,a){var m=b(a||this);m.removeClass(Na(m,!0))},fb=function(){var e=b(this).data("layoutEdge"),a=q[e];!a.isClosed&&(!a.isResizing&&!q.paneResizing)&&(b.fn.disableSelection&&
b("body").disableSelection(),r.maskPanesEarly&&va(e,{resizing:!0}))},gb=function(e,a){var m=a||this,E=b(m).data("layoutEdge"),g=E+"ResizerLeave";M.clear(E+"_openSlider");M.clear(g);a?q.paneResizing||(b.fn.enableSelection&&b("body").enableSelection(),r.maskPanesEarly&&za()):M.set(g,function(){gb(e,m)},200)},H=function(){return q.initialized||q.creatingLayout?!0:Aa()},Aa=function(e){var a=r;if(!u.is(":visible"))return!e&&(G.webkit&&"BODY"===u[0].tagName)&&setTimeout(function(){Aa(!0)},50),!1;if(!hb("center").length)return ca(a.errors.centerPaneMissing);
q.creatingLayout=!0;b.extend(v,R(u,a.inset));A(void 0);b.each(k.allPanes,function(e,b){ib(b,!0)});Pa();b.each(k.borderPanes,function(e,b){y[b]&&q[b].isVisible&&(Y(b),ha(b))});ia("center");b.each(k.allPanes,function(e,b){jb(b)});a.scrollToBookmarkOnLoad&&(e=self.location,e.hash&&e.replace(e.hash));z.hasParentLayout?a.resizeWithWindow=!1:a.resizeWithWindow&&b(window).bind("resize."+K,Ab);delete q.creatingLayout;q.initialized=!0;j(z,b.layout.onReady);C("onload_end");return!0},Qa=function(e,a){var m=
A.call(this,e),d=y[m];if(d){var g=U[m],t=q[m],n=r[m],c=r.stateManagement||{},n=a?n.children=a:n.children;if(b.isPlainObject(n))n=[n];else if(!n||!b.isArray(n))return;b.each(n,function(e,a){b.isPlainObject(a)&&(a.containerSelector?d.find(a.containerSelector):g||d).each(function(){var e=b(this),g=e.data("layout");if(!g){kb({container:e,options:a},t);if(c.includeChildren&&q.stateData[m]){var g=(q.stateData[m].children||{})[a.instanceKey],w=a.stateManagement||(a.stateManagement={autoLoad:!0});!0===w.autoLoad&&
g&&(w.autoSave=!1,w.includeChildren=!0,w.autoLoad=b.extend(!0,{},g))}(g=e.layout(a))&&Ba(m,g)}})})}},kb=function(e,b){var a=e.container,d=e.options,g=d.stateManagement,t=d.instanceKey||a.data("layoutInstanceKey");t||(t=(g&&g.cookie?g.cookie.name:"")||d.name);t=t?t.replace(/[^\w-]/gi,"_").replace(/_{2,}/g,"_"):"layout"+ ++b.childIdx;d.instanceKey=t;a.data("layoutInstanceKey",t);return t},Ba=function(e,a){var m=y[e],d=ba[e],g=q[e];b.isPlainObject(d)&&(b.each(d,function(e,b){b.destroyed&&delete d[e]}),
b.isEmptyObject(d)&&(d=ba[e]=null));!a&&!d&&(a=m.data("layout"));a&&(a.hasParentLayout=!0,m=a.options,kb(a,g),d||(d=ba[e]={}),d[m.instanceKey]=a.container.data("layout"));z[e].children=ba[e];a||Qa(e)},Ab=function(){var e=r,b=Number(e.resizeWithWindowDelay);10>b&&(b=100);M.clear("winResize");M.set("winResize",function(){M.clear("winResize");M.clear("winResizeRepeater");var b=R(u,e.inset);(b.innerWidth!==v.innerWidth||b.innerHeight!==v.innerHeight)&&oa()},b);M.data.winResizeRepeater||lb()},lb=function(){var e=
Number(r.resizeWithWindowMaxDelay);0<e&&M.set("winResizeRepeater",function(){lb();oa()},e)},mb=function(){C("onunload_start");j(z,b.layout.onUnload);C("onunload_end")},nb=function(e){e=e?e.split(","):k.borderPanes;b.each(e,function(e,a){var d=r[a];if(d.enableCursorHotkey||d.customHotkey)return b(document).bind("keydown."+K,p),!1})},hb=function(e){e=r[e].paneSelector;if("#"===e.substr(0,1))return u.find(e).eq(0);var b=u.children(e).eq(0);return b.length?b:u.children("form:first").children(e).eq(0)},
ib=function(e,b){if(b||H()){var m=r[e],c=q[e],g=k[e],t=g.dir,n="center"===e,f={},h=y[e],l,j;h?Ra(e,!1,!0,!1):U[e]=!1;h=y[e]=hb(e);if(h.length){h.data("layoutCSS")||h.data("layoutCSS",D(h,"position,top,left,bottom,right,width,height,overflow,zIndex,display,backgroundColor,padding,margin,border"));z[e]={name:e,pane:y[e],content:U[e],options:r[e],state:q[e],children:ba[e]};h.data({parentLayout:z,layoutPane:z[e],layoutEdge:e,layoutRole:"pane"}).css(g.cssReq).css("zIndex",r.zIndexes.pane_normal).css(m.applyDemoStyles?
g.cssDemo:{}).addClass(m.paneClass+" "+m.paneClass+"-"+e).bind("mouseenter."+K,Oa).bind("mouseleave."+K,da);g={hide:"",show:"",toggle:"",close:"",open:"",slideOpen:"",slideClose:"",slideToggle:"",size:"sizePane",sizePane:"sizePane",sizeContent:"",sizeHandles:"",enableClosable:"",disableClosable:"",enableSlideable:"",disableSlideable:"",enableResizable:"",disableResizable:"",swapPanes:"swapPanes",swap:"swapPanes",move:"swapPanes",removePane:"removePane",remove:"removePane",createChildren:"",resizeChildren:"",
resizeAll:"resizeAll",resizeLayout:"resizeAll"};for(j in g)h.bind("layoutpane"+j.toLowerCase()+"."+K,z[g[j]||j]);Sa(e,!1);n||(l=c.size=fa(e,m.size),n=fa(e,m.minSize)||1,j=fa(e,m.maxSize)||1E5,0<l&&(l=d(a(l,j),n)),c.autoResize=m.autoResize,c.isClosed=!1,c.isSliding=!1,c.isResizing=!1,c.isHidden=!1,c.pins||(c.pins=[]));c.tagName=h[0].tagName;c.edge=e;c.noRoom=!1;c.isVisible=!0;ob(e);"horz"===t?f.height=O(h,l):"vert"===t&&(f.width=Q(h,l));h.css(f);"horz"!=t&&ia(e,!0);q.initialized&&(Pa(e),nb(e));m.initClosed&&
m.closable&&!m.initHidden?ja(e,!0,!0):m.initHidden||m.initClosed?Ta(e):c.noRoom||h.css("display","block");h.css("visibility","visible");m.showOverflowOnHover&&h.hover(x,X);q.initialized&&jb(e)}else y[e]=!1}},jb=function(e){var b=y[e],a=q[e],d=r[e];b&&(b.data("layout")&&Ba(e,b.data("layout")),a.isVisible&&(q.initialized?oa():pa(e),d.triggerEventsOnLoad?C("onresize_end",e):db(e,!0)),d.initChildren&&d.children&&Qa(e))},ob=function(e){e=e?e.split(","):k.borderPanes;b.each(e,function(e,b){var a=y[b],g=
F[b],d=q[b],c=k[b].side,f={};if(a){switch(b){case "north":f.top=v.inset.top;f.left=v.inset.left;f.right=v.inset.right;break;case "south":f.bottom=v.inset.bottom;f.left=v.inset.left;f.right=v.inset.right;break;case "west":f.left=v.inset.left;break;case "east":f.right=v.inset.right}a.css(f);g&&d.isClosed?g.css(c,v.inset[c]):g&&!d.isHidden&&g.css(c,v.inset[c]+ga(b))}})},Pa=function(e){e=e?e.split(","):k.borderPanes;b.each(e,function(e,a){var d=y[a];F[a]=!1;P[a]=!1;if(d){var g=r[a],d=q[a],c="#"===g.paneSelector.substr(0,
1)?g.paneSelector.substr(1):"",n=g.resizerClass,f=g.togglerClass,h="-"+a,l=z[a],j=l.resizer=F[a]=b("<div></div>"),l=l.toggler=g.closable?P[a]=b("<div></div>"):!1;!d.isVisible&&g.slidable&&j.attr("title",g.tips.Slide).css("cursor",g.sliderCursor);j.attr("id",c?c+"-resizer":"").data({parentLayout:z,layoutPane:z[a],layoutEdge:a,layoutRole:"resizer"}).css(k.resizers.cssReq).css("zIndex",r.zIndexes.resizer_normal).css(g.applyDemoStyles?k.resizers.cssDemo:{}).addClass(n+" "+n+h).hover(Oa,da).hover(fb,gb).appendTo(u);
g.resizerDblClickToggle&&j.bind("dblclick."+K,na);l&&(l.attr("id",c?c+"-toggler":"").data({parentLayout:z,layoutPane:z[a],layoutEdge:a,layoutRole:"toggler"}).css(k.togglers.cssReq).css(g.applyDemoStyles?k.togglers.cssDemo:{}).addClass(f+" "+f+h).hover(Oa,da).bind("mouseenter",fb).appendTo(j),g.togglerContent_open&&b("<span>"+g.togglerContent_open+"</span>").data({layoutEdge:a,layoutRole:"togglerContent"}).data("layoutRole","togglerContent").data("layoutEdge",a).addClass("content content-open").css("display",
"none").appendTo(l),g.togglerContent_closed&&b("<span>"+g.togglerContent_closed+"</span>").data({layoutEdge:a,layoutRole:"togglerContent"}).addClass("content content-closed").css("display","none").appendTo(l),pb(a));var g=a,B=b.layout.plugins.draggable,g=g?g.split(","):k.borderPanes;b.each(g,function(e,a){var g=r[a];if(!B||!y[a]||!g.resizable)return g.resizable=!1,!0;var m=q[a],w=r.zIndexes,d=k[a],c="horz"==d.dir?"top":"left",t=F[a],n=g.resizerClass,f=0,l,h,E=n+"-drag",j=n+"-"+a+"-drag",J=n+"-dragging",
zb=n+"-"+a+"-dragging",cb=n+"-dragging-limit",v=n+"-"+a+"-dragging-limit",x=!1;m.isClosed||t.attr("title",g.tips.Resize).css("cursor",g.resizerCursor);t.draggable({containment:u[0],axis:"horz"==d.dir?"y":"x",delay:0,distance:1,grid:g.resizingGrid,helper:"clone",opacity:g.resizerDragOpacity,addClasses:!1,zIndex:w.resizer_drag,start:function(e,w){g=r[a];m=q[a];h=g.livePaneResizing;if(!1===C("ondrag_start",a))return!1;m.isResizing=!0;q.paneResizing=a;M.clear(a+"_closeSlider");Y(a);l=m.resizerPosition;
f=w.position[c];t.addClass(E+" "+j);x=!1;b("body").disableSelection();va(a,{resizing:!0})},drag:function(e,b){x||(b.helper.addClass(J+" "+zb).css({right:"auto",bottom:"auto"}).children().css("visibility","hidden"),x=!0,m.isSliding&&y[a].css("zIndex",w.pane_sliding));var d=0;b.position[c]<l.min?(b.position[c]=l.min,d=-1):b.position[c]>l.max&&(b.position[c]=l.max,d=1);d?(b.helper.addClass(cb+" "+v),window.defaultStatus=0<d&&a.match(/(north|west)/)||0>d&&a.match(/(south|east)/)?g.tips.maxSizeWarning:
g.tips.minSizeWarning):(b.helper.removeClass(cb+" "+v),window.defaultStatus="");h&&Math.abs(b.position[c]-f)>=g.liveResizingTolerance&&(f=b.position[c],p(e,b,a))},stop:function(e,g){b("body").enableSelection();window.defaultStatus="";t.removeClass(E+" "+j);m.isResizing=!1;q.paneResizing=!1;p(e,g,a,!0)}})});var p=function(b,e,a,g){var m=e.position,w=k[a];b=r[a];e=q[a];var d;switch(a){case "north":d=m.top;break;case "west":d=m.left;break;case "south":d=v.layoutHeight-m.top-b.spacing_open;break;case "east":d=
v.layoutWidth-m.left-b.spacing_open}d-=v.inset[w.side];g?(!1!==C("ondrag_end",a)&&Ca(a,d,!1,!0),za(!0),e.isSliding&&va(a,{resizing:!0})):Math.abs(d-e.size)<b.liveResizingTolerance||(Ca(a,d,!1,!0),ea.each(qb))};d.isVisible?Ua(a):(Da(a),ma(a,!0))}});qa()},Sa=function(b,a){if(H()){var m=r[b],d=m.contentSelector,g=z[b],c=y[b],n;d&&(n=g.content=U[b]=m.findNestedContent?c.find(d).eq(0):c.children(d).eq(0));n&&n.length?(n.data("layoutRole","content"),n.data("layoutCSS")||n.data("layoutCSS",D(n,"height")),
n.css(k.content.cssReq),m.applyDemoStyles&&(n.css(k.content.cssDemo),c.css(k.content.cssDemoPane)),c.css("overflowX").match(/(scroll|auto)/)&&c.css("overflow","hidden"),q[b].content={},!1!==a&&pa(b)):g.content=U[b]=!1}},qb=function(){var e=b(this),a=e.data("layoutMask"),a=q[a];"IFRAME"==a.tagName&&a.isVisible&&e.css({top:a.offsetTop,left:a.offsetLeft,width:a.outerWidth,height:a.outerHeight})},va=function(e,a){var m=k[e],d=["center"],g=r.zIndexes,c=b.extend({objectsOnly:!1,animation:!1,resizing:!0,
sliding:q[e].isSliding},a),n,f;c.resizing&&d.push(e);c.sliding&&d.push(k.oppositeEdge[e]);"horz"===m.dir&&(d.push("west"),d.push("east"));b.each(d,function(e,a){f=q[a];n=r[a];if(f.isVisible&&(n.maskObjects||!c.objectsOnly&&n.maskContents)){for(var m=b([]),d,w=0,h=ea.length;w<h;w++)d=ea.eq(w),d.data("layoutMask")===a&&(m=m.add(d));if(!m.length){m=y[a];d=q[a];var w=r[a],h=r.zIndexes,j=b([]),E,k,v,p,x;if(w.maskContents||w.maskObjects)for(x=0;x<(w.maskObjects?2:1);x++)E=w.maskObjects&&0==x,k=document.createElement(E?
"iframe":"div"),v=b(k).data("layoutMask",a),k.className="ui-layout-mask ui-layout-mask-"+a,p=k.style,p.display="block",p.position="absolute",p.background="#FFF",E&&(k.frameborder=0,k.src="about:blank",p.opacity=0,p.filter="Alpha(Opacity='0')",p.border=0),"IFRAME"==d.tagName?(p.zIndex=h.pane_normal+1,u.append(k)):(v.addClass("ui-layout-mask-inside-pane"),p.zIndex=w.maskZindex||h.content_mask,p.top=0,p.left=0,p.width="100%",p.height="100%",m.append(k)),j=j.add(k),ea=ea.add(k);m=j}m.each(function(){qb.call(this);
this.style.zIndex=f.isSliding?g.pane_sliding+1:g.pane_normal+1;this.style.display="block"})}})},za=function(a){if(a||!q.paneResizing)ea.hide();else if(!a&&!b.isEmptyObject(q.panesSliding)){a=ea.length-1;for(var d,m;0<=a;a--)m=ea.eq(a),d=m.data("layoutMask"),r[d].maskObjects||m.hide()}},Ra=function(a,d,m,c){if(H()){a=A.call(this,a);var g=y[a],t=U[a],n=F[a],f=P[a];g&&b.isEmptyObject(g.data())&&(g=!1);t&&b.isEmptyObject(t.data())&&(t=!1);n&&b.isEmptyObject(n.data())&&(n=!1);f&&b.isEmptyObject(f.data())&&
(f=!1);g&&g.stop(!0,!0);var h=r[a],l=ba[a],j=b.isPlainObject(l)&&!b.isEmptyObject(l);c=void 0!==c?c:h.destroyChildren;j&&c&&(b.each(l,function(a,b){b.destroyed||b.destroy(!0);b.destroyed&&delete l[a]}),b.isEmptyObject(l)&&(l=ba[a]=null,j=!1));g&&d&&!j?g.remove():g&&g[0]&&(d=h.paneClass,c=d+"-"+a,d=[d,d+"-open",d+"-closed",d+"-sliding",c,c+"-open",c+"-closed",c+"-sliding"],b.merge(d,Na(g,!0)),g.removeClass(d.join(" ")).removeData("parentLayout").removeData("layoutPane").removeData("layoutRole").removeData("layoutEdge").removeData("autoHidden").unbind("."+
K),j&&t?(t.width(t.width()),b.each(l,function(a,b){b.resizeAll()})):t&&t.css(t.data("layoutCSS")).removeData("layoutCSS").removeData("layoutRole"),g.data("layout")||g.css(g.data("layoutCSS")).removeData("layoutCSS"));f&&f.remove();n&&n.remove();z[a]=y[a]=U[a]=F[a]=P[a]=!1;m||oa()}},Ea=function(a){var b=y[a],d=b[0].style;r[a].useOffscreenClose?(b.data(k.offscreenReset)||b.data(k.offscreenReset,{left:d.left,right:d.right}),b.css(k.offscreenCSS)):b.hide().removeData(k.offscreenReset)},rb=function(a){var b=
y[a];a=r[a];var d=k.offscreenCSS,c=b.data(k.offscreenReset),g=b[0].style;b.show().removeData(k.offscreenReset);a.useOffscreenClose&&c&&(g.left==d.left&&(g.left=c.left),g.right==d.right&&(g.right=c.right))},Ta=function(a,b){if(H()){var d=A.call(this,a),c=r[d],g=q[d],t=F[d];y[d]&&!g.isHidden&&!(q.initialized&&!1===C("onhide_start",d))&&(g.isSliding=!1,delete q.panesSliding[d],t&&t.hide(),!q.initialized||g.isClosed?(g.isClosed=!0,g.isHidden=!0,g.isVisible=!1,q.initialized||Ea(d),ia("horz"===k[d].dir?
"":"center"),(q.initialized||c.triggerEventsOnLoad)&&C("onhide_end",d)):(g.isHiding=!0,ja(d,!1,b)))}},Fa=function(a,b,d,c){if(H()){a=A.call(this,a);var g=q[a];y[a]&&g.isHidden&&!1!==C("onshow_start",a)&&(g.isShowing=!0,g.isSliding=!1,delete q.panesSliding[a],!1===b?ja(a,!0):ra(a,!1,d,c))}},na=function(a,b){if(H()){var d=Ma(a),c=A.call(this,a),g=q[c];d&&d.stopImmediatePropagation();g.isHidden?Fa(c):g.isClosed?ra(c,!!b):ja(c)}},ja=function(a,b,d,c){function g(){l.isMoving=!1;ma(t,!0);var a=k.oppositeEdge[t];
q[a].noRoom&&(Y(a),ha(a));if(!c&&(q.initialized||h.triggerEventsOnLoad))p||C("onclose_end",t),p&&C("onshow_end",t),v&&C("onhide_end",t)}var t=A.call(this,a);if(!q.initialized&&y[t]){a=t;var n=q[a];Ea(a);n.isClosed=!0;n.isVisible=!1;Da(a)}else if(H()){var f=y[t],h=r[t],l=q[t],j,p,v;u.queue(function(a){if(!f||!h.closable&&!l.isShowing&&!l.isHiding||!b&&l.isClosed&&!l.isShowing)return a();var e=!l.isShowing&&!1===C("onclose_start",t);p=l.isShowing;v=l.isHiding;delete l.isShowing;delete l.isHiding;if(e)return a();
j=!d&&!l.isClosed&&"none"!=h.fxName_close;l.isMoving=!0;l.isClosed=!0;l.isVisible=!1;v?l.isHidden=!0:p&&(l.isHidden=!1);l.isSliding?wa(t,!1):ia("horz"===k[t].dir?"":"center",!1);Da(t);j?(Ga(t,!0),f.hide(h.fxName_close,h.fxSettings_close,h.fxSpeed_close,function(){Ga(t,!1);l.isClosed&&g();a()})):(Ea(t),g(),a())})}},Da=function(a){if(F[a]){var d=F[a],c=P[a],f=r[a],g=k[a].side,t=f.resizerClass,n=f.togglerClass,h="-"+a;d.css(g,v.inset[g]).removeClass(t+"-open "+t+h+"-open").removeClass(t+"-sliding "+
t+h+"-sliding").addClass(t+"-closed "+t+h+"-closed");f.resizable&&b.layout.plugins.draggable&&d.draggable("disable").removeClass("ui-state-disabled").css("cursor","default").attr("title","");c&&(c.removeClass(n+"-open "+n+h+"-open").addClass(n+"-closed "+n+h+"-closed").attr("title",f.tips.Open),c.children(".content-open").hide(),c.children(".content-closed").css("display","block"));Va(a,!1);q.initialized&&qa()}},ra=function(a,b,d,c){function g(){j.isMoving=!1;eb(f);j.isSliding||ia("vert"==k[f].dir?
"center":"",!1);Ua(f)}if(H()){var f=A.call(this,a),n=y[f],h=r[f],j=q[f],l,p;u.queue(function(a){if(!n||!h.resizable&&!h.closable&&!j.isShowing||j.isVisible&&!j.isSliding)return a();if(j.isHidden&&!j.isShowing)a(),Fa(f,!0);else{j.autoResize&&j.size!=h.size?ka(f,h.size,!0,!0,!0):Y(f,b);var e=C("onopen_start",f);if("abort"===e)return a();"NC"!==e&&Y(f,b);if(j.minSize>j.maxSize)return Va(f,!1),!c&&h.tips.noRoomToOpen&&alert(h.tips.noRoomToOpen),a();b?wa(f,!0):j.isSliding?wa(f,!1):h.slidable&&ma(f,!1);
j.noRoom=!1;ha(f);p=j.isShowing;delete j.isShowing;l=!d&&j.isClosed&&"none"!=h.fxName_open;j.isMoving=!0;j.isVisible=!0;j.isClosed=!1;p&&(j.isHidden=!1);l?(Ga(f,!0),n.show(h.fxName_open,h.fxSettings_open,h.fxSpeed_open,function(){Ga(f,!1);j.isVisible&&g();a()})):(rb(f),g(),a())}})}},Ua=function(a,d){var c=y[a],f=F[a],g=P[a],h=r[a],n=q[a],j=k[a].side,p=h.resizerClass,l=h.togglerClass,u="-"+a;f.css(j,v.inset[j]+ga(a)).removeClass(p+"-closed "+p+u+"-closed").addClass(p+"-open "+p+u+"-open");n.isSliding?
f.addClass(p+"-sliding "+p+u+"-sliding"):f.removeClass(p+"-sliding "+p+u+"-sliding");da(0,f);h.resizable&&b.layout.plugins.draggable?f.draggable("enable").css("cursor",h.resizerCursor).attr("title",h.tips.Resize):n.isSliding||f.css("cursor","default");g&&(g.removeClass(l+"-closed "+l+u+"-closed").addClass(l+"-open "+l+u+"-open").attr("title",h.tips.Close),da(0,g),g.children(".content-closed").hide(),g.children(".content-open").css("display","block"));Va(a,!n.isSliding);b.extend(n,R(c));q.initialized&&
(qa(),pa(a,!0));if(!d&&(q.initialized||h.triggerEventsOnLoad)&&c.is(":visible"))C("onopen_end",a),n.isShowing&&C("onshow_end",a),q.initialized&&C("onresize_end",a)},sb=function(a){function b(){g.isClosed?g.isMoving||ra(c,!0):wa(c,!0)}if(H()){var d=Ma(a),c=A.call(this,a),g=q[c];a=r[c].slideDelay_open;d&&d.stopImmediatePropagation();g.isClosed&&d&&"mouseenter"===d.type&&0<a?M.set(c+"_openSlider",b,a):b()}},Wa=function(a){function c(){g.isClosed?wa(f,!1):g.isMoving||ja(f)}if(H()){var m=Ma(a),f=A.call(this,
a);a=r[f];var g=q[f],h=g.isMoving?1E3:300;!g.isClosed&&!g.isResizing&&("click"===a.slideTrigger_close?c():a.preventQuickSlideClose&&g.isMoving||a.preventPrematureSlideClose&&m&&b.layout.isMouseOverElem(m,y[f])||(m?M.set(f+"_closeSlider",c,d(a.slideDelay_close,h)):c()))}},Ga=function(a,b){var d=y[a],c=q[a],g=r[a],f=r.zIndexes;b?(va(a,{animation:!0,objectsOnly:!0}),d.css({zIndex:f.pane_animate}),"south"==a?d.css({top:v.inset.top+v.innerHeight-d.outerHeight()}):"east"==a&&d.css({left:v.inset.left+v.innerWidth-
d.outerWidth()})):(za(),d.css({zIndex:c.isSliding?f.pane_sliding:f.pane_normal}),"south"==a?d.css({top:"auto"}):"east"==a&&!d.css("left").match(/\-99999/)&&d.css({left:"auto"}),G.msie&&(g.fxOpacityFix&&"slide"!=g.fxName_open&&d.css("filter")&&1==d.css("opacity"))&&d[0].style.removeAttribute("filter"))},ma=function(a,b){var d=r[a],c=F[a],g=d.slideTrigger_open.toLowerCase();if(c&&(!b||d.slidable)){g.match(/mouseover/)?g=d.slideTrigger_open="mouseenter":g.match(/(click|dblclick|mouseenter)/)||(g=d.slideTrigger_open=
"click");if(d.resizerDblClickToggle&&g.match(/click/))c[b?"unbind":"bind"]("dblclick."+K,na);c[b?"bind":"unbind"](g+"."+K,sb).css("cursor",b?d.sliderCursor:"default").attr("title",b?d.tips.Slide:"")}},wa=function(a,b){function d(b){M.clear(a+"_closeSlider");b.stopPropagation()}var c=r[a],g=q[a],f=r.zIndexes,h=c.slideTrigger_close.toLowerCase(),j=b?"bind":"unbind",k=y[a],l=F[a];M.clear(a+"_closeSlider");b?(g.isSliding=!0,q.panesSliding[a]=!0,ma(a,!1)):(g.isSliding=!1,delete q.panesSliding[a]);k.css("zIndex",
b?f.pane_sliding:f.pane_normal);l.css("zIndex",b?f.pane_sliding+2:f.resizer_normal);h.match(/(click|mouseleave)/)||(h=c.slideTrigger_close="mouseleave");l[j](h,Wa);"mouseleave"===h&&(k[j]("mouseleave."+K,Wa),l[j]("mouseenter."+K,d),k[j]("mouseenter."+K,d));b?"click"===h&&!c.resizable&&(l.css("cursor",b?c.sliderCursor:"default"),l.attr("title",b?c.tips.Close:"")):M.clear(a+"_closeSlider")},ha=function(a,d,c,f){d=r[a];var g=q[a],h=k[a],n=y[a],j=F[a],p="vert"===h.dir,l=!1;if("center"===a||p&&g.noVerticalRoom)(l=
0<=g.maxHeight)&&g.noRoom?(rb(a),j&&j.show(),g.isVisible=!0,g.noRoom=!1,p&&(g.noVerticalRoom=!1),eb(a)):!l&&!g.noRoom&&(Ea(a),j&&j.hide(),g.isVisible=!1,g.noRoom=!0);"center"!==a&&(g.minSize<=g.maxSize?(g.size>g.maxSize?ka(a,g.maxSize,c,!0,f):g.size<g.minSize?ka(a,g.minSize,c,!0,f):j&&(g.isVisible&&n.is(":visible"))&&(c=g.size+v.inset[h.side],b.layout.cssNum(j,h.side)!=c&&j.css(h.side,c)),g.noRoom&&(g.wasOpen&&d.closable?d.autoReopen?ra(a,!1,!0,!0):g.noRoom=!1:Fa(a,g.wasOpen,!0,!0))):g.noRoom||(g.noRoom=
!0,g.wasOpen=!g.isClosed&&!g.isSliding,g.isClosed||(d.closable?ja(a,!0,!0):Ta(a,!0))))},Ca=function(a,b,d,c,g){if(H()){a=A.call(this,a);var f=r[a],h=q[a];g=g||f.livePaneResizing&&!h.isResizing;h.autoResize=!1;ka(a,b,d,c,g)}},ka=function(e,c,f,h,g){function j(){for(var a="width"===ua?l.outerWidth():l.outerHeight(),a=[{pane:n,count:1,target:c,actual:a,correct:c===a,attempt:c,cssSize:D}],e=a[0],h={},t="Inaccurate size after resizing the "+n+"-pane.";!e.correct;){h={pane:n,count:e.count+1,target:c};h.attempt=
e.actual>c?d(0,e.attempt-(e.actual-c)):d(0,e.attempt+(c-e.actual));h.cssSize=("horz"==k[n].dir?O:Q)(y[n],h.attempt);l.css(ua,h.cssSize);h.actual="width"==ua?l.outerWidth():l.outerHeight();h.correct=c===h.actual;1===a.length&&(ca(t,!1,!0),ca(e,!1,!0));ca(h,!1,!0);if(3<a.length)break;a.push(h);e=a[a.length-1]}J.size=c;b.extend(J,R(l));J.isVisible&&l.is(":visible")&&(x&&x.css(B,c+v.inset[B]),pa(n));!f&&(!Z&&q.initialized&&J.isVisible)&&C("onresize_end",n);f||(J.isSliding||ia("horz"==k[n].dir?"":"center",
Z,g),qa());e=k.oppositeEdge[n];c<G&&q[e].noRoom&&(Y(e),ha(e,!1,f));1<a.length&&ca(t+"\nSee the Error Console for details.",!0,!0)}if(H()){var n=A.call(this,e),p=r[n],J=q[n],l=y[n],x=F[n],B=k[n].side,ua=k[n].sizeType.toLowerCase(),Z=J.isResizing&&!p.triggerEventsDuringLiveResize,z=!0!==h&&p.animatePaneSizing,G,D;u.queue(function(e){Y(n);G=J.size;c=fa(n,c);c=d(c,fa(n,p.minSize));c=a(c,J.maxSize);if(c<J.minSize)e(),ha(n,!1,f);else{if(!g&&c===G)return e();J.newSize=c;!f&&(q.initialized&&J.isVisible)&&
C("onresize_start",n);D=("horz"==k[n].dir?O:Q)(y[n],c);if(z&&l.is(":visible")){var h=b.layout.effects.size[n]||b.layout.effects.size.all,h=p.fxSettings_size.easing||h.easing,v=r.zIndexes,u={};u[ua]=D+"px";J.isMoving=!0;l.css({zIndex:v.pane_animate}).show().animate(u,p.fxSpeed_size,h,function(){l.css({zIndex:J.isSliding?v.pane_sliding:v.pane_normal});J.isMoving=!1;delete J.newSize;j();e()})}else l.css(ua,D),delete J.newSize,l.is(":visible")?j():(J.size=c,b.extend(J,R(l))),e()}})}},ia=function(a,c,
f){a=(a?a:"east,west,center").split(",");b.each(a,function(a,e){if(y[e]){var h=r[e],j=q[e],k=y[e],p=!0,l={},u=b.layout.showInvisibly(k),B={top:ga("north",!0),bottom:ga("south",!0),left:ga("west",!0),right:ga("east",!0),width:0,height:0};B.width=v.innerWidth-B.left-B.right;B.height=v.innerHeight-B.bottom-B.top;B.top+=v.inset.top;B.bottom+=v.inset.bottom;B.left+=v.inset.left;B.right+=v.inset.right;b.extend(j,R(k));if("center"===e){if(!f&&j.isVisible&&B.width===j.outerWidth&&B.height===j.outerHeight)return k.css(u),
!0;b.extend(j,ya(e),{maxWidth:B.width,maxHeight:B.height});l=B;j.newWidth=l.width;j.newHeight=l.height;l.width=Q(k,l.width);l.height=O(k,l.height);p=0<=l.width&&0<=l.height;if(!q.initialized&&h.minWidth>B.width){var h=h.minWidth-j.outerWidth,B=r.east.minSize||0,x=r.west.minSize||0,Z=q.east.size,z=q.west.size,A=Z,D=z;0<h&&(q.east.isVisible&&Z>B)&&(A=d(Z-B,Z-h),h-=Z-A);0<h&&(q.west.isVisible&&z>x)&&(D=d(z-x,z-h),h-=z-D);if(0===h){Z&&Z!=B&&ka("east",A,!0,!0,f);z&&z!=x&&ka("west",D,!0,!0,f);ia("center",
c,f);k.css(u);return}}}else{j.isVisible&&!j.noVerticalRoom&&b.extend(j,R(k),ya(e));if(!f&&!j.noVerticalRoom&&B.height===j.outerHeight)return k.css(u),!0;l.top=B.top;l.bottom=B.bottom;j.newSize=B.height;l.height=O(k,B.height);j.maxHeight=l.height;p=0<=j.maxHeight;p||(j.noVerticalRoom=!0)}p?(!c&&q.initialized&&C("onresize_start",e),k.css(l),"center"!==e&&qa(e),j.noRoom&&(!j.isClosed&&!j.isHidden)&&ha(e),j.isVisible&&(b.extend(j,R(k)),q.initialized&&pa(e))):!j.noRoom&&j.isVisible&&ha(e);k.css(u);delete j.newSize;
delete j.newWidth;delete j.newHeight;if(!j.isVisible)return!0;"center"===e&&(j=G.isIE6||!G.boxModel,y.north&&(j||"IFRAME"==q.north.tagName)&&y.north.css("width",Q(y.north,v.innerWidth)),y.south&&(j||"IFRAME"==q.south.tagName)&&y.south.css("width",Q(y.south,v.innerWidth)));!c&&q.initialized&&C("onresize_end",e)}})},oa=function(a){A(a);if(u.is(":visible"))if(q.initialized){if(!0===a&&b.isPlainObject(r.outset)&&u.css(r.outset),b.extend(v,R(u,r.inset)),v.outerHeight){!0===a&&ob();if(!1===C("onresizeall_start"))return!1;
var d,c,f;b.each(["south","north","east","west"],function(a,b){y[b]&&(c=r[b],f=q[b],f.autoResize&&f.size!=c.size?ka(b,c.size,!0,!0,!0):(Y(b),ha(b,!1,!0,!0)))});ia("",!0,!0);qa();b.each(k.allPanes,function(a,b){(d=y[b])&&q[b].isVisible&&C("onresize_end",b)});C("onresizeall_end")}}else Aa()},db=function(a,d){var c=A.call(this,a);r[c].resizeChildren&&(d||Ba(c),c=ba[c],b.isPlainObject(c)&&b.each(c,function(a,b){b.destroyed||b.resizeAll()}))},pa=function(a,c){if(H()){var h=A.call(this,a),h=h?h.split(","):
k.allPanes;b.each(h,function(a,e){function h(a){return d(u.css.paddingBottom,parseInt(a.css("marginBottom"),10)||0)}function j(){var a=r[e].contentIgnoreSelector,a=p.nextAll().not(".ui-layout-mask").not(a||":lt(0)"),b=a.filter(":visible"),d=b.filter(":last");v={top:p[0].offsetTop,height:p.outerHeight(),numFooters:a.length,hiddenFooters:a.length-b.length,spaceBelow:0};v.spaceAbove=v.top;v.bottom=v.top+v.height;v.spaceBelow=d.length?d[0].offsetTop+d.outerHeight()-v.bottom+h(d):h(p)}var m=y[e],p=U[e],
l=r[e],u=q[e],v=u.content;if(!m||!p||!m.is(":visible"))return!0;if(!p.length&&(Sa(e,!1),!p))return;if(!1!==C("onsizecontent_start",e)){if(!u.isMoving&&!u.isResizing||l.liveContentResizing||c||void 0==v.top)j(),0<v.hiddenFooters&&"hidden"===m.css("overflow")&&(m.css("overflow","visible"),j(),m.css("overflow","hidden"));m=u.innerHeight-(v.spaceAbove-u.css.paddingTop)-(v.spaceBelow-u.css.paddingBottom);if(!p.is(":visible")||v.height!=m){var x=p,l=x;f(x)?l=y[x]:x.jquery||(l=b(x));x=O(l,m);l.css({height:x,
visibility:"visible"});0<x&&0<l.innerWidth()?l.data("autoHidden")&&(l.show().data("autoHidden",!1),G.mozilla||l.css(k.hidden).css(k.visible)):l.data("autoHidden")||l.hide().data("autoHidden",!0);v.height=m}q.initialized&&C("onsizecontent_end",e)}})}},qa=function(a){a=(a=A.call(this,a))?a.split(","):k.borderPanes;b.each(a,function(a,d){var e=r[d],g=q[d],h=y[d],j=F[d],p=P[d],u;if(h&&j){var l=k[d].dir,x=g.isClosed?"_closed":"_open",B=e["spacing"+x],z=e["togglerAlign"+x],x=e["togglerLength"+x],A;if(0===
B)j.hide();else{!g.noRoom&&!g.isHidden&&j.show();"horz"===l?(A=v.innerWidth,g.resizerLength=A,h=b.layout.cssNum(h,"left"),j.css({width:Q(j,A),height:O(j,B),left:-9999<h?h:v.inset.left})):(A=h.outerHeight(),g.resizerLength=A,j.css({height:O(j,A),width:Q(j,B),top:v.inset.top+ga("north",!0)}));da(e,j);if(p){if(0===x||g.isSliding&&e.hideTogglerOnSlide){p.hide();return}p.show();if(!(0<x)||"100%"===x||x>A)x=A,z=0;else if(f(z))switch(z){case "top":case "left":z=0;break;case "bottom":case "right":z=A-x;break;
default:z=c((A-x)/2)}else h=parseInt(z,10),z=0<=z?h:A-x+h;if("horz"===l){var D=Q(p,x);p.css({width:D,height:O(p,B),left:z,top:0});p.children(".content").each(function(){u=b(this);u.css("marginLeft",c((D-u.outerWidth())/2))})}else{var C=O(p,x);p.css({height:C,width:Q(p,B),top:z,left:0});p.children(".content").each(function(){u=b(this);u.css("marginTop",c((C-u.outerHeight())/2))})}da(0,p)}if(!q.initialized&&(e.initHidden||g.isHidden))j.hide(),p&&p.hide()}}})},pb=function(a){if(H()){var b=A.call(this,
a);a=P[b];var d=r[b];a&&(d.closable=!0,a.bind("click."+K,function(a){a.stopPropagation();na(b)}).css("visibility","visible").css("cursor","pointer").attr("title",q[b].isClosed?d.tips.Open:d.tips.Close).show())}},Va=function(a,d){b.layout.plugins.buttons&&b.each(q[a].pins,function(c,f){b.layout.buttons.setPinState(z,b(f),a,d)})},u=b(this).eq(0);if(!u.length)return ca(r.errors.containerMissing);if(u.data("layoutContainer")&&u.data("layout"))return u.data("layout");var y={},U={},F={},P={},ea=b([]),v=
q.container,K=q.id,z={options:r,state:q,container:u,panes:y,contents:U,resizers:F,togglers:P,hide:Ta,show:Fa,toggle:na,open:ra,close:ja,slideOpen:sb,slideClose:Wa,slideToggle:function(a){a=A.call(this,a);na(a,!0)},setSizeLimits:Y,_sizePane:ka,sizePane:Ca,sizeContent:pa,swapPanes:function(a,c){function f(a){var d=y[a],c=U[a];return!d?!1:{pane:a,P:d?d[0]:!1,C:c?c[0]:!1,state:b.extend(!0,{},q[a]),options:b.extend(!0,{},r[a])}}function h(a,c){if(a){var e=a.P,f=a.C,g=a.pane,j=k[c],m=b.extend(!0,{},q[c]),
n=r[c],w={resizerCursor:n.resizerCursor};b.each(["fxName","fxSpeed","fxSettings"],function(a,b){w[b+"_open"]=n[b+"_open"];w[b+"_close"]=n[b+"_close"];w[b+"_size"]=n[b+"_size"]});y[c]=b(e).data({layoutPane:z[c],layoutEdge:c}).css(k.hidden).css(j.cssReq);U[c]=f?b(f):!1;r[c]=b.extend(!0,{},a.options,w);q[c]=b.extend(!0,{},a.state);e.className=e.className.replace(RegExp(n.paneClass+"-"+g,"g"),n.paneClass+"-"+c);Pa(c);j.dir!=k[g].dir?(e=p[c]||0,Y(c),e=d(e,q[c].minSize),Ca(c,e,!0,!0)):F[c].css(j.side,v.inset[j.side]+
(q[c].isVisible?ga(c):0));a.state.isVisible&&!m.isVisible?Ua(c,!0):(Da(c),ma(c,!0));a=null}}if(H()){var g=A.call(this,a);q[g].edge=c;q[c].edge=g;if(!1===C("onswap_start",g)||!1===C("onswap_start",c))q[g].edge=g,q[c].edge=c;else{var j=f(g),n=f(c),p={};p[g]=j?j.state.size:0;p[c]=n?n.state.size:0;y[g]=!1;y[c]=!1;q[g]={};q[c]={};P[g]&&P[g].remove();P[c]&&P[c].remove();F[g]&&F[g].remove();F[c]&&F[c].remove();F[g]=F[c]=P[g]=P[c]=!1;h(j,c);h(n,g);j=n=p=null;y[g]&&y[g].css(k.visible);y[c]&&y[c].css(k.visible);
oa();C("onswap_end",g);C("onswap_end",c)}}},showMasks:va,hideMasks:za,initContent:Sa,addPane:ib,removePane:Ra,createChildren:Qa,refreshChildren:Ba,enableClosable:pb,disableClosable:function(a,b){if(H()){var c=A.call(this,a),d=P[c];d&&(r[c].closable=!1,q[c].isClosed&&ra(c,!1,!0),d.unbind("."+K).css("visibility",b?"hidden":"visible").css("cursor","default").attr("title",""))}},enableSlidable:function(a){if(H()){a=A.call(this,a);var b=F[a];b&&b.data("draggable")&&(r[a].slidable=!0,q[a].isClosed&&ma(a,
!0))}},disableSlidable:function(a){if(H()){a=A.call(this,a);var b=F[a];b&&(r[a].slidable=!1,q[a].isSliding?ja(a,!1,!0):(ma(a,!1),b.css("cursor","default").attr("title",""),da(null,b[0])))}},enableResizable:function(a){if(H()){a=A.call(this,a);var b=F[a],c=r[a];b&&b.data("draggable")&&(c.resizable=!0,b.draggable("enable"),q[a].isClosed||b.css("cursor",c.resizerCursor).attr("title",c.tips.Resize))}},disableResizable:function(a){if(H()){a=A.call(this,a);var b=F[a];b&&b.data("draggable")&&(r[a].resizable=
!1,b.draggable("disable").css("cursor","default").attr("title",""),da(null,b[0]))}},allowOverflow:x,resetOverflow:X,destroy:function(a,c){b(window).unbind("."+K);b(document).unbind("."+K);"object"===typeof a?A(a):c=a;u.clearQueue().removeData("layout").removeData("layoutContainer").removeClass(r.containerClass).unbind("."+K);ea.remove();b.each(k.allPanes,function(a,b){Ra(b,!1,!0,c)});u.data("layoutCSS")&&!u.data("layoutRole")&&u.css(u.data("layoutCSS")).removeData("layoutCSS");"BODY"===v.tagName&&
(u=b("html")).data("layoutCSS")&&u.css(u.data("layoutCSS")).removeData("layoutCSS");j(z,b.layout.onDestroy);mb();for(var d in z)d.match(/^(container|options)$/)||delete z[d];z.destroyed=!0;return z},initPanes:H,resizeAll:oa,runCallbacks:C,hasParentLayout:!1,children:ba,north:!1,south:!1,west:!1,east:!1,center:!1},Xa;var V,Ya,N,Ha,la,sa,W;h=b.layout.transformData(h,!0);h=b.layout.backwardCompatibility.renameAllOptions(h);if(!b.isEmptyObject(h.panes)){V=b.layout.optionsMap.noDefault;la=0;for(sa=V.length;la<
sa;la++)N=V[la],delete h.panes[N];V=b.layout.optionsMap.layout;la=0;for(sa=V.length;la<sa;la++)N=V[la],delete h.panes[N]}V=b.layout.optionsMap.layout;var Bb=b.layout.config.optionRootKeys;for(N in h)Ha=h[N],0>b.inArray(N,Bb)&&0>b.inArray(N,V)&&(h.panes[N]||(h.panes[N]=b.isPlainObject(Ha)?b.extend(!0,{},Ha):Ha),delete h[N]);b.extend(!0,r,h);b.each(k.allPanes,function(a,c){k[c]=b.extend(!0,{},k.panes,k[c]);Ya=r.panes;W=r[c];if("center"===c){V=b.layout.optionsMap.center;a=0;for(sa=V.length;a<sa;a++)if(N=
V[a],!h.center[N]&&(h.panes[N]||!W[N]))W[N]=Ya[N]}else{W=r[c]=b.extend(!0,{},Ya,W);var d=r[c],f=r.panes;d.fxSettings||(d.fxSettings={});f.fxSettings||(f.fxSettings={});b.each(["_open","_close","_size"],function(a,e){var h="fxName"+e,j="fxSpeed"+e,k="fxSettings"+e,l=d[h]=d[h]||f[h]||d.fxName||f.fxName||"none",p=b.effects&&(b.effects[l]||b.effects.effect&&b.effects.effect[l]);if("none"===l||!r.effects[l]||!p)l=d[h]="none";l=r.effects[l]||{};h=l.all||null;l=l[c]||null;d[j]=d[j]||f[j]||d.fxSpeed||f.fxSpeed||
null;d[k]=b.extend(!0,{},h,l,f.fxSettings,d.fxSettings,f[k],d[k])});delete d.fxName;delete d.fxSpeed;delete d.fxSettings;W.resizerClass||(W.resizerClass="ui-layout-resizer");W.togglerClass||(W.togglerClass="ui-layout-toggler")}W.paneClass||(W.paneClass="ui-layout-pane")});var Ia=h.zIndex,xa=r.zIndexes;0<Ia&&(xa.pane_normal=Ia,xa.content_mask=d(Ia+1,xa.content_mask),xa.resizer_normal=d(Ia+2,xa.resizer_normal));delete r.panes;var Cb=r,tb=q;tb.creatingLayout=!0;j(z,b.layout.onCreate);if(!1===C("onload_start"))Xa=
"cancel";else{var Za=u[0],$=b("html"),ub=v.tagName=Za.tagName,vb=v.id=Za.id,wb=v.className=Za.className,L=r,Ja=L.name,$a={},Ka=u.data("parentLayout"),La=u.data("layoutEdge"),ab=Ka&&La,ta=b.layout.cssNum,bb,aa;v.selector=u.selector.split(".slice")[0];v.ref=(L.name?L.name+" layout / ":"")+ub+(vb?"#"+vb:wb?".["+wb+"]":"");v.isBody="BODY"===ub;!ab&&!v.isBody&&(bb=u.closest("."+b.layout.defaults.panes.paneClass),Ka=bb.data("parentLayout"),La=bb.data("layoutEdge"),ab=Ka&&La);u.data({layout:z,layoutContainer:K}).addClass(L.containerClass);
var xb={destroy:"",initPanes:"",resizeAll:"resizeAll",resize:"resizeAll"};for(Ja in xb)u.bind("layout"+Ja.toLowerCase()+"."+K,z[xb[Ja]||Ja]);ab&&(z.hasParentLayout=!0,Ka.refreshChildren(La,z));u.data("layoutCSS")||(v.isBody?(u.data("layoutCSS",b.extend(D(u,"position,margin,padding,border"),{height:u.css("height"),overflow:u.css("overflow"),overflowX:u.css("overflowX"),overflowY:u.css("overflowY")})),$.data("layoutCSS",b.extend(D($,"padding"),{height:"auto",overflow:$.css("overflow"),overflowX:$.css("overflowX"),
overflowY:$.css("overflowY")}))):u.data("layoutCSS",D(u,"position,margin,padding,border,top,bottom,left,right,width,height,overflow,overflowX,overflowY")));try{$a={overflow:"hidden",overflowX:"hidden",overflowY:"hidden"};u.css($a);L.inset&&!b.isPlainObject(L.inset)&&(aa=parseInt(L.inset,10)||0,L.inset={top:aa,bottom:aa,left:aa,right:aa});if(v.isBody)L.outset?b.isPlainObject(L.outset)||(aa=parseInt(L.outset,10)||0,L.outset={top:aa,bottom:aa,left:aa,right:aa}):L.outset={top:ta($,"paddingTop"),bottom:ta($,
"paddingBottom"),left:ta($,"paddingLeft"),right:ta($,"paddingRight")},$.css($a).css({height:"100%",border:"none",padding:0,margin:0}),G.isIE6?(u.css({width:"100%",height:"100%",border:"none",padding:0,margin:0,position:"relative"}),L.inset||(L.inset=R(u).inset)):(u.css({width:"auto",height:"auto",margin:0,position:"absolute"}),u.css(L.outset)),b.extend(v,R(u,L.inset));else{var yb=u.css("position");(!yb||!yb.match(/(fixed|absolute|relative)/))&&u.css("position","relative");u.is(":visible")&&(b.extend(v,
R(u,L.inset)),1>v.innerHeight&&ca(L.errors.noContainerHeight.replace(/CONTAINER/,v.ref)))}ta(u,"minWidth")&&u.parent().css("overflowX","auto");ta(u,"minHeight")&&u.parent().css("overflowY","auto")}catch(Db){}nb();b(window).bind("unload."+K,mb);j(z,b.layout.onLoad);Cb.initPanes&&Aa();delete tb.creatingLayout;Xa=q.initialized}return"cancel"===Xa?null:z}})(jQuery);
(function(b){b.ui||(b.ui={});b.ui.cookie={acceptsCookies:!!navigator.cookieEnabled,read:function(a){for(var d=document.cookie,d=d?d.split(";"):[],c,f=0,j=d.length;f<j;f++)if(c=b.trim(d[f]).split("="),c[0]==a)return decodeURIComponent(c[1]);return null},write:function(a,d,c){var f="",j="",h=!1;c=c||{};var p=c.expires||null,x=b.type(p);"date"===x?j=p:"string"===x&&0<p&&(p=parseInt(p,10),x="number");"number"===x&&(j=new Date,0<p?j.setDate(j.getDate()+p):(j.setFullYear(1970),h=!0));j&&(f+=";expires="+
j.toUTCString());c.path&&(f+=";path="+c.path);c.domain&&(f+=";domain="+c.domain);c.secure&&(f+=";secure");document.cookie=a+"="+(h?"":encodeURIComponent(d))+f},clear:function(a){b.ui.cookie.write(a,"",{expires:-1})}};b.cookie||(b.cookie=function(a,d,c){var f=b.ui.cookie;if(null===d)f.clear(a);else{if(void 0===d)return f.read(a);f.write(a,d,c)}});b.layout.plugins.stateManagement=!0;b.layout.config.optionRootKeys.push("stateManagement");b.layout.defaults.stateManagement={enabled:!1,autoSave:!0,autoLoad:!0,
animateLoad:!0,includeChildren:!0,stateKeys:"north.size,south.size,east.size,west.size,north.isClosed,south.isClosed,east.isClosed,west.isClosed,north.isHidden,south.isHidden,east.isHidden,west.isHidden",cookie:{name:"",domain:"",path:"",expires:"",secure:!1}};b.layout.optionsMap.layout.push("stateManagement");b.layout.state={saveCookie:function(a,d,c){var f=a.options,j=f.stateManagement;c=b.extend(!0,{},j.cookie,c||null);a=a.state.stateData=a.readState(d||j.stateKeys);b.ui.cookie.write(c.name||f.name||
"Layout",b.layout.state.encodeJSON(a),c);return b.extend(!0,{},a)},deleteCookie:function(a){a=a.options;b.ui.cookie.clear(a.stateManagement.cookie.name||a.name||"Layout")},readCookie:function(a){a=a.options;return(a=b.ui.cookie.read(a.stateManagement.cookie.name||a.name||"Layout"))?b.layout.state.decodeJSON(a):{}},loadCookie:function(a){var d=b.layout.state.readCookie(a);d&&(a.state.stateData=b.extend(!0,{},d),a.loadState(d));return d},loadState:function(a,d,c){if(b.isPlainObject(d)&&!b.isEmptyObject(d))if(d=
a.state.stateData=b.layout.transformData(d),c=b.extend({animateLoad:!1,includeChildren:a.options.stateManagement.includeChildren},c),a.state.initialized){var f=!c.animateLoad,j,h,p,x;b.each(b.layout.config.borderPanes,function(c,G){S=d[G];b.isPlainObject(S)&&(s=S.size,j=S.initClosed,h=S.initHidden,ar=S.autoResize,p=a.state[G],x=p.isVisible,ar&&(p.autoResize=ar),x||a._sizePane(G,s,!1,!1,!1),!0===h?a.hide(G,f):!0===j?a.close(G,!1,f):!1===j?a.open(G,!1,f):!1===h&&a.show(G,!1,f),x&&a._sizePane(G,s,!1,
!1,f))});if(c.includeChildren){var I,T;b.each(a.children,function(a,c){(I=d[a]?d[a].children:0)&&c&&b.each(c,function(a,b){T=I[a];b&&T&&b.loadState(T)})})}}else{var S=b.extend(!0,{},d);b.each(b.layout.config.allPanes,function(a,b){S[b]&&delete S[b].children});b.extend(!0,a.options,S)}},readState:function(a,d){"string"===b.type(d)&&(d={keys:d});d||(d={});var c=a.options.stateManagement,f=d.includeChildren,f=void 0!==f?f:c.includeChildren,c=d.stateKeys||c.stateKeys,j={isClosed:"initClosed",isHidden:"initHidden"},
h=a.state,p=b.layout.config.allPanes,x={},I,T,S,X,G,k;b.isArray(c)&&(c=c.join(","));for(var c=c.replace(/__/g,".").split(","),Q=0,O=c.length;Q<O;Q++)I=c[Q].split("."),T=I[0],I=I[1],0>b.inArray(T,p)||(S=h[T][I],void 0!=S&&("isClosed"==I&&h[T].isSliding&&(S=!0),(x[T]||(x[T]={}))[j[I]?j[I]:I]=S));f&&b.each(p,function(c,d){G=a.children[d];X=h.stateData[d];b.isPlainObject(G)&&!b.isEmptyObject(G)&&(k=x[d]||(x[d]={}),k.children||(k.children={}),b.each(G,function(a,c){c.state.initialized?k.children[a]=b.layout.state.readState(c):
X&&(X.children&&X.children[a])&&(k.children[a]=b.extend(!0,{},X.children[a]))}))});return x},encodeJSON:function(a){function d(a){var f=[],j=0,h,p,x,I=b.isArray(a);for(h in a)p=a[h],x=typeof p,"string"==x?p='"'+p+'"':"object"==x&&(p=d(p)),f[j++]=(!I?'"'+h+'":':"")+p;return(I?"[":"{")+f.join(",")+(I?"]":"}")}return d(a)},decodeJSON:function(a){try{return b.parseJSON?b.parseJSON(a):window.eval("("+a+")")||{}}catch(d){return{}}},_create:function(a){var d=b.layout.state,c=a.options.stateManagement;b.extend(a,
{readCookie:function(){return d.readCookie(a)},deleteCookie:function(){d.deleteCookie(a)},saveCookie:function(b,c){return d.saveCookie(a,b,c)},loadCookie:function(){return d.loadCookie(a)},loadState:function(b,c){d.loadState(a,b,c)},readState:function(b){return d.readState(a,b)},encodeJSON:d.encodeJSON,decodeJSON:d.decodeJSON});a.state.stateData={};if(c.autoLoad)if(b.isPlainObject(c.autoLoad))b.isEmptyObject(c.autoLoad)||a.loadState(c.autoLoad);else if(c.enabled)if(b.isFunction(c.autoLoad)){var f=
{};try{f=c.autoLoad(a,a.state,a.options,a.options.name||"")}catch(j){}f&&(b.isPlainObject(f)&&!b.isEmptyObject(f))&&a.loadState(f)}else a.loadCookie()},_unload:function(a){var d=a.options.stateManagement;if(d.enabled&&d.autoSave)if(b.isFunction(d.autoSave))try{d.autoSave(a,a.state,a.options,a.options.name||"")}catch(c){}else a.saveCookie()}};b.layout.onCreate.push(b.layout.state._create);b.layout.onUnload.push(b.layout.state._unload);b.layout.plugins.buttons=!0;b.layout.defaults.autoBindCustomButtons=
!1;b.layout.optionsMap.layout.push("autoBindCustomButtons");b.layout.buttons={init:function(a){var d=a.options.name||"",c;b.each("toggle open close pin toggle-slide open-slide".split(" "),function(f,j){b.each(b.layout.config.borderPanes,function(f,p){b(".ui-layout-button-"+j+"-"+p).each(function(){c=b(this).data("layoutName")||b(this).attr("layoutName");(void 0==c||c===d)&&a.bindButton(this,j,p)})})})},get:function(a,d,c,f){var j=b(d);a=a.options;var h=a.errors.addButtonError;j.length?0>b.inArray(c,
b.layout.config.borderPanes)?(b.layout.msg(h+" "+a.errors.pane+": "+c,!0),j=b("")):(d=a[c].buttonClass+"-"+f,j.addClass(d+" "+d+"-"+c).data("layoutName",a.name)):b.layout.msg(h+" "+a.errors.selector+": "+d,!0);return j},bind:function(a,d,c,f){var j=b.layout.buttons;switch(c.toLowerCase()){case "toggle":j.addToggle(a,d,f);break;case "open":j.addOpen(a,d,f);break;case "close":j.addClose(a,d,f);break;case "pin":j.addPin(a,d,f);break;case "toggle-slide":j.addToggle(a,d,f,!0);break;case "open-slide":j.addOpen(a,
d,f,!0)}return a},addToggle:function(a,d,c,f){b.layout.buttons.get(a,d,c,"toggle").click(function(b){a.toggle(c,!!f);b.stopPropagation()});return a},addOpen:function(a,d,c,f){b.layout.buttons.get(a,d,c,"open").attr("title",a.options[c].tips.Open).click(function(b){a.open(c,!!f);b.stopPropagation()});return a},addClose:function(a,d,c){b.layout.buttons.get(a,d,c,"close").attr("title",a.options[c].tips.Close).click(function(b){a.close(c);b.stopPropagation()});return a},addPin:function(a,d,c){var f=b.layout.buttons,
j=f.get(a,d,c,"pin");if(j.length){var h=a.state[c];j.click(function(d){f.setPinState(a,b(this),c,h.isSliding||h.isClosed);h.isSliding||h.isClosed?a.open(c):a.close(c);d.stopPropagation()});f.setPinState(a,j,c,!h.isClosed&&!h.isSliding);h.pins.push(d)}return a},setPinState:function(a,b,c,f){var j=b.attr("pin");if(!(j&&f===("down"==j))){a=a.options[c];var j=a.buttonClass+"-pin",h=j+"-"+c;c=j+"-up "+h+"-up";j=j+"-down "+h+"-down";b.attr("pin",f?"down":"up").attr("title",f?a.tips.Unpin:a.tips.Pin).removeClass(f?
c:j).addClass(f?j:c)}},syncPinBtns:function(a,d,c){b.each(a.state[d].pins,function(f,j){b.layout.buttons.setPinState(a,b(j),d,c)})},_load:function(a){var d=b.layout.buttons;b.extend(a,{bindButton:function(b,c,h){return d.bind(a,b,c,h)},addToggleBtn:function(b,c,h){return d.addToggle(a,b,c,h)},addOpenBtn:function(b,c,h){return d.addOpen(a,b,c,h)},addCloseBtn:function(b,c){return d.addClose(a,b,c)},addPinBtn:function(b,c){return d.addPin(a,b,c)}});for(var c=0;4>c;c++)a.state[b.layout.config.borderPanes[c]].pins=
[];a.options.autoBindCustomButtons&&d.init(a)},_unload:function(){}};b.layout.onLoad.push(b.layout.buttons._load);b.layout.plugins.browserZoom=!0;b.layout.defaults.browserZoomCheckInterval=1E3;b.layout.optionsMap.layout.push("browserZoomCheckInterval");b.layout.browserZoom={_init:function(a){!1!==b.layout.browserZoom.ratio()&&b.layout.browserZoom._setTimer(a)},_setTimer:function(a){if(!a.destroyed){var d=a.options,c=a.state,f=a.hasParentLayout?5E3:Math.max(d.browserZoomCheckInterval,100);setTimeout(function(){if(!a.destroyed&&
d.resizeWithWindow){var f=b.layout.browserZoom.ratio();f!==c.browserZoom&&(c.browserZoom=f,a.resizeAll());b.layout.browserZoom._setTimer(a)}},f)}},ratio:function(){function a(a,b){return(100*(parseInt(a,10)/parseInt(b,10))).toFixed()}var d=window,c=screen,f=document,j=f.documentElement||f.body,h=b.layout.browser,p=h.version,x,I,T;return h.msie&&8<p||!h.msie?!1:c.deviceXDPI&&c.systemXDPI?a(c.deviceXDPI,c.systemXDPI):h.webkit&&(x=f.body.getBoundingClientRect)?a(x.left-x.right,f.body.offsetWidth):h.webkit&&
(I=d.outerWidth)?a(I,d.innerWidth):(I=c.width)&&(T=j.clientWidth)?a(I,T):!1}};b.layout.onReady.push(b.layout.browserZoom._init)})(jQuery);
/* Modernizr 2.7.1 (Custom Build) | MIT & BSD
 * Build: http://modernizr.com/download/#-fontface-backgroundsize-borderimage-borderradius-boxshadow-flexbox-flexboxlegacy-hsla-multiplebgs-opacity-rgba-textshadow-cssanimations-csscolumns-generatedcontent-cssgradients-cssreflections-csstransforms-csstransforms3d-csstransitions-applicationcache-canvas-canvastext-draganddrop-hashchange-history-audio-video-indexeddb-input-inputtypes-localstorage-postmessage-sessionstorage-websockets-websqldatabase-webworkers-geolocation-inlinesvg-smil-svg-svgclippaths-touch-webgl-shiv-cssclasses-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-load
 */
;window.Modernizr=function(a,b,c){function C(a){j.cssText=a}function D(a,b){return C(n.join(a+";")+(b||""))}function E(a,b){return typeof a===b}function F(a,b){return!!~(""+a).indexOf(b)}function G(a,b){for(var d in a){var e=a[d];if(!F(e,"-")&&j[e]!==c)return b=="pfx"?e:!0}return!1}function H(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:E(f,"function")?f.bind(d||b):f}return!1}function I(a,b,c){var d=a.charAt(0).toUpperCase()+a.slice(1),e=(a+" "+p.join(d+" ")+d).split(" ");return E(b,"string")||E(b,"undefined")?G(e,b):(e=(a+" "+q.join(d+" ")+d).split(" "),H(e,b,c))}function J(){e.input=function(c){for(var d=0,e=c.length;d<e;d++)u[c[d]]=c[d]in k;return u.list&&(u.list=!!b.createElement("datalist")&&!!a.HTMLDataListElement),u}("autocomplete autofocus list placeholder max min multiple pattern required step".split(" ")),e.inputtypes=function(a){for(var d=0,e,f,h,i=a.length;d<i;d++)k.setAttribute("type",f=a[d]),e=k.type!=="text",e&&(k.value=l,k.style.cssText="position:absolute;visibility:hidden;",/^range$/.test(f)&&k.style.WebkitAppearance!==c?(g.appendChild(k),h=b.defaultView,e=h.getComputedStyle&&h.getComputedStyle(k,null).WebkitAppearance!=="textfield"&&k.offsetHeight!==0,g.removeChild(k)):/^(search|tel)$/.test(f)||(/^(url|email)$/.test(f)?e=k.checkValidity&&k.checkValidity()===!1:e=k.value!=l)),t[a[d]]=!!e;return t}("search tel url email datetime date month week time datetime-local number range color".split(" "))}var d="2.7.1",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k=b.createElement("input"),l=":)",m={}.toString,n=" -webkit- -moz- -o- -ms- ".split(" "),o="Webkit Moz O ms",p=o.split(" "),q=o.toLowerCase().split(" "),r={svg:"http://www.w3.org/2000/svg"},s={},t={},u={},v=[],w=v.slice,x,y=function(a,c,d,e){var f,i,j,k,l=b.createElement("div"),m=b.body,n=m||b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:h+(d+1),l.appendChild(j);return f=["&#173;",'<style id="s',h,'">',a,"</style>"].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},z=function(){function d(d,e){e=e||b.createElement(a[d]||"div"),d="on"+d;var f=d in e;return f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=E(e[d],"function"),E(e[d],"undefined")||(e[d]=c),e.removeAttribute(d))),e=null,f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),A={}.hasOwnProperty,B;!E(A,"undefined")&&!E(A.call,"undefined")?B=function(a,b){return A.call(a,b)}:B=function(a,b){return b in a&&E(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=w.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(w.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(w.call(arguments)))};return e}),s.flexbox=function(){return I("flexWrap")},s.flexboxlegacy=function(){return I("boxDirection")},s.canvas=function(){var a=b.createElement("canvas");return!!a.getContext&&!!a.getContext("2d")},s.canvastext=function(){return!!e.canvas&&!!E(b.createElement("canvas").getContext("2d").fillText,"function")},s.webgl=function(){return!!a.WebGLRenderingContext},s.touch=function(){var c;return"ontouchstart"in a||a.DocumentTouch&&b instanceof DocumentTouch?c=!0:y(["@media (",n.join("touch-enabled),("),h,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(a){c=a.offsetTop===9}),c},s.geolocation=function(){return"geolocation"in navigator},s.postmessage=function(){return!!a.postMessage},s.websqldatabase=function(){return!!a.openDatabase},s.indexedDB=function(){return!!I("indexedDB",a)},s.hashchange=function(){return z("hashchange",a)&&(b.documentMode===c||b.documentMode>7)},s.history=function(){return!!a.history&&!!history.pushState},s.draganddrop=function(){var a=b.createElement("div");return"draggable"in a||"ondragstart"in a&&"ondrop"in a},s.websockets=function(){return"WebSocket"in a||"MozWebSocket"in a},s.rgba=function(){return C("background-color:rgba(150,255,150,.5)"),F(j.backgroundColor,"rgba")},s.hsla=function(){return C("background-color:hsla(120,40%,100%,.5)"),F(j.backgroundColor,"rgba")||F(j.backgroundColor,"hsla")},s.multiplebgs=function(){return C("background:url(https://),url(https://),red url(https://)"),/(url\s*\(.*?){3}/.test(j.background)},s.backgroundsize=function(){return I("backgroundSize")},s.borderimage=function(){return I("borderImage")},s.borderradius=function(){return I("borderRadius")},s.boxshadow=function(){return I("boxShadow")},s.textshadow=function(){return b.createElement("div").style.textShadow===""},s.opacity=function(){return D("opacity:.55"),/^0.55$/.test(j.opacity)},s.cssanimations=function(){return I("animationName")},s.csscolumns=function(){return I("columnCount")},s.cssgradients=function(){var a="background-image:",b="gradient(linear,left top,right bottom,from(#9f9),to(white));",c="linear-gradient(left top,#9f9, white);";return C((a+"-webkit- ".split(" ").join(b+a)+n.join(c+a)).slice(0,-a.length)),F(j.backgroundImage,"gradient")},s.cssreflections=function(){return I("boxReflect")},s.csstransforms=function(){return!!I("transform")},s.csstransforms3d=function(){var a=!!I("perspective");return a&&"webkitPerspective"in g.style&&y("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(b,c){a=b.offsetLeft===9&&b.offsetHeight===3}),a},s.csstransitions=function(){return I("transition")},s.fontface=function(){var a;return y('@font-face {font-family:"font";src:url("https://")}',function(c,d){var e=b.getElementById("smodernizr"),f=e.sheet||e.styleSheet,g=f?f.cssRules&&f.cssRules[0]?f.cssRules[0].cssText:f.cssText||"":"";a=/src/i.test(g)&&g.indexOf(d.split(" ")[0])===0}),a},s.generatedcontent=function(){var a;return y(["#",h,"{font:0/0 a}#",h,':after{content:"',l,'";visibility:hidden;font:3px/1 a}'].join(""),function(b){a=b.offsetHeight>=3}),a},s.video=function(){var a=b.createElement("video"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),c.h264=a.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),c.webm=a.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,"")}catch(d){}return c},s.audio=function(){var a=b.createElement("audio"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),c.mp3=a.canPlayType("audio/mpeg;").replace(/^no$/,""),c.wav=a.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),c.m4a=(a.canPlayType("audio/x-m4a;")||a.canPlayType("audio/aac;")).replace(/^no$/,"")}catch(d){}return c},s.localstorage=function(){try{return localStorage.setItem(h,h),localStorage.removeItem(h),!0}catch(a){return!1}},s.sessionstorage=function(){try{return sessionStorage.setItem(h,h),sessionStorage.removeItem(h),!0}catch(a){return!1}},s.webworkers=function(){return!!a.Worker},s.applicationcache=function(){return!!a.applicationCache},s.svg=function(){return!!b.createElementNS&&!!b.createElementNS(r.svg,"svg").createSVGRect},s.inlinesvg=function(){var a=b.createElement("div");return a.innerHTML="<svg/>",(a.firstChild&&a.firstChild.namespaceURI)==r.svg},s.smil=function(){return!!b.createElementNS&&/SVGAnimate/.test(m.call(b.createElementNS(r.svg,"animate")))},s.svgclippaths=function(){return!!b.createElementNS&&/SVGClipPath/.test(m.call(b.createElementNS(r.svg,"clipPath")))};for(var K in s)B(s,K)&&(x=K.toLowerCase(),e[x]=s[K](),v.push((e[x]?"":"no-")+x));return e.input||J(),e.addTest=function(a,b){if(typeof a=="object")for(var d in a)B(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},C(""),i=k=null,function(a,b){function l(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x<style>"+b+"</style>",d.insertBefore(c.lastChild,d.firstChild)}function m(){var a=s.elements;return typeof a=="string"?a.split(" "):a}function n(a){var b=j[a[h]];return b||(b={},i++,a[h]=i,j[i]=b),b}function o(a,c,d){c||(c=b);if(k)return c.createElement(a);d||(d=n(c));var g;return d.cache[a]?g=d.cache[a].cloneNode():f.test(a)?g=(d.cache[a]=d.createElem(a)).cloneNode():g=d.createElem(a),g.canHaveChildren&&!e.test(a)&&!g.tagUrn?d.frag.appendChild(g):g}function p(a,c){a||(a=b);if(k)return a.createDocumentFragment();c=c||n(a);var d=c.frag.cloneNode(),e=0,f=m(),g=f.length;for(;e<g;e++)d.createElement(f[e]);return d}function q(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return s.shivMethods?o(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/[\w\-]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(s,b.frag)}function r(a){a||(a=b);var c=n(a);return s.shivCSS&&!g&&!c.hasCSS&&(c.hasCSS=!!l(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),k||q(a,c),a}var c="3.7.0",d=a.html5||{},e=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,f=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,g,h="_html5shiv",i=0,j={},k;(function(){try{var a=b.createElement("a");a.innerHTML="<xyz></xyz>",g="hidden"in a,k=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){g=!0,k=!0}})();var s={elements:d.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:c,shivCSS:d.shivCSS!==!1,supportsUnknownElements:k,shivMethods:d.shivMethods!==!1,type:"default",shivDocument:r,createElement:o,createDocumentFragment:p};a.html5=s,r(b)}(this,b),e._version=d,e._prefixes=n,e._domPrefixes=q,e._cssomPrefixes=p,e.hasEvent=z,e.testProp=function(a){return G([a])},e.testAllProps=I,e.testStyles=y,g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+v.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f<d;f++)g=a[f].split("="),(e=z[g.shift()])&&(c=e(c,g));for(f=0;f<b;f++)c=x[f](c);return c}function g(a,e,f,g,h){var i=b(a),j=i.autoCallback;i.url.split(".").pop().split("?").shift(),i.bypass||(e&&(e=d(e)?e:e[a]||e[g]||e[a.split("/").pop().split("?")[0]]),i.instead?i.instead(a,e,f,g,h):(y[i.url]?i.noexec=!0:y[i.url]=1,f.load(i.url,i.forceCSS||!i.forceJS&&"css"==i.url.split(".").pop().split("?").shift()?"c":c,i.noexec,i.attrs,i.timeout),(d(e)||d(j))&&f.load(function(){k(),e&&e(i.origUrl,h,g),j&&j(i.origUrl,h,g),y[i.url]=2})))}function h(a,b){function c(a,c){if(a){if(e(a))c||(j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}),g(a,j,b,0,h);else if(Object(a)===a)for(n in m=function(){var b=0,c;for(c in a)a.hasOwnProperty(c)&&b++;return b}(),a)a.hasOwnProperty(n)&&(!c&&!--m&&(d(j)?j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}:j[n]=function(a){return function(){var b=[].slice.call(arguments);a&&a.apply(this,b),l()}}(k[n])),g(a[n],j,b,n,h))}else!c&&l()}var h=!!a.test,i=a.load||a.both,j=a.callback||f,k=j,l=a.complete||f,m,n;c(h?a.yep:a.nope,!!i),i&&c(i)}var i,j,l=this.yepnope.loader;if(e(a))g(a,0,l,0);else if(w(a))for(i=0;i<a.length;i++)j=a[i],e(j)?g(j,0,l,0):w(j)?B(j):Object(j)===j&&h(j,l);else Object(a)===a&&h(a,l)},B.addPrefix=function(a,b){z[a]=b},B.addFilter=function(a){x.push(a)},B.errorTimeout=1e4,null==b.readyState&&b.addEventListener&&(b.readyState="loading",b.addEventListener("DOMContentLoaded",A=function(){b.removeEventListener("DOMContentLoaded",A,0),b.readyState="complete"},0)),a.yepnope=k(),a.yepnope.executeStack=h,a.yepnope.injectJs=function(a,c,d,e,i,j){var k=b.createElement("script"),l,o,e=e||B.errorTimeout;k.src=a;for(o in d)k.setAttribute(o,d[o]);c=j?h:c||f,k.onreadystatechange=k.onload=function(){!l&&g(k.readyState)&&(l=1,c(),k.onload=k.onreadystatechange=null)},m(function(){l||(l=1,c(1))},e),i?k.onload():n.parentNode.insertBefore(k,n)},a.yepnope.injectCss=function(a,c,d,e,g,i){var e=b.createElement("link"),j,c=i?h:c||f;e.href=a,e.rel="stylesheet",e.type="text/css";for(j in d)e.setAttribute(j,d[j]);g||(n.parentNode.insertBefore(e,n),m(c,0))}}(this,document),Modernizr.load=function(){yepnope.apply(window,[].slice.call(arguments,0))};
var RCBase64={keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t,r,s,o,i,n,a,h="",c=0;for(e=this.utf8_encode(e);c<e.length;)t=e.charCodeAt(c++),r=e.charCodeAt(c++),s=e.charCodeAt(c++),o=t>>2,i=(3&t)<<4|r>>4,n=(15&r)<<2|s>>6,a=63&s,isNaN(r)?n=a=64:isNaN(s)&&(a=64),h=h+this.keyStr.charAt(o)+this.keyStr.charAt(i)+this.keyStr.charAt(n)+this.keyStr.charAt(a);return h},decode:function(e){var t,r,s,o,i,n,a,h="",c=0;for(e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");c<e.length;)o=this.keyStr.indexOf(e.charAt(c++)),i=this.keyStr.indexOf(e.charAt(c++)),n=this.keyStr.indexOf(e.charAt(c++)),a=this.keyStr.indexOf(e.charAt(c++)),t=o<<2|i>>4,r=(15&i)<<4|n>>2,s=(3&n)<<6|a,h+=String.fromCharCode(t),64!==n&&(h+=String.fromCharCode(r)),64!==a&&(h+=String.fromCharCode(s));return h=this.utf8_decode(h)},utf8_encode:function(e){e=e.replace(/\r\n/g,"\n");var t,r,s="";for(t=0;t<e.length;t++)r=e.charCodeAt(t),128>r?s+=String.fromCharCode(r):r>127&&2048>r?(s+=String.fromCharCode(r>>6|192),s+=String.fromCharCode(63&r|128)):(s+=String.fromCharCode(r>>12|224),s+=String.fromCharCode(r>>6&63|128),s+=String.fromCharCode(63&r|128));return s},utf8_decode:function(e){for(var t="",r=0,s=0,o=0,i=0;r<e.length;)s=e.charCodeAt(r),128>s?(t+=String.fromCharCode(s),r++):s>191&&224>s?(o=e.charCodeAt(r+1),t+=String.fromCharCode((31&s)<<6|63&o),r+=2):(o=e.charCodeAt(r+1),i=e.charCodeAt(r+2),t+=String.fromCharCode((15&s)<<12|(63&o)<<6|63&i),r+=3);return t}},RestClient;if(RestClient=function(){this.VERSION="0.1.9\n",this.authorization={},this.server={},this.response={},this.headers={},this.accessToken={},this.autoUseRefreshToken=!0,this.autoStoreAccessToken=!0,this.authorizationType="none",this.contentType="application/json",this.sendOAuthBearerAuthorization=!1,this.dataType="json",this.oauth2NeedsAuthorization=!0,this.expiredAccessTokenMessage="",this.restfulBehavior=!0,this.backupAJAXURL=null,this.acceptType=null,this.RESTMethods={create:"POST",read:"GET",update:"PUT","delete":"DELETE"},this.OAUTH2GrantTypes={code:"authorization_code",implicit:"token",user:"password",client:"client_credentials",refresh:"refresh_token"},RestClient.prototype.initObject.call(this)},RestClient.prototype.HTTP_SUCCESS=["200","201","202","204","207"],RestClient.prototype.HTTP_BAD_REQUEST=400,RestClient.prototype.HTTP_UNAUTHORIZED=401,RestClient.prototype.OAUTH2_INVALID_GRANT="invalid_grant",RestClient.prototype.initObject=function(){this.authorization={},this.server={},this.response={},this.headers={},this.accessToken={},this.autoUseRefreshToken=!0,this.autoStoreAccessToken=!0,this.authorizationType="none",this.contentType="application/json",this.acceptType="application/json",this.sendOAuthBearerAuthorization=!1,this.oauth2NeedsAuthorization=!0,this.dataType="json",this.expiredAccessTokenMessage="The access token provided has expired."},RestClient.prototype.setUseRefreshTokenAutomatically=function(e){return _.isBoolean(e)&&(this.autoUseRefreshToken=e),this},RestClient.prototype.setStoreAccessTokenAutomatically=function(e){return _.isBoolean(e)&&(this.autoStoreAccessToken=e),this},RestClient.prototype.setAuthorizationType=function(e){var t={none:1,basic:1,oauth2:1};return t[e]&&(this.authorizationType=e),this},RestClient.prototype.setContentType=function(e){return this.contentType=e,this},RestClient.prototype.setAcceptType=function(e){var t={plain:"text/plain",xhtml_xml:"application/xhtml+xml",json:"application/json",xml:"application/xml",all:"*/*"};return t[e]&&(this.acceptType=t[e]),this},RestClient.prototype.setSendBearerAuthorization=function(e){return _.isBoolean(e)&&(this.sendOAuthBearerAuthorization=e),this},RestClient.prototype.setOAuth2NeedsAuthorization=function(e){return _.isBoolean(e)&&(this.oauth2NeedsAuthorization=e),this},RestClient.prototype.setDataType=function(e){var t={json:"application/json",plain:"text/plain",form:"application/x-www-form-urlencoded",html:"text/html"};return t[e]&&(this.dataType=e,this.contentType=t[e]),this},RestClient.prototype.setAccessTokenExpiredMessage=function(e){return this.expiredAccessTokenMessage=e,this},RestClient.prototype.getVersion=function(){return this.VERSION},RestClient.prototype.setClient=function(e,t,r){return this.authorization.client_id=e,this.authorization.client_secret=t,this.authorization.client_url="undefined"!==r?r:null,this},RestClient.prototype.setGrantType=function(e,t){return this.authorization.grant_type="undefined"!==this.OAUTH2GrantTypes[e]?this.OAUTH2GrantTypes[e]:null,this.authorization=_.extend(this.authorization,t),this},RestClient.prototype.setAuthorizationServer=function(e){var t,r=!0;return"undefined"==typeof e||null===e?r=!1:(t=/(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/,e.match(t)?this.server.rest_auth_uri=e:r=!1),r},RestClient.prototype.setHeader=function(e,t){var r,s=!0;return e&&t?(r=JSON.parse('{"'+e+'" :  "'+t+'"}'),this.headers=_.extend(this.headers,r)):s=!1,s},RestClient.prototype.JSONParse=function(e){var t;try{t=e?""===e?"":JSON.parse(e):""}catch(r){t="ERROR_PARSE"}return t},RestClient.prototype.setBasicCredentials=function(e,t){return this.authorization.basic_user=e,this.authorization.basic_password=t,this},RestClient.prototype.setAccessToken=function(e){return"object"==typeof e&&(this.accessToken=e),this},RestClient.prototype.setRestfulBehavior=function(e){return _.isBoolean(e)&&(this.restfulBehavior=e),this},RestClient.prototype.setBackupAjaxUrl=function(e){return this.backupAJAXURL=e,this},RestClient.prototype.toParams=function(e){var t=_.keys(e),r=[];return _.each(t,function(t){r.push(t+"="+e[t])}),r.join("&")},RestClient.prototype.prepareBody=function(e){var t="";return"json"===this.dataType||"jsonp"===this.dataType?"object"==typeof e&&(t=JSON.stringify(e)):t=this.toParams(e),t},RestClient.prototype.createXHR=function(){var e;if(window.XMLHttpRequest)e=new XMLHttpRequest;else try{e=new ActiveXObject("MSXML2.XMLHTTP")}catch(t){try{e=new ActiveXObject("Microsoft.XMLHTTP")}catch(r){}}return e?e:!1},RestClient.prototype.authorize=function(e){var t,r,s,o,i=this,n=!1,a="create",h=this.RESTMethods[a];t=RCBase64.encode(this.authorization.client_id+":"+this.authorization.client_secret),r=this.createXHR();try{r.open(h,this.server.rest_auth_uri,!1)}catch(c){return e.xhrfailure?e.xhrfailure(c,{}):this.XHRFailure(c,{}),n}if(r.onreadystatechange=function(){if(e.ready?e.ready(r):i.AuthorizeReady(r),4===r.readyState){if(o=i.JSONParse(r.responseText),-1!=i.HTTP_SUCCESS.indexOf(String(r.status))&&"ERROR_PARSE"!==o)if(i.autoStoreAccessToken&&(i.accessToken=o.token||{}),n=!0,e.success)try{e.success(r,o)}catch(t){throw new Error(t.message)}else i.AuthorizeSuccess(r,o);else if("ERROR_PARSE"===o&&(o={success:!1,error:{error:i.HTTP_BAD_REQUEST,error_description:"Response is not a valid JSON"}}),e.failure)try{e.failure(r,o)}catch(t){throw new Error(t.message)}else i.AuthorizeFailure(r,o);"function"==typeof e.complete&&e.complete(r,o)}},s={},this.authorization.grant_type)switch(s.grant_type=this.authorization.grant_type,this.authorization.grant_type){case"authorization_code":s.code=this.authorization.code;break;case"token":s.token=this.authorization.token;break;case"password":s.username=this.authorization.username,s.password=this.authorization.password;break;case"client_credentials":s.client_id=this.authorization.client_id,s.client_id=this.authorization.client_secret;break;case"refresh_token":s.refresh_token=this.authorization.refresh_token}return this.oauth2NeedsAuthorization&&r.setRequestHeader("Authorization","Basic "+t),r.setRequestHeader("Accept",this.acceptType),r.setRequestHeader("Content-Type",this.contentType),_.each(this.headers,function(e,t){r.setRequestHeader(t,e)}),r.send(this.prepareBody(s)),n},RestClient.prototype.prepareReqFields=function(e){var t;return t={success:!1,error:{error:this.HTTP_BAD_REQUEST,error_description:"Required fields not found"},fields:e}},RestClient.prototype.prepareConsumeUrl=function(e,t,r,s){var o,i,n=this.acceptType,a=this.contentType,h=!1;if(this.restfulBehavior)switch(e){case"read":o=t,r&&(o+=r),"oauth2"!==this.authorizationType||this.sendOAuthBearerAuthorization||(h=!0,o+="?access_token="+this.accessToken.access_token),s&&s!=={}&&(o+=h?"&":"?",o+=this.toParams(s)),i=null,null===a&&(a="application/json");break;case"create":o=t,i=s||{},"oauth2"!==this.authorizationType||this.sendOAuthBearerAuthorization||(i.access_token=this.accessToken.access_token),i=this.prepareBody(i);break;case"update":o=t,r&&(o+=r),i=s||{},"oauth2"!==this.authorizationType||this.sendOAuthBearerAuthorization||(i.access_token=this.accessToken.access_token),i=this.prepareBody(i);break;case"delete":o=t,r&&(o+=r),i=s||{},"oauth2"!==this.authorizationType||this.sendOAuthBearerAuthorization||(i.access_token=this.accessToken.access_token),i=this.prepareBody(i)}else o=this.backupAJAXURL,i={operation:e,url:t,id:r,data:s},i="data='"+encodeURIComponent(JSON.stringify(i))+"'",null===a&&(a="application/json");return n||(n="*/*"),{url:o,body:i,content_type:a,acceptType:n}},RestClient.prototype.getCall=function(e){return e.operation="read",this.consume(e)},RestClient.prototype.postCall=function(e){return e.operation="create",this.consume(e)},RestClient.prototype.putCall=function(e){return e.operation="update",this.consume(e)},RestClient.prototype.deleteCall=function(e){return e.operation="delete",this.consume(e)},RestClient.prototype.consume=function(e){var t,r,s,o,i,n,a,h,c,u,p,l,d,f,y,R={},T=!0,C=[],A=!1;if(e.operation?s=e.operation:(T=!1,C.push("operation")),e.url?c=e.url:(T=!1,C.push("url")),u=e.data||null,p=e.id||null,!T)return e.failure?e.failure(null,this.prepareReqFields(C)):this.ConsumeFailure(null,this.prepareReqFields(C)),T;l=this.prepareConsumeUrl(s,c,p,u),n=l.url,i=l.body,f=l.content_type,y=l.acceptType,r=this.createXHR(),o=this.restfulBehavior?this.RESTMethods[s]:this.RESTMethods.create;try{switch(r.open(o,n,!1),this.authorizationType){case"none":break;case"basic":t=RCBase64.encode(this.authorization.basic_user+":"+this.authorization.basic_password),r.setRequestHeader("Authorization","Basic "+t);break;case"oauth2":if(!this.accessToken.access_token)return T=!1,C.push("access_token"),h={success:!1,error:{error:this.HTTP_BAD_REQUEST,error_description:"Access Token not defined"}},e.failure?e.failure(null,this.prepareReqFields(C)):this.ConsumeFailure(null,this.prepareReqFields(C)),T;this.sendOAuthBearerAuthorization&&(d="Bearer "+this.accessToken.access_token,r.setRequestHeader("Authorization",d))}}catch(k){return e.xhrfailure?e.xhrfailure(k,u):this.XHRFailure(k,u),!1}return a=this,r.onreadystatechange=function(){if(e.ready?e.ready(r):a.ConsumeReady(r),4===r.readyState){if(R=a.JSONParse(r.responseText),-1!=a.HTTP_SUCCESS.indexOf(String(r.status))&&"ERROR_PARSE"!==R)if(a.autoStoreAccessToken&&(a.accessToken=R.token||{}),T=!0,e.success)try{e.success(r,R)}catch(t){throw new Error(t.message)}else a.AuthorizeSuccess(r,R);else if(T=!1,"ERROR_PARSE"===R?R={success:!1,error:{error:a.HTTP_BAD_REQUEST,error_description:"Response is not a valid JSON"}}:(R.error===a.OAUTH2_INVALID_GRANT&&R.error_description===a.expiredAccessTokenMessage&&(A=!0),r.status===a.HTTP_UNAUTHORIZED&&a.autoUseRefreshToken&&A&&(a.accessToken.refresh_token?(a.setGrantType("refresh",{refresh_token:a.accessToken.refresh_token}),a.authorize({success:function(){T=a.consume(e),T&&(e.autorefresh?e.autorefresh(a.accessToken):a.AuthorizeAutoRefresh(a.accessToken))},failure:function(t,r){T=!1,e.failure?e.failure(null,r):a.ConsumeFailure(null,r)}})):(T=!1,R={success:!1,error:{error:a.HTTP_UNAUTHORIZED,error_description:"Refresh token is not defined"}}))),e.failure)try{e.failure(r,R)}catch(t){throw new Error(t.message)}else a.AuthorizeFailure(r,R);"function"==typeof e.complete&&e.complete(r,R)}},r.setRequestHeader("Accept",y),r.setRequestHeader("Content-Type",f),_.each(this.headers,function(e,t){r.setRequestHeader(t,e)}),r.send(i),T},RestClient.prototype.XHRFailure=function(){},RestClient.prototype.AuthorizeSuccess=function(){},RestClient.prototype.AuthorizeFailure=function(){},RestClient.prototype.AuthorizeReady=function(){},RestClient.prototype.AuthorizeAutoRefresh=function(){},RestClient.prototype.ConsumeSuccess=function(){},RestClient.prototype.ConsumeFailure=function(){},RestClient.prototype.ConsumeReady=function(){},"undefined"!=typeof exports){module.exports={RestClient:RestClient,RCBase64:RCBase64};var _=require("underscore")}
/**
 * @class PMUI
 * Base class PMUI
 * @singleton
 */
var PMUI = {},
    getUsersOS;
/**
 * Detect the users' OS
 * @return {string}
 */
getUsersOS = function () {
    var userAgent = window.navigator.userAgent,
        platform = window.navigator.platform,
        macOsPlatforms = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'],
        windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE'],
        iosPlatforms = ['iPhone', 'iPad', 'iPod'],
        os = null;

    if (macOsPlatforms.indexOf(platform) !== -1) {
        os = 'MacOS';
    } else if (iosPlatforms.indexOf(platform) !== -1) {
        os = 'iOS';
    } else if (windowsPlatforms.indexOf(platform) !== -1) {
        os = 'Windows';
    } else if (/Android/.test(userAgent)) {
        os = 'Android';
    } else if (!os && /Linux/.test(platform)) {
        os = 'Linux';
    }
    return os;
};

PMUI.version = '0.1.1';
PMUI.isCtrl = false;
PMUI.isAlt = false;
PMUI.isShift = false;
PMUI.metaKey = false;
PMUI.activeCanvas = null;
PMUI.currentContextMenu = null;
PMUI.keyCodeF5 = 116;
PMUI.keyDown = false;
PMUI.isDelete = false;
PMUI.OS = getUsersOS();

$(document).keydown(function (e) {
    var elementSelected,
        flowSelected;
    if (PMUI.activeCanvas) {
        elementSelected = PMUI.activeCanvas.getCurrentSelection();
        flowSelected = PMUI.activeCanvas.getCurrentConnection();
        switch (e.which) {
            case 8: //BACKSPACE
                if (PMUI.metaKey && PMUI.OS === "MacOS" && !PMUI.activeCanvas.readOnly &&
                    (elementSelected.asArray().length !== 0 || flowSelected !== null) && !PMUI.isDelete) {
                    if (PMUI.activeCanvas && !PMUI.activeCanvas.currentLabel) {
                        PMUI.isDelete = true;
                        PMUI.activeCanvas.removeElements();
                    }
                }
                break;
            case 16: // SHIFT KEY
                PMUI.isShift = true;
                break;
            case 17: // CTRL KEY
                if (!PMUI.isAlt) {
                    PMUI.isCtrl = true;
                } else if (PMUI.OS !== "MacOS") {
                    PMUI.isAlt = false;
                    PMUI.isCtrl = false;
                }
                break;
            case 18: //ALT KEY
                if (!PMUI.isCtrl) {
                    PMUI.isAlt = true;
                } else if (PMUI.OS !== "MacOS") {
                    PMUI.isCtrl = false;
                    PMUI.isAlt = false;
                }
                break;
            case 116: // F5 KEY
                e.preventDefault();
                window.location.reload(true);
                break;
            case 91: //meta key - window key - command key
                PMUI.metaKey = true;
                break;
            case 37:
                // Left
                if (!PMUI.activeCanvas.currentLabel && !PMUI.isDelete) {
                    e.preventDefault();
                    if (!PMUI.getCoordinatesElement(elementSelected.asArray(), "LEFT")) {
                        PMUI.activeCanvas.moveElements(PMUI.activeCanvas, 'LEFT');
                    }
                }
                break;
            case 38:
                // Top
                if (!PMUI.activeCanvas.currentLabel && !PMUI.isDelete) {
                    e.preventDefault();
                    if (!PMUI.getCoordinatesElement(elementSelected.asArray(), "TOP")) {
                        PMUI.activeCanvas.moveElements(PMUI.activeCanvas, 'TOP');
                    }
                }
                break;
            case 39:
                // Right
                if (!PMUI.activeCanvas.currentLabel && !PMUI.isDelete) {
                    e.preventDefault();
                    if (!PMUI.getCoordinatesElement(elementSelected.asArray(), "RIGHT")) {
                        PMUI.activeCanvas.moveElements(PMUI.activeCanvas, 'RIGHT');
                    }
                }
                break;
            case 40:
                // Bottom
                if (!PMUI.activeCanvas.currentLabel && !PMUI.isDelete) {
                    e.preventDefault();
                    if (!PMUI.getCoordinatesElement(elementSelected.asArray(), "BOTTOM")) {
                        PMUI.activeCanvas.moveElements(PMUI.activeCanvas, 'BOTTOM');
                    }
                }
                break;
            case 67:    // char 'c'
                if (!PMUI.activeCanvas.currentLabel && PMUI.isCtrl) {
                    if (PMUI.activeCanvas.copyAndPaste) {
                        e.preventDefault();
                        PMUI.activeCanvas.copy();
                    }
                }
                break;
            case 86:    // char 'v'
                if (!PMUI.activeCanvas.currentLabel && PMUI.isCtrl) {
                    if (PMUI.activeCanvas.copyAndPaste) {
                        e.preventDefault();
                        PMUI.activeCanvas.paste();
                    }
                }
                break;
            case 90:    // char 'z'
                if ((PMUI.isCtrl && PMUI.OS !== "MacOS") || (PMUI.metaKey && PMUI.OS === "MacOS")
                    && !PMUI.activeCanvas.readOnly) {
                    if (PMUI.isShift) {
                        // ctrl + shift + z (redo)
                        PMUI.activeCanvas.redo();
                        e.preventDefault();
                    } else {
                        // ctrl + z (undo)
                        PMUI.activeCanvas.undo();
                        e.preventDefault();
                    }
                }
                break;
            case 46: //Delete Key - Don't go to default.
                if (!PMUI.isCtrl && !PMUI.isAlt && !PMUI.metaKey && PMUI.activeCanvas && !PMUI.keyDown &&
                    !PMUI.activeCanvas.currentLabel && !PMUI.activeCanvas.readOnly && !PMUI.isDelete &&
                    (elementSelected.asArray().length !== 0 || flowSelected !== null)) {
                    PMUI.isDelete = true;
                    PMUI.activeCanvas.removeElements();
                }
                break;
            default:
                PMUI.keyDown = true;
                break;
        }
    }
}).keypress(function (e) {

}).keyup(function (e) {
    var current;
    if (PMUI.activeCanvas) {
        e.preventDefault();
        switch (e.which) {
            case 8: //BACKSPACE
                if (PMUI.metaKey && PMUI.OS === "MacOS" && !PMUI.activeCanvas.readOnly) {
                    if (PMUI.activeCanvas && !PMUI.activeCanvas.currentLabel) {
                        PMUI.activeCanvas.removeElements();
                    }
                }
                break;
            case 13: // ENTER
                if (PMUI.activeCanvas && PMUI.activeCanvas.currentLabel) {
                    PMUI.activeCanvas.currentLabel.loseFocus();
                }
                PMUI.keyDown = false;
                break;
            case 91: // META KEY
                PMUI.metaKey = false;
                break;
            case 16: // SHIFT KEY
                PMUI.isShift = false;
                break;
            case 17: //CTRL KEY
                PMUI.isCtrl = false;
                break;
            case 18: //ALT KEY
                PMUI.isAlt = false;
                break;
            case 46:
                //PMUI.isDelete = false;
                break;
            case 113: //F2 KEY
                if (PMUI.activeCanvas &&
                    PMUI.activeCanvas.getCurrentSelection().getLast() !== null) {
                    //Run other code here when the element
                    // 'CurElement' is deleted
                    current = PMUI.activeCanvas.getCurrentSelection().getLast();
                    if (current !== undefined && current.label.html !== null) {
                        $(current.label.html).dblclick();
                        $(current.label.text.html).focus();
                    }
                }
                break;
            default:
                PMUI.keyDown = false;
                break;
        }
    }

});


/**
 * Extends the PMUI namespace with the given `path` and making a pointer
 * from `path` to the given `class` (note that the `path`'s last token will be the pointer visible from outside
 * the definition of the class).
 *
 *      // e.g.
 *      // let's define a class inside an anonymous function
 *      // so that the global scope is not polluted
 *      (function () {
 *          var Canvas = function () {...};
 *
 *          // let's extend the namespace
 *          PMUI.extendNamespace('PMUI.core.Panel', Canvas);
 *
 *      }());
 *
 *      // now PMDraw.draw.Canvas is a pointer to the class defined above
 *
 *  Another example:
 *
 *      // let's define a class inside an anonymous function
 *      // so that the global scope is not polluted
 *      (function () {
 *          var Shape = function () {...};
 *
 *          // let's extend the namespace
 *          PMUI.extendNamespace('PMUI.draw.RandomName', Shape);
 *
 *      }());
 *
 *      // now PMUI.draw.RandomName is a pointer to the class Shape
 *      // note that this class can only be accessed through this pointer
 *
 * @param {string} path
 * @param {Object} newClass
 * @return {Object} The argument `newClass`
 */
PMUI.extendNamespace = function (path, newClass) {
    var current,
        pathArray,
        extension,
        i;

    if (arguments.length !== 2) {
        throw new Error("PMUI.extendNamespace(): method needs 2 arguments");
    }

    pathArray = path.split('.');
    if (pathArray[0] === 'PMUI') {
        pathArray = pathArray.slice(1);
    }
    current = PMUI;

    // create the 'path' namespace
    for (i = 0; i < pathArray.length - 1; i += 1) {
        extension = pathArray[i];
        if (typeof current[extension] === 'undefined') {
            current[extension] = {};
        }
        current = current[extension];
    }

    extension = pathArray[pathArray.length - 1];
    if (current[extension]) {
    }
    current[extension] = newClass;
    return newClass;
};
/**
 * Checks if `path` (a string separated with dots) is a valid path inside the `from` object if provided otherwise
 * checks if `path` is a valid path inside the {@link PMUI} object,
 * if so then returns a pointer to the object which is the last token of the string
 *
 *      // e.g
 *      validPath('PMDraw.event.Keyboard.modifiers.alt');    // returns a pointer to alt
 *      validPath('modifiers.alt', PMUI.event.Keyboard);   // returns a pointer to alt
 *
 * @param {string} path
 * @param {Object} [from]
 * @return {Object}
 */
PMUI.validPath = function (path, from) {
    var pathArray = path.split('.'),
        current,
        extension,
        i;

    if (!from) {
        if (pathArray[0] === 'PMUI') {
            pathArray = pathArray.slice(1);
        }
        current = PMUI;
    } else {
        current = from;
    }
    for (i = 0; i < pathArray.length; i += 1) {
        extension = pathArray[i];
        if (!current[extension]) {
            return null;
        }
        current = current[extension];
    }
    return current;
};

/**
 * Creates an object whose [[Prototype]] link points to an object's prototype (the object is gathered using the
 * argument `path` and it's the last token in the string), since `subClass` is given it will also mimic the
 * creation of the property `constructor` and a pointer to its parent called `superclass`:
 *
 *      // constructor pointer
 *      subClass.prototype.constructor === subClass       // true
 *
 *      // let's assume that superClass is the last token in the string 'path'
 *      subClass.superclass === superClass         // true
 *
 * An example of use:
 *
 *      (function () {
 *          var Core = function () {...};
 *
 *          // extending the namespace
 *          PMDraw.extendNamespace('PMDraw.draw.Core', Core);
 *
 *      }());
 *
 *      (function () {
 *          var BehavioralElement = function () {...};
 *
 *          // this class inherits from PMDraw.draw.Core
 *          PMDraw.inheritFrom('PMDraw.draw.Core', BehavioralElement);
 *
 *          // extending the namespace
 *          PMDraw.extendNamespace('PMDraw.draw.BehavioralElement', BehavioralElement);
 *
 *      }());
 *
 * @param {string} path
 * @param {Object} subClass
 * @return {Object}
 */
PMUI.inheritFrom = function (path, subClass) {
    var current,
        extension,
        pathArray,
        i,
        prototype;

    if (arguments.length !== 2) {
        throw new Error("PMUI.inheritFrom(): method needs 2 arguments");
    }

    // function used to create an object whose [[Prototype]] link
    // points to `object`
    function clone(object) {
        var F = function () {
        };
        F.prototype = object;
        return new F();
    }

    pathArray = path.split('.');
    if (pathArray[0] === 'PMUI') {
        pathArray = pathArray.slice(1);
    }
    current = PMUI;

    // find that class the 'path' namespace
    for (i = 0; i < pathArray.length; i += 1) {
        extension = pathArray[i];
        if (typeof current[extension] === 'undefined') {
            throw new Error("PMUI.inheritFrom(): object " + extension + " not found, full path was " + path);
        }
        current = current[extension];
    }

    prototype = clone(current.prototype);

    prototype.constructor = subClass;
    subClass.prototype = prototype;
    subClass.superclass = current;
};

/**
 * Generates 32-digits alphanumeric unique IDs
 * @return {String} Alphanumeric 32-char unique string
 */
PMUI.generateUniqueId = function () {
    var rand = function (min, max) {
            // Returns a random number
            //
            // version: 1109.2015
            // discuss at: http://phpjs.org/functions/rand
            // +   original by: Leslie Hoare
            // +   bugfixed by: Onno Marsman
            // %          note 1: See the commented out code below for a
            // version which will work with our experimental
            // (though probably unnecessary) srand() function)
            // *     example 1: rand(1, 1);
            // *     returns 1: 1

            // fix for jsLint
            // from: var argc = arguments.length;
            if (typeof min === "undefined") {
                min = 0;
            }
            if (typeof max === "undefined") {
                max = 999999999;
            }
            return Math.floor(Math.random() * (max - min + 1)) + min;
        },
        uniqid = function (prefix, more_entropy) {
            // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
            // +    revised by: Kankrelune (http://www.webfaktory.info/)
            // %        note 1: Uses an internal counter (in php_js global) to avoid collision
            // *     example 1: uniqid();
            // *     returns 1: 'a30285b160c14'
            // *     example 2: uniqid('foo');
            // *     returns 2: 'fooa30285b1cd361'
            // *     example 3: uniqid('bar', true);
            // *     returns 3: 'bara20285b23dfd1.31879087'
            if (typeof prefix === 'undefined') {
                prefix = "";
            }

            var retId,
                formatSeed = function (seed, reqWidth) {
                    var tempString = "",
                        i;
                    seed = parseInt(seed, 10).toString(16); // to hex str
                    if (reqWidth < seed.length) { // so long we split
                        return seed.slice(seed.length - reqWidth);
                    }
                    if (reqWidth > seed.length) { // so short we pad
                        // jsLint fix
                        tempString = "";
                        for (i = 0; i < 1 + (reqWidth - seed.length); i += 1) {
                            tempString += "0";
                        }
                        return tempString + seed;
                    }
                    return seed;
                };

            // BEGIN REDUNDANT
            if (!this.php_js) {
                this.php_js = {};
            }
            // END REDUNDANT
            if (!this.php_js.uniqidSeed) { // init seed with big random int
                this.php_js.uniqidSeed = Math.floor(Math.random() * 0x75bcd15);
            }
            this.php_js.uniqidSeed += 1;

            retId = prefix; // start with prefix, add current milliseconds hex string
            retId += formatSeed(parseInt(new Date().getTime() / 1000, 10), 8);
            retId += formatSeed(this.php_js.uniqidSeed, 5); // add seed hex string
            if (more_entropy) {
                // for more entropy we add a float lower to 10
                retId += (Math.random() * 10).toFixed(8).toString();
            }

            return retId;
        },
        sUID;

    do {
        sUID = uniqid(rand(0, 999999999), true);
        sUID = sUID.replace('.', '0');
    } while (sUID.length !== 32);

    return sUID;
};

/**
 * Creates and returns a HTML element
 * @param  {String} type The type for the element to be created, for example: div, span, p
 * @return {HTMLElement}    An HTML element
 */
PMUI.createHTMLElement = function (type) {
    return document.createElement(type);
};

/**
 * Calculates the text width usign a font family
 * @param {String} text The text which width will be calculated
 * @param {String} font The font family and size (expressed as the 'font' css properties)
 * to be used to calculate the width
 * @return {Number}
 */
PMUI.calculateWidth = function (text, font) {
    var f = font || '12px arial',
        $o = $(this.createHTMLElement('div')), w;
    $o.text(text)
        .css({'position': 'absolute', 'float': 'left', 'white-space': 'nowrap', 'visibility': 'hidden', 'font': f})
        .appendTo($('body'));
    w = $o.width();

    $o.remove();

    return w;
};

PMUI.emToPx = function (emUnits, context) {
    var testDiv = PMUI.createHTMLElement('div'), theWidth, $div;

    testDiv.style.margin = '0';
    testDiv.style.padding = '0';
    testDiv.style.position = 'absolute';
    testDiv.style.display = 'inline-block';
    testDiv.style.backgroundColor = 'none';
    testDiv.style.width = emUnits + "em";
    context = PMUI.isHTMLElement(context) ? context : document.body;
    if (context !== document.body && !jQuery(context).parents('body').length) {
        throw new Error("The context must be an HTML element appended to the DOM.");
    }
    context.appendChild(testDiv);
    $div = jQuery(testDiv);
    theWidth = $div.outerWidth();
    $div.remove();
    return theWidth;
};

/**
 * Get PMUI Version.
 * @return {String}
 */
PMUI.getVersion = function () {
    return this.version;
};

/**
 * Trigger events defined in the element
 * @param  {PMUI.core.Element} el Element associated with the event
 * @param  {String} eventName Event Name or alias
 * @param  {Object} scope  Calling scope for the event
 */
PMUI.triggerEvent = function (el, eventName, scope) {
    var scopeTrigger = scope || this;
    if (el instanceof PMUI.core.Element) {
        if (el.events[eventName] instanceof PMUI.event.Event) {
            el.events[eventName].handler.call(scopeTrigger);
        } else {
            throw new Error('Event name is not registered int this element');
        }
    } else {
        throw new Error('Current Element is not able to trigger events');
    }
};

/**
 * Sets the active canvas.
 * @param {PMUI.draw.Canvas} canvas
 * @chainable
 */
PMUI.setActiveCanvas = function (canvas) {
    PMUI.activeCanvas = canvas;
    return this;
};
/**
 * Gets the active canvas
 * @return {PMUI.draw.Canvas}
 */
PMUI.getActiveCanvas = function () {
    return PMUI.activeCanvas;
};

/**
 * Converts the coordinates `xCoord` and `yCoord` (assuming that xCoord and yCoord are pageCoordinates)
 * or the page coordinates gathered from the object `e` if there is no `xCoord` or `yCoord` to
 * `shape` coordinates, this new coordinate also considers the scroll done in the canvas
 *
 *      // e.g.
 *      // Let's assume that:
 *      // the canvas coordinates are [100, 100] and that it has no scroll
 *      // the shape coordinates are [100, 100] (inside the canvas)
 *      // e is an object containing page.X = 300, page.Y = 300
 *      Utils.pageCoordinatesToShapeCoordinates(shape, e)  // new Point(100, 100) respect to the shape
 *
 *
 * @param {Object} shape
 * @param {Object} e
 * @param {number} [xCoord]
 * @param {number} [yCoord]
 * @return {PMUI.util.Point} a point relative to the canvas
 */
PMUI.pageCoordinatesToShapeCoordinates = function (shape, e, xCoord, yCoord, customShape) {
    var coordinates,
        x = (!xCoord) ? e.pageX : xCoord,
        y = (!yCoord) ? e.pageY : yCoord,
        orgX = (!xCoord) ? e.pageX : xCoord,
        orgY = (!yCoord) ? e.pageY : yCoord,
        canvas = shape.getCanvas();
    x += canvas.getLeftScroll() - shape.getAbsoluteX() - canvas.getX();
    y += canvas.getTopScroll() - shape.getAbsoluteY() - canvas.getY();
    if (orgX === x) {
        x -= parseInt(customShape.width / 2, 10);
    }
    if (orgY === y) {
        y -= parseInt(customShape.height / 2, 10);
    }
    coordinates = new PMUI.util.Point(x, y);
    return coordinates;
};
/**
 * Validates the parameters that represents the coordinates for create a new shape
 * @param  {Object} canvas      Represents the current canvas for the project
 * @param  {Object} e           Represents the event that triggered on the event
 * @param  {Object} customShape Represents the object that will create a new element on the canvas
 * @return {Boolean} flag       After the review of the coordinates the method return true if the component
 * will create on the canvas and false if not possible create it.
 */
PMUI.validCoordinatedToCreate = function (canvas, e, customShape) {
    return function (canvas, e, customShape) {
        return true;
    };
};
/**
 * Converts the coordinates of the `shape` to page coordinates, this method
 * also considers the scroll of the canvas in the calculation
 *
 *      // e.g.
 *      // Let's assume that:
 *      // the canvas coordinates are [100, 100] and that it has no scroll
 *      // the shape coordinates are [100, 100] (inside the canvas)
 *      Utils.getPointRelativeToPage(shape)     // new Point(200, 200) respect to the page
 *
 * @param {Object} shape
 * @return {PMUI.util.Point} a point relative to the page
 */
PMUI.getPointRelativeToPage = function (shape) {
    var canvas = shape.getCanvas(),
        x = shape.absoluteX + canvas.getX() - canvas.getLeftScroll() +
            shape.zoomWidth / 2,
        y = shape.absoluteY + canvas.getY() - canvas.getTopScroll() +
            shape.zoomHeight / 2;
    return new PMUI.util.Point(x, y);
};

PMUI.json2xml = function (o) {
    var len,
        toXML,
        safeXMLValue,
        a;

    len = function (o) {
        var n = 0,
            a;
        for (a in o) {
            if (o.hasOwnProperty(a)) {
                n += 1;
            }
        }
        return n;
    };

    toXML = function (tag, o) {
        var a,
            b,
            doc = '<' + tag,
            sw = false,
            i;

        if (typeof o === 'undefined' || o === null) {
            doc += '/>';
            return doc;
        }
        if (typeof o !== 'object') {
            doc += '>' + safeXMLValue(o) + '</' + tag + '>';
            return doc;
        }
        if (o.constructor === Object) {
            for (a in o) {
                if (o.hasOwnProperty(a)) {
                    if (a.charAt(0) === '@') {
                        if (typeof o[a] !== 'object') {
                            doc += ' ' + a.substring(1) + '="' + o[a] + '"';
                            delete o[a];
                        } else {
                            throw new Error((typeof o[a])
                                + ' being attribute is not supported.');
                        }
                    }
                }
            }
            if (len(o) === 0) {
                doc += '/>';
                sw = true;
            } else {
                doc += '>';
            }
            if (sw) {
                return doc;
            }
            if (typeof o['#text'] !== 'undefined') {
                if (typeof o['#text'] !== 'object') {
                    doc += o['#text'];
                    delete o['#text'];
                } else {
                    throw new Error((typeof o['#text'])
                        + ' being #text is not supported.');
                }
            }
            for (b in o) {
                if (o.hasOwnProperty(b)) {
                    if (o[b].constructor === Array) {
                        for (i = 0; i < o[b].length; i += 1) {
                            if (typeof o[b][i] !== 'object'
                                || o[b][i].constructor === Object) {
                                doc += toXML(b, o[b][i]);
                            } else {
                                throw new Error((typeof o[b][i])
                                    + ' is not supported.');
                            }
                        }
                    } else if (o[b].constructor === Object
                        || typeof o[b] !== 'object') {
                        doc += toXML(b, o[b]);
                    } else {
                        throw new Error((typeof o[b]) + ' is not supported.');
                    }
                }
            }
            doc += '</' + tag + '>';
            return doc;
        }


    };

    safeXMLValue = function (value) {
        var s = value.toString();
        s = s.replace(/\&/g, '&amp;');
        s = s.replace(/\"/g, '&quot;');
        s = s.replace(/</g, '&lt;');
        s = s.replace(/>/g, '&gt;');
        return s;
    };

    if (typeof o === 'object' && o.constructor === Object && len(o) === 1) {
        for (a in o) {
            if (o.hasOwnProperty(a)) {
                return toXML(a, o[a]);
            }
        }
    }

};

PMUI.linkToPMUIObject = function (element, pmuiObject) {
    if (this.isHTMLElement(element) && pmuiObject instanceof PMUI.core.Element) {
        jQuery(element).data('pmui', pmuiObject);
    } else {
        throw new Error("PMUI.linkToPMUIObject(): the first parameter must be a HTMLElement and the second one must be "
            + "an instance of PMUI.core.Element.");
    }
};

PMUI.getPMUIObject = function (element) {
    if (this.isHTMLElement(element)) {
        return jQuery(element).data("pmui");
    }
    throw new Error("PMUI.getPMUIObject(): the parameter must be a HTMLElement.");
};

PMUI.isHTMLElement = function (obj) {
    try {
        //Using W3 DOM2 (works for FF, Opera and Chrom)
        return obj instanceof HTMLElement;
    }
    catch (e) {
        //Browsers not supporting W3 DOM2 don't have HTMLElement and
        //an exception is thrown and we end up here. Testing some
        //properties that all elements have. (works on IE7)
        return (typeof obj === "object") &&
            (obj.nodeType === 1) && (typeof obj.style === "object") &&
            (typeof obj.ownerDocument === "object");
    }
};
PMUI.removeCurrentMenu = function () {
    if (PMUI.currentContextMenu && PMUI.currentContextMenu.displayed) {
        PMUI.currentContextMenu.hide();
        PMUI.currentContextMenu = null;
    }
};
PMUI.init = function () {
    String.prototype.translate = PMUI.lang.I18N.translate;
    String.prototype.translateContext = PMUI.lang.I18N.translateContext;

    if (!Array.prototype.indexOf) {
        Array.prototype.indexOf = function (elt /*, from*/) {
            var len = this.length >>> 0;

            var from = Number(arguments[1]) || 0;
            from = (from < 0)
                ? Math.ceil(from)
                : Math.floor(from);
            if (from < 0)
                from += len;

            for (; from < len; from += 1) {
                if (from in this &&
                    this[from] === elt)
                    return from;
            }
            return -1;
        };
    }
    return this;
};

PMUI.loadLanguage = function (data, lang, loaded) {
    PMUI.lang.I18N.loadLanguage(data, lang, loaded);
    return this;
};

PMUI.setCurrentLanguage = function (lang) {
    PMUI.lang.I18N.setCurrentLanguage(lang);
    return this;
};

PMUI.setDefaultLanguage = function (lang) {
    PMUI.lang.I18N.setDefaultLanguage(lang);
    return this;
};

PMUI.convertDateFormat = function (dateInput) {
    //TODO Implement this method considering locate options
    return dateInput;
};

PMUI.castValue = function (value, format) {
    try {
        switch (format) {
            case 'string':
                out = String(value);
                break;
            case 'number':
                if (value != '') {
                    out = Number(value);
                } else {
                    out = 'NaN';
                }
                break;
            case 'date':
                out = PMUI.convertDateFormat(value);
                break;
            case 'boolean':
                out = Boolean(value);
                break;
            default:
                out = value;
        }
        return out;
    } catch (e) {
        throw new Error("The value cannot be showed in '" + format + "' format");
    }
};
/**
 * validates that the items do not leave the parent container
 * @param elements array of items selecteds
 * @param direction string of cursor key
 * @returns {boolean}
 */
PMUI.getCoordinatesElement = function (elements, direction) {
    var element,
        factorPixel,
        swError,
        factorZoom,
        ParentPoints,
        ShapePoints,
        limitTop,
        limitRight,
        limitBottom,
        limitLeft;

    swError = false;
    if (elements.length > 0) {
        if (elements[0].parent.parent !== null) {
            element = elements[0];
            factorPixel = [4, 4, 4, 2];
            factorZoom = element.getCanvas().getZoomFactor();
            ParentPoints = PMUI.getCoordenatesParent(element.parent, factorZoom);
            ShapePoints = PMUI.getAbsoluteShape(elements, factorZoom);
            limitTop = ParentPoints.PInit.getY() + (factorPixel[0] * factorZoom);
            limitRight = ParentPoints.PFinal.getX() - (factorPixel[1] * factorZoom);
            limitBottom = ParentPoints.PFinal.getY() - (factorPixel[2] * factorZoom);
            limitLeft = ParentPoints.PInit.getX() + (factorPixel[3] * factorZoom);
            switch (direction) {
                case 'TOP':
                    if (limitTop > ShapePoints.PInit.getY()) {
                        swError = true;
                    }
                    break;
                case 'RIGHT':
                    if (limitRight < ShapePoints.PFinal.getX()) {
                        swError = true;
                    }
                    break;
                case 'BOTTOM':
                    if (limitBottom < ShapePoints.PFinal.getY()) {
                        swError = true;
                    }
                    break;
                case 'LEFT':
                    if (limitLeft > ShapePoints.PInit.getX()) {
                        swError = true;
                    }
                    break;
            }
        }
    }

    return swError;
};
/**
 * Get the parent coordenates
 * @param parent Object parent of the item selected
 * @param factorZoom
 * @returns {Object of Objects Point}
 */
PMUI.getCoordenatesParent = function (parent, factorZoom) {
    var coordenatesParent,
        xInit,
        yInit,
        xFinal,
        yFinal,
        pointInit,
        pointFinal,
        headParent;

    headParent = 0;
    if (parent.type === "PMPool") {
        headParent = parent.headLineCoord * factorZoom;
    }
    xInit = parent.getAbsoluteX() + headParent;
    yInit = parent.getAbsoluteY();
    xFinal = parent.getAbsoluteX() + (parent.getWidth() * factorZoom);
    yFinal = parent.getAbsoluteY() + (parent.getHeight() * factorZoom);
    pointInit = new PMUI.util.Point(xInit, yInit);
    pointFinal = new PMUI.util.Point(xFinal, yFinal);
    coordenatesParent = {
        "PInit": pointInit,
        "PFinal": pointFinal
    };

    return coordenatesParent;
};
/**
 * Get coordenates min y max of the items selecteds
 * @param shapes Array of items selecteds
 * @param factorZoom
 * @returns {Object of Objects Point}
 */
PMUI.getAbsoluteShape = function (shapes, factorZoom) {
    var coordenatesShapeAux,
        coordenatesShape,
        arrayShapesAux = [],
        pointShapeInit,
        pointShapeFinal,
        shape,
        xFinalAux,
        yFinalAux,
        i;

    for (i = 0; i < shapes.length; i += 1) {
        shape = shapes[i];
        xFinalAux = shape.getAbsoluteX() + (shape.getWidth() * factorZoom);
        yFinalAux = shape.getAbsoluteY() + (shape.getHeight() * factorZoom);
        pointShapeInit = new PMUI.util.Point(shape.getAbsoluteX(), shape.getAbsoluteY());
        pointShapeFinal = new PMUI.util.Point(xFinalAux, yFinalAux);
        coordenatesShapeAux = {
            "PInit": pointShapeInit,
            "PFinal": pointShapeFinal
        };
        arrayShapesAux.push(coordenatesShapeAux);
    }
    coordenatesShape = PMUI.getMinMaxCoordenates(arrayShapesAux);
    return coordenatesShape;
};
/**
 * Get the Min y Max coordenates of a array of Objects Point
 * @param coordenates array of Objects Point
 * @returns {Object of Objects Point}
 */
PMUI.getMinMaxCoordenates = function (coordenates) {
    var objectCoordenates,
        xInit,
        yInit,
        xFinal,
        yFinal,
        j;

    xInit = coordenates[0].PInit.getX();
    yInit = coordenates[0].PInit.getY();
    xFinal = coordenates[0].PFinal.getX();
    yFinal = coordenates[0].PFinal.getY();

    for (j = 0; j < coordenates.length; j += 1) {
        if (coordenates[j].PInit.getX() < xInit) {
            xInit = coordenates[j].PInit.getX();
        }
        if (coordenates[j].PInit.getY() < yInit) {
            yInit = coordenates[j].PInit.getY();
        }
        if (coordenates[j].PFinal.getX() > xFinal) {
            xFinal = coordenates[j].PFinal.getX();
        }
        if (coordenates[j].PFinal.getY() > yFinal) {
            yFinal = coordenates[j].PFinal.getY();
        }
    }

    objectCoordenates = {
        "PInit": new PMUI.util.Point(xInit, yInit),
        "PFinal": new PMUI.util.Point(xFinal, yFinal)
    };

    return objectCoordenates;
};

PMUI.isInDOM = function (html) {
    var targetHTML = html instanceof PMUI.core.Element ? html.html : html,
        aux;

    if (targetHTML === null) {
        return false;
    } else if (!PMUI.isHTMLElement(targetHTML)) {
        throw new Error('isInDOM(): invalid parameter, only HTML or instance of PMUI.core.Element are allowed.');
    }

    aux = jQuery(html).parents('html')[0];
    return !!aux && aux.parentNode === document;
};

String.prototype.capitalize = function () {
    return this.toLowerCase().replace(/(^|\s)([a-z])/g, function (m, p1, p2) {
        return p1 + p2.toUpperCase();
    });
};

if (typeof exports !== "undefined") {
    module.exports = PMUI;
}

(function () {
    /**
     * @class PMUI.util.ArrayList
     * Construct a List similar to Java's ArrayList that encapsulates methods for
     * making a list that supports operations like get, insert and others.
     *
     *      some examples:
     *      var item,
     *          arrayList = new ArrayList();
     *      arrayList.getSize()                 // 0
     *      arrayList.insert({                  // insert an object
     *          id: 100,
     *          width: 100,
     *          height: 100
     *      });
     *      arrayList.getSize();                // 1
     *      arrayList.asArray();                // [{id : 100, ...}]
     *      item = arrayList.find('id', 100);   // finds the first element with an id that equals 100
     *      arrayList.remove(item);             // remove item from the arrayList
     *      arrayList.getSize();                // 0
     *      arrayList.isEmpty();                // true because the arrayList has no elements
     *
     * @constructor Returns an instance of the class ArrayList
     */
    var ArrayList = function () {
        /**
         * The elements of the arrayList
         * @property {Array}
         * @private
         */
        var elements = [],
            /**
             * The size of the array
             * @property {number} [size=0]
             * @private
             */
            size = 0,
            index,
            i;
        return {

            /**
             * The ID of this ArrayList is generated using the function Math.random
             * @property {number} id
             */
            id: Math.random(),
            /**
             * Gets an element in the specified index or undefined if the index
             * is not present in the array
             * @param {number} index
             * @returns {Object / undefined}
             */
            get: function (index) {
                return elements[index];
            },
            /**
             * Inserts an element at the end of the list
             * @param {Object} item
             * @chainable
             */
            insert: function (item) {
                elements[size] = item;
                size += 1;
                return this;
            },
            /**
             * Inserts an element in a specific position
             * @param {Object} item
             * @chainable
             */
            insertAt: function (item, index) {
                elements.splice(index, 0, item);
                size = elements.length;
                return this;
            },
            /**
             * Removes an item from the list
             * @param {Object} item
             * @return {boolean}
             */
            remove: function (item) {
                index = this.indexOf(item);
                if (index === -1) {
                    return false;
                }
                //swap(elements[index], elements[size-1]);
                size -= 1;
                elements.splice(index, 1);
                return true;
            },
            /**
             * Gets the length of the list
             * @return {number}
             */
            getSize: function () {
                return size;
            },
            /**
             * Returns true if the list is empty
             * @returns {boolean}
             */
            isEmpty: function () {
                return size === 0;
            },
            /**
             * Returns the first occurrence of an element, if the element is not
             * contained in the list then returns -1
             * @param {Object} item
             * @return {number}
             */
            indexOf: function (item) {
                for (i = 0; i < size; i += 1) {
                    if (item === elements[i]) {
                        return i;
                    }
                }
                return -1;
            },
            /**
             * Returns the the first object of the list that has the
             * specified attribute with the specified value
             * if the object is not found it returns undefined
             * @param {string} attribute
             * @param {string} value
             * @return {Object / undefined}
             */
            find: function (attribute, value) {
                var i,
                    current;

                for (i = 0; i < elements.length; i += 1) {
                    current = elements[i];
                    if (current[attribute] === value) {
                        return current;
                    }
                }
                return undefined;
            },

            /**
             * Returns true if the list contains the item and false otherwise
             * @param {Object} item
             * @return {boolean}
             */
            contains: function (item) {
                if (this.indexOf(item) !== -1) {
                    return true;
                }
                return false;
            },
            /**
             * Sorts the list using compFunction if possible, if no compFunction
             * is passed as an parameter then a default sorting method will be used. This default method will sort in
             * ascending order.
             * @param {Function} [compFunction] The criteria function used to find out the position for the elements in
             * the array list. This function will receive two parameters, each one will be an element from the array
             * list, the function will compare those elements and it must return:
             *
             * - 1, if the first element must be before the second element.
             * - -1, if the second element must be before the first element.
             * - 0, if the current situation doesn't met any of the two situations above. In this case both elements
             * can be evaluated as they had the same value. For example, in an array list of numbers, when you are
             * trying to apply a lineal sorting (ascending/descending) in a array list of numbers, if the array sorting
             * function finds two elements with the value 3 they should be evaluated returning 0, since both values are
             * the same.
             *
             * IMPORTANT NOTE: for a correct performance the sent parameter must return at least two of the values
             * listed above, if it doesn't the function can produce an infinite loop and thus an error.
             * @return {boolean}
             */
//            sort : function (compFunction) {
//                var compFunction = compFunction || function(a, b) {
//                        if(a < b) {
//                            return 1;
//                        } else if(a > b) {
//                            return -1;
//                        } else {
//                            return 0;
//                        }
//                    }, swap = function (items, firstIndex, secondIndex){
//                        var temp = items[firstIndex];
//                        items[firstIndex] = items[secondIndex];
//                        items[secondIndex] = temp;
//                    }, partition = function(items, left, right) {
//                        var pivot = items[Math.floor((right + left) / 2)],
//                            i = left,
//                            j = right;
//                        while (i <= j) {
//                            while (compFunction(items[i], pivot) > 0) {
//                                i++;
//                            }
//                            while (compFunction(items[j], pivot) < 0) {
//                                j--;
//                            }
//                            if (i <= j) {
//                                swap(items, i, j);
//                                i++;
//                                j--;
//                            }
//                        }
//                        return i;
//                    }, quickSort = function (items, left, right) {
//                        var index;
//                        if (items.length > 1) {
//                            index = partition(items, left, right);
//                            if (left < index - 1) {
//                                quickSort(items, left, index - 1);
//                            }
//                            if (index < right) {
//                                quickSort(items, index, right);
//                            }
//                        }
//                        return items;
//                    };
//
//                return quickSort(elements, 0, size - 1);
//            },

            sort: function (compFunction) {
                var returnValue = false;

                if (compFunction) {
                    elements.sort(compFunction);
                    returnValue = true;
                }
                return returnValue;
            },
            /**
             * Bubble sort function in array
             * @param {*} compFunction 
             * @return {Array}
             */
            bubbleSort: function (compFunction) {
                var inputArr = elements,
                    i,
                    tmp,
                    len = inputArr.length,
                    swapped;
                do {
                    swapped = false;
                    for (i = 0; i < len; i++) {
                        if (inputArr[i] && inputArr[i + 1] && compFunction(inputArr[i], inputArr[i + 1])) {
                            tmp = inputArr[i];
                            inputArr[i] = inputArr[i + 1];
                            inputArr[i + 1] = tmp;
                            swapped = true;
                        }
                    }
                } while (swapped);
                elements = inputArr;
                return elements;
            },
            /**
             * Returns the list as an array
             * @return {Array}
             */
            asArray: function () {
                return elements.slice(0);
            },
            /**
             * Swaps the position of two elements
             * @chainable
             */
            swap: function (index1, index2) {
                var aux;

                if (index1 < size && index1 >= 0 && index2 < size && index2 >= 0) {
                    aux = elements[index1];
                    elements[index1] = elements[index2];
                    elements[index2] = aux;
                }
                return this;
            },
            /**
             * Returns the first element of the list
             * @return {Object}
             */
            getFirst: function () {
                return elements[0];
            },
            /**
             * Returns the last element of the list
             * @return {Object}
             */
            getLast: function () {
                return elements[size - 1];
            },

            /**
             * Returns the last element of the list and deletes it from the list
             * @return {Object}
             */
            popLast: function () {
                var lastElement;
                size -= 1;
                lastElement = elements[size];
                elements.splice(size, 1);
                return lastElement;
            },
            /**
             * Returns an array with the objects that determine the minimum size
             * the container should have
             * The array values are in this order TOP, RIGHT, BOTTOM AND LEFT
             * @return {Array}
             */
            getDimensionLimit: function () {
                var result = [100000, -1, -1, 100000],
                    objects = [undefined, undefined, undefined, undefined];
                //number of pixels we want the inner shapes to be
                //apart from the border

                for (i = 0; i < size; i += 1) {
                    if (result[0] > elements[i].y) {
                        result[0] = elements[i].y;
                        objects[0] = elements[i];

                    }
                    if (result[1] < elements[i].x + elements[i].width) {
                        result[1] = elements[i].x + elements[i].width;
                        objects[1] = elements[i];
                    }
                    if (result[2] < elements[i].y + elements[i].height) {
                        result[2] = elements[i].y + elements[i].height;
                        objects[2] = elements[i];
                    }
                    if (result[3] > elements[i].x) {
                        result[3] = elements[i].x;
                        objects[3] = elements[i];
                    }
                }
                return result;
            },
            /**
             * Clears the content of the arrayList
             * @chainable
             */
            clear: function () {
                if (size !== 0) {
                    elements = [];
                    size = 0;
                }
                return this;
            },
            /**
             * Sets the elements for the object.
             * @param {Array|null} items Array with the items to set.
             * @chainable
             */
            set: function (items) {
                if (!(items === null || jQuery.isArray(items))) {
                    throw new Error("set(): The parameter must be an array or null.");
                }
                elements = (items && items.slice(0)) || [];
                size = elements.length;
                return this;
            }
        };
    };

    PMUI.extendNamespace('PMUI.util.ArrayList', ArrayList);

    if (typeof exports !== "undefined") {
        module.exports = ArrayList;
    }

}());

(function () {
    /**
     * @class PMUI.util.Style
     * Class that represent the style of a an object.
     *
     *      // i.e
     *      // Let's assume that 'shape' is a CustomShape
     *      var style = new Style({
     *          cssClasses: [
     *              'sprite-class', 'marker-class', ...
     *          ],
     *          cssProperties: {
     *              border: 1px solid black,
     *              background-color: grey,
     *              ...
     *          },
     *          belongsTo: shape
     *      })
     *
     * @constructor Creates a new instance of this class
     * @param {Object} options
     * @cfg {Array} [cssClasses=[]] the classes that `this.belongsTo` has
     * @cfg {Object} [cssProperties={}] the css properties that `this.belongsTo` has
     * @cfg {Object} [belongsTo=null] a pointer to the owner of this instance
     */
    var Style = function (options) {

        /**
         * JSON Object used to map each of the css properties of the object,
         * this object has the same syntax as the object passed to jQuery.css()
         *      cssProperties: {
         *          background-color: [value],
         *          border: [value],
         *          ...
         *      }
         * @property {Object}
         */
        this.cssProperties = null;

        /**
         * Array of all the classes of this object
         *      cssClasses = [
         *          'class_1',
         *          'class_2',
         *          ...
         *      ]
         * @property {Array}
         */
        this.cssClasses = null;

        /**
         * Pointer to the object to whom this style belongs to
         * @property {Object}
         */
        this.belongsTo = null;


        Style.prototype.initObject.call(this, options);
    };


    /**
     * The type of this class
     * @property {String}
     */
    Style.prototype.type = "Style";

    /**
     * Constant for the max z-index
     * @property {number} [MAX_ZINDEX=100]
     */
    Style.MAX_ZINDEX = 100;

    /**
     * Instance initializer which uses options to extend the config options to
     * initialize the instance
     * @private
     * @param {Object} options
     */
    Style.prototype.initObject = function (options) {
        var defaults = {
            cssClasses: [],
            cssProperties: {},
            belongsTo: null
        };

        jQuery.extend(true, defaults, options);
        this.cssClasses = defaults.cssClasses;
        this.cssProperties = defaults.cssProperties;
        this.belongsTo = defaults.belongsTo;
    };

    /**
     * Applies cssProperties and cssClasses to `this.belongsTo`
     * @chainable
     */
    Style.prototype.applyStyle = function () {
        var i,
            t,
            class_i;

        if (!this.belongsTo.html) {
            throw new Error("applyStyle(): can't apply style to an" +
                " object with no html.");
        }

        // apply the cssProperties
        jQuery(this.belongsTo.html).css(this.cssProperties);

        //adding default classes
        t = this.belongsTo.type.toLowerCase();
        if (this.cssClasses.indexOf('pmui-' + t) === -1) {
            this.cssClasses.unshift('pmui-' + t);
        }
        if (this.cssClasses.indexOf('pmui') === -1) {
            this.cssClasses.unshift('pmui');
        }

        // apply saved classes
        for (i = 0; i < this.cssClasses.length; i += 1) {
            class_i = this.cssClasses[i];
            if (!$(this.belongsTo.html).hasClass(class_i)) {
                jQuery(this.belongsTo.html).addClass(class_i);
            }
        }
        return this;
    };
    Style.prototype.unapplyStyle = function () {
        var t,
            property;

        if (!this.belongsTo.html) {
            throw new Error("unapplyStyle(): can't unapply style to an object with no html.");
        }
        t = this.belongsTo.type.toLowerCase();
        jQuery(this.belongsTo.html).removeClass("pmui-" + t);
        for (property in this.cssProperties) {
            jQuery(this.belongsTo.html).css(property, "");
        }
    };

    /**
     * Extends the property `cssProperties` with a new object and also applies those new properties
     * @param {Object} properties
     * @chainable
     */
    Style.prototype.addProperties = function (properties) {
        jQuery.extend(true, this.cssProperties, properties);
        jQuery(this.belongsTo && this.belongsTo.html).css(properties);
        return this;
    };

    /**
     * Gets a property from `this.cssProperties` using jQuery or `window.getComputedStyle()`
     * @param {String} property
     * @return {String}
     */
    Style.prototype.getProperty = function (property) {
        return this.cssProperties[property] ||
            jQuery(this.belongsTo.html).css(property) ||
            (this.belongsTo.html && window.getComputedStyle(this.belongsTo.html, null)
                .getPropertyValue(property)) || "";
    };
    /**
     * Returns all the style's css properties set explicitly.
     * @return {Object} An object literal with the properties.
     */
    Style.prototype.getProperties = function () {
        return this.cssProperties;
    };
    /**
     * Removes ´properties´ from the ´this.cssProperties´, also disables those properties from
     * the HTMLElement
     * @param {Array} properties An array in which each element is th name of the cssProperty to be removed.
     * @chainable
     */
    Style.prototype.removeProperties = function (properties) {
        var property,
            i;

        for (i = 0; i < properties.length; i += 1) {
            property = properties[i];
            if (this.cssProperties.hasOwnProperty(property)) { // JS Code Convention
                jQuery(this.belongsTo.html).css(property, "");   // reset inline style
                delete this.cssProperties[property];
            }
        }
        return this;
    };
    /**
     * Removes all properties from the object.
     * @chainable
     */
    Style.prototype.removeAllProperties = function () {
        var key;

        if (this.belongsTo) {
            for (key in this.cssProperties) {
                jQuery(this.belongsTo.html).css(key, "");
            }
        }
        this.cssProperties = {};
        return this;
    };
    /**
     * Adds new classes to ´this.cssClasses´ array
     * @param {Array} cssClasses
     * @chainable
     */
    Style.prototype.addClasses = function (cssClasses) {
        var i,
            cssClass;

        if (cssClasses && cssClasses instanceof Array) {
            for (i = 0; i < cssClasses.length; i += 1) {
                cssClass = cssClasses[i];
                if (typeof cssClass === "string") {
                    if (this.cssClasses.indexOf(cssClass) === -1) {
                        this.cssClasses.push(cssClass);
                        jQuery(this.belongsTo && this.belongsTo.html).addClass(cssClass);
                    }
                } else {
                    throw new Error("addClasses(): array element is not of type string");
                }
            }
        } else {
            throw new Error("addClasses(): parameter must be of type Array");
        }
        return this;
    };

    /**
     * Removes classes from ´this.cssClasses´ array, also removes those classes from
     * the HTMLElement
     * @param {Array} cssClasses
     * @chainable
     */
    Style.prototype.removeClasses = function (cssClasses) {
        var i,
            index,
            cssClass;

        if (cssClasses && cssClasses instanceof Array) {
            for (i = 0; i < cssClasses.length; i += 1) {
                cssClass = cssClasses[i];
                if (typeof cssClass === "string") {
                    index = this.cssClasses.indexOf(cssClass);
                    if (index !== -1) {
                        jQuery(this.belongsTo.html).removeClass(this.cssClasses[index]);
                        this.cssClasses.splice(index, 1);
                    }
                } else {
                    throw new Error("removeClasses(): array element is not of " +
                        "type string");
                }
            }
        } else {
            throw new Error("removeClasses(): parameter must be of type Array");
        }
        return this;
    };
    /**
     * Removes all the classes from ´this.cssClasses´ array
     * @param {Array} cssClasses
     * @chainable
     */
    Style.prototype.removeAllClasses = function () {
        while (this.cssClasses.length) {
            jQuery(this.belongsTo && this.belongsTo.html).removeClass(this.cssClasses.pop());
        }
        return this;
    };

    /**
     * Checks if the class is a class stored in ´this.cssClasses´
     * @param cssClass
     * @return {boolean}
     */
    Style.prototype.containsClass = function (cssClass) {
        return this.cssClasses.indexOf(cssClass) !== -1;
    };

    /**
     * Returns an array with all the classes of ´this.belongsTo´
     * @return {Array}
     */
    Style.prototype.getClasses = function () {
        return this.cssClasses.slice(0);
    };

    /**
     * Clears all the css properties and classes.
     * @chainable
     */
    Style.prototype.clear = function () {
        return this.removeAllClasses().removeAllProperties();
    }

    /**
     * Serializes this instance
     * @return {Object}
     * @return {Array} return.cssClasses
     */
    Style.prototype.stringify = function () {
        return {
            cssClasses: this.cssClasses
        };
    };

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = Style;
    }

    PMUI.extendNamespace('PMUI.util.Style', Style);

}());
(function () {
    /**
     * @class PMUI.util.Factory
     * This class encapsulate the way to construct object using the product definition inside
     *
     * @constructor
     * This method creates a new instance of this object
     * @param {Object} settings Constructor setiings
     */
    var Factory = function (settings) {
        /**
         * Defines the products can be make by the factory
         * @type {Object}
         */
        this.products = null;

        /**
         * Defines the default product to make
         * @type {String}
         */
        this.defaultProduct = null;
        Factory.prototype.init.call(this, settings);
    };

    /**
     * Defines the object type
     * @type {String}
     */
    Factory.prototype.type = "Factory";

    /**
     * Defines the object family
     * @type {String}
     */
    Factory.prototype.family = "Factory";
    /**
     * Initializes the object with the default values
     * @param  {Object} options Contructor options
     */
    Factory.prototype.init = function (options) {
        var defaults;

        if (!options) {
            options = {};
        }
        defaults = {
            defaultProduct: options.defaultProduct || "element",
            products: options.products || {"element": PMUI.core.Element}
        };
        this.setDefaultProduct(defaults.defaultProduct)
            .setProducts(defaults.products);
    };

    /**
     * Sets the default product property
     * @param {String} def Default value
     */
    Factory.prototype.setDefaultProduct = function (def) {
        this.defaultProduct = def;
        return this;
    };

    /**
     * Sets the product object
     * @param {Object} products Products object
     */
    Factory.prototype.setProducts = function (products) {
        this.products = products;
        return this;
    };
    /**
     * Removes a product from the factory.
     * @param  {String|Object} product The product to be removed, it can be:
     *
     *  - a string: the product referenced with that pmType will be removed from factory.
     *  - a object: the constructor, all the products with that constructor will be removed.
     * @return {[type]}         [description]
     */
    Factory.prototype.removeProduct = function (product) {
        var key,
            products = this.products;

        if (typeof product === 'string') {
            delete products[product];
        } else {
            for (key in products) {
                if (products[key] === product) {
                    delete products[key];
                }
            }
        }
        return this;
    };
    /**
     * Removes all the productrs from the factory.
     * @chainable
     */
    Factory.prototype.clearProducts = function () {
        var key;

        for (key in this.products) {
            this.removeProduct(key);
        }
        return this;
    };
    /**
     * Register a new product into the products object
     * @param  {String} name  Product type
     * @param  {Object} classObj Product Class
     * @chainable
     */
    Factory.prototype.register = function (name, classObj) {
        var aux = this.products || {};

        aux[name] = classObj;
        this.products = aux;
        return this;
    };

    /**
     * Retuns a new instance (product)
     * @param  {String} type    Product type
     * @param  {Object} options Settings object
     * @return {Object}         Instance of the object
     */
    Factory.prototype.build = function (type, options) {
        var Constructor,
            instance;

        if (this.isValidName(type)) {
            Constructor = this.products[type];
            instance = new Constructor(options);
        } else {
            throw new Error('The type "' + type + '" has not valid constructor or is undefined.');
        }
        return instance;
    };

    /**
     * Retuns true if the type is valid into the products object
     * @param  {String}  name Product Name
     * @return {Boolean}
     */
    Factory.prototype.isValidName = function (name) {
        var test = this.products[name];

        return !!test;
    };

    /**
     * Returns true if the class input is instance of one class into the product object
     * @param  {Object}  className [description]
     * @return {Boolean}           [description]
     */
    Factory.prototype.isValidClass = function (className) {
        var valid = false;

        jQuery.each(this.products, function (type, classProduct) {
            if (className instanceof classProduct) {
                valid = true;
            }
        });
        return valid;
    };


    /**
     * Comprobes the obj in and returns the instance of the object
     * @param  {Object} obj Input can be an isntance or an object with the pmType property or simply a JSON
     * @return {Object}     Returns an instance made from the products object.
     */
    Factory.prototype.make = function (obj) {
        var product,
            productType = obj.pmType || '';

        if (this.isValidClass(obj)) {
            product = obj;
        } else if (this.isValidName(productType)) {
            product = this.build.call(this, productType, obj);
        } else {
            product = this.build.call(this, this.defaultProduct, obj);
        }
        return product;
    };

    //Create a namespace for Factory class
    PMUI.extendNamespace('PMUI.util.Factory', Factory);

    // Publish to NodeJS environment
    if (typeof exports !== 'undefined') {
        module.exports = Factory;
    }

}());
(function () {
    /**
     * @class PMUI.util.Color
     * This class holds the representation and operations of RGBa representation of color,
     * it's very useful if we want to save color constants as an instance and later get the representation
     * in CSS.
     *
     *      //e.g.
     *      var color = new PMUI.util.Color(
     *          128,    // red
     *          128,    // green
     *          128,    // blue
     *          1       // opacity
     *      )
     *
     * @constructor Creates an instance of this class.
     * @param {number} red
     * @param {number} green
     * @param {number} blue
     * @param {number} opacity
     * @return {PMUI.util.Color}
     */
    var Color = function (red, green, blue, opacity) {
        /**
         * Red value of the RGB Color
         * @property {number} [red=0]
         */
        this.red = (!red) ? 0 : red;
        /**
         * Green value of the RGB Color
         * @property {number} [green=0]
         */
        this.green = (!green) ? 0 : green;
        /**
         * Blue value of the RGB Color
         * @property {number} [blue=0]
         */
        this.blue = (!blue) ? 0 : blue;
        /**
         * Opacity of the RGB Color
         * @property {number} [opacity=1]
         */
        this.opacity = (!opacity) ? 1 : opacity;
    };

    /**
     * Type of this class
     * @property {String}
     */
    Color.prototype.type = "Color";

    /**
     * Constant for the color grey
     * @property {PMUI.util.Color} [GREY=new Color(192, 192, 192, 1)]
     */
    Color.GREY = new Color(192, 192, 192, 1);

    /**
     * Returns the red value of the RGB Color
     * @returns {number}
     */
    Color.prototype.getRed = function () {
        return this.red;
    };

    /**
     * Returns the green value of the RGB Color
     * @returns {number}
     */
    Color.prototype.getGreen = function () {
        return this.green;
    };

    /**
     * Returns the blue value of the RGB Color
     * @returns {number}
     */
    Color.prototype.getBlue = function () {
        return this.blue;
    };

    /**
     * Returns the opacity of the RGB Color
     * @returns {number}
     */
    Color.prototype.getOpacity = function () {
        return this.opacity;
    };

    /**
     * Sets the red value of the RGB Color
     * @param {number} newRed
     * @chainable
     */
    Color.prototype.setRed = function (newRed) {
        if (typeof newRed === "number" && newRed >= 0 && newRed <= 255) {
            this.red = newRed;
        }
        return this;
    };

    /**
     * Sets the green value of the RGB Color
     * @param {number} newRed
     * @chainable
     */
    Color.prototype.setGreen = function (newGreen) {
        if (typeof newGreen === "number" && newGreen >= 0 && newGreen <= 255) {
            this.green = newGreen;
        }
        return this;
    };

    /**
     * Sets the blue value of the RGB Color
     * @param {number} newBlue
     * @chainable
     */
    Color.prototype.setBlue = function (newBlue) {
        if (typeof newBlue === "number" && newBlue >= 0 && newBlue <= 255) {
            this.blue = newBlue;
        }
        return this;
    };

    /**
     * Sets the opacity of the RGB Color
     * @param {number} newOpacity
     * @chainable
     */
    Color.prototype.setOpacity = function (newOpacity) {
        if (typeof newOpacity === "number" && newOpacity >= 0 && newOpacity <= 255) {
            this.opacity = newOpacity;
        }
        return this;
    };

    /**
     * Returns the css representation of the RGB color
     *      //e.g.
     *      var color = new PMUI.util.Color(10, 20, 30, 0.1);
     *      color.getCSS();         // "rgba(10, 20, 30, 0.1)"
     * @returns {String}
     */
    Color.prototype.getCSS = function () {
        var css = "rgba(" + this.red + "," + this.green + "," + this.blue +
            "," + this.opacity + ")";
        return css;
    };

    PMUI.extendNamespace('PMUI.util.Color', Color);
}());

(function () {
    /**
     * @class PMUI.util.Point
     * Class to represent points in the PMDraw library
     *
     *        // e.g.
     *        var p = new PMUI.util.Point(100, 100);
     *
     * @constructor Creates an instance of this class
     * @param {number} xCoordinate x-coordinate of the point
     * @param {number} yCoordinate y-coordinate of the point
     * @return {PMUI.util.Point}
     */
    var Point = function (xCoordinate, yCoordinate) {
        /**
         * x coordinate of the point in the plane
         */
        this.x = xCoordinate;
        /**
         * y coordinate of the point in the plane
         */
        this.y = yCoordinate;
    };

    /**
     * Type of this class
     * @property {String}
     */
    Point.prototype.type = "Point";

    /**
     * Returns the X coordinate
     * @property {number}
     **/
    Point.prototype.getX = function () {
        return this.x;
    };

    /**
     * Returns the Y coordinate
     * @property {number}
     **/
    Point.prototype.getY = function () {
        return this.y;
    };

    /**
     * Adds `other` point to `this` point and returns a new point with those coordinates.
     *
     *      // e.g.
     *      var p1 = new PMUI.util.Point(3, 5),
     *          p2 = new PMUI.util.Point(2, 3);
     *      p1.add(p2);     // new Point(5, 8)
     *
     * @param {PMUI.util.Point} other Point to be added to the current point
     * @returns {PMUI.util.Point}
     */
    Point.prototype.add = function (other) {
        return new Point(this.x + other.x, this.y + other.y);
    };

    /**
     * Subtracts the other point to the one that called the function.
     *
     *      // e.g.
     *      var p1 = new PMUI.util.Point(3, 5),
     *          p2 = new PMUI.util.Point(2, 3);
     *      p1.subtract(p2);     // new Point(1, 2)
     *
     * @param {PMUI.util.Point} other Point to be added to the current point
     * @returns {PMUI.util.Point}
     */
    Point.prototype.subtract = function (other) {
        return new Point(this.x - other.x, this.y - other.y);
    };

    /**
     * Multiplies the point with a scalar k.
     *
     *      // e.g.
     *      var p1 = new PMUI.util.Point(3, 5),
     *          k = 3;
     *      p1.multiply(k);     // new Point(9, 15)
     *
     * @param {number} k
     * @return {PMUI.util.Point}
     */
    Point.prototype.multiply = function (k) {
        return new Point(this.x * k, this.y * k);
    };

    /**
     * Determine if the points are equal.
     *
     *      // e.g.
     *      var p1 = new PMUI.util.Point(3, 5),
     *          p2 = new PMUI.util.Point(2, 3),
     *          p3 = new PMUI.util.Point(3, 5);
     *      p1.equals(p2);     // false
     *      p1.equals(p3);     // true
     *      p1.equals(p1);     // true
     *
     * @param {PMUI.util.Point} other Point to be compared with the current point
     * @returns {boolean}
     */
    Point.prototype.equals = function (other) {
        return (Math.abs(this.x - other.x) < PMUI.draw.Geometry.eps) &&
            (Math.abs(this.y - other.y) < PMUI.draw.Geometry.eps);
    };

    /**
     * Determine the distance between two Points
     *
     *      // e.g.
     *      // distance = sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2))
     *      var p1 = new PMUI.util.Point(3, 5),
     *          p2 = new PMUI.util.Point(2, 3);
     *      p1.getDistance(p2);         // sqrt(1 + 4)
     *
     * @param {PMUI.util.Point} other Point to be calculated from current point
     * @returns {number}
     **/
    Point.prototype.getDistance = function (other) {
        return Math.sqrt(
            (this.x - other.x) * (this.x - other.x) +
            (this.y - other.y) * (this.y - other.y)
        );
    };

    /**
     * Determine the squared distance between two Points
     *
     *      // e.g.
     *      // distance = sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2))
     *      // but since it's the squared distance then
     *      // distance = pow(distance, 2)
     *      var p1 = new PMUI.util.Point(3, 5),
     *          p2 = new PMUI.util.Point(2, 3);
     *      p1.getSquaredDistance(p2);         // (1 + 4)
     *
     * @param {PMUI.util.Point} other Point to be calculated from current point
     * @returns {number}
     **/
    Point.prototype.getSquaredDistance = function (other) {
        return (this.x - other.x) * (this.x - other.x) +
            (this.y - other.y) * (this.y - other.y);
    };

    /**
     * Determine the manhattan distance between two Points
     *
     *      // e.g.
     *      var p1 = new PMUI.util.Point(3, 5),
     *          p2 = new PMUI.util.Point(2, 3);
     *      p1.getManhattanDistance(p2);         // (1 + 2)
     *
     * @param {PMUI.util.Point} other Point to be calculated from current point
     * @returns {number}
     **/
    Point.prototype.getManhattanDistance = function (other) {
        return Math.abs(this.x - other.x) + Math.abs(this.y - other.y);
    };

    /**
     * Makes a clone of this
     *
     *      // e.g.
     *      var p1 = new PMUI.util.Point(3, 5),
     *          cloneP1;
     *      cloneP1 = p1.clone();       // cloneP1 is Point(3, 5)
     *
     * @returns {PMUI.util.Point} This point
     */
    Point.prototype.clone = function () {
        return new Point(this.x, this.y);
    };

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== 'undefined') {
        module.exports = Point;
    }

    // extend namespace
    PMUI.extendNamespace('PMUI.util.Point', Point);

}());

(function () {
    /**
     * @class PMUI.draw.Graphics
     * Class Graphics is the HTMLElement drawing engine used to draw lines (as HTMLElement divs),
     * arcs (using HTMLElement divs) and ovals.
     * Currently some elements in the designer are completely represented with the drawing engine:
     *
     * - Connection => a set of segments (regular, segmented, dotted)
     * - Port => oval
     * - Intersection => arc
     *
     * Some important notes:
     *
     * - Currently this class acts as an interface between the library **PMDraw** and the HTMLElement
     * drawing engine wz_jsGraphics
     * - The drawing engine constructor needs the HTMLElement where it will go as a parameter **(this
     * HTMLElement must exist in the DOM)**
     *
     * @constructor
     * Creates an instance of this class (currently it's an interface of the wz_graphics framework)
     * @param {Object} html This parameter can be either an id, or an html object
     */
    var Graphics = function (html) {

        if (!html) {
            return null;
        }

        /**
         * Create an instance of the class JSGraphics (this.graphics is an interface)
         * @type {Object}
         */
        this.graphics = new JSGraphics(html);

        /**
         * Creates an instance of the class Color (color black)
         * @type {PMUI.util.Color}
         */
        this.color = new PMUI.util.Color(0, 0, 0);
    };

    /**
     * Draws a line of a given type between two points.
     *
     * @param {number} x1
     * @param {number} y1
     * @param {number} x2
     * @param {number} y2
     * @param {string} type the type of line we wish to draw
     * @param {PMUI.util.Color} color The color of the segment
     * @param {number} segLength the segment length for segmented and segmentdot type of line
     * @param {number} spaceLength the space length for segmented and segmentdot type of line
     * @param {boolean} [doNotErasePrevious] if set to true then it won't clear the elements drew with this instance
     */
    Graphics.prototype.drawLine = function (x1, y1, x2, y2, type, color, segLength, spaceLength, doNotErasePrevious) {

        if (!doNotErasePrevious) {
            this.graphics.clear();
        }

        if (!type) {
            type = "regular";
        }
        switch (type) {
            case "dotted":
                this.graphics.setStroke(-1);
                break;
            case "segmented":
                this.graphics.setStroke(1);
                this.graphics.drawLine = this.makeSegmentedLine;
                break;
            case "segmentdot":
                this.graphics.setStroke(1);
                this.graphics.drawLine = this.makeSegmentDotLine;
                break;
            default:
                this.graphics.setStroke(1);
        }

        this.graphics.setColor(color.getCSS());
        this.graphics.drawLine(x1, y1, x2, y2, segLength, spaceLength);
        this.graphics.paint();

    };

    /**
     * Returns the color that was being used for drawing
     * @returns {PMUI.util.Color}
     */
    Graphics.prototype.getColor = function () {
        return this.color;
    };

    /**
     * Sets the color to be used for drawing
     * @param newColor
     * @chainable
     */
    Graphics.prototype.setColor = function (newColor) {
        if (newColor.type === "Color") {
            this.color = newColor;
        }
        return this;
    };

    /**
     * This function will make a segmented line between two points in the same axis,
     * if points in different axis are provided the method would simple return
     *
     * @param {number} x1
     * @param {number} y1
     * @param {number} x2
     * @param {number} y2
     * @param {number} segmentLength the segment length for segmented and
     * segmentdot type of line
     * @param {number} spLength the space length for segmented and
     * segmentdot type of line
     */
    Graphics.prototype.makeSegmentedLine = function (x1, y1, x2, y2, segmentLength, spLength) {
        var dx,
            dy,
            aux,
            segLength = 4,
            spaceLength = 3,
            diff = 0,
            x,
            y;

        //not same axis so just return
        if ((x2 !== x1 && y2 !== y1)) {
            return;
        }

        if (x2 === x1) {
            //same point just return
            if (y2 === y1) {
                return;
            }
            dx = 0;
            //swap
            if (y2 < y1) {
                aux = y2;
                y2 = y1;
                y1 = aux;
            }
            dy = diff = y2 - y1;
        } else {
            dy = 0;
            if (x2 < x1) {
                aux = x2;
                x2 = x1;
                x1 = aux;
            }
            dx = diff = x2 - x1;
        }

        x = x1;
        y = y1;

        if (diff < 7) {
            segLength = 2;
            spaceLength = 1;
        }

        segLength = (!segmentLength) ? segLength : segmentLength;
        spaceLength = (!spLength) ? spaceLength : spLength;

        if (dy === 0) {
            while (dx > 0) {
                if (dx >= segLength) {
                    this._mkDiv(x, y, segLength, 1);
                    x += segLength + spaceLength;
                    dx -= (segLength + spaceLength);
                } else {
                    this._mkDiv(x, y, dx, 1);
                    dx = 0;
                }
            }
        } else {
            while (dy > 0) {
                if (dy >= segLength) {
                    this._mkDiv(x, y, 1, segLength);
                    y += segLength + spaceLength;
                    dy -= (segLength + spaceLength);
                } else {
                    this._mkDiv(x, y, 1, dy);
                    dy = 0;
                }
            }
        }
    };

    /**
     * This function will make a segment between two points in the same axis with
     * the following structure segment-dot-segment
     * if points in different axis are provided the function will simply return.
     *
     * @param {number} x1
     * @param {number} y1
     * @param {number} x2
     * @param {number} y2
     * @param {number} segmentLength the segment length for segmented and
     * segmentdot type of line
     * @param {number} spLength the space length for segmented and
     * segmentdot type of line
     */

    Graphics.prototype.makeSegmentDotLine = function (x1, y1, x2, y2, segmentLength, spLength) {
        var dx,
            dy,
            aux,
            segLength = 7,
            spaceLength = 4,
            dotLength = 1,
            diff = 0,
            x,
            y;

        //not same axis so just return
        if ((x2 !== x1 && y2 !== y1)) {
            return;
        }


        if (x2 === x1) {
            //same point just return
            if (y2 === y1) {
                return;
            }
            dx = 0;
            //swap
            if (y2 < y1) {
                aux = y2;
                y2 = y1;
                y1 = aux;
            }
            dy = y2 - y1;
            diff = dy;
        } else {
            dy = 0;
            if (x2 < x1) {
                aux = x2;
                x2 = x1;
                x1 = aux;
            }
            dx = x2 - x1;
            diff = dx;
        }

        x = x1;
        y = y1;

        segLength = (!segmentLength) ? segLength : segmentLength;
        spaceLength = (!spLength) ? spaceLength : spLength;

        if (dy === 0) {
            while (dx > 0) {
                if (dx >= segLength) {
                    this._mkDiv(x, y, segLength, 1);
                    dx -= (segLength + spaceLength);
                    x += segLength + spaceLength;
                    if (dx > 0) {
                        this._mkDiv(x, y, dotLength, 1);
                        dx -= (dotLength + spaceLength);
                        x += dotLength + spaceLength;
                    }
                } else {
                    this._mkDiv(x, y, dx, 1);
                    dx = 0;
                }
            }
        } else {
            while (dy > 0) {
                if (dy >= segLength) {
                    this._mkDiv(x, y, 1, segLength);
                    dy -= (segLength + spaceLength);
                    y += segLength + spaceLength;
                    if (dy > 0) {
                        this._mkDiv(x, y, 1, dotLength);
                        dy -= (dotLength + spaceLength);
                        y += dotLength + spaceLength;

                    }
                } else {
                    this._mkDiv(x, y, 1, dy);
                    dy = 0;
                }
            }
        }

    };
    /**
     * Draws an arc with the center `[cx, cy]`, with a radius equal to `radius` from `startAngle` to `endAngle`
     * and drawing a line every `step` steps.
     * Logic:
     *
     * 1. Let's assume that we have a circle with center `[cx, cy]` and with a radius equal to `radius`
     * 2. We want to draw only a portion of the circle (from `startAngle` to `endAngle`)
     * 3. Given any angle of the circle `0 <= angle < 360` we can get its `x` and `y` coordinates using
     *    Pythagoras triangle rectangle laws.
     *      - We know that `hyp^2 = dx^2 + dy^2` and that `hyp = radius`
     *      - We know that `cos(angle) = dx / radius` so `dx = radius * cos(angle)`
     *      - We know that `sin(angle) = dy / radius` so `dx = radius * cos(angle)`
     * 4. Finally let's use the given center of the circle to move the triangle
     *
     * @param {number} cx
     * @param {number} cy
     * @param {number} radius
     * @param {number} startAngle
     * @param {number} endAngle
     * @param {number} step
     */
    Graphics.prototype.drawArc = function (cx, cy, radius, startAngle, endAngle, step) {
        var x,
            y,
            angle = startAngle;

        if (!step) {
            step = 10;
        }

        while (Math.abs(angle - endAngle) > 1e-5) {
            angle = (angle + step) % 360;
            x = cx + radius * Math.cos(angle * PMUI.draw.Geometry.pi / 180.0);
            y = cy + radius * Math.sin(angle * PMUI.draw.Geometry.pi / 180.0);
            this.graphics.drawLine(x, y, x, y);
        }
    };

    PMUI.extendNamespace('PMUI.draw.Graphics', Graphics);
}());

(function () {
    /**
     * @class PMUI.draw.Geometry
     * A little object that encapsulates most geometry functions used in the designer, most of the examples
     * are in 'spec/draw/geometry.spec.js'
     *
     * @singleton
     */
    var Geometry = {
        /**
         * The number pi
         * @property {number} [pi=Math.acos(-1)]
         */
        pi: Math.acos(-1),
        /**
         * Epsilon used for the correctness in comparison of float numbers
         * @property {number} [eps=1e-8]
         */
        eps: 1e-8,
        /**
         * Calculates the cross product of 2-dimensional vectors
         * @param {PMUI.util.Point} p1
         * @param {PMUI.util.Point} p2
         * @return {number}
         */
        cross: function (p1, p2) {
            return p1.x * p2.y - p1.y * p2.x;
        },
        /**
         * Calculates the SIGNED area of a parallelogram given three points, these three points are the points
         * that conforms the triangle that is half of the parallelogram, so. the area of the triangle
         * defined with these points is half the returned number (this method can return negative values)
         *
         *      // e.g.
         *      var p1 = new PMUI.util.Point(0, 0),
         *          p2 = new PMUI.util.Point(0, 1),
         *          p3 = new PMUI.util.Point(1, 0),
         *          parallelogramArea,
         *          triangleArea;
         *
         *      parallelogramArea = Geometry.area(p1, p2, p3)   // -1 (area of the parallelogram)
         *      triangleArea = parallelogramArea / 2            // -0.5 (area of the triangle)
         *
         * @param {PMUI.util.Point} p1
         * @param {PMUI.util.Point} p2
         * @param {PMUI.util.Point} p3
         * @return {number}
         */
        area: function (p1, p2, p3) {
            var auxP2 = p2.clone(),
                auxP3 = p3.clone();
            return this.cross(auxP2.subtract(p1), auxP3.subtract(p1));
        },
        /**
         * Determines if the point P is on segment AB
         * @param {PMUI.util.Point} P
         * @param {PMUI.util.Point} A
         * @param {PMUI.util.Point} B
         * @return {boolean}
         */
        onSegment: function (P, A, B) {
            return (Math.abs(this.area(A, B, P)) < this.eps &&
            P.x >= Math.min(A.x, B.x) && P.x <= Math.max(A.x, B.x) &&
            P.y >= Math.min(A.y, B.y) && P.y <= Math.max(A.y, B.y));
        },
        /**
         * Checks if two perpendicular segments intersect, if so it returns the intersection point,
         * (this method only allows the perpendicular segment to be parallel to the x and y axis)
         * @param {PMUI.util.Point} A
         * @param {PMUI.util.Point} B
         * @param {PMUI.util.Point} C
         * @param {PMUI.util.Point} D
         * @return {Object}
         */
        perpendicularSegmentIntersection: function (A, B, C, D) {
            var clone,
                returnValue = null;

            // swap the segments if possible
            if (A.x > B.x || A.y > B.y) {
                clone = A.clone();
                A = B.clone();
                B = clone;
            }

            if (C.x > D.x || C.y > D.y) {
                clone = C.clone();
                C = D.clone();
                D = clone;
            }

            if (A.x === B.x) {
                if (C.y === D.y && C.x < A.x && A.x < D.x &&
                    A.y < C.y && C.y < B.y) {
                    returnValue = new PMUI.util.Point(A.x, C.y);
                }
            } else if (A.y === B.y) {
                if (C.x === D.x && A.x < C.x && C.x < B.x &&
                    C.y < A.y && A.y < D.y) {
                    returnValue = new PMUI.util.Point(C.x, A.y);
                }
            }
            return returnValue;
        },
        /**
         * Determines if segment AB intersects with segment CD (won't check infinite intersections),
         * if `strict` is set to `true` then it'll consider the case when one end of a segment is right in the
         * other segment
         * @param {PMUI.util.Point} A
         * @param {PMUI.util.Point} B
         * @param {PMUI.util.Point} C
         * @param {PMUI.util.Point} D
         * @param {boolean} [strict]
         * @return {boolean}
         */
        segmentIntersection: function (A, B, C, D, strict) {

            var area1 = this.area(C, D, A),
                area2 = this.area(C, D, B),
                area3 = this.area(A, B, C),
                area4 = this.area(A, B, D),
                returnValue;
            if (((area1 > 0 && area2 < 0) || (area1 < 0 && area2 > 0)) &&
                ((area3 > 0 && area4 < 0) || (area3 < 0 && area4 > 0))) {
                return true;
            }

            returnValue = false;
            if (strict) {
                if (area1 === 0 && this.onSegment(A, C, D)) {
                    returnValue = true;
                } else if (area2 === 0 && this.onSegment(B, C, D)) {
                    returnValue = true;
                } else if (area3 === 0 && this.onSegment(C, A, B)) {
                    returnValue = true;
                } else if (area4 === 0 && this.onSegment(D, A, B)) {
                    returnValue = true;
                }
            }
            return returnValue;
        },
        /**
         * Checks if two segments intersect, if so it returns the intersection point
         * @param {PMUI.util.Point} A
         * @param {PMUI.util.Point} B
         * @param {PMUI.util.Point} C
         * @param {PMUI.util.Point} D
         * @return {PMUI.util.Point}
         */
        segmentIntersectionPoint: function (A, B, C, D) {
            return A.add((B.subtract(A))
                .multiply(this.cross(C.subtract(A), D.subtract(A)) /
                    this.cross(B.subtract(A), D.subtract(C))));
        },
        /**
         * Determines whether a point is in a given rectangle or not given its
         * upperLeft and bottomRight corner (consider that a rectangle is turned in the y-axis)
         * @param {PMUI.util.Point} point
         * @param {PMUI.util.Point} upperLeft
         * @param {PMUI.util.Point} bottomRight
         * @return {boolean}
         */
        pointInRectangle: function (point, upperLeft, bottomRight) {
            return (point.x >= upperLeft.x && point.x <= bottomRight.x &&
            point.y >= upperLeft.y && point.y <= bottomRight.y);
        },
        /**
         * Determines whether a point is in a circle or not given its center and
         * radius
         * @param {PMUI.util.Point} point
         * @param {PMUI.util.Point} center
         * @param {number} radius
         * @returns {boolean}
         */
        pointInCircle: function (point, center, radius) {
            return center.getDistance(point) <= radius;
        },
        /**
         * Determine whether a point is inside a rhombus or not given its center
         * and its points in clockwise order
         * @param {PMUI.util.Point} point
         * @param {Array} rhombus
         * @param {PMUI.util.Point} center
         * @return {boolean}
         */
        pointInRhombus: function (point, rhombus, center) {
            var i,
                j = rhombus.length - 1;
            for (i = 0; i < rhombus.length; j = i, i += 1) {
                if (this.segmentIntersection(center, point,
                        rhombus[j], rhombus[i], true) &&
                    this.onSegment(point, rhombus[j], rhombus[i]) === false) {
                    return false;
                }
            }
            return true;
        }
    };

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== 'undefined') {
        module.exports = Geometry;
    }

    // extend the namespace
    PMUI.extendNamespace('PMUI.draw.Geometry', Geometry);
}());

(function () {
    /**
     * @class PMUI.event.Action
     * Handles the action for buttons, menues, toolbars
     *
     * Example:
     *
     *      var action = new PMUI.event.Action({
     *          text: 'Save Document',
     *          icon: 'pm-icon-save',
     *          disabled: false,
     *          handler: function () {
     *              alert('Action has been called!');
     *          }
     *      });
     *
     * Using action as parameter to create another objects:
     *
     *      var button = new PMUI.ui.Button(action);
     *
     *      var form = new PMUI.form.Form({
     *          ...
     *          buttons: [
     *              action,
     *              {
     *                  text: 'Refresh',
     *                  handler: function() {
     *                      obj.refresh()  //this object is fake used for sample purposes only
     *                  }
     *              }
     *          ]
     *          ...
     *      });
     *
     * Actions are created to handle user functionality you can extend or use over several places or components
     *
     *
     * @constructor
     * Creates a new instance of object
     * @param {Object} options
     */
    var Action = function (options) {
        /**
         * Defines the text of the action
         * @type {String}
         */
        this.actionText = null;
        /**
         * Defines the icon asssociated with the action
         * @type {String}
         */
        this.actionIcon = null;
        /**
         * Defines the state of the action
         * @type {Boolean}
         */
        this.disabled = false;
        /**
         * Defines the action to be executed
         * @type {Function}
         */
        this.handler = null;

        Action.prototype.init.call(this, options);
    };

    /**
     * Defines the object's type
     * @type {String}
     */
    Action.prototype.type = 'Action';
    /**
     * Defines the object's family
     * @type {String}
     */
    Action.prototype.family = 'Action';

    /**
     * Defines the action flag
     * @type {Boolean}
     */
    Action.prototype.isAction = true;

    /**
     * @private
     * Initializes the object with the default values
     * @param  {Object} options Constructor options
     */
    Action.prototype.init = function (options) {
        var defaults;
        defaults = {
            icon: null,
            text: null,
            disabled: false,
            handler: function () {
            }
        };
        jQuery.extend(true, defaults, options);
        this.setActionIcon(defaults.icon)
            .setActionText(defaults.text)
            .setDisable(defaults.disabled)
            .setHandler(defaults.handler);
    };

    /**
     * Sets the action Icon
     * @param {String} icon Icon URL or class
     */
    Action.prototype.setActionIcon = function (icon) {
        this.actionIcon = icon;
        return this;
    };

    /**
     * Sets the action's text
     * @param {String} text Actions's name
     */
    Action.prototype.setActionText = function (text) {
        this.actionText = text;
        return this;
    };

    /**
     * Sets the disabled value
     * @param {Boolean} value
     */
    Action.prototype.setDisable = function (value) {
        if (typeof(value) === 'boolean') {
            this.disabled = value;
        }
        return this;
    };

    /**
     * Returns the icon url or class
     * @return {String} [description]
     */
    Action.prototype.getIcon = function () {
        return this.actionIcon;
    };

    /**
     * Returns the action name
     * @return {String} [description]
     */
    Action.prototype.getText = function () {
        return this.actionText;
    };

    /**
     * Sets the handler function
     * @param {Function} fn
     */
    Action.prototype.setHandler = function (fn) {
        if (typeof(fn) === 'function') {
            this.handler = fn;
        }
        return this;
    };

    /**
     * Enables the action functionality
     */
    Action.prototype.enable = function () {
        this.setDisable(false);
    };

    /**
     * Disables the action functionality
     */
    Action.prototype.disable = function () {
        this.setDisable(true);
    };

    /**
     * Returns if the action is enabled
     * @return {Boolean}
     */
    Action.prototype.isEnabled = function () {
        return !this.disabled;
    };

    /**
     * Executes the action if is enabled and has a valid handler
     */
    Action.prototype.execute = function () {
        if (!this.disabled && typeof this.handler === 'function') {
            this.handler();
        }
    };


    if (typeof exports !== "undefined") {
        module.exports = Action;
    }

    PMUI.extendNamespace('PMUI.event.Action', Action);

}());
(function () {
    /**
     * @class PMUI.core.Base
     * Base class for any other class in PMUI, this class is an abstract so it shouldn't be instantiated.
     * @param {Object} settings All the settings the the class requires
     *
     * Usage example:
     *
     *      var anObject = new PMUI.core.Base({
     *          id: "myUniqueId"
     *      });
     *
     * @constructor
     * Creates a new instance of the class 'Base'.
     * @param {Object} [options={}] The config options for the object.
     *
     * @cfg {String} [id=[an autogenerated unique id]] The unique identifier for the object. If it is not specified
     * then one is created an assigned to it.
     */
    var Base = function (settings) {
        /**
         * Unique ID
         * @type {String}
         */
        this.id = null;
        Base.prototype.init.call(this, settings);
    };
    /**
     * Class type
     * @type {String}
     */
    Base.prototype.type = 'Base';
    /**
     * Class family
     * @type {String}
     */
    Base.prototype.family = 'Core';
    /**
     * Initialize the object
     * @param  {Object} settings Settings for Base Class
     * @return {String}          the generated id
     * @private
     */
    Base.prototype.init = function (settings) {
        var defaults = {
            id: 'pmui-' + PMUI.generateUniqueId()
        };

        jQuery.extend(true, defaults, settings);

        this.setID(defaults.id);
    };
    /**
     * Set the id property for the object
     * @param {String} id
     * @chainable
     */
    Base.prototype.setID = function (id) {
        this.id = id;
        return this;
    };
    /**
     * Returns the id property from the object
     * @return {String} [description]
     */
    Base.prototype.getID = function () {
        return this.id;
    };
    /**
     * Returns the object's type
     * @return {String}
     */
    Base.prototype.getType = function () {
        return this.type;
    };
    /**
     * Return's the object's family name
     * @return {String}
     */
    Base.prototype.getFamily = function () {
        return this.family;
    };

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = Base;
    }

    PMUI.extendNamespace('PMUI.core.Base', Base);
}());
(function () {
    /**
     * @class PMUI.core.Element
     * Base class to handle objects that have a visual representation (HTML representation).
     * @extends PMUI.core.Base
     *
     * Usage example:
     *
     *      @example
     *      var element;
     *
     *      element = new PMUI.core.Element({
     *          x: 10,
     *          y: 20,
     *          positionMode: "absolute",
     *          width: 200,
     *          height: 200,
     *          style: {
     *              cssProperties: {
     *                  "background-color": "red"
     *              },
     *              cssClasses: [".square"]
     *          }
     *      });
     *
     *      document.body.appendChild(element.getHTML());
     *
     * @constructor
     * Create a new instace of the class 'Element'
     * @param {Object} options
     *
     * @cfg {String} [elementTag="div"] The html tag for the object's HTML element.
     * @cfg {String} [positionMode="relative"] The css position for the object's HTML element. It can take one of the
     * following values "static", "absolute", "fixed", "relative", "inherit" or "". Please read about the css "position"
     * property to know about how each value impacts in the html.
     * @cfg {Object} [style={cssProperties:{}, cssClasses:[]}] A JSON object which will contain the css properties for
     * the object's html.
     *
     * This object can have optionally two properties:
     *
     * - cssProperties: another JSON object in which each property can be a css property (if the css property has any
     * special character like "-" enclose the property name between quotes), the value can be any valid css value for
     * the property.
     * - cssClasses: An array where each element is a String, this String is the css class name.
     *
     * @cfg {Number} [x=0] The position coordinate in the x axis for the object's HTML, please note that the visual
     * position will depend on the {@link PMUI.core.Element#cfg-positionMode positionMode option}.
     * @cfg {Number} [y=0] The position coordinate in the y axis for the object's HTML, please note that the visual
     * position will depend on the {@link PMUI.core.Element#cfg-positionMode positionMode option}.
     * @cfg {Number|String} [width="auto"] The width for the object's HTML, it can be a Number or an String. In the
     * latter case the value must be "auto" or "inherit" or have one of the following formats:
     *
     * - "##px".
     * - "##%".
     * - "##em"
     *
     * Note that ## is a number.
     * @cfg {Number|String} [height="auto"] The height for the object's HTML, the values that can take are the same
     * specified in the {@link PMUI.core.Element#cfg-width}.
     * @cfg {Number|String} [zOrder="auto"] The position in the z axis for the object's HTML, it can be a Number or a
     * String, in the latter case the value only can be "auto" or "inherit".
     * @cfg {Boolean} [visible=true] If the object will be visible or not.
     * @cfg {Number} [proportion=1] A numeric value that specifies the final width of the object's HTML in case to be
     * contained by a {@link PMUI.core.Container Container} which is applying a {@link PMUI.layout.Layout Layout}.
     * @cfg {String} [display=""] The display property specifies the type of box used for an HTML element.
     * find out more about the css display property
     * @cfg {Function} [onBeforeContextMenu=null] description here.
     */
    var Element = function (settings) {
        Element.superclass.call(this, settings);
        /**
         * HTML element.
         * @type {HTMLElement}
         * @readonly
         */
        this.html = null;
        /**
         * @property {Object|PMUI.util.Style} [style={
                cssProperties: {},
                cssClasses: []
            }]
         * A {@link PMUI.util.Style Style} object or a JSON object with the settings to create a new
         * {@link PMUI.util.Style Style} object for the current's HTML element.
         * @readonly
         */
        this.style = null;
        /**
         * X coordinate for the HTML element.
         * @type {Number}
         * @readonly
         */
        this.x = null;
        /**
         * @property {Number} [y=0]
         * Y Coordinate for the HTML element.
         * @readonly
         */
        this.y = null;
        /**
         * Width for the HTML element, it can be a number or a string with the following format:
         ##px when ## is a number.
         * @type {Number|String}
         * @readonly
         */
        this.width = null;
        /**
         * Height for the HTML element, it can be a number or a string with the following format:
         ##px when ## is a number.
         * @type {Number|String}
         * @readonly
         */
        this.height = null;
        /**
         * A boolean value that indicates if the HTML element is visible or not.
         * @type {Boolean}
         * @readonly
         */
        this.visible = null;
        /**
         * A Number that indicates the HTML element's position on the Z axis.
         * @type {Number}
         * @readonly
         */
        this.zOrder = null;
        /**
         * @property {String} [elementTag="div"]
         * Tag name for the element to be created, it defaults to "div".
         * @readonly
         */
        this.elementTag = null;
        /**
         * @property {String} [positionMode="relative"]
         * Position for the object's html element, it must be a valid value for the "position" CSS property.
         * @readonly
         */
        this.positionMode = null;
        /**
         * Defines the proportion of the html.
         * @type {Number}
         */
        this.proportion = null;
        /**
         * @property {String} [display=""] The display mode for the element. It is set by the
         * {@link #cfg-display display} config option and the {@link #method-setDisplay setDisplay} method.
         * @readonly
         */
        this.display = null;
        /**
         * Defines the menu component for the element
         * @type {PMUI.ui.Menu}
         */
        this.menu = null;
        /**
         * Defines an object to handle/register events.
         * @type {Object}
         */
        this.events = {};
        /**
         * @property {Boolean} eventsDefined If the events were defined or not
         */
        this.eventsDefined = false;
        /**
         * [onContextMenu description]
         * @type {Boolean}
         */
        this.onContextMenu = false;
        /**
         * [onBeforeContextMenu description]
         * @type {Boolean}
         */
        this.onBeforeContextMenu = false;
        /**
         * The element's parent Container.
         * @type {PMUI.core.Element}
         */
        this.parent = null;
        /**
         * The HTML area in which some activities, actions can be performed (like display the context menu).
         * @type {HTMLElement}
         * @private
         */
        this.actionHTML = null;

        Element.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Base', Element);
    /**
     * The Object's type.
     * @type {String}
     */
    Element.prototype.type = 'Element';
    /**
     * Initialize the object.
     * @param  {Object} settings A JSON object with the settings to create the new class instance.
     * @private
     */
    Element.prototype.init = function (settings) {
        var defaults = {
            elementTag: "div",
            positionMode: "relative",
            menu: null,
            style: {
                cssProperties: {},
                cssClasses: []
            },
            x: 0,
            y: 0,
            width: "auto",
            height: "auto",
            zOrder: "auto",
            display: "",
            visible: true,
            proportion: 1,
            onContextMenu: null,
            onBeforeContextMenu: null
        };

        jQuery.extend(true, defaults, settings);

        this.setElementTag(defaults.elementTag)
            .setStyle(defaults.style)
            .setPositionMode(defaults.positionMode)
            .setDisplay(defaults.display)
            .setX(defaults.x)
            .setY(defaults.y)
            .setWidth(defaults.width)
            .setHeight(defaults.height)
            .setZOrder(defaults.zOrder)
            .setVisible(defaults.visible)
            .setProportion(defaults.proportion)
            .setContextMenu(defaults.menu)
            .setOnBeforeContextMenu(defaults.onBeforeContextMenu)
            .setOnContextMenuHandler(defaults.onContextMenu);
    };
    /**
     * [setOnBeforeContextMenu description]
     */
    Element.prototype.setOnBeforeContextMenu = function (handler) {
        if (!(handler === null || typeof handler === 'function')) {
            throw new Error("setOnBeforeContextMenu(): The parameter must be a function or null.");
        }
        this.onBeforeContextMenu = handler;
        return this;
    };
    /**
     * [setOnContextMenuHandler description]
     * @param {[type]} handler [description]
     */
    Element.prototype.setOnContextMenuHandler = function (handler) {
        if (typeof handler === 'function' || null) {
            this.onContextMenu = handler;
        }
        return this;
    };
    /**
     * Sets the display mode for the Element.
     * @param {String} display It can take one of the following values:
     *
     * - ""
     * - "block"
     * - "inline"
     * - "inline-block"
     * - "none"
     *
     * @chainable
     */
    Element.prototype.setDisplay = function (display) {
        if (display === "" || display === 'block' || display === 'inline'
            || display === 'inline-block' || display === 'none') {
            this.display = display;
            this.applyStyle();
        } else {
            throw new Error('The setDisplay() method only accepts one od the following options: ' +
                ' "", "block", "inline", "inline-block", "none"');
        }

        return this;
    };
    /**
     * Sets the position mode for the Element.
     * @param {String} position It can take one ot the following values:
     *
     * - "static"
     * - "asolute"
     * - "fixed"
     * - "relative"
     * - "inherit"
     *
     * @chainable
     */
    Element.prototype.setPositionMode = function (position) {
        if (position === 'static' || position === 'absolute' || position === 'fixed' || position === 'relative' ||
            position === 'inherit' || position === "") {
            this.positionMode = position;
            this.applyStyle();
        } else {
            throw new Error('The setPosition() method only accepts one of the following options:' +
                ' "static", "absolute", "fixed", "relative", "inherit" or an empty string.');
        }

        return this;
    };
    /**
     * Set the HTML tag for the HTML element to be created, note that it'll only work when its html property
     is still not set.
     * @param {String} tag a HTML tag
     * @chainable
     */
    Element.prototype.setElementTag = function (tag) {
        if (!this.html && typeof tag === 'string') {
            this.elementTag = tag;
        }

        return this;
    };
    /**
     * [addCSSClasses description]
     * @param {[type]} classes [description]
     */
    Element.prototype.addCSSClasses = function (classes) {
        this.style.addClasses(classes);
        return this;
    };
    /**
     * [addCSSProperties description]
     * @param {[type]} properties [description]
     */
    Element.prototype.addCSSProperties = function (properties) {
        this.style.addProperties(properties);
        return this;
    };
    /**
     * [removeCSSClasses description]
     * @param  {[type]} classes [description]
     * @return {[type]}         [description]
     */
    Element.prototype.removeCSSClasses = function (classes) {
        this.style.removeClasses(classes);
        return this;
    };
    /**
     * [removeCSSProperties description]
     * @param  {[type]} properties [description]
     * @return {[type]}            [description]
     */
    Element.prototype.removeCSSProperties = function (properties) {
        this.style.removeProperties(properties);
        return this;
    };
    /**
     * Set the style properties for the HTML element.
     * @param {Object|null} style an JSON structure with attributes cssProperties (another object) and cssClasses
     * (array). It also can be null, in this case no custom styles will be applied to the the element.
     * @chainable
     */
    Element.prototype.setStyle = function (style) {
        style = style || {};
        if (this.style && this.html) {
            this.style.unapplyStyle();
        }
        if (style instanceof PMUI.util.Style) {
            this.style = style;
            style.belongsTo = this;
        } else if (typeof style === 'object') {
            style.belongsTo = this;
            this.style = new PMUI.util.Style(style);
        }
        this.applyStyle();
        return this;
    };
    /**
     * Set the x position coordinate for the HTML element.
     * @chainable
     * @param {Number} x
     * @chainable
     */
    Element.prototype.setX = function (x) {
        if (typeof x === 'number') {
            this.x = x;
        } else if (/^\d+(\.\d+)?px$/.test(x)) {
            this.x = parseInt(x, 10);
        } else {
            throw new Error('setX: x param is not a number');
        }
        this.style.addProperties({left: this.x});

        return this;
    };
    /**
     * Returns the x position coordinate for the HTML element
     * @return {Number}
     */
    Element.prototype.getX = function () {
        return this.x;
    };
    /**
     * Set the y position coordinate for the HTML element.
     * @param {Number} y
     * @chainable
     */
    Element.prototype.setY = function (y) {
        if (typeof y === 'number') {
            this.y = y;
        } else if (/^\d+(\.\d+)?px$/.test(y)) {
            this.y = parseInt(y, 10);
        } else {
            throw new Error('setY: y param is not a number');
        }
        this.style.addProperties({top: this.y});
        return this;
    };
    /**
     * Returns the y position coordinate for the HTML element
     * @return {Number}
     */
    Element.prototype.getY = function () {
        return this.y;
    };
    /**
     * Set the width for the HTML element
     * @param {Number|String} width height it can be a number or a string.
     In case of using a String you only can use 'auto' or ##px or ##% or ##em when ## is a number.
     * @chainable
     */
    Element.prototype.setWidth = function (width) {
        if (typeof width === 'number') {
            this.width = width;
        } else if (/^\d+(\.\d+)?px$/.test(width)) {
            this.width = parseInt(width, 10);
        } else if (/^\d+(\.\d+)?%$/.test(width)) {
            this.width = width;
        } else if (/^\d+(\.\d+)?em$/.test(width)) {
            this.width = width;
        } else if (width === 'auto') {
            this.width = width;
        } else {
            throw new Error('setWidth: width param is not a number');
        }
        this.style.addProperties({width: this.width});
        return this;
    };
    /**
     * Returns the HTML element's width
     * @return {Number}
     */
    Element.prototype.getWidth = function () {
        return this.width;
    };
    /**
     * Set the height for the HTML element
     * @param {Number|String} height it can be a number or a string.
     In case of using a String you only can use 'auto' or 'inherit' or ##px or ##% or ##em when ## is a number.
     * @chainable
     */
    Element.prototype.setHeight = function (height) {
        if (typeof height === 'number') {
            this.height = height;
        } else if (/^\d+(\.\d+)?px$/.test(height)) {
            this.height = parseInt(height, 10);
        } else if (/^\d+(\.\d+)?%$/.test(height)) {
            this.height = height;
        } else if (/^\d+(\.\d+)?em$/.test(height)) {
            this.height = height;
        } else if (height === 'auto' || height === 'inherit') {
            this.height = height;
        } else {
            throw new Error('setHeight: height param is not a number');
        }
        this.style.addProperties({height: this.height});
        return this;
    };
    /**
     * Returns the HTML element's height.
     * @return {Number}
     */
    Element.prototype.getHeight = function () {
        return this.height;
    };
    /**
     * Set the position index om the z axis for the object's HTML element.
     * @param {Number|String} zOrder it can be a Number or a String.
     In case of using a String you only can use 'auto' or 'inherit'.
     * @chainable
     */
    Element.prototype.setZOrder = function (zOrder) {
        if (typeof zOrder === 'number') {
            this.zOrder = parseInt(zOrder, 10);
        } else if (zOrder === 'auto' || zOrder === 'inherit') {
            this.zOrder = zOrder;
        } else {
            throw new Error('setZOrder: zOrder param is not a number');
        }
        if (this.html) {
            this.style.addProperties({"z-index": this.zOrder});
        }
        return this;
    };
    /**
     * Returns the HTML element's zOrder.
     * @return {Number}
     */
    Element.prototype.getZOrder = function () {
        return this.zOrder;
    };
    /**
     * Set if the HTML element is visible or not.
     * @param {Boolean} visible
     * @chainable
     */
    Element.prototype.setVisible = function (visible) {
        visible = !!visible;
        this.visible = visible;
        if (this.html) {
            this.style.addProperties({"display": this.visible ? this.display : "none"});
        }

        return this;
    };
    /**
     * Returns a boolean value that indicates if the HTML element is visible or not.
     * @return {Boolean} [description]
     */
    Element.prototype.isVisible = function () {
        return this.visible;
    };
    /**
     * Set the HTML element's position.
     * @param {Object} position an object with x and y properties, both of them are Numbers
     * @chainable
     */
    Element.prototype.setPosition = function (position) {
        this.setX(position.x);
        this.setY(position.y);

        return this;
    };
    /**
     * Returns and object which contains the x and y positions from the HTML element.
     * @return {Object}
     */
    Element.prototype.getPosition = function () {
        return {
            x: this.getX(),
            y: this.getY()
        };
    };
    /**
     * Set the HTML element's dimension (width and height).
     * @param {Object} dimension
     * @chainable
     */
    Element.prototype.setDimension = function (dimension) {
        this.setWidth(dimension.width);
        this.setHeight(dimension.height);

        return this;
    };
    /**
     * Returns the HTML element's dimension.
     * @return {Object} and object with width and height properties.
     */
    Element.prototype.getDimension = function () {
        return {
            width: this.getWidth(),
            height: this.getHeight()
        };
    };
    /**
     * Applies the css classes and ptoperties to the element's html which the object is related.
     * @chainable
     */
    Element.prototype.applyStyle = function () {
        if (this.html) {
            this.style.applyStyle();

            this.style.addProperties({
                display: this.visible ? this.display : "none",
                position: this.positionMode,
                left: this.x,
                top: this.y,
                width: this.width,
                height: this.height,
                zIndex: this.zOrder
            });
        }
        return this;
    };
    /**
     * Creates the object's HTML element.
     * @return {HTMLElement} returns a HTML element
     */
    Element.prototype.createHTML = function () {
        var html;
        if (this.html) {
            return this.html;
        }
        html = PMUI.createHTMLElement(this.elementTag || "div");
        html.id = this.id;
        PMUI.linkToPMUIObject(html, this);
        this.html = html;
        this.actionHTML = html;
        this.applyStyle();
        if (this.eventsDefined) {
            this.defineEvents();
        }
        return this.html;
    };
    /**
     * Returns the object's HTML element, if it doesn't exist it's created then it is returned.
     * @return {HTMLElement}
     */
    Element.prototype.getHTML = function () {
        if (!this.html) {
            this.html = this.createHTML();
        }
        return this.html;
    };

    /**
     * Set the proportion of the html element.
     * @param {Number} p Proportion value.
     * @chainable
     */
    Element.prototype.setProportion = function (p) {
        this.proportion = p;
        return this;
    };

    /**
     * Creates and Instanciate an event.
     * @param {String} type  Event type.
     * @param {String} [alias] Registered Name.
     * @return {PMUI.event.Event}
     */
    Element.prototype.addEvent = function (type, alias) {
        var factory = new PMUI.event.EventFactory(),
            newEvent = factory.make(type),
            registerName = alias || PMUI.generateUniqueId();
        this.events[registerName] = newEvent;
        return newEvent;
    };

    /**
     * @abstract
     * Defines the events associated with the element.
     * @chainable
     * @abstract
     */
    Element.prototype.defineEvents = function () {
        var that = this;
        this.removeEvents();
        this.addEvent('contextmenu').listen(this.actionHTML, function (e) {
            if (typeof that.onBeforeContextMenu === 'function') {
                that.onBeforeContextMenu(that);
            }
            if (typeof that.onContextMenu === 'function') {
                that.onContextMenu(that);
            }
            if (that.menu) {
                e.stopPropagation();
                e.preventDefault();
                that.menu.show(e.pageX, e.pageY);
            }
        });
        this.eventsDefined = true;
        return this;
    };
    /**
     * Remove a specific event attached to the object.
     * @param  {String} identifier The id or alias used in the moment of event addition.
     * @chainable
     */
    Element.prototype.removeEvent = function (identifier) {
        if (this.events[identifier]) {
            this.events[identifier].unlisten();
            delete this.events[identifier];
        }
        return this;
    };
    /**
     * Removes events attached to the object.
     * @chainable
     */
    Element.prototype.removeEvents = function () {
        var k;
        for (k in this.events) {
            this.removeEvent(k);
        }
        ;
        return this;
    };
    /**
     * Calculates the width size of the container.
     *
     * @param {String} text
     * @param {String} font
     * @chainable
     */
    Element.prototype.calculateWidth = function (text, font) {
        //TODO Improve the div creation (maybe we can use a singleton for this)
        var f = font || '12px arial',
            $o = $(PMUI.createHTMLElement('div')), w;
        $o.text(text)
            .css({
                'position': 'absolute',
                'float': 'left',
                'white-space': 'nowrap',
                'visibility': 'hidden',
                'font': f
            })
            .appendTo($('body'));
        w = $o.width();

        $o.remove();

        return w;
    };
    /**
     * Sets the context menu for the component and the menu will be contained on the {@link PMUI.core.Element#menu menu} property.
     *
     * If the settings parameter is not a valid json the {@link PMUI.core.Element#menu menu} property will be an simple empty object.
     *
     * The example below shows the appropriate way to send a basic settings parameter.
     *
     *      component = new Class();
     *      component.setContextMenu({
     *          id: "id", //optional
     *          items: [
     *              {
     *                  text: "Option 1"
     *              },
     *              {
     *                  text: "Option 2"
     *              }
     *          ]
     *      });
     *
     * For more details about the context menu, the documentation is on the {@link PMUI.ui.Menu Menu} class.
     *
     * @param {Object} settings Configuration or config options for the context {@link PMUI.ui.Menu menu}.
     * @return {Object} this Represents the component of the class.
     */
    Element.prototype.setContextMenu = function (menuItems) {
        var contextMenu;
        this.removeContextMenu();
        if (menuItems !== null) {
            if (jQuery.isArray(menuItems)) {
                contextMenu = new PMUI.menu.Menu({items: menuItems});
            } else if (menuItems instanceof PMUI.menu.Menu) {
                contextMenu = menuItems;
            } else if (typeof menuItems === 'object') {
                contextMenu = new PMUI.menu.Menu(menuItems);
            }
            if (contextMenu) {
                this.menu = contextMenu.setTargetElement(this);
            }
        }
        return this;
    };
    /**
     * [removeContextMenu description]
     * @return {[type]} [description]
     */
    Element.prototype.removeContextMenu = function () {
        if (this.menu) {
            this.menu.setTargetElement(null);
            this.menu = null;
        }
        return this;
    };
    /**
     * Returns the elemnent's parent container.
     * @return {PMUI.core.Element|null} The parent container.
     */
    Element.prototype.getParent = function () {
        return this.parent;
    };
    /**
     * Shows the context menu for the element provided that the element contains an object that represent a
     * {@link PMUI.ui.Menu Menu}.
     *
     * If the {@link PMUI.core.Element#menu menu} property is an empty object the context menu will
     * never appear on the page.
     *
     * By default the context menu will appear after to do right click on the element.
     */
    Element.prototype.showContextMenu = function () {
        if (this.menu) {
            this.menu.show();
        }
        return this;
    };
    /**
     * Return true if the object's events were defined, otherwise it returns false.
     * @return {Boolean}
     */
    Element.prototype.eventsAreDefined = function () {
        return this.eventsDefined;
    };
    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = Element;
    }

    PMUI.extendNamespace('PMUI.core.Element', Element);

}());
(function () {
    /**
     * @class PMUI.core.Container
     * Handles HTML elements that have a container behavior (they can be contain {Element} objects)
     * @abstract
     * @extend PMUI.core.Element
     *  * Usage example (only for subclasses since this is an abstract class):
     *
     *      @example
     *       //Remember, this is an abstract class so it shouldn't be instantiate,
     *       //anyway we are instantiating it just for this example
     *       c = new PMUI.core.Container({
     *            x: 10,
     *            y: 20,
     *            positionMode: "absolute",
     *            width: 300,
     *            height: 100,
     *            style: {
     *                cssProperties: {
     *                    "background-color": "yellow"
     *                },
     *                cssClasses: [".square"]
     *            },
     *            items : [
     *               new PMUI.core.Element({
     *                    y : 0,
     *                    width: 100,
     *                    height: 100,
     *                    style: {
     *                        cssProperties: {
     *                            "background-color": "red"
     *                        },
     *                        cssClasses: [".square"]
     *                    }
     *               }),
     *               new PMUI.core.Element({
     *                    y : 100,
     *                    width: 100,
     *                    height: 100,
     *                    style: {
     *                        cssProperties: {
     *                            "background-color": "yellow"
     *                        },
     *                        cssClasses: [".square"]
     *                    }
     *               })
     *            ]
     *        });
     *        document.body.appendChild(c.getHTML());
     *
     * @constructor
     * Creates a new instance
     * @param {Object} settings
     *
     * @cfg {Array} [items=[]] An array with the items to be added to tyhe container. If the
     * {@link PMUI.core.Container#cfg-dataItems dataItems config option} is  specified then this config option will be
     * ignored.
     *
     * Each array's item can be:
     *
     *  - A JSON object with the config options required by an {@link PMUI.core.Element Element object}.
     *  - An instance of the {@link PMUI.core.Element Element class}.
     *
     * @cfg {String} [behavior='nobehavior'] A string that specifies the behavior for the container and its items.
     * The accepted values are the same ones that the specified ones for the
     * {@link PMUI.core.Container#method-setBehavior setBehavior() method}.
     *
     * @cfg {Array} [dataItems=null] An array in which each element is the data for the items that will be contained by
     * the container. If this config option is specfied the {@link PMUI.core.Container#cfg-items items config option}
     * will be ignored.
     *
     * @cfg {Function} [onDrop=null] The callback function to be executed everytime a draggable item is dropped on the
     * container. This callback can return a boolean value or not return anything. If it returns "false" then the drop
     * action will be cancelled. For info about the parameters sent to the callback function please read the
     * {@link PMUI.core.Container#event-onDrop onDrop} event documentation.
     *
     * @cfg {Function} [onSort=null] The callback function to be executed everytime a container's item order changes.
     * For info about the parameters sent to the callback function please read the
     * {@link PMUI.core.Container#event-onSort onSort} event documentation.
     *
     * @cfg {String} [sortableItems=null] Specifies which items inside the element should be sortable in case a
     * sort behavior is applied. It must be a jQuery selector.
     *
     * @cfg {Boolean} [disabled=false] If the interactive functions like the drag, drop, sort behaviors will be
     * disabled or not.
     *
     * @cfg {Function|null} [onBeforeDrop=null] The callback to be executed before a draggable item that was dropped
     * on the container is added.
     *
     * @cfg {Function|null} [onDropOut=null] The callback to be executed everytime a draggable item is drop out from
     * the container to another one. To know the parameters sent to the callback function read the documentation for
     * the {@link #event-onDropOut onDropOut} event.
     *
     * @cfg {Function|null} [onDragStart=null] The callback function to be executed everytime any of its draggable items
     * starts to be dragged. For info about the parameters sent to the callback function read the documentation for
     * the {@link #event-onDropOut onDropOut} event.
     *
     * @cfg {Function|null} [onAdd=null] The callback function be executed eveytime the {@link #event-onAdd onAdd event}.
     */
    var Container = function (settings) {
        Container.superclass.call(this, settings);
        /**
         * @property {PMUI.util.ArrayList} items
         * An ArrayList object that contains all the child Items
         */
        this.items = null;
        /**
         * @property {PMUI.util.Factory} factory
         * X
         */
        this.factory = null;
        /**
         * @property {PMUI.behavior.ContainerItemBehavior} behaviorObject The container's behavior object.
         * @private
         */
        this.behaviorObject = null;
        /**
         * @property {String} behavior The behavior's pmType being used by the object.
         * @readonly
         */
        this.behavior = null;
        /**
         * @property {HTMLElement} containmentArea The HTML area in which the items HTML will be append
         * @readonly
         */
        this.containmentArea = null;
        /**
         * @event onDragStart
         * Fired when the dragging is starting on any ot its items.
         * @param {PMUI.core.Container} container The container from which the draggable is starting to be dragged.
         * @param {PMUI.core.Element} draggableItem The item thais starting to be dragged.
         */
        this.onDragStart = null;
        /**
         * @event onDrop
         * Fired everytime a draggable item is dropped on the container.
         * @param {PMUI.core.Container} container The container on which the draggable item was dropped.
         * @param {PMUI.core.Element} draggableItem The item that was dropped on the container.
         * @param {Number} index The order index in which the new element was dropped.
         */
        this.onDrop = null;
        /**
         * @event onDropOut
         * Fired when a draggable item is drop out from the container to another one.
         * @param {PMUI.core.Element} draggable The draggable PMUI object involved in the action.
         * @param {PMUI.core.Container} origin The container the draggable item was drag from.
         * @param {PMUI.core.Container} destiny The container the draggable was dropped on.
         */
        this.onDropOut = null;
        /**
         * @event
         * Fired before an item is added to the container by dropping.
         * @param {PMUI.core.Element} draggableItem The container.
         * @param {PMUI.core.Element} draggableItem The item that was dropped and is ready to be appended to the
         * container.
         * @param {Number} index The new element's order index in which it will be added.
         */
        this.onBeforeDrop = null;
        /**
         * @event onSort
         * Fired everytime a container's item order changes.
         * @param {PMUI.core.Container} container The container on which the sort occur.
         * @param {PMUI.core.Element} orderedElement The element which was ordered.
         * @param {Number} index The new element's order index.
         */
        this.onSort = null;
        /**
         * Specifies which items inside the element should be sortable in case a drag/sort behavior is applied.
         * @type {String}
         */
        this.sortableItems = null;
        /**
         * If the container is disabled or not. Set by the {@link #cfg-disabled disabled} config option and the
         * {@link #method-disable disable()} method.
         * @type {Boolean}
         * @readonly
         */
        this.disabled = null;
        /**
         * If the container's behavior is disabled or not. Set by the {@link #cfg-disabledBehavio disabledBehavior} config option and the
         * {@link #method-disableBehavior disableBehavior()} method.
         * @type {Boolean}
         */
        this.disabledBehavior = null;
        /**
         * A flag that indicates if actually a massive action is being performed.
         * @private
         * @type {Boolean}
         */
        this.massiveAction = false;
        /**
         * @event onAdd
         * Fired everytime an item is added to the object.
         * @param {PMUI.core.Container} container The container in which the item was added to.
         * @param {PMUI.core.Element} item The item that was added.
         * @param {Number} index The index in which the item was added.
         */
        this.onAdd = null;
        /**
         * @property {String} dragDropBehaviorScope A draggable/droppable container with the same scope value as
         * another droppable/draggable container's item will be accepted by the behavior.
         */
        this.dragDropBehaviorScope = null;

        this.onSortStart = null;
        this.onStopDrag = null;
        /**
         * Restricts dragging/sorting from starting unless the mousedown occurs on the specified element(s). Only
         * elements that descend from the draggable element are permitted.
         * @type {String}
         */
        this.dragHandler = null;
        Container.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Element', Container);

    Container.prototype.type = 'Container';
    /**
     * Initializes the object.
     * @param  {Object} settings A JSON object with the config options.
     * @private
     */
    Container.prototype.init = function (settings) {
        var defaults = {
            items: [],
            behavior: 'nobehavior',
            dataItems: null,
            onDragStart: null,
            onBeforeDrop: null,
            onDrop: null,
            onDropOut: null,
            onSort: null,
            sortableItems: '> *',
            disabled: false,
            onAdd: null,
            onSortStart: null,
            onStopDrag: null,
            dragDropBehaviorScope: 'pmui-containeritem-behavior',
            dragHandler: null
        };

        jQuery.extend(true, defaults, settings);

        this.items = new PMUI.util.ArrayList();

        this.setFactory(defaults.factory)
            .setOnAddHandler(defaults.onAdd);

        if (jQuery.isArray(defaults.dataItems)) {
            this.setDataItems(defaults.dataItems);
        } else {
            this.setItems(defaults.items);
        }

        this.setSortableItems(defaults.sortableItems)
            .setDragHandler(defaults.dragHandler)
            .setDragDropBehaviorScope(defaults.dragDropBehaviorScope)
            .setBehavior(defaults.behavior)
            .setOnDragStartHandler(defaults.onDragStart)
            .setOnBeforeDropHandler(defaults.onBeforeDrop)
            .setOnDropHandler(defaults.onDrop)
            .setOnDropOutHandler(defaults.onDropOut)
            .setOnSortHandler(defaults.onSort);
        this.setOnSortStartHandler(defaults.onSortStart);
        this.setOnStopDragHandler(defaults.onStopDrag);
        if (this.disabled) {
            this.disable();
        } else {
            this.enable();
        }
    };
    /**
     * If specified, restricts dragging/sorting from starting unless the mousedown occurs on the specified element(s).
     * Only elements that descend from the draggable element are permitted.
     * @param {String} handler The selector for the handler.
     * @chainable
     */
    Container.prototype.setDragHandler = function (handler) {
        if (!(typeof handler === 'string' || handler === null)) {
            throw new Error("The parameter must be a string or null");
        }
        this.dragHandler = handler;
        this.setBehavior(this.behavior);
        return this;
    };
    /**
     * Sets the {@link #property-dragDropBehaviorScope dragDropBehaviorScope} property.
     * @param {String} scope Any string that represents a scope.
     * @chainable
     */
    Container.prototype.setDragDropBehaviorScope = function (scope) {
        if (typeof scope !== 'string') {
            throw new Error("setDragDropBehaviorScope(): The parameter must be a string.");
        }
        this.dragDropBehaviorScope = scope;
        this.setBehavior(this.behavior);
        return this;
    };
    /**
     * Sets the handler to be executed everytime the {@link #event-onAdd onAdd} event is fired.
     * @param {Function|null} handler The handler to execute, it also can be null.
     * @chainable
     */
    Container.prototype.setOnAddHandler = function (handler) {
        if (!(handler === null || typeof handler === 'function')) {
            throw new Error('setOnAddHandler(): The parameter ust be a function or null.');
        }
        this.onAdd = handler;
        return this;
    };
    /**
     * Sets the callback function to be executed everytime any of its draggable items stop to be dragged.
     * @param {Function|null} handler It can be a function or null.
     */
    Container.prototype.setOnStopDragHandler = function (handler) {
        if (handler !== null && typeof handler !== 'function') {
            throw new Error('setOnStopDragHandler(): the parameter must be a function or null.');
        }
        this.onStopDrag = handler;
        return this;
    };
    /**
     * Sets the callback function to be executed everytime any of its draggable items starts to be dragged. For info
     * about the parameters sent to the callback function read the documentation for
     * the {@link #event-onDropOut onDropOut} event.
     * @param {Function|null} handler It can be a function or null.
     */
    Container.prototype.setOnDragStartHandler = function (handler) {
        if (!(typeof handler === 'function' || handler === null)) {
            throw new Error('setOnDragStartHandler(): the parameter must be a function or null.');
        }
        this.onDragStart = handler;
        return this;
    };
    /**
     * Sets the callback function to be executed when a draggable item is dragged from the container and dropped on
     * another container. This callback can return a boolean value or not return anything. If it returns "false" then
     * the drop action will be cancelled. For info about the parameters sent to the callback function please read the
     * {@link PMUI.core.Container#event-onDrop onDrop} event documentation.
     * @param {Function|null} handler
     * @chainable
     */
    Container.prototype.setOnDropOutHandler = function (handler) {
        if (!(typeof handler === 'function' || handler === null)) {
            throw new Error('setOnDropOutHandler(): the parameter must be a function or null.');
        }
        this.onDropOut = handler;
        return this;
    };
    /**
     * Disables the container's behavior.
     * @chainable
     */
    Container.prototype.disableBehavior = function () {
        this.disabledBehavior = true;
        this.behaviorObject.disable();
        return this;
    };
    /**
     * Enables the container's behavior.
     * @chainable
     */
    Container.prototype.enableBehavior = function () {
        this.disabledBehavior = false;
        this.behaviorObject.enable();
        this.disabled = false;
        this.style.removeClasses(['pmui-disabled']);
        return this;
    };
    /**
     * Disables interactive functions like the drag, drop, sort behaviors.
     * @chainable
     */
    Container.prototype.disable = function () {
        this.disabled = true;
        this.style.addClasses(['pmui-disabled']);
        return this.disableBehavior();
    };
    /**
     * Enables interactive functions like the drag, drop, sort behaviors.
     * @chainable
     */
    Container.prototype.enable = function () {
        this.disabled = false;
        this.style.removeClasses(['pmui-disabled']);
        return this.enableBehavior();
    };
    /**
     * Specifies which items inside the element should be sortable in case a drag/sort behavior is applied.
     * @param {String} selector A jQuery selector.
     * @chainable
     */
    Container.prototype.setSortableItems = function (selector) {
        this.sortableItems = selector;
        return this.setBehavior(this.behavior);
    };
    /**
     * Sets the callback function to be executed everytime a container's item order changes.
     * @param {Function} handler It can be a function or null. For info about the parameters sent to the callback
     * function please read the {@link PMUI.core.Container#event-onSort onSort} event documentation.
     */
    Container.prototype.setOnSortHandler = function (handler) {
        if (handler !== null && typeof handler !== 'function') {
            throw new Error('setOnSortHandler(): the parameter must be a function or null.');
        }
        this.onSort = handler;
        return this;
    };

    Container.prototype.setOnSortStartHandler = function (handler) {
        if (handler !== null && typeof handler !== 'function') {
            throw new Error('setOnSortStartHandler(): the parameter must be a function or null.');
        }
        this.onSortStart = handler;
        return this;
    };
    /**
     * Sets the callback function to be executed before a draggable item is added to the container by dropping.
     * @param {Function} handler It can be a function or null. For info about the parameters sent to the callback
     * function please read the {@link PMUI.core.Container#event-onBeforeDrop onBeforeDrop} event documentation.
     */
    Container.prototype.setOnBeforeDropHandler = function (handler) {
        if (handler !== null && typeof handler !== 'function') {
            throw new Error('setOnDropHandler(): the parameter must be a function or null.');
        }
        this.onBeforeDrop = handler;
        return this;
    };
    /**
     * Sets the callback function to be executed everytime a draggable item is dropped on the container.
     * @param {Function} handler It can be a function or null. For info about the parameters sent to the callback
     * function please read the {@link PMUI.core.Container#event-onDrop onDrop} event documentation.
     */
    Container.prototype.setOnDropHandler = function (handler) {
        if (handler !== null && typeof handler !== 'function') {
            throw new Error('setOnDropHandler(): the parameter must be a function or null.');
        }
        this.onDrop = handler;
        return this;
    };
    /**
     * Adds a new item but using raw data to create it.
     * @param {Object} dataItem A JSON object with the data to create the new object.
     * @param {Number} [index] The position index in which the data item will be inserted.
     */
    Container.prototype.addDataItem = function (dataItem, index) {
        var args,
            i;
        if (typeof dataItem !== 'object') {
            throw new Error('addDataItem(): The parameter must be an object.');
        }
        args = [{
            data: dataItem
        }];
        for (i = 1; i < arguments.length; i += 1) {
            args.push(arguments[i]);
        }
        this.addItem.apply(this, args);
        return this;
    };
    /**
     * Sets the data toi be used to create every item to be contained by the object.
     * @param {Array} dataItems An array in which each element is an object with the data for each of the items.
     */
    Container.prototype.setDataItems = function (dataItems) {
        var i;
        if (jQuery.isArray(dataItems)) {
            this.clearItems();
            this.massiveAction = true;
            for (i = 0; i < dataItems.length; i += 1) {
                this.addDataItem(dataItems[i]);
            }
            this.massiveAction = false;
            this.paintItems();
        }

        return this;
    };
    /**
     * Returns the html element in which the items are appended.
     * @return {HTMLElement}
     */
    Container.prototype.getContainmentArea = function () {
        this.getHTML();
        return this.containmentArea;
    };
    /**
     * Returns all the container's items the behavior will be applied on.
     * @return {Array} An array of items.
     * @private
     */
    Container.prototype.getBehavioralItems = function () {
        return this.getItems();
    };
    /**
     * Enables/disables one, two, all of the following behaviors:
     *
     * - "drag", allows to drag items from the object, also them can be dropped on another containers that have enables
     * the "drop" behavior.
     * - "drop", allows to drop draggable items from another container on the object.
     * - "sort", allows reorder the items in the object. Combined with "drop" behavior makes the object capable to
     * receive objects (drop) in a specific position.
     * @param {String} behavior The behavior to be set, the accepted values are:
     *
     * - "drag", enables the dragging functionality.
     * - "dragclone", enables the clone-dragging functionality.
     * - "drop", enables the dropping functionality.
     * - "sort", enabled the sorting functionality.
     * - "dragdrop", enables both dragging and dropping functionality.
     * - "dragsort", enables both dragging and sorting functionality.
     * - "dropsort", enables both dropping and sorting functionality.
     * - "dragdropsort", enables the three behaviors: dragging, dropping and sorting.
     * - "nobehavior", disables all the behaviors.
     * @chainable
     */
    Container.prototype.setBehavior = function (behavior, index) {
        var obj,
            behaviorObject,
            factory;

        if (typeof behavior === 'string') {
            factory = new PMUI.behavior.ContainerItemBehaviorFactory();
            obj = {
                pmType: behavior,
                targetObject: this,
                scope: this.dragDropBehaviorScope,
                handle: this.dragHandler
            };
            if (this.behaviorObject) {
                this.behaviorObject.detachBehavior();
            }
            behaviorObject = factory.make(obj);
            if (behaviorObject) {
                this.behaviorObject = behaviorObject;
                this.behavior = behavior;
            }
            if (this.html) {
                if (index !== undefined) {
                    this.behaviorObject.attachBehavior(index);
                } else {
                    this.behaviorObject.attachBehavior();
                }
            }
        }
        return this;
    };

    Container.prototype.addingBehaviorsElement = function (behavior, index) {
        var obj,
            behaviorObject,
            factory;

        if (typeof behavior === 'string') {
            factory = new PMUI.behavior.ContainerItemBehaviorFactory();
            obj = {
                pmType: behavior,
                targetObject: this,
                scope: this.dragDropBehaviorScope,
                handle: this.dragHandler
            };
            if (this.behaviorObject) {
                this.behaviorObject.detachBehavior();
            }
            behaviorObject = factory.make(obj);
            if (behaviorObject) {
                this.behaviorObject = behaviorObject;
                this.behavior = behavior;
            }
            if (this.html) {
                if (index !== undefined) {
                    this.behaviorObject.attachBehavior(index);
                } else {
                    this.behaviorObject.attachBehavior();
                }
            }
        }
        return this;
    };
    /**
     * Sets the "factory" property with a {PMUI.util.Factory} object
     * @param {Object} factory
     * @chainable
     * @private
     */
    Container.prototype.setFactory = function (factory) {
        if (factory instanceof PMUI.util.Factory) {
            this.factory = factory;
        } else {
            this.factory = new PMUI.util.Factory(factory);
        }
        return this;
    };

    /**
     * Removes a child element from the object
     * @param  {PMUI.core.Element|String|Number} item It can be a string (id of the child to remove),
     a number (index of the child to remove) or a {Element} object.
     * @chainable
     */
    Container.prototype.removeItem = function (item) {
        var itemToRemove,
            parent;
        if (item instanceof PMUI.core.Element) {
            itemToRemove = item;
        } else {
            if (typeof item === 'string') {
                itemToRemove = this.items.find("id", item.id);
            } else if (typeof item === 'number') {
                itemToRemove = this.items.get(item);
            }
        }
        if (itemToRemove) {
            parent = itemToRemove.parent;
            if (parent) {
                parent.items.remove(itemToRemove);
            } else {
                this.items.remove(itemToRemove);
            }
            itemToRemove.parent = null;
            if (this.html) {
                jQuery(itemToRemove.html).detach();
            }
        }
        if (!this.items.getSize()) {
            this.style.addClasses(['pmui-empty']);
        }
        if (this.massiveAction) {
            return this;
        }
        return this;
    };
    /**
     * Removes all the child items
     * @chainable
     */
    Container.prototype.clearItems = function () {
        this.massiveAction = true;
        while (this.items.getSize() > 0) {
            this.removeItem(0);
        }
        this.massiveAction = false;
        return this;
    };
    /**
     * Returns true if the item, used as the unique method parameter, is a direct child of the current Container object,
     otherwise returns false
     * @param  {PMUI.core.Element}  item The target child object
     * @return {Boolean}
     */
    Container.prototype.isDirectParentOf = function (item) {
        return this.items.indexOf(item) >= 0;
    };
    /**
     * Returns true if the supplied argument can be accepted as an item of the container.
     * @param  {PMUI.core.Element} obj The element to be evaluated.
     * @return {Boolean}
     */
    Container.prototype.canContain = function (obj) {
        return obj instanceof PMUI.core.Element ? this.factory.isValidClass(obj) : false;
    };
    /**
     * Paint a container's item (puts its HTML into the container's HTML).
     * @param  {PMUI.core.Element} item The item to add.
     * @chainable
     * @private
     */
    Container.prototype.paintItem = function (item, index) {
        var referenceItem;
        if (this.html) {
            if (index !== null && index !== undefined) {
                referenceItem = this.items.get(index + 1);
                if (referenceItem) {
                    this.containmentArea.insertBefore(item.getHTML(), referenceItem.getHTML());
                } else {
                    this.containmentArea.appendChild(item.getHTML());
                }
            } else {
                this.containmentArea.appendChild(item.getHTML());
            }
            if (this.eventsDefined) {
                item.defineEvents();
            }
            index = this.items.indexOf(item);
            this.setBehavior(this.behavior, index);
        }
        return this;
    };
    /**
     * Paint all the container items.
     * @chainable
     * @private
     */
    Container.prototype.paintItems = function () {
        var items,
            i;
        if (this.containmentArea) {
            items = this.items.asArray()
            for (i = 0; i < items.length; i += 1) {
                this.paintItem(items[i]);
            }
        }
        return this;
    };
    /**
     * Adds an child item to the object.
     * @param {PMUI.core.Element|Object} item It can be one of the following data types:
     * - {PMUI.core.Element} the object to add.
     * - {Object} a JSON object with the settings for the Container to be added.
     * @param {Number} [index] An index in which the item will be added.
     * @chainable
     */
    Container.prototype.addItem = function (item, index) {
        var itemToBeAdded;
        if (this.factory) {
            itemToBeAdded = this.factory.make(item);
        }
        if (itemToBeAdded && !this.isDirectParentOf(itemToBeAdded)) {
            if (index !== null && index !== undefined) {
                this.items.insertAt(itemToBeAdded, index);
            } else {
                this.items.insert(itemToBeAdded);
            }
            itemToBeAdded.parent = this;
            if (!this.massiveAction) {
                this.paintItem(itemToBeAdded, index);
            }
            if (typeof this.onAdd === 'function') {
                index = typeof index === 'number' ? index : this.items.getSize() - 1;
                this.onAdd(this, this.getItem(index), index);
            }
        }
        return this;
    };
    /**
     * Clear all the object's current child items and add new ones
     * @param {Array} items An array where each element can be a {Element} object or a JSON object
     where is specified the setting for the child item to be added
     * @chainable
     */
    Container.prototype.setItems = function (items) {
        var i;
        if (jQuery.isArray(items)) {
            this.clearItems();
            this.massiveAction = false;
            for (i = 0; i < items.length; i += 1) {
                this.addItem(items[i]);
            }
        }
        return this;
    };
    /**
     * Returns an array with all the child elements
     * @return {Array}
     */
    Container.prototype.getItems = function () {
        return this.items.asArray();
    };
    /**
     * Returns one single item based on the id or index position
     * @param  {String|Number} id If the parameter is a string then
     it will be take as the id for the element to find and return,
     but if the element is a Number it returns the object iwth that
     index position
     * @return {PMUI.core.Element}
     */
    Container.prototype.getItem = function (i) {
        var t;
        if (typeof i === 'number') {
            t = this.items.get(i);
        } else if (typeof i === 'string') {
            t = this.items.find('id', i);
        } else if (this.items.indexOf(i) >= 0) {
            t = i;
        }
        return t;
    };
    /**
     * Returns the index for the specified item.
     * @param  {PMUI.core.Element|String} item It can be:
     *
     * - {@link PMUI.core.Element}
     * - String, it will be interpreted as the items's id.
     * @return {Number}
     */
    Container.prototype.getItemIndex = function (item) {
        var evaluatedItem;

        if (item instanceof PMUI.core.Element) {
            evaluatedItem = item;
        } else if (typeof item === 'number') {
            evaluatedItem = this.items.get(item);
        } else if (typeof item === 'string') {
            evaluatedItem = this.items.find("id", item);
        }

        return this.items.indexOf(evaluatedItem);
    };
    /**
     * Creates the object's HTML element
     * @return {HTMLElement}
     */
    Container.prototype.createHTML = function () {
        if (this.html) {
            return this.html;
        }
        Container.superclass.prototype.createHTML.call(this);
        this.containmentArea = this.html;
        this.paintItems();
        this.style.applyStyle();
        this.setBehavior(this.behavior);

        return this.html;
    };
    /**
     * Moves an object's item from a position to another.
     * @param  {PMUI.core.Element} item The item to move.
     * @param  {Number} index The index position in which the item will be moved.
     * @chainable
     */
    Container.prototype.moveItem = function (item, index) {
        var items = this.items,
            currentIndex,
            referenceObject;

        item = this.getItem(item);

        if (item instanceof PMUI.core.Element && this.isDirectParentOf(item)) {
            currentIndex = items.indexOf(item);
            items = items.asArray();
            referenceObject = this.items.get(index + (currentIndex < index ? 1 : 0));
            item = items.splice(currentIndex, 1)[0];
            this.items.remove(item);
            this.items.insertAt(item, index);
            if (this.html) {
                if (index === this.items.getSize() - 1) {
                    this.containmentArea.appendChild(item.getHTML());
                } else {
                    this.containmentArea.insertBefore(item.getHTML(), referenceObject.getHTML());
                }
            }
        }
        return this;
    };
    /**
     * Returns the function to be executed when an accepted draggable is dragged over the droppable area.
     * @return {Function}
     */
    Container.prototype.onDragOver = function () {
        return function () {
        };
    };
    /**
     * Returns the function to be executed when the items order has been changed.
     * @return {Function}
     * @private
     */
    Container.prototype.onSortingChange = function () {
        var that = this;
        return function (e, ui) {
            var itemHTML = ui.item.get(0),
                item = PMUI.getPMUIObject(itemHTML),
                newIndex;
            newIndex = jQuery(that.getContainmentArea()).find('>*').index(itemHTML);

            that.moveItem(item, newIndex);
            if (typeof that.onSort === 'function') {
                that.onSort(that, item, newIndex);
            }
        };
    };
    /**
     * Handler to be executed everytime the mouse enter a position over any container's draggable/sortable item.
     * @param {PMUI.core.Element} draggable The draggable PMUI object.
     * @chainable
     */
    Container.prototype.onDraggableMouseOver = function (draggable) {
        return this;
    };
    /**
     * Handler to be executed everytime the mouse leaves a position over any container's draggable/sortable item.
     * @param {PMUI.core.Element} draggable The draggable PMUI object.
     * @chainable
     */
    Container.prototype.onDraggableMouseOut = function (draggable) {
        return this;
    };
    /**
     * Executes children events defined
     * @return {PMUI.core.Container}
     * @chainable
     */
    Container.prototype.defineEvents = function () {
        var j,
            children,
            that = this;
        Container.superclass.prototype.defineEvents.call(this);
        if (that.items.getSize() > 0) {
            children = that.getItems();
            for (j = 0; j < children.length; j += 1) {
                children[j].defineEvents();
            }
        }
        return this;
    };

    PMUI.extendNamespace('PMUI.core.Container', Container);

    // Publish to NodeJS environment
    if (typeof exports !== 'undefined') {
        module.exports = Container;
    }

}());
(function () {
    /**
     * @class  PMUI.core.Item
     * @extends PMUI.core.Container
     * Create a items container
     *
     * @constructor
     * Creates a new instance of the class
     * @param {Object} options Contructor object
     */
    var Item = function (options) {
        Item.superclass.call(this, options);

        /**
         * Parent object associated to this item
         * @type {Object}
         */
        this.parent = null;
        Item.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.core.Container', Item);

    /**
     * Defines the object's type
     * @type {String}
     */
    Item.prototype.type = "Item";

    /**
     * Defines the object's family
     * @type {String}
     */
    Item.prototype.family = "Item";

    /**
     * @private
     * Initializes the object with default values
     * @param  {Object} options
     */
    Item.prototype.init = function (options) {
        var defaults = {
            parent: null
        };
        jQuery.extend(true, defaults, options);
        this.setParent(defaults.parent);
    };

    /**
     * Sets the parent object of the current item
     * @param {Object} parent Parent object
     */
    Item.prototype.setParent = function (parent) {
        this.parent = parent;
        return this;
    };

    /**
     * Returns the parent object pointer
     * @return {Object}
     */
    Item.prototype.getParent = function () {
        return this.parent;
    };
    /**
     * Removes the item from its parent container.
     * @chainable
     */
    Item.prototype.remove = function () {
        if (this.parent) {
            this.parent.removeItem(this);
        }

        return this;
    };

    PMUI.extendNamespace('PMUI.core.Item', Item);

    if (typeof exports !== 'undefined') {
        module.exports = Item;
    }
}());
(function () {
    /**
     * @class PMUI.core.Panel
     * Handles panels to be inserted into containers, it is composed by three main elements: header, body and footer
     * @abstract
     * @extends {PMUI.core.Container}
     *
     *       //Remember, this is an abstract class so it shouldn't be instantiate,
     *       //anyway we are instantiating it just for this example
     *        var p1, p2, panel;
     *        p1 = new PMUI.core.Panel({
     *            height : 140,
     *            width : 90,
     *            display : 'inline-block',
     *            style : {
     *                cssProperties : {
     *                    'background-color':'red'
     *                }
     *            }
     *        });
     *        p2 = new PMUI.core.Panel({
     *            height : 140,
     *            width : 90,
     *            display : 'inline-block',
     *            style : {
     *                cssProperties : {
     *                    'background-color':'yellow'
     *                }
     *            }
     *        });
     *        panel = new PMUI.core.Panel({
     *            height : 150,
     *            width : 200,
     *            display : 'block',
     *            items : [
     *                p1,
     *                p2
     *            ],
     *            layout : 'vbox',
     *            padding : "5px",
     *            borderWidth : "1px",
     *            border : true
     *        });
     *        document.body.appendChild(panel.getHTML());
     *
     * @constructor
     * Creates a new instacne of the object
     * @param {Object} settings
     *
     * @cfg {Object|PMUI.core.Panel|null} [panel=null] The panel to be appended into the current Panel. It can be an
     * object literal with the {@link PMUI.core.Panel config options for Panel} or an instance of
     * {@link PMUI.core.Panel Panel} or null.
     * @cfg {PMUI.core.Container} [parent=null] The panel's parent element.
     * @cfg {String|PMUI.layout.Layout} [layout='box'] The layout to be applied to the panel, the layout determines the
     * render position for the panel's items. The possible layouts are:
     *
     * - box: this applies no layout to the panel. To set the layout to box if you use a string as value for the config
     * option use "box", in the other case, if you use an object, use an instance of {@link PMUI.layout.Box Box}.
     * - hbox: this applies a layout that renders the panel's items side by side using the items's
     * {@link PMUI.core.Element#property-propotion proportion property} to calculate the width for each item. To set
     * this layout to the panel, if you are using a string as the value for the config option, use "hbox", in the other
     * case, if you are using an object use an instance of {@link PMUI.layout.HBox HBox}.
     * - vbox: this applies a layout that renders the panel's items in vertical position using the items's
     * {@link PMUI.core.Element#property-propotion proportion property} to calculate the height for each item. To set
     * this layout to the panel, if you are using a string as the value for the config option, use "vbox", in the other
     * case, if you are using an object use an instance of {@link PMUI.layout.VBox VBox}.
     * @cfg {String|Number} [padding=""] Sets the css padding property for the panel.  It can be a number or and string
     * with the format ##px or an empty string. If you use an empty string as the parameter, the padding will be set
     * dinamically from the used css file.
     * @cfg {String|Number} [borderWidth=""] Sets the border width for the panel. It can be a number or a string with
     * the format '##px' or an empty string. If you use an empty string as the parameter, the border width will be set
     * dinamically from the used css file.
     */
    var Panel = function (settings) {
        Panel.superclass.call(this, settings);
        /**
         * The child {Panel} object
         * @type {PMUI.core.Panel}
         */
        this.panel = null;
        /**
         * The Panel's parent object
         * @type {PMUI.core.Container}
         */
        this.parent = null;
        /**
         * A {Layout} object which handles the position layout for the object's direct child elements
         * @property {PMUI.layout.Layout}
         */
        this.layout = null;
        /**
         * @property {String} [topPadding=""]
         * The value for the css padding-top property to be applied to the object's html.
         * @readonly
         */
        this.topPadding = null;
        /**
         * @property {String} [leftPadding=""]
         * The value for the css padding-left property to be applied to the object's html.
         * @readonly
         */
        this.leftPadding = null;
        /**
         * @property {String} [bottomPadding=""]
         * The value for the css padding-bottom property to be applied to the object's html.
         * @readonly
         */
        this.bottomPadding = null;
        /**
         * @property {String} [rightPadding=""]
         * The value for the css padding-right property to be applied to the object's html.
         * @readonly
         */
        this.rightPadding = null;
        /**
         * @property {String} [borderWidth=""] A string with the format ##px (where ## is a number).
         * It determines the panel's border width.
         * @readonly
         */
        this.borderWidth = null;
        /**
         * @property {Boolean} [border=false] If the border is being shown.
         * @readonly
         */
        this.border = null;
        Panel.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Container', Panel);

    /**
     * Defines the object's type
     * @type {String}
     */
    Panel.prototype.type = 'Panel';

    /**
     * @private
     * Initializes the object with default values
     * @param  {Object} options
     */
    Panel.prototype.init = function (settings) {
        var defaults = {
            panel: null,
            parent: null,
            layout: 'box',
            padding: "",
            borderWidth: ""
        };

        jQuery.extend(true, defaults, settings);

        this.setPanel(defaults.panel)
            .setParent(defaults.parent)
            .setLayout(defaults.layout)
            .setPadding(defaults.padding)
            .setBorderWidth(defaults.borderWidth);

    };
    /**
     * Applies the css classes and ptoperties to the element's html which the object is related
     * @chainable
     */
    Panel.prototype.applyStyle = function () {
        if (this.html) {
            this.style.applyStyle();

            this.style.addProperties({
                display: this.visible ? this.display : "none",
                position: this.positionMode,
                left: this.x,
                top: this.y,
                width: this.width,
                height: this.height,
                zIndex: this.zOrder,
                "padding-top": this.topPadding,
                "padding-right": this.rightPadding,
                "padding-bottom": this.bottomPadding,
                "padding-left": this.leftPadding,
                "border-width": this.borderWidth,
                "border-style": this.borderWidth !== "" ? "solid" : "",
                "box-sizing": "border-box"
            });
        }
        return this;
    };
    /**
     * Sets the border width for the panel
     * @param {String|Number} width It can be a number or a string with the format '##px' or an empty string.
     * If you use an empty string as the parameter, the border width will be set dinamically from the used css file.
     */
    Panel.prototype.setBorderWidth = function (width) {
        var error = false;
        if (typeof width === 'number') {
            this.borderWidth = width + "px";
        } else if (typeof width === 'string') {
            width = jQuery.trim(width);
            if (/^\d+(\.\d+)?px$/.test(width) || width === "") {
                this.borderWidth = width;
            } else {
                error = true;
            }
        } else {
            error = false;
        }

        if (error) {
            throw new Error("setBorderWidth(): invalid parameter.");
        } else {
            this.applyStyle();
            if (this.layout) {
                this.layout.applyLayout();
            }
        }

        return this;
    };
    /**
     * Returns the border width from the object
     * @return {String}
     */
    Panel.prototype.getBorderWidth = function () {
        return this.borderWidth || this.style.getProperty("border-width");
    };
    /**
     * Sets the padding values for the panel.
     * @param {String|Number} padding It can be a number or and string with the format ##px or an empty string.
     * If you use an empty string as the parameter, the padding will be set dinamically from the used css file.
     */
    Panel.prototype.setPadding = function (padding) {
        var error = false;
        if (typeof padding === 'string') {
            padding = jQuery.trim(padding);
            padding = padding.split(/\s+/).join(" ");
            if (/^(\d+(\.\d+)?(px|%)\s)+$/.test(padding + " ")) {
                padding = padding.match(/\d+(\.\d+)?(px|%)/g);
                switch (padding.length) {
                    case 4:
                        this.topPadding = padding[0];
                        this.rightPadding = padding[1];
                        this.bottomPadding = padding[2];
                        this.leftPadding = padding[3];
                        break;
                    case 3:
                        this.topPadding = padding[0];
                        this.rightPadding = this.leftPadding = padding[1];
                        this.bottomPadding = padding[2];
                        break;
                    case 2:
                        this.topPadding = this.bottomPadding = padding[0];
                        this.rightPadding = this.leftPadding = padding[1];
                        break;
                    case 1:
                        this.topPadding = this.rightPadding = this.bottomPadding = this.leftPadding = padding[0];
                        break;
                }
            } else if (padding === "") {
                this.topPadding = this.rightPadding = this.bottomPadding = this.leftPadding = "";
            } else {
                error = true;
            }
        } else if (typeof padding === 'number') {
            this.topPadding = this.rightPadding = this.bottomPadding = this.leftPadding = padding + "px";
        } else {
            error = true;
        }

        if (error) {
            throw new Error("setPadding(): Invalid parameter.");
        } else {
            this.applyStyle();
            if (this.layout) {
                this.layout.applyLayout();
            }
        }

        return this;
    };
    /**
     * Returns an array with the padding values in the following order: [top, right, bottom, left].
     * @return {Array}
     */
    Panel.prototype.getPadding = function () {
        return [
            this.topPadding || this.style.getProperty("padding-top"),
            this.rightPadding || this.style.getProperty("padding-right"),
            this.bottomPadding || this.style.getProperty("padding-bottom"),
            this.leftPadding || this.style.getProperty("padding-left")
        ];
    };
    /**
     * Sets the panel object
     * @param {Object} panel Panel object
     */
    Panel.prototype.setPanel = function (panel) {
        if (panel) {
            if (panel instanceof Panel) {
                this.panel = panel;
            } else if (typeof panel === 'object') {
                this.panel = new Panel(panel);
            }

            if (this.html) {
                jQuery(this.html).empty().append(panel.getHTML());
            }
        }

        return this;
    };
    /**
     * Returns the object's html usable width.
     * @return {Number|String} It can return a Number or an String with a css property like "auto".
     */
    Panel.prototype.getUsableWidth = function () {
        var padding = this.getPadding(), border = parseInt(this.getBorderWidth(), 10) * 2;
        if (isNaN(this.getWidth())) {
            return this.getWidth();
        }
        return this.getWidth() - (parseInt(padding[1], 10) || 0) - (parseInt(padding[3], 10) || 0) - (border || 0);
    };
    /**
     * Returns the object's html usable height
     * @return {Number|String} It can return a Number or an String with a css property like "auto".
     */
    Panel.prototype.getUsableHeight = function () {
        var padding = this.getPadding(), border = parseInt(this.getBorderWidth(), 10) * 2;
        if (isNaN(this.getHeight())) {
            return this.getHeight();
        }
        return this.getHeight() - (parseInt(padding[0], 10) || 0) - (parseInt(padding[2], 10) || 0) - (border || 0);
    };
    /**
     * Sets the parent object
     * @param {Object} parent
     */
    Panel.prototype.setParent = function (parent) {
        this.parent = parent;
        return this;
    };

    /**
     * Sets the layout for the panel.
     * @param {Object|String} layout Layout object
     */
    Panel.prototype.setLayout = function (layout) {
        var factory = new PMUI.layout.LayoutFactory();
        if (!(typeof layout === "string")) {
            throw new Error("setLayout(): Property values sent an invalid, expected 'box' , 'vbox' or 'hbox'");
        }
        this.layout = factory.make(layout);
        this.layout.setContainer(this);
        if (this.html) {
            this.layout.applyLayout();
        }
        return this;
    };

    /**
     * Add an item to the panel.
     * @param {PMUI.core.Element|Object} item.
     * @param {Number} [index] The index position in which the item will be appended.
     * It can be a valid JSON object or an object that inherits from {@link PMUI.core.Element Element}.
     * @chainable
     */
    Panel.prototype.addItem = function (item, index) {
        Panel.superclass.prototype.addItem.call(this, item, index);
        if (this.layout) {
            this.layout.applyLayout();
        }

        return this;
    };
    /**
     * Sets the width for the Panel's HTML element
     * @param {Number|String} width It can be a number or a string.
     * In case of using a String you only can use 'auto' or 'inherit' or ##px or ##% or ##em when ## is a number
     * @chainable
     */
    Panel.prototype.setWidth = function (width) {
        Panel.superclass.prototype.setWidth.call(this, width);
        if (this.layout) {
            this.layout.applyLayout();
        }

        return this;
    };
    /**
     * Sets the height for the Panel's HTML element
     * @param {Number|String} height It can be a number or a string.
     * In case of using a String you only can use 'auto' or 'inherit' or ##px or ##% or ##em when ## is a number
     * @chainable
     */
    Panel.prototype.setHeight = function (height) {
        Panel.superclass.prototype.setHeight.call(this, height);
        if (this.layout) {
            this.layout.applyLayout();
        }
        return this;
    };
    /**
     * Returns the object pointed to the parent
     * @return {Object}
     */
    Panel.prototype.getParent = function () {
        return this.parent;
    };
    /**
     * Creates the HTML element for the Panel
     * @return {HTMLElement}
     */
    Panel.prototype.createHTML = function () {
        if (this.html) {
            return this.html;
        }

        Panel.superclass.prototype.createHTML.call(this);
        this.applyStyle();
        if (this.layout) {
            this.layout.applyLayout();
        }

        return this.html;
    };

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = Panel;
    }

    PMUI.extendNamespace('PMUI.core.Panel', Panel);
}());
(function () {
    /**
     * @class PMUI.control.Control
     * Class that encapsulates the basic bahavior for a form control.
     * Since this class is abstract it shouldn't be instantiated.
     * @extends PMUI.core.Element
     * @abstract
     *
     * Usage example (only for subclasses since this is an abstract class):
     *
     *      //Remember, this is an abstract class so it shouldn't be instantiate,
     *      //anyway we are instantiating it just for this example
     *      var myControl = new PMUI.control.Control({
     *          name: "phrase",
     *          value: "Sometimes the sun goes down!",
     *          disabled: false,
     *          onChange: function(currentValue, previousValue) {
     *              console.log(this.name + " has changed!");
     *              console.log("Its previous value was: " + previousValue);
     *              console.log("Its current value is: " + currentValue);
     *          }
     *      });
     *
     * @constructor
     * While it is true that this class must not be instantiated,
     * it is useful to mention the settings parameter for the constructor
     * function (which will be used for the non abstract subclasses).
     * The parameter for the constructor will be a JSON object whose properties are specified in the
     * {@link PMUI.control.Control#cfg-disabled Config Options section}.
     *
     * @cfg {String} [name=The object's ID] The name for the control. If it is not specified then the object's id is
     * used.
     * @cfg {String} [value=""] The initial value to be set to the control.
     * @cfg {PMUI.form.Field} [field=null] The parent {@link PMUI.form.Field Field} object for the control.
     * @cfg {Boolean} [disabled=false] A boolean value which determines if the control will be enabled or not.
     * @cfg {Function} [onChange=null] A callback function to be invoked when the control changes. For info about the
     * received parameters please read the {@link PMUI.control.Control#event-onChange onChange event} section.
     * @cfg {Function} [onBeforeChange=null] A callback function to be invoked after the control's value changes. The
     * function can return false to avoid the value changing. For info about the received parameter please read the
     * {@link #event-onBeforeChange onBeforeChange event} documentation.
     */
    var Control = function (settings) {
        Control.superclass.call(this, settings);
        /**
         * @property {String} [name=id] The control's name, it defaults to null.
         * @readonly
         */
        this.name = null;
        /**
         * @property {String} [value=""] The control's value.
         * @readonly
         */
        this.value = null;
        /**
         * @property {PMUI.form.Field} [field=null] The {@link PMUI.form.Field Field}
         object the current object belongs to.
         * @readonly
         */
        this.field = null;
        /**
         * @property {Boolean} [disabled=false] If the control is disabled or not.
         * @readonly
         */
        this.disabled = null;
        /**
         * @event onChange
         * Fired when the control's value is changed.
         * @param {String} newValue The new control's value.
         * @param {String} prevValue The previous control's value.
         */
        this.onChange = null;
        /**
         * @event onBeforeChange
         * Fired after the control's value changes.
         * @param {String} newValue The new control's value.
         * @param {String} prevValue The previous vontrol's value.
         */
        this.onBeforeChange = null;
        Control.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Element', Control);

    Control.prototype.type = "Control";
    /**
     * Initialize the object.
     * @param  {Object} settings The config options.
     */
    Control.prototype.init = function (settings) {
        var defaults = {
            name: this.id,
            value: "",
            field: null,
            disabled: false,
            onBeforeChange: null,
            onChange: null
        };

        $.extend(true, defaults, settings);

        this.setName(defaults.name)
            .setValue(defaults.value)
            .setField(defaults.field)
            .disable(defaults.disabled)
            .setOnBeforeChangeHandler(defaults.onBeforeChange)
            .setOnChangeHandler(defaults.onChange);
    };
    /**
     * Sets the callback function to be executed when the {@link #event-onBeforeChange onBeforeChange event} fires.
     * @param {Function|null} handler
     * @chainable
     */
    Control.prototype.setOnBeforeChangeHandler = function (handler) {
        if (!(handler === null || typeof handler === 'function')) {
            throw new Error('setOnBeforeChangeHandler(): The parameter must be a function or null.');
        }
        this.onBeforeChange = handler;
        return this;
    };
    /**
     * Sets the control's name.
     * @param {String} name
     * @chainable
     */
    Control.prototype.setName = function (name) {
        if (typeof name === "string" || typeof name === "number") {
            this.name = name.toString();
            if (this.html) {
                this.html.setAttribute("name", name);
            }
        } else {
            throw new Error("The setName() method only accepts string or number values");
        }

        return this;
    };
    /**
     * Returns the control's name.
     * @return {String}
     */
    Control.prototype.getName = function () {
        return this.name;
    };
    /**
     * Sets the control's value.
     * @param {String} value
     * @chainable
     */
    Control.prototype.setValue = function (value) {
        if (typeof value !== 'undefined') {
            this.value = value.toString();
        } else {
            throw new Error("setValue(): a parameter is required.");
        }

        return this;
    };
    /**
     * Returns the control's value.
     * @return {String}
     */
    Control.prototype.getValue = function () {
        return this.value;
    };
    /**
     * Sets the control's parent {@link PMUI.form.Field Field} object.
     * @param {PMUI.form.Field}
     * @chainable
     */
    Control.prototype.setField = function (field) {
        if (field instanceof PMUI.form.Field) {
            this.field = field;
        }

        return this;
    };
    /**
     * Returns the control's parent {@link PMUI.form.Field Field} object.
     * @return {PMUI.form.Field}
     */
    Control.prototype.getField = function () {
        return this.field;
    };
    /**
     * Disables/enables the control.
     * @param {Boolean} disable If the value is evaluated as true then the control is disabled,
     otherwise the control is enabled.
     * @chainable
     */
    Control.prototype.disable = function (disable) {
        this.disabled = !!disable;
        return this;
    };
    /**
     * Returns true if the control is enabled, if it don't then it returns false
     * @return {Boolean}
     */
    Control.prototype.isEnabled = function () {
        return !this.disabled;
    };
    /**
     * Sets the callback function which will be executed everytime the control changes (using the interface).
     *
     * The callback function will receive two parameters:
     *
     * - first argument: the current control's value.
     * - second argument: the previous control's value.
     *
     *
     *          //Remember, this is an abstract class so it shouldn't be instantiate,
     *          //anyway we are instantiating it just for this example
     *          var myControl = new PMUI.control.Control({
     *              name: "phrase",
     *              value: "Sometimes the sun goes down!",
     *              disabled: false
     *          });
     *
     *          myControl.setOnChangeHandler(function(currentValue, previousValue) {
     *              console.log(this.name + " has changed!");
     *              console.log("Its previous value was: " + previousValue);
     *              console.log("Its current value is: " + currentValue);
     *          });
     *
     * @chainable
     */
    Control.prototype.setOnChangeHandler = function (handler) {
        if (typeof handler === 'function') {
            this.onChange = handler;
        }

        return this;
    };
    /**
     * Returns the control's value directly from the control's HTML element.
     *
     * This method is used internally by the object, so in most of the cases you won't need to invocated.
     * To get the control's value please use the {@link PMUI.control.Control#getValue getValue()} method.
     *
     * Since this is an abstract method, it must be implemented in its non-abstract subclasses
     * @return {String}
     * @abstract
     */
    Control.prototype.getValueFromRawElement = function () {
        throw new Error("Calling getValueFromRawElement() from PMUI.control.Control: this is an abstract method!");
    };
    /**
     * A method which is called everytime the control changes.
     *
     * This method is used internally by the object, so in most of the cases you won't need to invocated.
     * To execute instructions when the control changes, please use the
     {@link PMUI.control.Control#setOnChangeHandler setOnChangeHandler()} method.
     *
     * @chainable
     * @private
     */
    Control.prototype.onChangeHandler = function () {
        var prevValue = this.value, newValue = this.getValueFromRawElement(), resCallback;
        if (typeof this.onBeforeChange === 'function' && newValue !== prevValue) {
            resCallback = this.onBeforeChange(newValue, prevValue);
            if (resCallback === false) {
                newValue = prevValue;
                this.setValue(newValue);
            }
        }
        this.value = newValue;
        if (typeof this.onChange === 'function' && this.value !== prevValue) {
            this.onChange(this.value, prevValue);
        }

        return this;
    };
    /**
     * Attach the event listeners for the control's HTML element.
     *
     * Since this is an abstract method, it must be implemented in its non-abstract subclasses
     * @abstract
     * @chainable
     */
    Control.prototype.attachListeners = function () {
        throw new Error("Calling attachListeners() from PMUI.control.Control: this is an abstract method!");
    };
    /**
     * Creates the HTML element for the control.
     *
     * Since this is an abstract method, it must be implemented in its non-abstract subclasses
     * @return {HTMLElement}
     * @abstract
     */
    Control.prototype.createHTML = function () {
        Control.superclass.prototype.createHTML.call(this);
        return this.html;
    };
    /**
     * Returns the HTML element for the control
     * @return {HTMLElement}
     */
    Control.prototype.getHTML = function () {
        if (!this.html) {
            this.html = this.createHTML();
        }

        return this.html;
    };
    /**
     * @method setFocus
     * set the focus on control
     * @chainable
     */
    Control.prototype.setFocus = function () {
        if (this.html && this.html.focus) {
            this.html.focus();
        }
    };

    PMUI.extendNamespace('PMUI.control.Control', Control);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = Control;
    }
}());
(function () {
    /**
     * @class PMUI.control.HTMLControl
     * Class that encapsulates the HTML native control's behavior.
     * Since this class is abstract it shouldn't be instantiated.
     * @extends PMUI.control.Control
     * @abstract
     *
     * @constructor
     * While it is true that this class must not be instantiated,
     * it is useful to mention that the settings parameter for the constructor function
     * has the same structure that the one for the superclass
     * (for more info see the {@link PMUI.control.Control#constructor constructor} method for Control class).
     * @param {Object} [settings=null] A JSON object with the config options.
     */
    var HTMLControl = function (settings) {
        HTMLControl.superclass.call(this, settings);
        /**
         * @property {String} [elementTag='input'] The tag for the HTML element to be created.
         * @private
         */
        this.elementTag = 'input';
    };

    PMUI.inheritFrom('PMUI.control.Control', HTMLControl);

    HTMLControl.prototype.type = "HTMLControl";

    /**
     * Sets the value for the control
     * @param {String} value
     * @chainable
     */
    HTMLControl.prototype.setValue = function (value) {
        HTMLControl.superclass.prototype.setValue.call(this, value);
        if (this.html && this.html.value !== this.value) {
            this.html.value = this.value;
        }

        return this;
    };

    /**
     * Disables/enables the control
     * @param {Boolean} disable If the value is evaluated as true then the control
     is disabled, otherwise the control is enabled.
     * @chainable
     */
    HTMLControl.prototype.disable = function (disable) {
        HTMLControl.superclass.prototype.disable.call(this, disable);
        if (this.html) {
            this.html.disabled = this.disabled;
        }

        return this;
    };
    /**
     * Returns the control's value directly from the control's HTML element.
     *
     * This method is used internally by the object, so in most of the cases you won't need to invocated.
     * To get the control's value please use the {@link PMUI.control.Control#getValue getValue()} method.
     * @return {String}
     */
    HTMLControl.prototype.getValueFromRawElement = function () {
        return this.html.value;
    };
    /**
     * Attach the event listeners for the control's HTML element
     * @chainable
     */
    HTMLControl.prototype.defineEvents = function () {
        var that = this;
        HTMLControl.superclass.prototype.defineEvents.call(this);
        if (this.html) {
            this.addEvent('blur').listen(this.html, function () {
                that.onChangeHandler();
            });
            this.addEvent('keydown').listen(this.html, function (e) {
                if (e.which === PMUI.keyCodeF5) {
                    this.blur();
                    e.preventDefault();
                    window.location.reload(true);
                }
            });
        }
        return this;
    };
    /**
     * Creates the HTML element for the control.
     * @return {HTMLElement}
     */
    HTMLControl.prototype.createHTML = function () {
        if (this.html) {
            return this.html;
        }
        HTMLControl.superclass.prototype.createHTML.call(this);
        this.setName(this.name)
            .setValue(this.value)
            .disable(this.disabled);

        return this.html;
    };

    PMUI.extendNamespace('PMUI.control.HTMLControl', HTMLControl);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = HTMLControl;
    }
}());
(function () {
    /**
     * @class PMUI.control.TextControl
     * Class for handle the HTML native text input control.
     * @extends PMUI.control.HTMLControl
     *
     * Quick usage example:
     *
     *      @example
     *      myTextbox = new PMUI.control.TextControl({
     *          name: "my_name",
     *          value: "John Doe",
     *          maxLength: 12,
     *          placeholder: "insert your name here",
     *          disabled: false,
     *          onChange: function(currentValue, previousValue) {
     *              if(previousValue !== "") {
     *                  alert("Your name is not \"" + previousValue + "\" anymore.\nNow it's \"" + currentValue + "\"");
     *              } else {
     *                  alert("Now your name is " + currentValue);
     *              }
     *          }
     *      });
     *
     *      document.body.appendChild(myTextbox.getHTML());
     *
     * @constructor
     * Creates a new instance of the TextControl object.
     * @param {Object} [settings=null] A JSON object with the config options.
     *
     * @cfg {String} [placeholder=""] A string to be used as the control's placeholder.
     * @cfg {Number} [maxLength=524288] A number which specifies the maximum character length the control can accept.
     * @cfg {Function} [onKeyUp=null] A callback function to be called every time a pressed key is released on the
     * control. For info about the callback parameters please read the
     * {@link PMUI.control.TextControl#event-onKeyUp onKeyUp event} section.
     */
    var TextControl = function (settings) {
        TextControl.superclass.call(this, settings);
        /**
         * @property {String} [placeholder=""]
         * The control's placeholder (the text to be shown inside the control when there is not any text in it).
         * @readonly
         */
        this.placeholder = null;
        /**
         * @property {Number} [maxLength=524288] The maximum character length the control accepts.
         * @readonly
         */
        this.maxLength = null;
        /**
         * @event onKeyUp
         * Fired when a pressed key is released.
         * @param {Object} event The event object.
         */
        this.onKeyUp = null;
        TextControl.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.control.HTMLControl', TextControl);

    TextControl.prototype.type = "TextControl";
    /**
     * Initializes the object.
     * @param  {Object} [settings=null] A JSON object with the config options.
     * @private
     */
    TextControl.prototype.init = function (settings) {
        var defaults = {
            placeholder: "",
            maxLength: 524288,
            onKeyUp: null,
            height: 30
        };

        $.extend(true, defaults, settings);

        this.setPlaceholder(defaults.placeholder)
            .setMaxLength(defaults.maxLength)
            .setOnKeyUpHandler(defaults.onKeyUp)
            .setHeight(defaults.height);

    };
    /**
     * Sets the callback function to be called when the keyup event occurs.
     * @chainable
     */
    TextControl.prototype.setOnKeyUpHandler = function (handler) {
        if (typeof handler === 'function') {
            this.onKeyUp = handler;
        }

        return this;
    };
    /**
     * Sets the placeholder test to show in the control when there's not ant value in it.
     * @param {String} placeholder
     * @chainable
     */
    TextControl.prototype.setPlaceholder = function (placeholder) {
        if (typeof placeholder === 'string') {
            this.placeholder = placeholder;
            if (this.html) {
                this.html.placeholder = placeholder;
            }
        }

        return this;
    };
    /**
     * Sets the maximun character number to be accepted in the control.
     * @param {Number} maxLength The number must be an integer.
     * If the value is minor or equal to 0 then the maxLength property is set to the default (524288).
     * @chainable>
     */
    TextControl.prototype.setMaxLength = function (maxLength) {
        if (typeof maxLength === 'number' && maxLength % 1 === 0) {
            this.maxLength = maxLength;
            if (this.html) {
                this.html.maxLength = maxLength > 0 ? maxLength : 524288;
            }
        } else {
            throw new Error("method setMaxLength() only accepts integer values.");
        }

        return this;
    };
    /**
     * Set a value in the parameter disabled.
     * @param {Boolean} value
     */
     TextControl.prototype.setDisabled = function (value) {
        if (typeof value === 'boolean') {
            this.disabled = value;
            if (this.html) {
                this.html.disabled = value;
            }
        } else {
            throw new Error("method setDisabled() only accepts boolean values.");
        }
    };
    /**
     * Set the events for the object.
     * @chainable
     */
    TextControl.prototype.defineEvents = function () {
        var that = this;
        TextControl.superclass.prototype.defineEvents.call(this);
        if (this.html) {
            this.addEvent('keyup').listen(this.html, function (e) {
                if (typeof that.onKeyUp === 'function') {
                    that.onKeyUp(e);
                }
                //if(e.which == 13){
                //    that.onChangeHandler();
                //}
            });
        }
        return this;
    };
    /**
     * Creates the HTML element for the object
     * @return {HTMLElement}
     */
    TextControl.prototype.createHTML = function () {
        TextControl.superclass.prototype.createHTML.call(this);
        this.html.type = "text";
        this.setPlaceholder(this.placeholder)
            .setMaxLength(this.maxLength)
            .setReadOnly(this.readonly);

        return this.html;
    };
    /**
     * Sets if the control will be enabled for read only.
     * @param {Boolean}
     * @chainable
     */
    TextControl.prototype.setReadOnly = function (readonly) {
        if (typeof readonly !== 'undefined') {
            this.readonly = !!readonly;
            if (this.html) {
                this.html.readOnly = this.readonly;
            }
        }
        return this;
    };
    /**
     * Returns true if the control is enabled for read only.
     * @return {Boolean}
     */
    TextControl.prototype.isReadOnly = function () {
        return this.readonly;
    };

    TextControl.prototype.getMaxLength = function () {
        return this.maxLength;
    }

    PMUI.extendNamespace('PMUI.control.TextControl', TextControl);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = TextControl;
    }
}());
(function () {
    /**
     * @class PMUI.control.PasswordControl
     * Class for handle the HTML native password input control.
     * @extends PMUI.control.HTMLControl
     *
     * Quick usage example:
     *
     *      @example
     *      myTextbox = new PMUI.control.PasswordControl({
     *          name: "my_name",
     *          value: "John Doe",
     *          maxLength: 12,
     *          disabled: false,
     *          onChange: function(currentValue, previousValue) {
     *              if(previousValue !== "") {
     *                  alert("Your name is not \"" + previousValue + "\" anymore.\nNow it's \"" + currentValue + "\"");
     *              } else {
     *                  alert("Now your name is " + currentValue);
     *              }
     *          }
     *      });
     *
     *      document.body.appendChild(myTextbox.getHTML());
     *
     * @constructor
     * Creates a new instance of the PasswordControl object.
     * @param {Object} [settings=null] A JSON object with the config options.
     *
     * @cfg {Number} [maxLength=524288] A number which specifies the maximum character length the control can accept.
     * @cfg {Function} [onKeyUp=null] A callback function to be called every time a pressed key is released on the
     * control. For info about the callback parameters please read the
     * {@link PMUI.control.PasswordControl#event-onKeyUp onKeyUp event} section.
     */
    var PasswordControl = function (settings) {
        PasswordControl.superclass.call(this, settings);
//        /**
//         * @property {String} [placeholder=""]
//         * The control's placeholder (the text to be shown inside the control when there is not any text in it).
//         * @readonly
//         */
//        this.placeholder = null;
        /**
         * @property {Number} [maxLength=524288] The maximum character length the control accepts.
         * @readonly
         */
        this.maxLength = null;
        /**
         * @event onKeyUp
         * Fired when a pressed key is released.
         * @param {Object} event The event object.
         */
        this.onKeyUp = null;
        PasswordControl.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.control.HTMLControl', PasswordControl);

    PasswordControl.prototype.type = "PasswordControl";
    /**
     * Initializes the object.
     * @param  {Object} [settings=null] A JSON object with the config options.
     * @private
     */
    PasswordControl.prototype.init = function (settings) {
        var defaults = {
            maxLength: 524288,
            onKeyUp: null,
            height: 30
        };

        $.extend(true, defaults, settings);

        this.setMaxLength(defaults.maxLength)
            .setOnKeyUpHandler(defaults.onKeyUp)
            .setHeight(defaults.height);
    };
    /**
     * Sets the callback function to be called when the keyup event occurs.
     * @chainable
     */
    PasswordControl.prototype.setOnKeyUpHandler = function (handler) {
        if (typeof handler === 'function') {
            this.onKeyUp = handler;
        }

        return this;
    };
    /**
     * Sets the maximun character number to be accepted in the control.
     * @param {Number} maxLength The number must be an integer.
     * If the value is minor or equal to 0 then the maxLength property is set to the default (524288).
     * @chainable>
     */
    PasswordControl.prototype.setMaxLength = function (maxLength) {
        if (typeof maxLength === 'number' && maxLength % 1 === 0) {
            this.maxLength = maxLength;
            if (this.html) {
                this.html.maxLength = maxLength > 0 ? maxLength : 524288;
            }
        } else {
            throw new Error("method setMaxLength() only accepts integer values.");
        }

        return this;
    };
    /**
     * Set the events for the object.
     * @chainable
     */
    PasswordControl.prototype.defineEvents = function () {
        var that = this;

        PasswordControl.superclass.prototype.defineEvents.call(this);
        this.addEvent('keyup').listen(this.html, function (e) {
            if (typeof that.onKeyUp === 'function') {
                that.onKeyUp(e);
            }
        });
        return this;
    };
    /**
     * Creates the HTML element for the object
     * @return {HTMLElement}
     */
    PasswordControl.prototype.createHTML = function () {
        PasswordControl.superclass.prototype.createHTML.call(this);
        this.html.type = "password";
        this.setMaxLength(this.maxLength);
        return this.html;
    };

    PMUI.extendNamespace('PMUI.control.PasswordControl', PasswordControl);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = PasswordControl;
    }
}());
(function () {
    /**
     * @class PMUI.control.DropDownListControl
     * @extends PMUI.control.HTMLControl
     * Class to handle the Select HTML form element.
     *
     * Example usage:
     *
     *      @example
     *      var control;
     *      $(function() {
     *          var settings = {
     *              name: "myList",
     *              options: [
     *                  {
     *                      label: "one",
     *                      value: 1
     *                  },
     *                  {
     *                      label: "two",
     *                      value: 2,
     *                      disabled: true
     *                  },
     *                  {
     *                      label: "three",
     *                      value: 3,
     *                      selected: true
     *                  }, 
     *                  {
     *                      label: "Letters",
     *                      options: [
     *                          {
     *                              value: "A"
     *                          },
     *                          {
     *                              value: "B"
     *                          }
     *                      ]
     *                  },
     *                  {
     *                      label: "months",
     *                      disabled: true,
     *                      options: [
     *                          {
     *                              value: "january"
     *                          },
     *                          {
     *                              value: "february"
     *                          }
     *                      ]
     *                  }
     *              ],
     *              value: 2
     *          };
     *          control = new PMUI.control.DropDownListControl(settings);
     *          document.body.appendChild(control.getHTML());
     *      });
     *
     * @constructor
     * Creates a new instance of the DropDownListControl.
     * @param {Object} [settings=null] An JSON object with the config options.
     */
    /**
     * @cfg {Array} [options=[]]
     * An array with all the options to be contained by the control.
     *
     * Each element in the array is a JSON object, this JSON object can represent an option group
     * or an option item.
     *
     * In case to represent an option item it can contain the next properties:
     *
     * - value {String} (required): the value for the option.
     * - label {String} (optional): the label for the option, if isn't specified the value is used instead.
     * - selected {Boolean} (optional): if the option is selected. #Note. If the configuration object has the
     * "value" propery set then this "selected" property will be
     * - disabled {Boolean} (optional): if the option is disabled or not.
     *
     * On the other hand, in case to represent an option group, it can contain the next properties:
     *
     * - label {String} (required): The name for the option group.
     * - disabled {Boolean} (optional): If the group is disabled or not.
     * - options {Array} (required): An array in which each element is a JSON object representing an option item,
     * so every item must have the structure explained above (for represent option items). #Note. This propery makes
     * the difference between an option and a option group. If the "options" property is not specified or if it isn't
     * an array then it will treated like a option item.
     * @cfg {String|Number} [value=null] The value of the option that is wanted to be selected. It must be one of the values
     * of the list options, otherwise it will be set to "".
     */
    var DropDownListControl = function (settings) {
        DropDownListControl.superclass.call(this, settings);
        /**
         * @property {Array} [options] An array with all the options/option groups form the control.
         * @private
         */
        this.options = [];
        /**
         * @property {String} [elementTag='input'] The tag for the HTML element to be created.
         * @private
         */
        this.elementTag = 'select';
        DropDownListControl.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.control.HTMLControl', DropDownListControl);

    DropDownListControl.prototype.type = "DropDownListControl";
    /**
     * Initializes the object.
     * @param  {Oject} [settings=null] A JSON opbject with the config options.
     * @private
     */
    DropDownListControl.prototype.init = function (settings) {
        var defaults = {
            options: [],
            value: null,
            height: 30
        };

        jQuery.extend(true, defaults, settings);

        this.setOptions(defaults.options);
        this.setHeight(defaults.height);

        if (defaults.value !== null) {
            this.setValue(defaults.value);
        }
    };
    /**
     * Clear all the options from the control.
     * @chainable
     */
    DropDownListControl.prototype.clearOptions = function () {
        this.options = [];
        if (this.html) {
            jQuery(this.html).empty();
        }
        this.value = "";
        return this;
    };
    /**
     * Enables or disables one or more options/option groups.
     * @param  {Boolean} disabled If the function will disable (use true) or enable (use false)
     * the options/option groups.
     * @param  {String|Number|Object} option It can be a string, a number or a JSON object.
     *
     * - In case to be a String, it will be enabled/disabled the options that match the string in its value and the
     * option groups that match the string in its label. In this case more than one single item can be
     * enabled/disabled.
     * - In case to be a Number, it will be enabled/disabled the option/option group which index position matches the
     * number. Obviously, in this case only one item will be enabled/disabled.
     * - In case to be an object you can specify if the change will be applied only to options or option groups,
     * it should have two properties:
     *     - criteria {String}: The value (in case of options) or the label (in case of option groups) the items needs
     *     to match for apply the changes.
     *     - applyTo {String} (optional), it can take the following values:
     *         - "groups", the change will be applied only to the option groups.
     *         - "options", the change will be applied only to the options (direct child of the object).
     *         - [any other string value], the default value, it indicates that the change will be applied to both
     *         options/option groups that matches the criteria in its value/label respectly.
     *
     * @param  {String} [group] It it is specified must be an String making reference to an existing option group label.
     * Using this parameter, the elements to be match by the second parameter will be search only in the option groups
     * that match this parameter in its label.
     * @chainable
     * @private
     */
    DropDownListControl.prototype.enableDisableOption = function (disabled, option, group) {
        var dataGroupTarget = this,
            htmlGroupTarget = jQuery(this.html),
            i,
            j,
            suboptions,
            objectDefaults = {
                applyTo: 'all'
            };
        disabled = !!disabled;

        if (group) {
            for (i = 0; i < this.options.length; i += 1) {
                if (this.options[i].isGroup && this.options[i].label === group) {
                    dataGroupTarget = this.options[i];
                    break;
                }
            }
            if (dataGroupTarget === this.options) {
                throw new Error('disableOption(): the group "' + group + '" wasn\'t found.');
            }
            htmlGroupTarget = jQuery(this.html).find('>optgroup[label="' + group + '"]');
        }

        if (typeof option === 'number') {
            dataGroupTarget.options[option].disabled = disabled;
            htmlGroupTarget.find(">*").eq(option).attr("disabled", disabled);
        } else if (typeof option === 'string') {
            for (i = 0; i < dataGroupTarget.options.length; i += 1) {
                if (!dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].value === option) {
                    dataGroupTarget.options[i].disabled = disabled;
                } else if (dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].label === option) {
                    dataGroupTarget.options[i].disabled = disabled;
                    suboptions = dataGroupTarget.options[i].options;
                    for (j = 0; j < suboptions.length; j += 1) {
                        if (suboptions[j].value === option) {
                            suboptions[j].disabled = true;
                        }
                    }
                }
            }
            jQuery(htmlGroupTarget).find('option[value="' + option + '"]').add('optgroup[label="' + option + '"]')
                .attr("disabled", disabled);
        } else if (typeof option === 'object') {
            jQuery.extend(true, objectDefaults, option);
            if (objectDefaults.applyTo === 'groups') {
                for (i = 0; i < dataGroupTarget.options.length; i += 1) {
                    if (dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].label === option.criteria) {
                        dataGroupTarget.options[i].disabled = disabled;
                    }
                }
                jQuery(htmlGroupTarget).find('optgroup[label="' + option.criteria + '"]').attr("disabled", disabled);
            } else if (objectDefaults.applyTo === 'options') {
                for (i = 0; i < dataGroupTarget.options.length; i += 1) {
                    if (!dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].value === option.criteria) {
                        dataGroupTarget.options[i].disabled = disabled;
                    }
                }
                jQuery(htmlGroupTarget).find('option[value="' + option.criteria + '"]')
                    .attr("disabled", disabled.criteria);
            } else {
                option = option.criteria;
                for (i = 0; i < dataGroupTarget.options.length; i += 1) {
                    if (!dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].value === option) {
                        dataGroupTarget.options[i].disabled = disabled;
                    } else if (dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].label === option) {
                        dataGroupTarget.options[i].disabled = disabled;
                        suboptions = dataGroupTarget.options[i].options;
                        for (j = 0; j < suboptions.length; j += 1) {
                            if (suboptions[j].value === option) {
                                suboptions[j].disabled = true;
                            }
                        }
                    }
                }
                jQuery(htmlGroupTarget).find('option[value="' + option + '"]').add('optgroup[label="' + option + '"]')
                    .attr("disabled", disabled);
            }
        } else {
            throw new Error('disableOption(): the first parameter must be a Number or a String.');
        }

        return this;
    };
    /**
     * Disables one or more options/option groups.
     * @param  {String|Number|Object} option It can be a string, a number or a JSON object.
     *
     * - In case to be a String, it will be disabled the options that match the string in its value and the option
     * groups which match the string in its label. In this case more than one single item can be disabled.
     * - In case to be a Number, it will be disabled the option/option group which index position matches the number.
     * Obviously, in this case only one item will be disabled.
     * - In case to be an object you can specify if the change will be applied only to options or option groups,
     * it should have two properties:
     *     - criteria {String}: The value (in case of options) or the label (in case of option groups) the items needs
     *     to match for apply the changes.
     *     - applyTo {String} (optional), it can take the following values:
     *         - "groups", the change will be applied only to the option groups.
     *         - "options", the change will be applied only to the options (direct child of the object).
     *         - [any other string value], the default value, it indicates that the change will be applied to both
     *         options/option groups that matches the criteria in its value/label respectly.
     *
     * @param  {String} [group] It it is specified must be an String making reference to an existing option group label.
     * Using this parameter, the elements to be match by the first parameter will be search only in the option groups
     * that match this parameter in its label.
     * @chainable
     */
    DropDownListControl.prototype.disableOption = function (option, group) {
        return this.enableDisableOption(true, option, group);
    };
    /**
     * Enables one or more options/option groups.
     * @param  {String|Number|Object} option It can be a string, a number or a JSON object.
     *
     * - In case to be a String, it will be enabled the options that match the string in its value and the option
     * groups which match the string in its label. In this case more than one single item can be enabled.
     * - In case to be a Number, it will be enabled the option/option group which index position matches the number.
     * Obviously, in this case only one item will be enabled.
     * - In case to be an object you can specify if the change will be applied only to options or option groups,
     * it should have two properties:
     *     - criteria {String}: The value (in case of options) or the label (in case of option groups) the items needs
     *     to match for apply the changes.
     *     - applyTo {String} (optional), it can take the following values:
     *         - "groups", the change will be applied only to the option groups.
     *         - "options", the change will be applied only to the options (direct child of the object).
     *         - [any other string value], the default value, it indicates that the change will be applied to both
     *         options/option groups that matches the criteria in its value/label respectly.
     *
     * @param  {String} [group] It it is specified must be an String making reference to an existing option group label.
     * Using this parameter, the elements to be match by the first parameter will be search only in the option groups
     * that match this parameter in its label.
     * @chainable
     */
    DropDownListControl.prototype.enableOption = function (option, group) {
        return this.enableDisableOption(false, option, group);
    };
    /**
     * Removes one or more option/option groups.
     * @param  {String|Number|Object} option It can be a string, a number or a JSON object.
     *
     * - In case to be a String, it will be removed the options that match the string in its value and the option
     * groups which match the string in its label. In this case more than one single item can be removed.
     * - In case to be a Number, it will be removed the option/option group which index position matches the number.
     * Obviously, in this case only one item will be removed.
     * - In case to be an object you can specify if the change will be applied only to options or option groups,
     * it should have two properties:
     *     - criteria {String}: The value (in case of options) or the label (in case of option groups) the items needs
     *     to match for apply the changes.
     *     - applyTo {String} (optional), it can take the following values:
     *         - "groups", the change will be applied only to the option groups.
     *         - "options", the change will be applied only to the options (direct child of the object).
     *         - [any other string value], the default value, it indicates that the change will be applied to both
     *         options/option groups that matches the criteria in its value/label respectly.
     *
     * @param  {String} [group] It it is specified must be an String making reference to an existing option group label.
     * Using this parameter, the elements to be match by the first parameter will be search only in the option groups
     * that match this parameter in its label.
     *
     * ##Note. Removing an option group implies removing all its child options.
     * @chainable
     */
    DropDownListControl.prototype.removeOption = function (option, group) {
        var dataGroupTarget = this, htmlGroupTarget = jQuery(this.html), i, j, suboptions,
            objectDefaults = {
                applyTo: 'all'
            };

        if (group) {
            for (i = 0; i < this.options.length; i += 1) {
                if (this.options[i].isGroup && this.options[i].label === group) {
                    dataGroupTarget = this.options[i];
                    break;
                }
            }
            if (dataGroupTarget === this.options) {
                throw new Error('disableOption(): the group "' + group + '" wasn\'t found.');
            }
            htmlGroupTarget = jQuery(this.html).find('>optgroup[label="' + group + '"]');
        }

        if (typeof option === 'number') {
            dataGroupTarget.options.splice(option, 1);
            htmlGroupTarget.find(">*").eq(option).remove();
        } else if (typeof option === 'string') {
            for (i = 0; i < dataGroupTarget.options.length; i += 1) {
                if (!dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].value === option) {
                    dataGroupTarget.options.splice(i, 1);
                    i -= 1;
                } else if (dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].label === option) {
                    suboptions = dataGroupTarget.options[i].options;
                    for (j = 0; j < suboptions.length; j += 1) {
                        if (suboptions[j].value === option) {
                            suboptions.splice(j, 1);
                            j -= 1;
                        }
                    }
                    dataGroupTarget.options.splice(i, 1);
                    i -= 1;
                }
            }
            jQuery(htmlGroupTarget).find('option[value="' + option + '"]').add('optgroup[label="' + option + '"]')
                .remove();
        } else if (typeof option === 'object') {
            jQuery.extend(true, objectDefaults, option);
            if (objectDefaults.applyTo === 'groups') {
                for (i = 0; i < dataGroupTarget.options.length; i += 1) {
                    if (dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].label === option.criteria) {
                        dataGroupTarget.options.splice(i, 1);
                        i -= 1;
                    }
                }
                jQuery(htmlGroupTarget).find('optgroup[label="' + option.criteria + '"]').remove();
            } else if (objectDefaults.applyTo === 'options') {
                for (i = 0; i < dataGroupTarget.options.length; i += 1) {
                    if (!dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].value === option.criteria) {
                        dataGroupTarget.options.splice(i, 1);
                        i -= 1;
                    }
                }
                jQuery(htmlGroupTarget).find('option[value="' + option.criteria + '"]').remove();
            } else {
                option = option.criteria;
                for (i = 0; i < dataGroupTarget.options.length; i += 1) {
                    if ((!dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].value === option) ||
                        (dataGroupTarget.options[i].isGroup && dataGroupTarget.options[i].label === option)) {
                        dataGroupTarget.options.splice(i, 1);
                        i -= 1;
                    }
                }
                jQuery(htmlGroupTarget).find('option[value="' + option + '"]').add('optgroup[label="' + option + '"]')
                    .remove();
            }
        } else {
            throw new Error('disableOption(): the first parameter must be a Number or a String.');
        }

        return this;
    };
    /**
     * Adds a new option group to the control
     * @param {Object} optionGroup A JSON object with the following properties:
     *
     * - label {String} (required): the label for the option group.
     * - disabled {Boolean}(optional): if the option group will be disabled or not.
     * it defaults to false.
     * - options {Array} (optional): An array of JSON object, each one represents an option and
     * should have the same structure than the "option" paremeter for the
     * {@link PMUI.control.DropDownListControl#addOption addOption() method}.
     * @chainable
     */
    DropDownListControl.prototype.addOptionGroup = function (optionGroup) {
        var newOptionGroup = {},
            optionGroupHTML,
            i;

        if (!optionGroup.label) {
            throw new Error("addOptionGroup(): a label for the new option group is required!");
        }

        newOptionGroup.label = optionGroup.label;
        newOptionGroup.disabled = !!optionGroup.disabled;
        newOptionGroup.isGroup = true;
        newOptionGroup.options = [];

        this.options.push(newOptionGroup);

        if (this.html) {
            optionGroupHTML = PMUI.createHTMLElement('optgroup');
            optionGroupHTML.label = newOptionGroup.label;
            optionGroupHTML.disabled = newOptionGroup.disabled;
            this.html.appendChild(optionGroupHTML);
        }
        if (!jQuery.isArray(optionGroup.options)) {
            optionGroup.options = [];
        }
        for (i = 0; i < optionGroup.options.length; i += 1) {
            this.addOption(optionGroup.options[i], newOptionGroup.label);
        }

        return this;
    };
    /**
     * Adds a new option to the control or to an option group.
     * @param {Object} option An object with ther settings for the new option.
     * this object can have the following properties:
     *
     * - value {String} (required): the value for the option.
     * - label {String} (optional): the label for the option, if isn't specified the value is used instead.
     * - selected {Boolean} (optional): if the option is selected. #Note. If the configuration object has the
     * "value" propery set then this "selected" property will be
     * - disabled {Boolean} (optional): if the option is disabled or not.
     *
     * @param {String} group The name of the option group in which the new option will be added. If it doesn't exist
     * it will be created.
     *
     * @chainable
     */
    DropDownListControl.prototype.addOption = function (option, group) {
        var newOption = {},
            optionHTML,
            i,
            groupHTML,
            flag = false;

        newOption.value = option.value !== null && option.value !== undefined && option.value.toString ? option.value : (option.label || "");
        newOption.value = newOption.value.toString();
        newOption.label = option.label || newOption.value;
        newOption.label = newOption.label.toString();
        newOption.disabled = !!option.disabled;
        newOption.isGroup = false;

        if (!group) {
            this.options.push(newOption);
        } else {
            for (i = 0; i < this.options.length; i += 1) {
                if (this.options[i].isGroup && this.options[i].label === group) {
                    this.options[i].options.push(newOption);
                    flag = true;
                    break;
                }
            }
            if (!flag) {
                this.addOptionGroup({
                    label: group
                });
                this.options[this.options.length - 1].options.push(newOption);
            }
        }

        if (this.html) {
            optionHTML = PMUI.createHTMLElement('option');
            optionHTML.value = newOption.value;
            optionHTML.selected = !!option.selected;
            optionHTML.label = newOption.label;
            optionHTML.disabled = newOption.disabled;
            optionHTML.textContent = optionHTML.label;

            if (group) {
                groupHTML = jQuery(this.html).find('optgroup[label="' + group + '"]');
                if (groupHTML.length) {
                    groupHTML.get(0).appendChild(optionHTML);
                } else {
                    throw new Error("addOption(): the optiongroup \"" + group + "\" wasn't found");
                }
            } else {
                jQuery(this.html).append(optionHTML);
            }
        }

        if (option.selected) {
            this.value = newOption.value;
        }
        if (this.getOptions().length == 1) {
            this.value = newOption.value;
        }
        return this;
    };
    /**
     * Returns the label from the option currently selected.
     * @return {String}
     */
    DropDownListControl.prototype.getSelectedLabel = function () {
        var i;
        if (this.html) {
            return jQuery(this.html).find('option:selected').attr('label');
        }
        for (i = 0; i < this.options.length; i += 1) {
            if (this.options[i].value === this.value) {
                return this.options[i].label;
            }
        }

        return "";
    };
    /**
     * Determines if a value exists in any of the list options.
     * @param  {String|Number} value The value to be searched
     * @return {Boolean} It returns true if the value was found in any of the list options, otherwise it returns false.
     */
    DropDownListControl.prototype.valueExistsInOptions = function (value) {
        var i,
            j,
            options,
            optionsLength,
            subOptions,
            subOptionsLength;

        optionsLength = (options = this.options || []).length;
        for (i = 0; i < optionsLength; i += 1) {
            if (options[i].isGroup) {
                subOptionsLength = (subOptions = options[i].options || []).length;
                for (j = 0; j < subOptionsLength; j += 1) {
                    if (subOptions[j].value == value) {
                        return true;
                    }
                }
            } else if (options[i].value == value) {
                return true;
            }
        }
        return false;
    };
    /**
     * Returns the first available option in the list.
     * @return {Object|null} It returns a object literal with the label and value properties from the found option.
     * @private
     */
    DropDownListControl.prototype.getFirstAvailableOption = function () {
        var i,
            j,
            options,
            optionsLength,
            subOptions,
            subOptionsLength;

        optionsLength = (options = this.options || []).length;
        for (i = 0; i < optionsLength; i += 1) {
            if (options[i].isGroup) {
                return (options[i].options.length && options[i].options[0]) || null;
            } else {
                return options[i];
            }
        }
        return null;
    }
    /**
     * Sets the options/option groups for the control.
     * @param {Array} options An array with the same structure that the
     * {@link PMUI.control.DropDownListControl#cfg-options "options"} property in the
     * Config options section.
     * @chainable
     */
    DropDownListControl.prototype.setOptions = function (options) {
        var i,
            valueExists,
            firstOption;
        if (jQuery.isArray(options)) {
            this.clearOptions();
            for (i = 0; i < options.length; i += 1) {
                if (jQuery.isArray(options[i].options)) {
                    this.addOptionGroup(options[i]);
                } else {
                    this.addOption(options[i]);
                }
            }
            if (!this.valueExistsInOptions(this.value)) {
                firstOption = this.getFirstAvailableOption();
                this.value = (firstOption && firstOption.value) || "";
            }
        }

        return this;
    };
    /**
     * Returns the options/option groups from the field
     * @param  {Boolean} [includeGroups=false] If it's evaluated as true then it will include
     * the option groups with its child elements, otherwise it will return only the option items.
     * @return {Array}
     *
     * example
     *    list.getOptions(false);
     *     [La Paz][Cochabamba][Santa Cruz][Buenos Aires][Santa Fe][Cordoba][Santiago][.][.][Mexico D.F]
     *
     *    list.getOptions(true)
     *      [BOLIVIA
     *          [La Paz][Cochabamba][SantaCruz]
     *      ]
     *      [ARGENTINA
     *          [Buenos Aires][Santa Fe][Cordoba]
     *       ]
     *      [CHILE
     *         [x][y][z]
     *      ]
     *      [New York]
     *      [Mexico D.F.]
     */
    DropDownListControl.prototype.getOptions = function (includeGroups) {
        var options = [],
            i,
            j;
        if (includeGroups) {
            return this.options.slice(0);
        }
        for (i = 0; i < this.options.length; i += 1) {
            if (!this.options[i].isGroup) {
                options.push(this.options[i]);
            } else {
                for (j = 0; j < this.options[i].options.length; j += 1) {
                    options.push(this.options[i].options[j]);
                }
            }
        }

        return options;
    };
    /**
     * Defines the events for the control
     * @chainable
     */
    DropDownListControl.prototype.defineEvents = function () {
        var that = this;
        DropDownListControl.superclass.superclass.prototype.defineEvents.call(this);
        if (this.html) {
            this.addEvent('change').listen(this.html, function () {
                that.onChangeHandler();
            });
        }
        return this;
    };
    /**
     * Sets the selected option in the dropdown.
     * @param {String|Number} value The value of the option that is wanted to be selected. It must be one of the values
     * of the list options, otherwise it will be set to "".
     */
    DropDownListControl.prototype.setValue = function (value) {
        var firstOption;
        if (!this.valueExistsInOptions(value)) {
            firstOption = this.getFirstAvailableOption();
            value = (firstOption && firstOption.value) || "";
        }
        return DropDownListControl.superclass.prototype.setValue.call(this, value);
    };
    /**
     * Creates the HTML element for the control.
     * @return {HTMLElement}
     */
    DropDownListControl.prototype.createHTML = function () {
        var value;
        if (this.html) {
            return this.html;
        }
        value = this.value;
        DropDownListControl.superclass.prototype.createHTML.call(this);
        this.setOptions(this.options.slice(0))
            .setValue(value);

        return this.html;
    };

    PMUI.extendNamespace('PMUI.control.DropDownListControl', DropDownListControl);
}());
(function () {
    /**
     * @class PMUI.control.TextAreaControl
     * Class to handle a HTML TextArea control.
     * @extends PMUI.control.TextControl
     *
     * Usage example:
     *
     *      @example
     *          var myTextArea;
     *          $(function() {
     *              myTextArea = new PMUI.control.TextAreaControl({
     *                  name: "some_text",
     *                  value: "John Doe",
     *                  maxLength: 12,
     *                  readonly: false,
     *                  width: 300,
     *                  height: 200,
     *                  placeholder: "insert some text",
     *                  disabled: false,
     *                  onChange: function(currentValue, previousValue) {
     *                      if(previousValue !== "") {
     *                          alert("the text is not \"" + previousValue + "\" anymore.\nNow it's \"" 
     *                              + currentValue + "\"");
     *                      } else {
     *                          alert("Now your name is " + currentValue);
     *                      }
     *                  }
     *              });
     *      
     *              document.body.appendChild(myTextArea.getHTML());
     *              
     *              myTextArea.defineEvents();
     *          });
     *
     * @constructor
     * Creates a new instance
     * @param {Object} [settings] A JSON object that can contain the properties specified in the
     * config options section.
     *
     * @cfg {Boolean} [readonly=false] If the control will be readonly.
     */
    var TextAreaControl = function (settings) {
        TextAreaControl.superclass.call(this, settings);
        /**
         * @property {Boolean} readonly A Boolean that specifies if the control is enabled for read only.
         * @readonly
         */
        this.readonly = null;
        /**
         * @property {String} [elementTag='input'] The tag for the HTML element to be created.
         * @private
         */
        this.elementTag = 'textarea';
        TextAreaControl.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.control.TextControl', TextAreaControl);

    TextAreaControl.prototype.type = "TextAreaControl";
    /**
     * Initializes the object.
     * @param  {Object} [settings=null] A JSON object with the config options.
     * @private
     */
    TextAreaControl.prototype.init = function (settings) {
        var defaults = {
            readonly: false
        };

        jQuery.extend(true, defaults, settings);

        this.setReadOnly(defaults.readonly);
    };
    /**
     * Creates the HTML element for the object
     * @return {HTMLElement}
     */
    TextAreaControl.prototype.createHTML = function () {
        TextAreaControl.superclass.prototype.createHTML.call(this);

        this.setReadOnly(this.readonly);

        return this.html;
    };
    /**
     * Sets the maximun character number to be accepted in the control.
     * @param {Number} maxLength The number must be an integer.
     * If the value is minor or equal to 0 then the maxLength property is set to the default (524288).
     * @chainable>
     */
    TextAreaControl.prototype.setMaxLength = function (maxLength) {
        var that = this;

        if (typeof maxLength === 'number' && maxLength % 1 === 0) {
            this.maxLength = maxLength;
            if (this.html) {
                this.html.maxLength = maxLength > 0 ? maxLength : 524288;
            }
        } else {
            throw new Error("method setMaxLength() only accepts integer values.");
        }

        return this;
    };
    PMUI.extendNamespace('PMUI.control.TextAreaControl', TextAreaControl);

    if (typeof exports !== "undefined") {
        module.exports = TextAreaControl;
    }
}());
(function () {
    /**
     * @class PMUI.control.SelectableControl
     * A checkbox is a graphical component that can be in either an "on" (true) or "off" (false) state.
     * Clicking on a check box changes its state from "on" to "off," or from "off" to "on."
     * @extends PMUI.control.HTMLControl
     *
     * Usage example:
     *
     *      @example
     *      var a;
     *      $(function() {
     *          a = new PMUI.control.SelectableControl({
     *              name: "music",
     *              label: "Do you like music?",
     *              value: true,
     *              mode: 'checkbox', //it also can be "radio"
     *              onSelect: function() {
     *                  console.log("checked");
     *              },
     *              onDeselect: function() {
     *                  console.log("unchecked");
     *              }
     *          });
     *          document.body.appendChild(a.getHTML());
     *          a.defineEvents();
     *      });
     *
     * @constructor
     * Creates a new instance of the SelectableControl class.
     * @param {Object} [settings=null] A JSON object with the config options.
     *
     * @cfg {Boolean} [selected=false] If the control will be selected initially.
     * @cfg {String} [label=""] The label for the control.
     * @cfg {String} [value=""] The value for the control.
     * @cfg {Function} [onSelect=null] The function to be call when the item will be selected. For info about the
     * parameters please read the {@link PMUI.control.SelectableControl#event-onSelect onSelect event} documentation.
     * @cfg {Function} [onDeselect=null] The function to be call when the item will be deselected
     * (only supported when the mode is set to "checkbox"). For info about the callback parameters please read the
     * {@link PMUI.control.SelectableControl#event-onDeselect onDeselect event} documentation.
     * @cfg {String} [mode="checkbox"] The mode for the control, it can be: "checkbox" (default)
     * for a checkbox control or "radio" for a readio button.
     */
    var SelectableControl = function (settings) {
        SelectableControl.superclass.call(this, settings);
        /**
         * If the control is selected or not.
         * @type {Boolean}
         * @readonly
         */
        this.selected = null;
        /**
         * The control's label.
         * @type {String}
         * @readonly
         */
        this.label = null;
        /**
         * @event onSelect
         * Fired when the control is selected.
         */
        this.onSelect = null;
        /**
         * @event onDeselect
         * Fired when the control is deselected.
         */
        this.onDeselect = null;
        /**
         * The control's selection mode.
         * @type {String}
         * @readonly
         */
        this.mode = null;
        /**
         * The interactive control's HTML element.
         * @type {HTMLElement}
         * @private
         */
        this.control = null;
        /**
         * The control's HTML element that will contain the label text.
         * @type {[type]}
         */
        this.textContainer = null;
        SelectableControl.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.control.HTMLControl', SelectableControl);
    /**
     * The object's type.
     * @type {String}
     */
    SelectableControl.prototype.type = 'SelectableControl';
    /**
     * Initializes the object.
     * @param  {Object} [settings=null] A JSON object with the config options.
     * @private
     */
    SelectableControl.prototype.init = function (settings) {
        var defaults = {
            selected: false,
            mode: 'checkbox',
            label: '',
            value: "",
            onSelect: null,
            onDeselect: null
        };

        jQuery.extend(true, defaults, settings);

        if (defaults.mode === 'checkbox' || defaults.mode === 'radio') {
            this.mode = defaults.mode;
        } else {
            throw new Error('SelectableControl: it only accepts "checkbox" and "radio" as value for the "mode"'
                + ' property');
        }
        this.onSelect = defaults.onSelect;
        this.onDeselect = defaults.onDeselect;

        this.setLabel(defaults.label);

        if (defaults.selected) {
            this.select();
        } else {
            this.deselect();
        }
    };
    /**
     * Sets the control's name
     * @param {String} name
     * @chainable
     */
    SelectableControl.prototype.setName = function (name) {
        SelectableControl.superclass.prototype.setName.call(this, name);
        if (this.control) {
            this.control.name = name;
        }

        return this;
    };
    /**
     * Sets the value for the control
     * @param {String|Boolean|Number} setValue This param value is evaluated as boolean.
     * @chainable
     */
    SelectableControl.prototype.setValue = function (value) {
        SelectableControl.superclass.superclass.prototype.setValue.call(this, value);
        if (this.control) {
            this.control.value = value;
        }
        return this;
    };
    /**
     * Returns a boolean that specifies if the checkbox/radio button is selected.
     * @return {Boolean}
     */
    SelectableControl.prototype.isSelected = function () {
        if (this.html) {
            this.selected = jQuery(this.control).is(':checked');
        }
        return this.selected;
    };
    /**
     * Sets deselected the checkbox/radio button.
     * @chainable
     */
    SelectableControl.prototype.deselect = function () {
        this.selected = false;

        if (this.control) {
            this.control.checked = false;
        }

        return this;
    };
    /**
     * Sets selected the checkbox/radio button.
     * @chainable
     */
    SelectableControl.prototype.select = function () {
        this.selected = true;

        if (this.control) {
            this.control.checked = true;
        }

        return this;
    };
    /**
     * A method which is called everytime the checked state from the control changes.
     *
     * This method is used internally by the object, so in most of the cases you won't need to invocated.
     * To execute instructions when the control changes, please use the
     {@link PMUI.control.Control#setOnChangeHandler setOnChangeHandler()} method.
     *
     * @chainable
     * @private
     */
    SelectableControl.prototype.onChangeHandler = function () {
        this.selected = $(this.control).is(":checked");
        if (typeof this.onChange === 'function') {
            this.onChange(this.value, this.value);
        }
        if (this.selected && typeof this.onSelect === 'function') {
            this.onSelect();
        } else if (!this.selected && typeof this.onDeselect === 'function') {
            this.onDeselect();
        }

        return this;
    };
    /**
     * Sets the label for the control.
     * @param {String} label
     * @chainable
     */
    SelectableControl.prototype.setLabel = function (label) {
        if (typeof label !== 'string') {
            throw new Error("setLabel(): it only accepts string type values.");
        }
        this.label = label;
        if (this.html) {
            this.textContainer.textContent = label;
        }

        return this;
    };
    /**
     * Disables/enables the control
     * @param {Boolean} disable If the value is evaluated as true then the control
     is disabled, otherwise the control is enabled.
     * @chainable
     */
    SelectableControl.prototype.disable = function (disable) {
        SelectableControl.superclass.superclass.prototype.disable.call(this, disable);
        if (this.html) {
            this.control.disabled = this.disabled;
        }

        return this;
    };
    /**
     * @inheritDoc
     */
    SelectableControl.prototype.defineEvents = function () {
        var that = this,
            stopPropagation = function (e) {
                e.stopPropagation();
            };
        this.removeEvents();
        this.eventsDefined = true;
        if (this.html) {
            this.addEvent('change').listen(this.html, function () {
                that.onChangeHandler();
            });
            this.addEvent('keydown').listen(this.html, function (e) {
                if (e.which === PMUI.keyCodeF5) {
                    this.blur();
                    e.preventDefault();
                    window.location.reload(true);
                }
            });
        }
        return this;
    };
    /**
     * Creates the HTML element for the control.
     * @return {HTMLElement}
     */
    SelectableControl.prototype.createHTML = function () {
        var label,
            textContainer,
            control;
        if (this.html) {
            return this.html;
        }
        label = PMUI.createHTMLElement('label');
        control = PMUI.createHTMLElement('input');
        control.type = this.mode;
        textContainer = PMUI.createHTMLElement('span');
        textContainer.contentText = this.label;

        label.appendChild(control);
        label.appendChild(textContainer);
        this.control = control;
        this.textContainer = textContainer;
        this.html = label;

        this.html.id = this.id;

        this.setName(this.name)
            .setValue(this.value)
            .disable(this.disabled)
            .setLabel(this.label);

        if (this.selected) {
            this.select();
        } else {
            this.deselect();
        }

        this.applyStyle();

        return this.html;
    };

    SelectableControl.prototype.setFocus = function () {
        if (this.html) {
            this.control.focus();
        }
    };

    PMUI.extendNamespace('PMUI.control.SelectableControl', SelectableControl);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = SelectableControl;
    }
}());
(function () {
    /**
     * @class PMUI.control.HiddenControl
     * The HTML input tag with the type attribute set to "hidden".
     * @extends PMUI.control.HTMLControl
     *
     * Usage example:
     *
     *     var hiddenControl = new PMUI.control.HiddenControl();
     *
     *     document.body.appendChild(hiddenControl.getHTML());
     *
     * @constructor
     * Creates a new instance of the HiddenControl class.
     * @param {Object} [settings=null] A JSON object with the config options.
     */
    var HiddenControl = function (settings) {
        HiddenControl.superclass.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.control.HTMLControl', HiddenControl);

    HiddenControl.prototype.type = "HiddenControl";

    /**
     * Creates the HTML element for the control.
     * @return {HTMLElement}
     */
    HiddenControl.prototype.createHTML = function () {
        HiddenControl.superclass.prototype.createHTML.call(this);
        this.html.type = 'hidden';

        return this.html;
    };

    PMUI.extendNamespace('PMUI.control.HiddenControl', HiddenControl);

    if (typeof exports !== "undefined") {
        module.exports = HiddenControl;
    }
}());
(function () {
    /**
     * @class  PMUI.control.DateTimeControl
     * A control to handle dates and times.
     * @extends {PMUI.control.HTMLControl}
     *
     *
     * Usage example:
     *
     *      @example
     *      var dateTimePicker;
     *
     *      $(function() {
     *          dateTimePicker = new PMUI.control.DateTimeControl({
     *              dateFormat: 'M dd yy',
     *              minDate: -90,
     *              maxDate: "+1y -1m -4d",
     *              firstDay: 1,
     *              months: {
     *                  "january": {
     *                      name: "Enero",
     *                      shortname: "Ene"
     *                  },
     *                  "february": {
     *                      name: "Febrero",
     *                      shortname: "Feb"
     *                  },
     *                  "march": {
     *                      name: "Marzo",
     *                      shortname: "Mar"
     *                  },
     *                  "april": {
     *                      name: "Abril",
     *                      shortname: "Abr"
     *                  },
     *                  "may": "May",
     *                  "june": "Junio",
     *                  "july": "July",
     *                  "august": "Agosto",
     *                  "september": "Septiembre",
     *                  "october": "Octubre",
     *                  "november": "Noviembre",
     *                  "december": "Diciembre"
     *              },
     *              days: {
     *                  "sunday": {
     *                      name: "Domingo",
     *                      shortname: "Do"
     *                  },
     *                  "monday": {
     *                      name: "Lunes",
     *                      shortname: "Lu"
     *                  },
     *                  "tuesday": {
     *                      name: "Martes",
     *                      shortname: "Ma"
     *                  },
     *                  "wednesday": {
     *                      name: "Miércoles",
     *                      shortname: "Mi"
     *                  },
     *                  "thursday": {
     *                      name: "Jueves",
     *                      shortname: "Ju"
     *                  },
     *                  "friday": "Viernes",
     *                  "saturday": "Sábado"
     *              }
     *          });
     *          document.body.appendChild(dateTimePicker.getHTML());
     *          dateTimePicker.defineEvents();
     *      });
     *
     * @cfg {Boolean} [datetime=false] If the control will have time supporting.
     * @cfg {String} [dateFormat="yy-mm-dd HH:ii:ss"|"yy-mm-dd"] The format for the date to show in the text box.
     * It defaults to:
     *
     * - "yy-mm-dd HH:ii:ss" if {@link PMUI.control.DateTimeControl#cfg-datetime datetime} is set to true.
     * - "yy-mm-dd" if {@link PMUI.control.DateTimeControl#cfg-datetime datetime} is set to false.
     *
     * You can set a customized date format using the following wildcards:
     *
     * - d, day of month (no leading zero).
     * - dd, day of month (two digit).
     * - o, day of the year (no leading zeros).
     * - oo, day of the year (three digit).
     * - D, day name short.
     * - DD, day name long.
     * - m, month of year (no leading zero).
     * - mm, month of year (two digit).
     * - M, month name short.
     * - MM, month name long.
     * - y, year (two digit).
     * - yy, year (four digit).
     * - P, period (AM or PM).
     * - H, hours (0-23).
     * - HH, hours (00-23).
     * - h, hours (1-12).
     * - hh, hours (01-12).
     * - i, minutes (0-59).
     * - ii, minutes (00-59).
     * - s, seconds (0-59).
     * - ss, seconds (00-59).
     * - @, Unix timestamp (ms since 01/01/1970).
     * - !, Windows ticks (100ns since 01/01/0001).
     * - '...', literal text.
     * - '', single quote.
     * - anything else, literal text.
     *
     * The wildcards can be used together in the same string.
     *
     * @cfg {Object} [months={"january": "January", "february": "February", "march": "March", "april": "April",
     * "may": "May", "june": "June", "july": "July", "august": "August", "september": "September",
     * "october": "October", "november": "November", "december": "December"}]
     * A JSON object to set the names and shortnames for every month in year. Each property of this object can be:
     *
     * - A string, in this case the name for the month is set to this string, and the shortname is set using
     * the first 3 characters of the string.
     * - A JSON object, in this case the JSON may have two properties:
     *     - "name", will be used as the name for the month.
     *     - "shortname", will be used as the shortname for the month, if it is not specified the the shortname for
     *     the month will be set using the first 3 characters of the object's "name" property.
     *
     * @cfg {Object} [days={"sunday": "Sunday","monday": "Monday","tuesday": "Tuesday","wednesday": "Wednesday",
     * "thursday": "Thursday","friday": "Friday","saturday": "Saturday"}]
     * A JSON object to set the name and shortname for every day of week. Each property of this object can be:
     *
     * - A string, in this case the name for the day is set to this string, and the shortname is set using
     * the first 3 characters of the string.
     * - A JSON object, in this case the JSON may have two properties:
     *     - "name", will be used as the name for the day.
     *     - "shortname", will be used as the shortname for the day, if it is not specified the the shortname for
     *     the day will be set using the first 3 characters of the object's "name" property.
     *
     * @cfg {String|Number} [minDate=-365] A value which sets the minimum selectable date for the calendar. It can be:
     *
     * - a Date object.
     * - a String with the following format: "([+-]\d+[dmyw]\s)?". It will represent an addition or substraction of
     * time units to the current date, for example the expression "+1y -2m +3d" means
     * "the current date plus 1 year, minus 2 months plus 3 days". The prefixes you can use are:
     *     - "d" for days.
     *     - "w" for weeks.
     *     - "m" for months.
     *     - "y" for years.
     *
     *     You can also use one single unit, i.e. "+3y", "-1m", or a combination of two: "-2y +3d", etc.
     * - a Number, in this case the number is taken as the number of days that will be sum/substracted from the
     * current date. This number can be positive (for add days) or negative (for substract days).
     * - an Object, in this case the object must have the following structure:
     *
     *      {
     *          year: 2013,
     *          month: 5,
     *          day: 4,
     *          hours: 20,
     *          minutes: 15,
     *          seconds: 3,
     *          millisenconds
     *      }
     *
     * In this case only year and month are required.
     *
     * @cfg {String|Number} [maxDate=365] A value which sets the maximum selectable date for the calendar. It can take
     * the same type of values that the {@link PMUI.contriol.DateTimeControl#cfg-minDate minDate config option}.
     *
     * - a Date object.
     * - a String with the following format: "([+-]\d+[dmyw]\s)?". It will represent an addition or substraction of
     * time units to the current date, for example the expression "+1y -2m +3d" means
     * "the current date plus 1 year, minus 2 months plus 3 days". The prefixes you can use are:
     *     - "d" for days.
     *     - "w" for weeks.
     *     - "m" for months.
     *     - "y" for years.
     *
     *     You can also use one single unit, i.e. "+3y", "-1m", or a combination of two: "-2y +3d", etc.
     * - a Number, in this case the number is taken as the number of days that will be sum/substracted from the
     * current date. This number can be positive (for add days) or negative (for substract days).
     * - an Object, in this case the object must have the following structure:
     *
     *      {
     *          year: 2013,
     *          month: 5,
     *          day: 4,
     *          hours: 20,
     *          minutes: 15,
     *          seconds: 3,
     *          millisenconds
     *      }
     *
     * In this case only year and month are required.
     *
     * @cfg {Number} [firstDay=0] Sets the first day of week. You can use numbers from 0 to 6, 0 means Sunday,
     * 1 means Monday and so on.
     */
    var DateTimeControl = function (settings) {
        DateTimeControl.superclass.call(this, settings);
        /**
         * @property {Object} dom A JSON object that contains the control's DOM elements.
         * @private
         */
        this.dom = {};
        /**
         * @property {Boolean} datetime If the calendar has time supporting.
         * @readonly
         */
        this.datetime = null;
        /**
         * @property {String} dateFormat The format for the date to be shown on the control's textbox.
         * @readonly
         */
        this.dateFormat = null;
        /**
         * @property {Date} dateObject The control's date object.
         * @private
         */
        this.dateObject = null;
        /**
         * @property {Date} minDate The minimum selectable date.
         * @private
         */
        this.minDate = null;
        /**
         * @property {Date} maxDate The maximum selectable date.
         * @private
         */
        this.maxDate = null;
        /**
         * @property {Number} firstDay The first day of the week beginning with 0: Sunday and ending with 6: Saturday.
         * @readonly
         */
        this.firstDay = null;
        DateTimeControl.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.control.HTMLControl', DateTimeControl);

    DateTimeControl.prototype.type = "DateTimeControl";

    /**
     * An array with the name of each day of the week.
     * @type {Array}
     * @private
     */
    DateTimeControl.prototype.daysOrder = [
        "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"
    ];
    /**
     * An array with the name fo the months of year.
     * @type {Array}
     * @private
     */
    DateTimeControl.prototype.monthsOrder = [
        "january", "february", "march", "april", "may", "june", "july", "august", "september", "october",
        "november", "december"
    ];
    /**
     * An Object that contains the name, shortname and code for the days of the week.
     * @type {Object}
     * @private
     */
    DateTimeControl.prototype.days = {
        "sunday": {
            value: 0
        },
        "monday": {
            value: 1
        },
        "tuesday": {
            value: 2
        },
        "wednesday": {
            value: 3
        },
        "thursday": {
            value: 4
        },
        "friday": {
            value: 5
        },
        "saturday": {
            value: 6
        }
    };
    /**
     * An Object that contains the name, shortname and code for the months of year.
     * @type {Object}
     * @private
     */
    DateTimeControl.prototype.months = {
        "january": {
            value: 0
        },
        "february": {
            value: 1
        },
        "march": {
            value: 2
        },
        "april": {
            value: 3
        },
        "may": {
            value: 4
        },
        "june": {
            value: 5
        },
        "july": {
            value: 6
        },
        "august": {
            value: 7
        },
        "september": {
            value: 8
        },
        "october": {
            value: 9
        },
        "november": {
            value: 10
        },
        "december": {
            value: 11
        }
    };
    /**
     * Initialize the object.
     * @param  {Object} settings An object with the config options.
     * @private
     */
    DateTimeControl.prototype.init = function (settings) {
        var defaults = {
            datetime: false,
            dateFormat: settings && settings.datetime ? 'yy-mm-dd HH:ii:ss' : 'yy-mm-dd',
            months: {
                "january": "January",
                "february": "February",
                "march": "March",
                "april": "April",
                "may": "May",
                "june": "June",
                "july": "July",
                "august": "August",
                "september": "September",
                "october": "October",
                "november": "November",
                "december": "December"
            },
            days: {
                "sunday": "Sunday",
                "monday": "Monday",
                "tuesday": "Tuesday",
                "wednesday": "Wednesday",
                "thursday": "Thursday",
                "friday": "Friday",
                "saturday": "Saturday"
            },
            minDate: -365,
            maxDate: 365,
            firstDay: 0,
            height: 30

        };

        jQuery.extend(true, defaults, settings);

        this.setFirstDay(defaults.firstDay)
            .setDateFormat(defaults.dateFormat)
            .setMonths(defaults.months)
            .setDays(defaults.days)
            .setMinDate(defaults.minDate)
            .setMaxDate(defaults.maxDate)
            .visibleDateTime(defaults.datetime)
            .setHeight(defaults.height);
    };
    /**
     * @inheritdoc
     */
    DateTimeControl.prototype.setID = function (id) {
        DateTimeControl.superclass.prototype.setID.call(this, id);
        if (this.dom) {
            this.dom.calendar.id = 'pmui-datepicker-calendar-' + this.id;
        }
        return this;
    };
    /**
     * Returns the day index of the first day of week.
     * @return {Number} A number refering a day: 0 for Sunday, 1 for Monday and so on.
     */
    DateTimeControl.prototype.getFirstDay = function () {
        return this.firstDay;
    };
    /**
     * Returns the control's minimum selectable date.
     * @param {String} [format="UTC"] The format to applied to the returning date.
     * @return {String} The minimum selectable date in string format.
     */
    DateTimeControl.prototype.getMinDate = function (format) {
        return this.formatDate(this.minDate, format || "UTC");
    };
    /**
     * Returns the control's maximum selectable date.
     * @param {String} [format="UTC"] The format to applied to the returning date.
     * @return {String} The maximum selectable date in string format.
     */
    DateTimeControl.prototype.getMaxDate = function (format) {
        return this.formatDate(this.maxDate, format || "UTC");
    };
    /**
     * Enables/disabled the calendar's time supporting.
     * @param  {Boolean} visible If it's true, then the time supporting is enabled, otherwise it's disabled.
     * @chainable
     */
    DateTimeControl.prototype.visibleDateTime = function (visible) {
        visible = !!visible;
        this.datetime = visible;
        if (this.html) {
            this.dom.footer.style.display = this.datetime ? 'block' : 'none';
        }

        return this;
    };
    /**
     * @method  setValue
     * Sets the value for the Control.
     * @param {Date|Number|String} value The date to set to the control, it can be:
     *
     *  - A Date object, is used the date set in this object.
     *  - A Number, the number is used as the timestamp for the date (set in UTC).
     *  - A String, in this case the string must have one of the following formats:
     *
     *      - "" (empty string), the value is set to empty.
     *      - "\d", the value is parsed to a number to be used as the timestamp for the date.
     *      - "\d{4}\-\d{2}\-\d{2}", this date is taken as "yyyy-mm-dd" and it's parsed.
     *      - "\d{4}\-\d{2}\-\d{2}T\d{2}\:\d{2}\:\d{2}[\\+\-]\d{2}\:\d{2}", the date is taken as
     *      "yyyy-mm-ddTHH:ii:ss+HH:ii" (the timezone offset is used).
     *
     * @param {Boolean} [utc=false] If the date will be set as a UTC date.
     * (Only applicable if the first parameter is String and has the format "\d{4}\-\d{2}\-\d{2}").
     * @chainable
     */
    DateTimeControl.prototype.setValue = function (value, utc) {
        var newDate, aux, aux2, y, m, d, h, i, s, hd, id, datetime = false, error = false;
        if (value instanceof Date) {
            newDate = value;
        } else if (typeof value === 'number') {
            newDate = new Date(value);
        } else if (typeof value === 'string') {
            if (value === "") {
                this.dateObject = null;
            } else if (/^\d+$/.test(value)) {
                newDate = new Date(parseInt(value, 10));
            } else if (/^\d{4}\-\d{2}\-\d{2}$/.test(value)
                || /^\d{4}\-\d{2}\-\d{2}T\d{2}\:\d{2}\:\d{2}[\\+\-]\d{2}\:\d{2}$/.test(value)) {
                newDate = new Date();
                aux = value.split(/T|\+|\-(?=\d{2}\:\d{2})/);
                aux2 = aux[0].split("-");
                y = parseInt(aux2[0], 10);
                m = parseInt(aux2[1], 10);
                d = parseInt(aux2[2], 10);
                if (aux[1]) {
                    datetime = true;
                    aux2 = aux[1].split(":");
                    h = parseInt(aux2[0], 10);
                    i = parseInt(aux2[1], 10);
                    s = parseInt(aux2[2], 10);
                    aux2 = aux[2].split(":");
                    hd = (value[19] === '+' ? -1 : 1) * parseInt(aux2[0], 10);
                    id = (hd < 0 ? -1 : 1) * parseInt(aux2[1], 10);
                } else {
                    h = i = s = hd = id = 0;
                }

                if (!this.isValidDateTime(y, m, d, h, i, s)) {
                    throw new Error("setValue(): invalid date time.");
                }

                newDate.setFullYear(y);
                newDate.setMonth(m - 1);
                newDate.setDate(d);
                newDate.setHours(h);
                newDate.setMinutes(i);
                newDate.setSeconds(s);
                newDate.setMilliseconds(0);

                newDate.setHours(newDate.getHours() + hd);
                newDate.setMinutes(newDate.getMinutes() + id);

                if (!this.dateObject) {
                    this.dateObject = new Date();
                }

                if (datetime || utc) {
                    this.dateObject.setUTCFullYear(newDate.getFullYear());
                    this.dateObject.setUTCMonth(newDate.getMonth());
                    this.dateObject.setUTCDate(newDate.getDate());
                    this.dateObject.setUTCHours(newDate.getHours());
                    this.dateObject.setUTCMinutes(newDate.getMinutes());
                    this.dateObject.setUTCSeconds(newDate.getSeconds());
                } else {
                    this.dateObject.setFullYear(y);
                    this.dateObject.setMonth(m - 1);
                    this.dateObject.setDate(d);
                    this.dateObject.setHours(h);
                    this.dateObject.setMinutes(i);
                    this.dateObject.setSeconds(s);
                    this.dateObject.setMilliseconds(0);
                }
                newDate = null;
            } else {
                error = true;
            }
        } else {
            error = true;
        }

        if (error) {
            throw new Error("setValue(): Invalid parameter format/data type.");
        }
        if (value !== "") {
            if (!this.dateObject) {
                this.dateObject = new Date();
            }
            if (newDate) {
                this.dateObject.setFullYear(newDate.getFullYear());
                this.dateObject.setMonth(newDate.getMonth());
                this.dateObject.setDate(newDate.getDate());
                this.dateObject.setHours(newDate.getHours());
                this.dateObject.setMinutes(newDate.getMinutes());
                this.dateObject.setSeconds(newDate.getSeconds());
            }
        }

        if (this.html) {
            if (this.dateObject) {
                this.html.value = this.formatDate(this.dateObject, this.dateFormat);
                this.dom.hoursInput.value = this.dateObject.getHours();
                this.dom.minutesInput.value = this.dateObject.getMinutes();
                this.dom.secondsInput.value = this.dateObject.getSeconds();
            } else {
                this.html.value = "";
            }
            this.fillYearSelector();
            this.fillMonthSelector();
            this.buildDaysTable();
        }
        this.value = this.getValue('UTC');

        return this;
    };
    /**
     * Sets the first day of week.
     * @param {Number} day Use 0 for Sunday, 1 for Monday, 2 for Tuesday and so on!.
     * @chainable
     */
    DateTimeControl.prototype.setFirstDay = function (day) {
        if (typeof day === 'number') {
            if (day >= 0 && day < 7) {
                this.firstDay = Math.floor(day);
                if (this.html) {
                    this.setDays(this.days);
                    this.buildDaysTable();
                }
                return this;
            }
        }

        throw new Error("setFirstDay(): The parameter must be a number between 0 and 6.");
    };
    /**
     * Sets the format for the date to be displayed in the control's textbox.
     * @param {String} dateFormat A string that contains wildcards that represent a date part (day, month, etc.).
     * The valid wildcards are the same ones that are specified in the
     * {@link PMUI.control.DateTimeControl#cfg-dateFormat dateFormat config option}.
     * @chainable
     */
    DateTimeControl.prototype.setDateFormat = function (dateFormat) {
        if (typeof dateFormat === 'string') {
            this.dateFormat = dateFormat;
            this.updateValue();
        } else {
            throw new Error("setDateFormat(): The parameter must be a string.");
        }
        return this;
    };
    /**
     * Returns the date format to be displayed in the control's textbox.
     * @return {String}
     */
    DateTimeControl.prototype.getDateFormat = function () {
        return this.dateFormat;
    };
    /**
     * Returns the date using the format set by {@link PMUI.control.DateTimeControl#setDateFormat setDateFormat()}
     * method.
     * @return {String}
     */
    DateTimeControl.prototype.getFormatedDate = function () {
        return this.html.value;
    };
    /**
     * Returns the selected date.
     * @param  {String} format Specifies the date format that will be used for the returning date.
     * The valid values are:
     *
     * - "UTC", returns a date in the format: yyyy-mm-ddTHH:ii:ss-HH:mm (i.e. 2013-08-31T00:08:00+04:00).
     * - "@" or "timestamp", returns a date in timestamp format.
     * - [any other valid format string], will return the date using the string as the dateformat. This string can
     * contain any of the wilcards specified in the
     * {@link PMUI.control.DateTimeControl#cfg-dateFormat dateFormat config option}.
     * @return {String}
     */
    DateTimeControl.prototype.getValue = function (returningFormat) {
        var res;
        returningFormat = returningFormat || "UTC";
        if (this.dateObject) {
            switch (returningFormat) {
                case 'timestamp':
                case '@':
                    res = this.formatDate(this.dateObject, '@');
                    break;
                default:
                    res = this.formatDate(this.dateObject, returningFormat);
            }
        } else {
            res = "";
        }

        return res;
    };
    /**
     * Returns true if the parameter is a leap year, otherwise returns false.
     * @param  {Number}  year The year to be evaluated.
     * @return {Boolean}
     * @private
     */
    DateTimeControl.prototype.isLeapYear = function (year) {
        if (year % 400 === 0) {
            return true;
        }
        if (year % 100 === 0) {
            return false;
        }
        if (year % 4 === 0) {
            return true;
        }
        return false;
    };
    /**
     * Returns true if the bunch of arguments represents a valid date otherwise it returns false.
     * @param  {Number}  year
     * @param  {Number}  month The month number (a number from 1 to 12)
     * @param  {Number}  day
     * @param  {Number}  [hours]
     * @param  {Number}  [minutes]
     * @param  {Number}  [seconds]
     * @param  {Number}  [milliseconds]
     * @return {Boolean}
     * @private
     */
    DateTimeControl.prototype.isValidDateTime = function (year, month, day, hours, minutes, seconds, milliseconds) {
        if (!(typeof year === 'number' && typeof month === 'number' && typeof day === 'number')) {
            return false;
        }

        hours = hours || 0;
        minutes = minutes || 0;
        seconds = seconds || 0;
        milliseconds = milliseconds || 0;

        if (!(typeof hours === 'number' && typeof minutes === 'number' && typeof seconds === 'number'
            && typeof milliseconds === 'number')) {
            return false;
        }

        if (!(hours >= 0 && hours <= 23)) {
            return false;
        }

        if (!(minutes >= 0 && minutes <= 59)) {
            return false;
        }

        if (!(seconds >= 0 && seconds <= 59)) {
            return false;
        }

        if (!(milliseconds >= 0 && milliseconds <= 999)) {
            return false;
        }

        if (day < 1 || day > 31 || month < 1 || month > 12) {
            return false;
        }

        switch (month) {
            case 4:
            case 6:
            case 9:
            case 11:
                if (day > 30) {
                    return false;
                }
                break;
            case 2:
                if (this.isLeapYear(year)) {
                    return day <= 29;
                }
                return day <= 28;
        }

        return true;
    };
    /**
     * It parses the argument into a date.
     * @param  {String|Date} date It can be a String or a Date:
     *
     * - Date, in this case the argument isn't parse and it is returned.
     * - String, in this case the string must have the following format: "[-+]\d+[dwmy]". For example "+1y -4m +3d"
     * means that te returning date will be 1 year minus 4 months plus 3 days from today.
     * @return {Date}
     * @private
     */
    DateTimeControl.prototype.parseDate = function (date) {
        var startDate,
            aux,
            i,
            amount;

        if (typeof date === 'number') {
            startDate = new Date();
            startDate.setDate(startDate.getDate() + date);
        } else if (typeof date === 'string') {
            startDate = new Date();
            date = " " + jQuery.trim(date);
            if (/^(\s[\\+|\-]\d+[y|m|d|w])+$/.test(date)) {
                date = date.match(/[\\+|\-]\d+[y|m|d]/g);
                for (i = 0; i < date.length; i += 1) {
                    aux = date[i].match(/[\-|\\+]|\d+|[m|d|y]/g);
                    amount = parseInt(aux[1], 10);
                    if (aux[0] === '-') {
                        amount *= -1;
                    }
                    switch (aux[2]) {
                        case 'd':
                            startDate.setDate(startDate.getDate() + amount);
                            break;
                        case 'm':
                            startDate.setMonth(startDate.getMonth() + amount);
                            break;
                        case 'y':
                            startDate.setFullYear(startDate.getFullYear() + amount);
                            break;
                        case 'w':
                            startDate.setDate(startDate.getDate() + (amount * 7));
                            break;
                    }
                }
            }
        } else if (date instanceof Date) {
            startDate = date;
        } else if (typeof date === 'object') {
            startDate = new Date(date.year, date.month - 1, date.day || 1, date.hours || 0, date.minutes || 0, date.seconds || 0,
                date.milliseconds || 0);
        } else {
            throw new Error("parseDate(): invalid parameter.");
        }

        return startDate;
    };
    /**
     * Sets the minimum date the control can accept as a selectable one.
     * @param {String|Date|Number|Object} date It can be:
     *
     * - a Date object.
     * - a String with the following format: "([+-]\d+[dmyw]\s)?". It will represent an addition or substraction of
     * time units to the current date, for example the expression "+1y -2m +3d" means
     * "the current date plus 1 year, minus 2 months plus 3 days". The prefixes you can use are:
     *     - "d" for days.
     *     - "w" for weeks.
     *     - "m" for months.
     *     - "y" for years.
     *
     *     You can also use one single unit, i.e. "+3y", "-1m", or a combination of two: "-2y +3d", etc.
     * - a Number, in this case the number is taken as the number of days that will be sum/substracted from the
     * current date. This number can be positive (for add days) or negative (for substract days).
     * - an Object, in this case the object must have the following structure:
     *
     *      {
     *          year: 2013,
     *          month: 5,
     *          day: 4,
     *          hours: 20,
     *          minutes: 15,
     *          seconds: 3,
     *          millisenconds
     *      }
     *
     * In this case only year and month are required.
     *
     * @chainable
     */
    DateTimeControl.prototype.setMinDate = function (date) {
        var minDate = this.parseDate(date), d;

        if (this.maxDate) {
            if (minDate > this.maxDate) {
                throw new Error("setMinDate(): The min date can't be major than the max date");
            }
        }
        this.minDate = minDate;
        this.fillYearSelector();
        this.fillMonthSelector();
        this.buildDaysTable();

        if (this.html) {
            d = new Date();
            this.dom.todayButton.disabled = !(d >= minDate && (this.maxDate && d <= this.maxDate));
        }

        return this;
    };
    /**
     * Sets the maximum date the control can accept as a selectable one.
     * @param {String|Date|Number|Object} date It can be:
     *
     * - a Date object.
     * - a String with the following format: "([+-]\d+[dmyw]\s)?". It will represent an addition or substraction of
     * time units to the current date, for example the expression "+1y -2m +3d" means
     * "the current date plus 1 year, minus 2 months plus 3 days". The prefixes you can use are:
     *     - "d" for days.
     *     - "w" for weeks.
     *     - "m" for months.
     *     - "y" for years.
     *
     *     You can also use one single unit, i.e. "+3y", "-1m", or a combination of two: "-2y +3d", etc.
     * - a Number, in this case the number is taken as the number of days that will be sum/substracted from the
     * current date. This number can be positive (for add days) or negative (for substract days).
     * - an Object, in this case the object must have the following structure:
     *
     *      {
     *          year: 2013,
     *          month: 5,
     *          day: 4,
     *          hours: 20,
     *          minutes: 15,
     *          seconds: 3,
     *          millisenconds
     *      }
     *
     * In this case only year and month are required.
     * @chainable
     */
    DateTimeControl.prototype.setMaxDate = function (date) {
        var maxDate = this.parseDate(date), d;

        if (this.minDate) {
            if (maxDate < this.minDate) {
                throw new Error("setMaxDate(): The max date can't be minor than the min date");
            }
        }
        this.maxDate = maxDate;
        this.fillYearSelector();
        this.fillMonthSelector();
        this.buildDaysTable();

        if (this.html) {
            d = new Date();
            this.dom.todayButton.disabled = !(d <= maxDate && (this.minDate && d >= this.minDate));
        }

        return this;
    };
    /**
     * Sets the months names/shortnames to be used by the calendar.
     * @param {Object} months An object with the same structure that the
     * {@link PMUI.control.DateTimeControl#cfg-months months config option}.
     * @chainable
     */
    DateTimeControl.prototype.setMonths = function (months) {
        var key,
            changed;

        for (key in this.months) {
            if (this.months.hasOwnProperty(key)) {
                if (months.hasOwnProperty(key)) {
                    if (typeof months[key] === 'string') {
                        changed = true;
                        this.months[key].name = months[key];
                        this.months[key].shortname = months[key].charAt(0).toUpperCase() + months[key].slice(1, 3);
                    } else if (typeof months[key] === 'object') {
                        this.months[key].name = months[key].name;
                        this.months[key].shortname = months[key].shortname;
                        changed = true;
                    }
                }
            }
        }

        if (changed) {
            this.fillMonthSelector();
        }

        return this;
    };
    /**
     * Fills the months selector.
     * @chainable
     * @private
     */
    DateTimeControl.prototype.fillMonthSelector = function () {
        var currentMonth,
            option,
            year,
            i,
            j;

        if (this.dom.monthSelector) {
            year = this.dom.yearSelector.value;
            currentMonth = (this.dateObject && this.dateObject.getMonth()) || this.dom.monthSelector.value
                || (new Date()).getMonth().toString();
            jQuery(this.dom.monthSelector).empty();
            i = 0;
            j = this.monthsOrder.length - 1;
            if (year === this.minDate.getFullYear().toString()) {
                i = this.minDate.getMonth();
            }
            if (year === this.maxDate.getFullYear().toString()) {
                j = this.maxDate.getMonth();
            }
            for (i; i <= j; i += 1) {
                option = PMUI.createHTMLElement('option');
                option.label = this.months[this.monthsOrder[i]].name;
                option.textContent = option.label;
                option.value = this.months[this.monthsOrder[i]].value;
                option.selected = currentMonth.toString() === option.value;
                this.dom.monthSelector.appendChild(option);
            }
        }

        return this;
    };
    /**
     * Sets the name/shortnames for the days to be used in the calendar.
     * @param {Object} days A JSON object with the same structure than the
     * {@PMUI.control.DateTimeControl#cfg-days days config option}.
     * @chainable
     */
    DateTimeControl.prototype.setDays = function (days) {
        var key,
            changed,
            i,
            cell,
            daysLength = this.daysOrder.length,
            aux;

        for (key in this.days) {
            if (this.days.hasOwnProperty(key)) {
                if (days.hasOwnProperty(key)) {
                    if (typeof days[key] === 'string') {
                        this.days[key].name = days[key];
                        this.days[key].shortname = days[key].substr(0, 3);
                    } else if (typeof days[key] === 'object') {
                        this.days[key].name = days[key].name || this.days[key].name || "";
                        this.days[key].shortname = (days[key].shortname && days[key].shortname.substr(0, 3))
                            || (this.days[key].name && this.days[key].name.substr(0, 3)) || "";
                    } else {
                        throw new Error("setDays(): The argument supplied must be a string or an object.");
                    }
                    changed = true;
                }
            }
        }

        if (changed && this.html) {
            jQuery(this.dom.daysHeader).empty();
            aux = 0;
            i = this.firstDay;
            while (aux < daysLength) {
                if (i === daysLength) {
                    i = 0;
                }
                cell = PMUI.createHTMLElement('th');
                cell.textContent = this.days[this.daysOrder[i]].shortname;
                cell.setAttribute("data-value", this.days[this.daysOrder[i]].value);
                this.dom.daysHeader.appendChild(cell);
                i += 1;
                aux += 1;
            }
        }

        return this;
    };
    /**
     * Fill the year selector.
     * @chainable
     * @private
     */
    DateTimeControl.prototype.fillYearSelector = function () {
        var minYear,
            maxYear,
            option,
            selectedYear;

        if (this.dom.yearSelector) {
            selectedYear = (this.dateObject && this.dateObject.getFullYear()) || this.dom.yearSelector.value
                || (new Date()).getFullYear();
            selectedYear = selectedYear.toString();
            jQuery(this.dom.yearSelector).empty();
            minYear = this.minDate.getFullYear();
            maxYear = this.maxDate.getFullYear();
            for (minYear; minYear <= maxYear; minYear += 1) {
                option = PMUI.createHTMLElement('option');
                option.textContent = option.label = option.value = minYear;
                if (option.value.toString() === selectedYear) {
                    option.selected = true;
                }
                this.dom.yearSelector.appendChild(option);
            }
        }

        return this;
    };
    /**
     * Returns the number of days in a month
     * @param  {Number} y The year (it's necessary to determine the days in February.)
     * @param  {Number} m The month
     * @return {Number}   The number of days.
     * @private
     */
    DateTimeControl.prototype.daysInMonth = function (y, m) {
        if (m < 1 || m > 12) {
            return 0;
        }
        switch (m) {
            case 4:
            case 6:
            case 9:
            case 11:
                return 30;
            case 2:
                if (this.isLeapYear(y)) {
                    return 29;
                }
                return 28;
            default:
                return 31;
        }
    };
    /**
     * Returns the day of week for a date.
     * @param  {Number} y The year.
     * @param  {Number} m The month.
     * @param  {Number} d The day of month.
     * @return {Number}   A number from 0 to 6. 0 means Sunday, 1, Monday and so on.
     * @private
     */
    DateTimeControl.prototype.whichDay = function (y, m, d) {
        var centuryCode,
            monthCode,
            lastTwoYearDigits = y % 100,
            gregorianCenturyCodes = [6, 4, 2, 0],
            i;

        if (!this.isValidDateTime(y, m, d)) {
            throw new Error("whichDay(): invalid date.");
        }

        switch (m) {
            case 3:
            case 11:
                monthCode = 3;
                break;
            case 4:
            case 7:
                monthCode = 6;
                break;
            case 5:
                monthCode = 1;
                break;
            case 6:
                monthCode = 4;
                break;
            case 8:
                monthCode = 2;
                break;
            case 9:
            case 12:
                monthCode = 5;
                break;
            case 10:
                monthCode = 0;
                break;
            case 1:
                if (this.isLeapYear(y)) {
                    monthCode = -1;
                } else {
                    monthCode = 0;
                }
                break;
            case 2:
                if (this.isLeapYear(y)) {
                    monthCode = 2;
                } else {
                    monthCode = 3;
                }
                break;
        }

        centuryCode = Math.floor(y / 100);

        i = 0;
        while (centuryCode % 4 !== 0) {
            centuryCode += 1;
            i += 1;
        }
        if (i === 0) {
            centuryCode = gregorianCenturyCodes[0];
        } else {
            centuryCode = gregorianCenturyCodes[gregorianCenturyCodes.length - i];
        }

        return (d + monthCode + lastTwoYearDigits + Math.floor(lastTwoYearDigits / 4) + centuryCode    ) % 7;
    };
    /**
     * Build the days table for the calendar
     * @chainable
     * @private
     */
    DateTimeControl.prototype.buildDaysTable = function () {
        var dayOfTheWeek,
            daysInMonth,
            y,
            m,
            i,
            day = 1,
            row,
            cell,
            limit,
            link,
            selectableDate;

        if (this.dom.yearSelector && this.dom.monthSelector) {
            jQuery(this.dom.tableBody).empty();
            y = parseInt(this.dom.yearSelector.value, 10);
            m = parseInt(this.dom.monthSelector.value, 10) + 1;
            dayOfTheWeek = this.whichDay(y, m, 1) - this.firstDay;
            if (dayOfTheWeek < 0) {
                dayOfTheWeek = 7 + dayOfTheWeek;
            }
            daysInMonth = this.daysInMonth(y, m);
            limit = Math.ceil((dayOfTheWeek + daysInMonth) / 7) * 7;
            row = PMUI.createHTMLElement('tr');

            for (i = 0; i < limit; i += 1) {
                cell = PMUI.createHTMLElement("td");
                if (i - dayOfTheWeek >= 0 && day <= daysInMonth) {
                    selectableDate = true;
                    if ((m - 1) === this.maxDate.getMonth() && y === this.maxDate.getFullYear()) {
                        if (day > this.maxDate.getDate()) {
                            selectableDate = false;
                        }
                    } else if ((m - 1) === this.minDate.getMonth() && y === this.minDate.getFullYear()) {
                        if (day < this.minDate.getDate()) {
                            selectableDate = false;
                        }
                    }
                    if (selectableDate) {
                        link = PMUI.createHTMLElement("a");
                        link.href = "#";
                        link.setAttribute("data-date", day);
                        link.textContent = day;
                        link.className = (this.dateObject && this.dateObject.getFullYear() === y
                            && this.dateObject.getMonth() + 1 === m && this.dateObject.getDate() === day) ? 'selected' : '';
                        cell.appendChild(link);
                    } else {
                        cell.className = 'pmui-datepicker-disabled-date';
                        cell.textContent = day;
                    }
                    day += 1;
                }
                row.appendChild(cell);
                if ((i + 1) % 7 === 0) {
                    this.dom.tableBody.appendChild(row);
                    row = PMUI.createHTMLElement('tr');
                }
            }
            this.dom.tableBody.appendChild(row);
        }

        return this;
    };
    /**
     * Shows the control's calendar.
     * @chainable
     * @private
     */
    DateTimeControl.prototype.showCalendar = function () {
        var position,
            inputHeight,
            maxZIndex;

        if (this.html) {
            this.fillYearSelector();
            this.fillMonthSelector();
            this.buildDaysTable();

            position = jQuery(this.html).offset();
            inputHeight = jQuery(this.html).outerHeight();

            jQuery(document.body).find(">*").each(function () {
                var n;

                n = parseInt(jQuery(this).css("z-index"));
                if (!isNaN(n)) {
                    maxZIndex = n > maxZIndex ? n : maxZIndex;
                }
            });

            document.body.appendChild(this.dom.calendar);
            this.dom.calendar.style.zIndex = maxZIndex ? maxZIndex + 1 : 999;
            this.dom.calendar.style.left = position.left + 'px';
            this.dom.calendar.style.top = (position.top + inputHeight) + 'px';
            this.dom.calendar.style.display = "";
        }

        return this;
    };
    /**
     * Hide's the control's calendar.
     * @return {HTMLElement} The object's html.
     * @private
     */
    DateTimeControl.prototype.hideCalendar = function () {
        if (this.html) {
            jQuery(this.dom.calendar).detach();
        }

        return this.html;
    };
    /**
     * Returns the day number of a date in a whole year.
     * @param  {Number} y The year.
     * @param  {Number} m The month.
     * @param  {Number} d The day of month.
     * @return {Number}   The day of year.
     * @private
     */
    DateTimeControl.prototype.getDayOfYear = function (y, m, d) {
        var day = 0,
            month = 1;

        if (!(typeof y === 'number' && typeof m === 'number' && typeof d === 'number')) {
            throw new Error("getDayOfYear(): invalid parameters.");
        }
        if (!this.isValidDateTime(y, m, d)) {
            throw new Error("getDayOfYear(): invalid date.");
        }

        while (month < m) {
            day += this.daysInMonth(y, m);
            month += 1;
        }

        day += d;

        return day;
    };
    /**
     * Formats a date using the specified format string.
     * @param  {Date} dateObject a Date object.
     * @param  {String} format     String that contains wildcards specified in the
     * {@link PMUI.control.DateTimeControl#cfg-dateFormat dateFormat config option}.
     * @return {String}            The formatted date as a string.
     * @private
     */
    DateTimeControl.prototype.formatDate = function (dateObject, format) {
        var finalValue,
            d = dateObject.getDate(),
            o = this.getDayOfYear(dateObject.getFullYear(), dateObject.getMonth() + 1, dateObject.getDate()),
            m = dateObject.getMonth() + 1,
            h = dateObject.getHours(),
            h12 = h > 12 ? h - 12 : h,
            i = dateObject.getMinutes(),
            s = dateObject.getSeconds(),
            yy = dateObject.getFullYear(),
            formatArray,
            timeOffset,
            timeOffsetString,
            aux;

        timeOffset = dateObject.getTimezoneOffset();

        timeOffsetString = timeOffset < 0 ? '+' : '-';
        timeOffset = Math.sqrt(timeOffset * timeOffset);
        aux = Math.floor(timeOffset / 60);
        timeOffset = timeOffset - (aux * 60);
        timeOffsetString += (aux < 10 ? '0' : '') + aux + ':' + (timeOffset < 10 ? '0' : '') + timeOffset;

        formatArray = [
            {
                regExp: /dd/g,
                value: d < 10 ? "0" + d : d
            },
            {
                regExp: /d/g,
                value: d
            },
            {
                regExp: /oo/g,
                value: o < 10 ? "00" + o : (o < 100 ? "0" + o : o)
            },
            {
                regExp: /o/g,
                value: o
            },
            {
                regExp: /mm/g,
                value: m < 10 ? "0" + m : m
            },
            {
                regExp: /m/g,
                value: m
            },
            {
                regExp: /yy/g,
                value: yy
            },
            {
                regExp: /y/g,
                value: yy % 100
            },
            {
                regExp: /\@/g,
                value: dateObject.getTime()
            },
            {
                regExp: /\!/g,
                value: (dateObject.getTime() * 10000) + 621355968000000000
            },
            {
                regExp: /MM/g,
                value: this.months[this.monthsOrder[dateObject.getMonth()]].name
            },
            {
                regExp: /M/g,
                value: this.months[this.monthsOrder[dateObject.getMonth()]].shortname
            },
            {
                regExp: /HH/g,
                value: (h < 10 ? '0' : '') + h
            },
            {
                regExp: /H/g,
                value: h
            },
            {
                regExp: /hh/g,
                value: (h12 < 10 ? '0' : '') + h12
            },
            {
                regExp: /h/g,
                value: h12
            },
            {
                regExp: /ii/g,
                value: (i < 10 ? '0' : '') + i
            },
            {
                regExp: /i/g,
                value: i
            },
            {
                regExp: /ss/g,
                value: (s < 10 ? '0' : '') + s
            },
            {
                regExp: /s/g,
                value: s
            },
            {
                regExp: /UTC/g,
                value: yy + '-' + (m < 10 ? '0' : '') + m + '-' + (d < 10 ? '0' : '') + d + "T"
                + (h < 10 ? '0' : '') + h + ':' + (i < 10 ? '0' : '') + i + ':' + (s < 10 ? '0' : '') + s
                + timeOffsetString
            },
            {
                regExp: /P/g,
                value: h > 12 ? 'PM' : 'AM'
            },
            {
                regExp: /DD/g,
                value: this.days[this.daysOrder[dateObject.getDay()]].name
            },
            {
                regExp: /D/g,
                value: this.days[this.daysOrder[dateObject.getDay()]].shortname
            }
        ];

        finalValue = format || this.dateFormat;

        for (i = 0; i < formatArray.length; i += 1) {
            finalValue = finalValue.replace(formatArray[i].regExp, formatArray[i].value);
        }

        return finalValue;
    };
    /**
     * Updates the control's date text.
     * @chainable
     * @private
     */
    DateTimeControl.prototype.updateValue = function () {
        var finalValue;

        if (this.dateObject) {
            finalValue = this.formatDate(this.dateObject, this.dateFormat);
        } else {
            finalValue = "";
        }

        this.value = finalValue;

        if (this.html) {
            this.html.value = finalValue;
        }

        return this;
    };
    /**
     * Gets the control's value directly from the element.
     * @return {String}
     */
    DateTimeControl.prototype.getValueFromRawElement = function () {
        return this.getValue('UTC');
    };
    /**
     * The handler to be executed everytime the control's value changes.
     * @chainable
     * @private
     */
    DateTimeControl.prototype.onChangeHandler = function (hide) {
        var prevValue = this.value,
            newValue,
            resCallback,
            date,
            month,
            year,
            auxDate,
            valueChanged = false;

        auxDate = new Date();
        date = parseInt($(this.dom.tableBody).find("a.selected").text(), 10);
        if (isNaN(date)) {
            return this;
        }
        month = parseInt(this.dom.monthSelector.value, 10);
        year = parseInt(this.dom.yearSelector.value, 10);
        auxDate.setYear(year);
        auxDate.setMonth(month);
        auxDate.setDate(date);
        if (this.datetime) {
            auxDate.setMilliseconds(0);
            auxDate.setSeconds(parseInt(this.dom.secondsInput.value, 10));
            auxDate.setMinutes(parseInt(this.dom.minutesInput.value, 10));
            auxDate.setHours(parseInt(this.dom.hoursInput.value, 10));
            this.dom.doneButton.disabled = true;
        } else {
            if (!this.dateObject) {
                this.dateObject = new Date();
            }
            auxDate.setMilliseconds(0);
            auxDate.setSeconds(0);
            auxDate.setMinutes(0);
            auxDate.setHours(0);
        }
        if (!this.dateObject) {
            this.dateObject = new Date();
        }
        if (auxDate.getTime() !== this.dateObject.getTime()) {
            valueChanged = true;
        }
        if (typeof this.onBeforeChange === 'function' && valueChanged) {
            resCallback = this.onBeforeChange(this.formatDate(auxDate), prevValue);
        }
        if (resCallback !== false) {
            this.dateObject = auxDate;
        } else {
            if (this.dom.monthSelector.value == this.dateObject.getMonth()
                && this.dom.yearSelector.value == this.dateObject.getFullYear()) {

                jQuery(this.dom.tableBody).find('a.selected').removeClass('selected')
                    .find('a[data-date="' + this.dateObject.getDate() + '"]').addClass('selected');
            }
        }
        newValue = this.getValueFromRawElement();
        this.updateValue();
        if (hide) {
            this.hideCalendar();
        }
        if (typeof this.onChange === 'function' && this.value !== prevValue) {
            this.onChange(this.value, prevValue);
        }

        return this;
    };
    /**
     * Defines the events for the control
     * @chainable
     */
    DateTimeControl.prototype.defineEvents = function () {
        var that = this,
            preselectedDate,
            specialCloseHandler = function (e) {
                if ((e.target !== that.html && !$(e.target).parents('#pmui-datepicker-calendar-' + that.id).length)
                    || (e.type === 'keyup' && !jQuery(document.activeElement).parents('#pmui-datepicker-calendar-'
                        + that.id).length)) {
                    that.hideCalendar();
                }
            },
            timeInputFilter;

        if (this.dom.todayButton) {
            timeInputFilter = function () {
                var num = parseInt(this.value, 10),
                    maxValue;

                switch (this) {
                    case that.dom.hoursInput:
                        maxValue = 23;
                        break;
                    case that.dom.minutesInput:
                    case that.dom.secondsInput:
                        maxValue = 59;
                        break;
                    default:
                        throw new Error("defineEvents() at change event: No valid element.");
                }

                if (isNaN(num)) {
                    this.value = 0;
                } else {
                    this.value = num < 0 ? 0 : Math.min(num, maxValue);
                }
               that.dom.doneButton.disabled = false;
            };

            this.addEvent('change').listen(this.dom.yearSelector, function () {
                that.fillMonthSelector();
                that.buildDaysTable();
            });
            this.addEvent('change').listen(this.dom.monthSelector, function () {
                that.buildDaysTable();
            });
            this.addEvent('focusin click').listen(this.html, function () {
                that.showCalendar();
            });
            this.addEvent('change').listen(this.dom.hoursInput, timeInputFilter);
            this.addEvent('change').listen(this.dom.minutesInput, timeInputFilter);
            this.addEvent('change').listen(this.dom.secondsInput, timeInputFilter);
            this.addEvent('click').listen(this.dom.todayButton, function () {
                preselectedDate = new Date();
                that.dom.doneButton.disabled = false;
                that.dom.yearSelector.value = preselectedDate.getFullYear();
                that.fillMonthSelector();
                that.dom.monthSelector.value = preselectedDate.getMonth();
                that.buildDaysTable();
                jQuery(that.dom.tableBody).find(".selected").removeClass('selected').end()
                    .find('a[data-date=' + preselectedDate.getDate() + ']').addClass("selected");
            });
            this.addEvent('click keyup mousedown').listen(document, specialCloseHandler)
                .listen('.pmui-window', specialCloseHandler);
            //In the future, if the new Event objects model is implemented the following line should be listening to an
            //event close or windowClose. Currently a dirty implementation will fix he problem.
            this.addEvent('click').listen('.pmui-button.pmui-window-close', function () {
                that.hideCalendar();
            });
            this.addEvent('click').listen(this.dom.doneButton, function () {
                that.onChangeHandler(true);
            });
            jQuery(this.dom.tableBody).on('click', 'a', function (e) {
                e.preventDefault();
                jQuery(that.dom.tableBody).find("a").filter(".selected").removeClass("selected");
                jQuery(this).addClass("selected");
                var date, month, year;

                date = parseInt(this.textContent, 10);
                month = parseInt(that.dom.monthSelector.value, 10);
                year = parseInt(that.dom.yearSelector.value, 10);
                if (that.datetime) {
                    that.dom.doneButton.disabled = false;
                } else {
                    that.onChangeHandler(true);
                }
            });
        }
        return this;
    };
    /**
     * Creates the HTML for the control.
     * @return {HTMLElement}
     */
    DateTimeControl.prototype.createHTML = function () {
        var header,
            table,
            tableHeader,
            monthSelector,
            yearSelector,
            row,
            cell,
            tableBody,
            calendar,
            footer,
            button,
            input;
        if (this.html) {
            return this.html;
        }

        DateTimeControl.superclass.prototype.createHTML.call(this);

        this.html.type = 'text';
        this.html.readOnly = true;

        calendar = PMUI.createHTMLElement('div');
        calendar.className = 'pmui-datepicker';
        calendar.id = 'pmui-datepicker-calendar-' + this.id;
        header = PMUI.createHTMLElement('div');
        header.className = 'pmui-datepicker-header';
        table = PMUI.createHTMLElement('table');
        table.className = 'pmui-datepicker-head-table';
        monthSelector = PMUI.createHTMLElement("select");
        yearSelector = PMUI.createHTMLElement("select");
        row = PMUI.createHTMLElement("tr");
        cell = PMUI.createHTMLElement("td");
        cell.className = 'pmui-datepicker-month-selector';
        cell.appendChild(monthSelector);
        row.appendChild(cell);
        cell = PMUI.createHTMLElement("td");
        cell.className = 'pmui-datepicker-year-selector';
        cell.appendChild(yearSelector);
        row.appendChild(cell);
        table.appendChild(row);
        header.appendChild(table);

        this.dom.yearSelector = yearSelector;
        this.dom.monthSelector = monthSelector;

        table = PMUI.createHTMLElement('table');
        table.className = 'pmui-datepicker-table';
        tableHeader = PMUI.createHTMLElement('thead');
        row = PMUI.createHTMLElement('tr');
        this.dom.daysHeader = row;
        tableHeader.appendChild(row);
        table.appendChild(tableHeader);

        tableBody = PMUI.createHTMLElement('tbody');
        this.dom.tableBody = tableBody;
        table.appendChild(tableBody);

        footer = this.createFooterHTML();
        calendar.appendChild(header);
        calendar.appendChild(table);
        calendar.appendChild(footer);

        this.dom.calendar = calendar;
        this.dom.footer = footer;

        this.setMinDate(this.minDate)
            .setMaxDate(this.maxDate)
            .setDays(this.days)
            .visibleDateTime(this.datetime);

        if (this.eventsDefined) {
            this.defineEvents();
        }

        return this.html;
    };
    /**
     * Creates the Fotter HTML for the control.
     * @return {HTMLElement}
     */
    DateTimeControl.prototype.createFooterHTML = function () {
        var footertr,
            tableFooter,
            footer,
            button,
            cell1,
            cell2,
            cell3,
            cell4,
            cell5,
            input;

        footer = PMUI.createHTMLElement('div');
        tableFooter = PMUI.createHTMLElement('table');
        footertr = PMUI.createHTMLElement('tr');
        footer.className = 'pmui-datepicker-footer';

        cell1 = PMUI.createHTMLElement('td');
        cell2 = cell1.cloneNode(false);
        cell3 = cell1.cloneNode(false);
        cell4 = cell1.cloneNode(false);
        cell5 = cell1.cloneNode(false);

        button = PMUI.createHTMLElement('button');
        button.textContent = 'Today';
        button.className = 'pmui-datepicker-button';
        this.dom.todayButton = button;
        cell1.appendChild(button);
        footertr.appendChild(cell1);

        input = PMUI.createHTMLElement('input');
        input.setAttribute("type", "number");
        input.size = 2;
        input.min = 0;
        input.max = 23;
        input.value = 0;
        input.placeholder = "hrs.";
        this.dom.hoursInput = input;
        cell2.appendChild(input);
        footertr.appendChild(cell2);

        input = input.cloneNode(false);
        input.max = 59;
        input.placeholder = "min.";
        this.dom.minutesInput = input;
        cell3.appendChild(input);
        footertr.appendChild(cell3);

        input = input.cloneNode(false);
        input.max = 59;
        input.placeholder = "sec.";
        this.dom.secondsInput = input;
        cell4.appendChild(input);
        footertr.appendChild(cell4);

        button = button.cloneNode(true);
        button.textContent = "Done";
        button.disabled = true;
        this.dom.doneButton = button;
        cell5.appendChild(button);
        footertr.appendChild(cell5);
        tableFooter.appendChild(footertr);
        footer.appendChild(tableFooter);
        return footer;
    };
    PMUI.extendNamespace('PMUI.control.DateTimeControl', DateTimeControl);

    if (typeof exports !== "undefined") {
        module.exports = DateTimeControl;
    }
}());

(function () {
    /**
     * @class PMUI.ui.Window
     * @extend PMUI.core.Panel
     *    Usage example:
     *
     *      @example
     *
     *        f = new PMUI.form.Form({
     *               items: [
     *                   {
     *                       pmType: "text",
     *                       label: "Name",
     *                       id: "123",
     *                       value: "",
     *                       placeholder: "insert your name",
     *                       name: "name",
     *                       helper: "Introduce your name",
     *                       required : true,
     *                       controlsWidth: 200,
     *                       valueType: 'string'
     *                   },
     *                   {   
     *                       pmType: "text",
     *                       label: "test Number",
     *                       id: "1234",
     *                       value: 10,
     *                       placeholder: "insert your number",
     *                       name: "numberTest",
     *                       helper: "Introduce your number",
     *                       required : true,
     *                       controlsWidth: 200,
     *                       valueType: 'number'
     *                   },
     *                   {
     *                       pmType: "datetime",
     *                       visible : true,
     *                       label: "birth date",
     *                       name: "birthdate",
     *                       valueType: 'date',
     *                       required: true
     *                   }
     *               ]
     *           });
     *
     *      w = new PMUI.ui.Window({
     *               title: "Window Example",
     *               width: 500,
     *               height: 230,
     *               modal: true,
     *               buttons: [
     *                   {
     *                       pmType: 'button',
     *                       text: 'Save',
     *                       handler: function() {
     *                           alert("Saved!");
     *                       }
     *                   },
     *                   {
     *                       pmType: 'label',
     *                       text: " or "
     *                   },
     *                   {
     *                       pmType: 'button',
     *                       text: "Close",
     *                       handler : function () {
     *                           w.close();
     *                       },
     *                       border: 'link'
     *                   }
     *               ],
     *               closable: true,
     *               footerItems: [
     *                   {
     *                       text: "Close"
     *                   }
     *               ],
     *               visibleFooter: true,
     *               buttonPanelPosition: 'top'
     *           });
     *           w.addItem(f);
     *           w.open();
     *           w.defineEvents();
     *
     * Create a new instace of the Window class
     *
     * @cfg {String}  [title = [Untitled window]] Title The title for the Window
     * @cfg {Boolean} [modal = true] Defines the  window property  is modal or not
     * @cfg {String|Number} [height = "auto"] window height can be a number or a string. height
     * is the div
     * Element which contains a header, body and footer. When using a string that you can only use
     * 'auto' or 'inherit' or # # # # px or% or # # # # em when is a number
     * @cfg {String|Number} [width = 400] The width of the main window which contains a html element
     * Element 'div' header, body and footer
     * @cfg {String|Number} [footerHeight = "auto"] the height of the Element div footer (px, auto,
     * number)which default 'auto'
     * @cfg {PMUI.util.ArrayList} [buttons = []], defines an array of objects that are buttons
     * settings may in the fotter Element div.
     * @deprecated This config optio will be removed soon, please use the {@link #cfg-footerItems footerItems} config
     * option instead.
     * @cfg {boolean} [visibleCloseButton = true], the button close are can define or not in the window.
     * @cfg {Array} [footerItems=[]] Sets the elements in the window footer, this elements can be instances of Button
     * and/or instances of Label. The value for this config option must be an array in which each element can be:
     *
     * - An object literal, in this case the object literal must have the property "pmType" with its value set to
     * "button" (if you want the element be a {@link PMUI.ui.Button Button}) or "label" (if you want the element be a
     * {@link PMUI.ui.TextLabel Label}). Optionally you can add the respective config options for each case.
     *
     * - A PMUI object, in this case it must be an instance of {@link PMUI.ui.Button Button} or an instance of
     * {@link PMUI.ui.TextLabel Label}.
     *
     * @cfg {String} [footerAlign="center"] Sets the horizontal alignment for the items in the wondow footer, the
     * possible values are "center", "left", "right".
     * @cfg {Boolean} [visibleFooter="false"] indicating whether the footer of the Window is visible or was hidden
     * @cfg {String}  [buttonPanelPosition="bottom"] indicates that position does the panel of buttons, you can be
     * above or below body.
     * @cfg {Function|null} [onOpen=null] The callback function to be executed everytime the
     * {@link #event-onOpen onOpen} event.
     * @cfg {Function|null} [onClose=null] The callback function to be executed everytime the
     * {@link #event-onClose onClose} event.
     */
    var Window = function (settings) {
        Window.superclass.call(this, jQuery.extend(true, {
            positionMode: 'absolute'
        }, settings));
        /**
         * The window's header.
         * @type {HTMLElement}
         * @private
         */
        this.header = null;
        /**
         * The window's body.
         * @type {HTMLElement}
         * @private
         */
        this.body = null;
        /**
         * The window's footer.
         * @type {PMUI.panel.ButtonPanel}
         * @private
         */
        this.footer = null;
        /**
         * @property {String} title
         * The window's title. Set by the {@link #cfg-title title} config option and the
         * {@link #method-setTitle setTitle()} method.
         * @readonly
         */
        this.title = null;
        /**
         * The windo's close button.
         * @type {PMUI.ui.Button}
         * @private
         */
        this.closeButton = null;
        /**
         * If the window is opened or not.
         * @type {Boolean}
         * @readonly
         */
        this.isOpen = false;
        /**
         * is defined if button panes Object is visible
         * @type {[type]}
         */
        this.visibleFooter = null;
        /**
         * If the window's close button is visible or not. Set by the
         * {@link #cfg-visibleCloseButton visibleCloseButton} config option and the
         * {@link #method-showCloseButton showCloseButton()} and {@link #method-hideCloseButton hideCloseButton()}
         * methods.
         * @type {Boolean}
         * @readonly
         */
        this.visibleCloseButton = null;
        /**
         * If the window will be a modal window. Set by the {@link #cfg-modal modal} config option and the
         * {@link #method-setModal setModal()} method.
         * @type {Boolean}
         * @readonly
         */
        this.modal = null;
        /**
         * The window's footer height.
         * @type {Number|String}
         */
        this.footerHeight = null;
        /**
         * The window's footer alignment.
         * @type {String}
         */
        this.footerAlign = null;
        /**
         * defines the position of the buttons panel on the window
         * @type {String}
         */
        this.buttonPanelPosition = null;
        /**
         * @event onOpen
         * Fired everytime the window opens.
         * @param {PMUI.ui.Window} window The window that was opened.
         */
        this.onOpen = null;
        /**
         * @event onClose
         * Fired everytime the window closes.
         * @param {PMUI.ui.Window} window The window that was closed.
         */
        this.onClose = null;
        this.dom = {};
        this.onBeforeClose = null;
        Window.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Panel', Window);
    /**
     * Defines the object's type
     * @type {String}
     */
    Window.prototype.type = 'Window';
    /**
     * Defines the object's family
     * @type {String}
     */
    Window.prototype.family = 'ui';

    Window.prototype.init = function (settings) {
        var defaults = {
            height: 'auto',
            width: 'auto',
            title: '[Untitled window]',
            modal: true,
            footerHeight: 'auto',
            zOrder: 100,
            footerItems: [],
            footerAlign: 'center',
            visibleCloseButton: true,
            visibleFooter: false,
            buttons: [],
            buttonPanelPosition: 'bottom',
            onOpen: null,
            onClose: null,
            onBeforeClose: null
        };

        jQuery.extend(true, defaults, settings);

        this.footer = new PMUI.panel.ButtonPanel({
            style: {
                cssClasses: ['pmui-window-footer']
            }
        });

        this.setTitle(defaults.title)
            .setModal(defaults.modal)
            .setWidth(defaults.width)
            .setHeight(defaults.height)
            .setFooterHeight(defaults.footerHeight)
            .setFooterAlign(defaults.footerAlign)
            .setZOrder(defaults.zOrder)
            .setButtonPanelPosition(defaults.buttonPanelPosition)
            .setFooterItems(defaults.footerItems)
            .setOnOpenHandler(defaults.onOpen)
            .setOnCloseHandler(defaults.onClose);
        this.setOnBeforeCloseHandler(defaults.onBeforeClose);

        if (defaults.visibleCloseButton) {
            this.showCloseButton();
        } else {
            this.hideCloseButton();
        }
        if (defaults.visibleFooter) {
            this.showFooter();
        } else {
            this.hideFooter();
        }
        //TODO tell the guys to replace its 'buttons' config option by FooterItems
        if (defaults.footerItems && defaults.footerItems.length) {
            this.setFooterItems(defaults.footerItems);
        } else {
            this.setFooterItems(defaults.buttons);
        }
    };
    /**
     * Sets the callback function to be executed everytime the {@link #event-onClose onClose} event fires.
     * @param {Function|null} handler It can be a function or null.
     */
    Window.prototype.setOnCloseHandler = function (handler) {
        if (!(handler === null || typeof handler === 'function')) {
            throw new Error("setOnCloseHandler(): The parameter must be a function or null.");
        }
        this.onClose = handler;
        return this;
    };

    Window.prototype.setOnBeforeCloseHandler = function (handler) {
        if (!(handler === null || typeof handler === 'function')) {
            throw new Error("setOnCloseHandler(): The parameter must be a function or null.");
        }
        this.onBeforeClose = handler;
        return this;
    };

    /**
     * Sets the callback function to be executed everytime the {@link #event-onOpen onOpen} event fires.
     * @param {Function|null} handler It can be a function or null.
     */
    Window.prototype.setOnOpenHandler = function (handler) {
        if (!(handler === null || typeof handler === 'function')) {
            throw new Error("setOnOpenHandler(): The parameter must be a function or null.");
        }
        this.onOpen = handler;
        return this;
    };
    /**
     * updates the dimensions of the height of the HTMLElement window, body and foot when
     it is modified by the user
     * @chainable
     */
    Window.prototype.updateDimensionsAndPosition = function () {
        var bodyHeight,
            footerHeight,
            headerHeight,
            windowHeight = this.height,
            windowWidth;

        if (!this.footer || !this.html || !this.isOpen) {
            return this;
        }
        if (this.footerHeight === 'auto') {
            this.footer.setHeight('auto');
        } else {
            this.footer.setHeight(this.footerHeight);
        }

        if (windowHeight === 'auto') {
            this.body.style.height = 'auto';
            this.body.style.minHeight = '150px';
        } else {
            if (/^\d+(\.\d+)?em$/.test(windowHeight)) {
                windowHeight = PMUI.emToPx(parseInt(windowHeight, 10), this.modalObject);
            } else if (/^\d+(\.\d+)?%$/.test(windowHeight)) {
                windowHeight = jQuery(this.html).outerHeight();
            }
            footerHeight = this.visibleFooter ? jQuery(this.footer.getHTML()).outerHeight() : 0;
            headerHeight = jQuery(this.header).outerHeight();
            bodyHeight = windowHeight - footerHeight - headerHeight;
            if (bodyHeight <= 0) {
                bodyHeight = 0;
            }
            this.body.style.minHeight = '';
            this.body.style.height = bodyHeight + "px";
        }

        windowWidth = jQuery(this.header).width();
        windowWidth = windowWidth - (this.visibleCloseButton ? jQuery(this.closeButton.getHTML()).outerWidth() : 0);
        this.dom.titleContainer.style.width = windowWidth < 0 ? 0 : windowWidth + 'px';

        windowWidth = jQuery(this.html).outerWidth();
        windowHeight = jQuery(this.html).outerHeight();

        this.addCSSProperties({
            left: '50%',
            "margin-left": (windowWidth / -2) + "px",
            top: '50%',
            "margin-top": (windowHeight / -2) + "px"
        });

        return this;
    };
    /**
     * shows the footer where the buttons are located
     * @chainable
     */
    Window.prototype.showFooter = function () {
        this.footer.setVisible(this.visibleFooter = true);
        return this.updateDimensionsAndPosition();
    };
    /**
     * hides the footer where the buttons are located
     * @chainable
     */
    Window.prototype.hideFooter = function () {
        this.footer.setVisible(this.visibleFooter = false);
        return this.updateDimensionsAndPosition();
    };
    /**
     * @method setTitle
     * Sets the window title in the header part of the window.
     * @param {String} title
     * @chainable
     */
    Window.prototype.setTitle = function (title) {
        if (typeof title === 'string') {
            this.title = title;
            if (this.dom.titleContainer) {
                this.dom.titleContainer.textContent = title;
                this.dom.titleContainer.title = title;
            }
        } else {
            throw new Error("The setTitle() method accepts only string values.");
        }
        return this;
    };
    /**
     * @method getTitle
     * its title of teh window
     * @return {String}
     */
    Window.prototype.getTitle = function () {
        return this.title;
    };
    /**
     * @method setModal
     * Set if the window is modal or not.
     * @param {Boolean} modal
     * @chainable
     */
    Window.prototype.setModal = function (modal) {
        if (typeof modal !== 'undefined') {
            this.modal = !!modal;
        }
        return this;
    };
    /**
     * @inheritdoc
     */
    Window.prototype.setWidth = function (width) {
        Window.superclass.prototype.setWidth.call(this, width);
        return this.updateDimensionsAndPosition();
    };
    /**
     * @inheritdoc
     */
    Window.prototype.setHeight = function (height) {
        Window.superclass.prototype.setHeight.call(this, height);
        return this.updateDimensionsAndPosition();
    };
    /**
     * @method  setFooterHeight
     * high fixed size for the footer of the window
     * @param {Number|String} height it can be a number or a string.
     In case of using a String equals to 'auto','inherit','##px','##%','##em'
     * @chainable
     */
    Window.prototype.setFooterHeight = function (footerHeight) {
        if (typeof footerHeight === 'number') {
            this.footerHeight = footerHeight;
        } else if (/^\d+(\.\d+)?px$/.test(footerHeight)) {
            this.footerHeight = parseInt(footerHeight, 10);
        } else if (footerHeight === 'auto') {
            this.footerHeight = footerHeight;
        } else {
            throw new Error('setFooterHeight: footerHeight param is not valid.');
        }
        if (this.footer) {
            this.footer.style.height = this.footerHeight + 'px';
        }
        if (this.isOpen) {
            this.updateDimensionsAndPosition();
        }

        return this;
    };
    /**
     * Sets the alignment for the items on the footer.
     * @param {String} align Possible values: "center", "left", "right".
     * @chainable
     */
    Window.prototype.setFooterAlign = function (align) {
        this.footer.setAlignment(align);
        this.footerAlign = align;
        return this;
    };
    /**
     * buttonPanelPosition parameter sets that have the panel of buttons, within the window
     * @param {String} buttonPanelPosition Accepted values ​​are 'top' or 'bottom'
     */
    Window.prototype.setButtonPanelPosition = function (buttonPanelPosition) {
        if (!(buttonPanelPosition === 'top' || buttonPanelPosition === 'bottom')) {
            throw  new Error('setButtonPanelPosition(): the value is not valid, should be a "top" or "bottom"');
        }
        this.buttonPanelPosition = buttonPanelPosition;
        if (this.html) {
            if (buttonPanelPosition === 'top') {
                this.html.insertBefore(this.footer.html, this.body);
            } else {
                this.html.appendChild(this.footer.html);
            }
        }
        return this;
    };
    /**
     * Clear the items in the window footer.
     * @chainable
     */
    Window.prototype.clearFooterItems = function () {
        this.footer.clearItems();
        return this;
    };
    /**
     * Adds an item to the window footer.
     * @param {Object|PMUI.ui.Button|PMUI.ui.TextLabel} item This parameter can be:
     *
     * - An object literal, in this case the object literal must have the property "pmType" with its value set to
     * "button" (if you want the element be a {@link PMUI.ui.Button Button}) or "label" (if you want the element be a
     * {@link PMUI.ui.TextLabel Label}). Optionally you can add the respective config options for each case.
     *
     * - A PMUI object, in this case it must be an instance of {@link PMUI.ui.Button Button} or an instance of
     * {@link PMUI.ui.TextLabel Label}.
     * @chainable
     */
    Window.prototype.addFooterItem = function (item) {
        this.footer.addItem(item);
        return this;
    };
    /**
     * Sets the items for the window's footer.
     * @param {Array} items This accepted value for this parameter is the same than the one for the
     * {@link #cfg-footerItems footerItems} config option.
     * @chainable
     */
    Window.prototype.setFooterItems = function (items) {
        var i;

        if (!jQuery.isArray(items)) {
            throw new Error("setFooterItems(): The parameter must be an array.");
        }
        this.clearFooterItems();
        for (i = 0; i < items.length; i += 1) {
            this.addFooterItem(items[i]);
        }
        return this;
    };
    /**
     * @method close
     * if the window is open, sets the status to closed
     * @chainable
     */
    Window.prototype.close = function () {
        jQuery(this.modalObject).detach();
        jQuery(this.html).detach();
        jQuery(this.closeButton).detach();
        if (typeof this.onClose === 'function') {
            this.onClose(this);
        }
        this.isOpen = false;
        if (document.body && this.modal) {
            document.body.style.overflow = "auto";
        }
        return this;
    };
    /**
     * Shows the window's close button.
     * @chainable
     */
    Window.prototype.showCloseButton = function () {
        this.visibleCloseButton = true;
        if (this.closeButton) {
            this.closeButton.setVisible(true);
        }
        return this;
    };
    /**
     * Adds an child item to the body HTMLElement
     * @param {PMUI.core.Window|Object} item It can be one of the following data types:
     * - {PMUI.core.Element} the object to add
     * - {Object} a JSON object with the settings for the Container to be added
     * @chainable
     */
    Window.prototype.addItem = function (item) {
        var itemToBeAdded;

        if (this.factory) {
            itemToBeAdded = this.factory.make(item);
        }
        if (itemToBeAdded && !this.isDirectParentOf(itemToBeAdded)) {
            itemToBeAdded.parent = this;
            this.items.insert(itemToBeAdded);
            if (this.body) {
                this.body.appendChild(itemToBeAdded.getHTML());
                if (this.eventsDefined) {
                    itemToBeAdded.defineEvents();
                }
            }
        }
        return this;
    };
    /**
     * @method open
     * If the window is closed, sets the status to open and displays the values ​​set in the properties window, header, body and footer
     * @chainable
     */
    Window.prototype.open = function () {
        var the_window;

        if (this.isOpen) {
            return this;
        }
        the_window = this.getHTML();
        if (this.modal) {
            this.modalObject.appendChild(the_window);
            document.body.appendChild(this.modalObject);
            jQuery(the_window).draggable({
                handle: $(this.header),
                containment: '#' + this.modalObject.id, 
                scroll: false
            });
        } else {
            document.body.appendChild(the_window);
            jQuery(this.getHTML()).draggable();
        }
        if (typeof this.onOpen === 'function') {
            this.onOpen(this);
        }
        this.isOpen = true;
        this.updateDimensionsAndPosition();
        this.setVisible(true);
        this.defineEvents();
        if (document.body && this.modal) {
            document.body.style.overflow = "hidden"
        }
        return this;
    };
    /**
     * Hides the window's close button.
     * @chainable
     */
    Window.prototype.hideCloseButton = function () {
        this.visibleCloseButton = false;
        if (this.closeButton) {
            this.closeButton.setVisible(false);
        }
        return this;
    };
    /**
     * Shows the window's close button.
     * @chainable
     */
    Window.prototype.showCloseButton = function () {
        this.visibleCloseButton = true;
        if (this.closeButton) {
            this.closeButton.setVisible(true);
        }
        return this;
    };
    /**
     * @method updateModalDimensions
     * Updates the status of modal HTMLElement and
     fixed all styles required to show
     */
    Window.prototype.updateModalDimensions = function () {
        if (document && this.modalObject) {
            this.modalObject.style.height = this.modalObject.style.width = "0px";
            this.modalObject.style.width = window.innerWidth + "px";
            this.modalObject.style.height = window.innerHeight + "px";
        }
        return this;
    };
    /**
     * Sets the padding for the windows body.
     * @param {Number|String} bodyPadding A number or a string with a pixel-unit formatted text.d
     */
    Window.prototype.setBodyPadding = function (bodyPadding) {
        if (typeof bodyPadding === 'number') {
            this.bodyPadding = bodyPadding
        } else if (/^\d+(\.\d+)?px$/.test(height)) {
            this.bodyPadding = parseInt(bodyPadding, 10);
        } else {
            throw new Error('setHeight: height param is not valid.');
        }

        this.bodyPadding = bodyPadding;
        if (this.html) {
            this.body.style.padding = bodyPadding + 'px';
        }
        return this;
    };
    /**
     * @inheritdoc
     */
    Window.prototype.paintItems = function () {
        if (this.body) {
            Window.superclass.prototype.paintItems.call(this);
        }
        return this;
    };
    /**
     * @method defineEvents
     * Defines the events associated with the Window
     */
    Window.prototype.defineEvents = function () {
        var that = this,
            html = this.html,
            modal = this.modal,
            stopPropagation,
            updateDimensions,
            cancelWheeling = function (e) {
                var height = that.body.clientHeight,
                    scrollHeight = that.body.scrollHeight,
                    scrollTop = that.body.scrollTop;

                e.stopPropagation();
            };

        Window.superclass.prototype.defineEvents.call(this);
        stopPropagation = new PMUI.event.Action({
            handler: function (e) {
                e.stopPropagation();
            }
        }),
            updateDimensions = new PMUI.event.Action({
                handler: function () {
                    that.updateModalDimensions();
                }
            });
        this.addEvent('mousedown').listen(this.body, stopPropagation);
        that.addEvent('click').listen(this.html, stopPropagation);
        that.addEvent('mouseover').listen(this.html, stopPropagation);
        that.addEvent('mouseout').listen(this.html, stopPropagation);
        that.addEvent('mousedown').listen(this.html, stopPropagation);
        that.addEvent('resize').listen(window, updateDimensions);
        this.addEvent('keydown').listen(this.html, stopPropagation);
        this.addEvent('keypress').listen(this.html, stopPropagation);
        this.addEvent('keyup').listen(this.html, stopPropagation);

        this.modalObject.addEventListener('wheel', function (e) {
            e.preventDefault();
            e.stopPropagation();
        }, false);

        this.body.addEventListener('wheel', cancelWheeling, false);
        this.footer.defineEvents();
        that.closeButton.defineEvents();
        this.updateModalDimensions();
        return this;
    };
    /**
     * @method createHTML
     * Creates natural elements to the window html: head, body and foot
     * @return {HTMLElement} returns a HTML element
     */
    Window.prototype.createHTML = function () {
        var header,
            body,
            titleContainer,
            closeButton,
            modal,
            that = this;

        if (this.html) {
            return this.html;
        }

        Window.superclass.prototype.createHTML.call(this);

        header = PMUI.createHTMLElement('div');
        header.className = 'pmui-window-header';
        body = PMUI.createHTMLElement('div');
        body.className = 'pmui-window-body';
        modal = PMUI.createHTMLElement('div');
        modal.className = 'pmui-window-modal';
        modal.id = this.id + "-modal";

        titleContainer = PMUI.createHTMLElement('span');
        titleContainer.className = 'pmui-window-title';
        titleContainer.style.display = "inline-block";

        closeButton = new PMUI.ui.Button({
            style: {
                cssClasses: ['pmui-window-close']
            },
            text: "",
            width: '16px',
            handler: function (event) {
                if (typeof that.onBeforeClose === "function") {
                    that.onBeforeClose(that);
                } else {
                    that.close();
                }
            }
        });

        this.modalObject = modal;
        header.appendChild(titleContainer);
        header.appendChild(closeButton.getHTML());
        $(closeButton.html).find(".button-icon").css('opacity', 'inherit');

        this.html.appendChild(header);
        this.html.appendChild(body);
        this.html.appendChild(this.footer.getHTML());

        this.dom.titleContainer = titleContainer;
        this.header = header;
        this.body = this.containmentArea = body;
        this.closeButton = closeButton;

        this.setTitle(this.title)
            .setFooterHeight(this.footerHeight)
            .setItems(this.items.asArray().slice(0))
            .setButtonPanelPosition(this.buttonPanelPosition);

        if (this.visibleCloseButton) {
            this.showCloseButton();
        } else {
            this.hideCloseButton();
        }
        if (this.visibleFooter) {
            this.showFooter();
        } else {
            this.hideFooter();
        }

        if (this.eventsDefined) {
            this.defineEvents();
        }

        return this.html;
    };

    PMUI.extendNamespace('PMUI.ui.Window', Window);

    if (typeof exports !== "undefined") {
        module.exports = Window;
    }
}());
(function () {
    var MessageWindow = function (settings) {
        MessageWindow.superclass.call(this, settings);
        /**
         * The message for the message window.
         * @type {String}
         * @readonly
         */
        this.message = null;
        /**
         * An object for store the necessary dom elements to build the message window HTML.
         * @type {Object}
         * @private
         */
        this.dom = {};
        this.iconClass = null;
        this.windowMessageType = null;
        MessageWindow.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.ui.Window', MessageWindow);


    MessageWindow.prototype.type = 'WindowMessage';

    MessageWindow.prototype.family = 'ui';

    MessageWindow.prototype.init = function (settings) {
        var defaults = {
            message: '[not-message]',
            iconClass: '',
            windowMessageType: 'default',
            title: '',
            footerAlign: "right"
        };

        jQuery.extend(true, defaults, settings);

        this.setFooterAlign(defaults.footerAlign);
        this.setMessage(defaults.message);
        this.setWindowMessageType(defaults.windowMessageType);
        this.setTitle(defaults.title);

    };

    MessageWindow.prototype.setMessage = function (message) {
        if (typeof message !== 'string') {
            throw new Error('setMessage(): the parameter must be a string.');
        }

        this.message = jQuery.trim(message);

        if (this.dom.messageContainer) {
            this.dom.messageContainer.textContent = message;
        }

        return this;
    };
    MessageWindow.prototype.getMessage = function () {
        return this.message;
    };

    MessageWindow.prototype.setWindowMessageType = function (type) {
        var classMessage = "";

        if (typeof type !== "string") {
            throw  new Error("settype(): the type value is not valid");
        }
        this.windowMessageType = type;
        this.style.removeClasses(["pmui-windowmessage-default", "pmui-windowmessage-error", "pmui-windowmessage-warning", "pmui-windowmessage-success"]);
        classMessage = 'pmui-windowmessage-' + this.windowMessageType;
        switch (this.windowMessageType) {
            case 'error':
                this.style.addClasses([classMessage]);
                break;
            case 'warning':
                this.style.addClasses([classMessage]);
                break;
            case 'success':
                this.style.addClasses([classMessage]);
                break;
            case 'default':
                this.style.addClasses([classMessage]);
                break;
        }
        return this;
    };

    MessageWindow.prototype.getWindowMessageType = function () {
        return this.windowMessageType;
    };

    MessageWindow.prototype.addItem = function () {
        return this;
    };

    MessageWindow.prototype.createHTML = function () {
        var table,
            row,
            cellIcon,
            cellLabel,
            icon,
            p,
            textContent;

        MessageWindow.superclass.prototype.createHTML.call(this);

        table = PMUI.createHTMLElement('table');
        table.className = 'pmui-messagewindow-container';
        row = PMUI.createHTMLElement('tr');
        cellIcon = PMUI.createHTMLElement('td');
        cellLabel = PMUI.createHTMLElement('td');
        cellLabel.style.textAlign = "center";
        cellLabel.className = "pmui-content-label"
        icon = PMUI.createHTMLElement('span');
        icon.className = 'pmui-messagewindow-icon';
        textContent = PMUI.createHTMLElement('span');
        textContent.className = 'pmui-messagewindow-message';

        cellIcon.appendChild(icon);
        row.appendChild(cellIcon);


        cellLabel.appendChild(textContent);
        row.appendChild(cellLabel);
        table.appendChild(row);

        this.dom.icon = icon;
        this.dom.messageContainer = textContent;

        this.body.appendChild(table);

        this.setMessage(this.message);
        this.body.style.padding = "15px";
        cellLabel.style.width = (typeof this.width == 'number') ? this.width * (0.84) + "px" : "70%";
        this.body.style.minHeight = "inherit";
        return this.html;
    };

    MessageWindow.prototype.updateDimensionsAndPosition = function () {
        var bodyHeight,
            footerHeight,
            headerHeight,
            windowHeight = this.height,
            windowWidth;

        if (!this.footer || !this.html || !this.isOpen) {
            return this;
        }
        if (this.footerHeight === 'auto') {
            this.footer.setHeight('auto');
        } else {
            this.footer.setHeight(this.footerHeight);
        }

        if (windowHeight === 'auto') {
            this.body.style.height = 'auto';
            this.body.style.minHeight = 'inherit';
        } else {
            if (/^\d+(\.\d+)?em$/.test(windowHeight)) {
                windowHeight = PMUI.emToPx(parseInt(windowHeight, 10), this.modalObject);
            } else if (/^\d+(\.\d+)?%$/.test(windowHeight)) {
                windowHeight = jQuery(this.html).outerHeight();
            }
            footerHeight = this.visibleFooter ? jQuery(this.footer.getHTML()).outerHeight() : 0;
            headerHeight = jQuery(this.header).outerHeight();
            bodyHeight = windowHeight - footerHeight - headerHeight;
            if (bodyHeight <= 0) {
                bodyHeight = 0;
            }
            this.body.style.minHeight = '';
            this.body.style.height = bodyHeight + "px";
        }

        windowWidth = jQuery(this.header).width();
        windowWidth = windowWidth - (this.visibleCloseButton ? jQuery(this.closeButton.getHTML()).outerWidth() : 0);
        this.dom.titleContainer.style.width = windowWidth < 0 ? 0 : windowWidth + 'px';

        windowWidth = jQuery(this.html).outerWidth();
        windowHeight = jQuery(this.html).outerHeight();

        this.addCSSProperties({
            left: '50%',
            "margin-left": (windowWidth / -2) + "px",
            top: '50%',
            "margin-top": (windowHeight / -2) + "px"
        });
        return this;
    }

    PMUI.extendNamespace('PMUI.ui.MessageWindow', MessageWindow);

    if (typeof exports !== "undefined") {
        module.exports = MessageWindow;
    }
}());

(function () {
    /**
     * @class PMUI.ui.ErrorMessageWindow
     * A window to show a error message.
     * @extends {PMUI.ui.MessageWindow}
     *
     * Usage example:
     *
     *      @example
     *      var message_window = new PMUI.ui.ErrorMessageWindow({
     *          message: 'This is an error message.'
     *      });
     *      message_window.open();
     *
     * @constructor
     * Creates a new instance of the class ErrorMessageWindow
     * @param {Object} [settings={}] An object with the config options.
     *
     * @cfg {String} [title='Error'] The message to show in the message window.
     */
    var ErrorMessageWindow = function (settings) {
        ErrorMessageWindow.superclass.call(this, settings);
        ErrorMessageWindow.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.ui.MessageWindow', ErrorMessageWindow);
    /**
     * Initializes the object.
     * @param  {Object} settings The object with the config options.
     * @private
     */
    ErrorMessageWindow.prototype.init = function (settings) {
        var defaults = {
            title: 'Error'
        };

        jQuery.extend(true, defaults, settings);

        this.setTitle(defaults.title);
    };
    /**
     * Creates the message window HTML.
     * @return {HTMLElement}
     */
    ErrorMessageWindow.prototype.createHTML = function () {
        ErrorMessageWindow.superclass.prototype.createHTML.call(this);

        jQuery(this.dom.icon).addClass('pmui-messagewindow-icon-error');

        return this.html;
    };

    PMUI.extendNamespace('PMUI.ui.ErrorMessageWindow', ErrorMessageWindow);

    if (typeof exports !== "undefined") {
        module.exports = ErrorMessageWindow;
    }
}());
(function () {
    /**
     * @class PMUI.ui.WarningMessageWindow
     * A window to show a warning message.
     * @extends {PMUI.ui.MessageWindow}
     *
     * Usage example:
     *
     *      @example
     *      var message_window = new PMUI.ui.WarningMessageWindow({
     *          message: 'This is a warning message.'
     *      });
     *      message_window.open();
     *
     * @constructor
     * Creates a new instance of the class WarningMessageWindow
     * @param {Object} [settings={}] An object with the config options.
     *
     * @cfg {String} [title='Warning'] The message to show in the message window.
     */
    var WarningMessageWindow = function (settings) {
        WarningMessageWindow.superclass.call(this, settings);
        WarningMessageWindow.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.ui.MessageWindow', WarningMessageWindow);
    /**
     * Initializes the object.
     * @param  {Object} settings The object with the config options.
     * @private
     */
    WarningMessageWindow.prototype.init = function (settings) {
        var defaults = {
            title: 'Warning'
        };

        jQuery.extend(true, defaults, settings);

        this.setTitle(defaults.title);
    };
    /**
     * Creates the message window ĤTML.
     * @return {HTMLElement}
     */
    WarningMessageWindow.prototype.createHTML = function () {
        WarningMessageWindow.superclass.prototype.createHTML.call(this);

        jQuery(this.dom.icon).addClass('pmui-messagewindow-icon-warning');

        return this.html;
    };

    PMUI.extendNamespace('PMUI.ui.WarningMessageWindow', WarningMessageWindow);

    if (typeof exports !== "undefined") {
        module.exports = WarningMessageWindow;
    }
}());
(function () {
    /**
     * @class PMUI.ui.SuccessMessageWindow
     * A window to show a success message.
     * @extends {PMUI.ui.MessageWindow}
     *
     * Usage example:
     *
     *      @example
     *      var message_window = new PMUI.ui.SuccessMessageWindow({
     *          message: 'This is a success message.'
     *      });
     *      message_window.open();
     *
     * @constructor
     * Creates a new instance of the class SuccessMessageWindow
     * @param {Object} [settings={}] An object with the config options.
     *
     * @cfg {String} [title='Success'] The message to show in the message window.
     */
    var SuccessMessageWindow = function (settings) {
        SuccessMessageWindow.superclass.call(this, settings);
        SuccessMessageWindow.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.ui.MessageWindow', SuccessMessageWindow);
    /**
     * Initializes the object.
     * @param  {Object} settings The object with the config options.
     * @private
     */
    SuccessMessageWindow.prototype.init = function (settings) {
        var defaults = {
            title: 'Success'
        };

        jQuery.extend(true, defaults, settings);

        this.setTitle(defaults.title);
    };
    /**
     * Creates the message window ĤTML.
     * @return {HTMLElement}
     */
    SuccessMessageWindow.prototype.createHTML = function () {
        SuccessMessageWindow.superclass.prototype.createHTML.call(this);

        jQuery(this.dom.icon).addClass('pmui-messagewindow-icon-success');

        return this.html;
    };

    PMUI.extendNamespace('PMUI.ui.SuccessMessageWindow', SuccessMessageWindow);

    if (typeof exports !== "undefined") {
        module.exports = SuccessMessageWindow;
    }
}());
(function () {
    /**
     * @class PMUI.ui.InfoMessageWindow
     * A window to show an info message.
     * @extends {PMUI.ui.MessageWindow}
     *
     * Usage example:
     *
     *      @example
     *      var message_window = new PMUI.ui.InfoMessageWindow({
     *          message: 'This is an info message.'
     *      });
     *      message_window.open();
     *
     * @constructor
     * Creates a new instance of the class InfoMessageWindow
     * @param {Object} [settings={}] An object with the config options.
     *
     * @cfg {String} [title='Information'] The message to show in the message window.
     */
    var InfoMessageWindow = function (settings) {
        InfoMessageWindow.superclass.call(this, settings);
        InfoMessageWindow.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.ui.MessageWindow', InfoMessageWindow);
    /**
     * Initializes the object.
     * @param  {Object} settings The object with the config options.
     * @private
     */
    InfoMessageWindow.prototype.init = function (settings) {
        var defaults = {
            title: 'Info'
        };

        jQuery.extend(true, defaults, settings);

        this.setTitle(defaults.title);
    };
    /**
     * Creates the message window HTML.
     * @return {HTMLElement}
     */
    InfoMessageWindow.prototype.createHTML = function () {
        InfoMessageWindow.superclass.prototype.createHTML.call(this);

        jQuery(this.dom.icon).addClass('pmui-messagewindow-icon-info');

        return this.html;
    };

    PMUI.extendNamespace('PMUI.ui.InfoMessageWindow', InfoMessageWindow);

    if (typeof exports !== "undefined") {
        module.exports = InfoMessageWindow;
    }
}());
(function () {
    /**
     * @class PMUI.ui.ConfirmMessageWindow
     * A window to show a question message. It haas built-in Yes and No buttons, but they can be overrode by some
     * custom ones.
     * @extends {PMUI.ui.MessageWindow}
     *
     * Usage example:
     *
     *      @example
     *      var message_window = new PMUI.ui.ConfirmMessageWindow({
     *          message: 'Do you want to display an alert?',
     *              onYes: function() {
     *                  alert("this is an alert");
     *                  message_window.close();
     *              }, 
     *              onNo: function() {
     *                  message_window.close();
     *              }
     *      });
     *      message_window.open();
     *
     * @constructor
     * Creates a new instance of the class ConfirmMessageWindow
     * @param {Object} [settings={}] An object with the config options.
     *
     * @cfg {String} [title='Error'] The message to show in the message window.
     * @cfg {String} [yesText='Yes'] The text for the window's built-in Yes button.
     * @cfg {String} [noText='No'] The text for the window's built-in No button.
     * @cfg {Function|null} [onYes=null] The handler to be executed when the built-in Yes button is clicked.
     * @cfg {Function|null} [onNo=null] The handler to be executed when the built-in No button is clicked.
     * @cfg footerItems
     * The items to be shown in the message window's footer, the accepted values for this are the same that the ones
     * for the Window's footerItems config option.
     *
     * By default this is an empty array, if you modify this then the built-in Yes and No buttons won't be used,
     * therefore the yesText, noText config options and onYes, onNo handlers won't be used either.
     */
    var ConfirmMessageWindow = function (settings) {
        ConfirmMessageWindow.superclass.call(this, settings);
        ConfirmMessageWindow.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.ui.MessageWindow', ConfirmMessageWindow);
    /**
     * Initializes the object.
     * @param  {Object} settings The object with the config options.
     * @private
     */
    ConfirmMessageWindow.prototype.init = function (settings) {
        var yesButton = new PMUI.ui.Button(),
            noButton = new PMUI.ui.Button(),
            defaults = {
                title: 'Confirm',
                yesText: 'Yes',
                noText: 'No',
                onYes: null,
                onNo: null,
                footerItems: []
            };

        jQuery.extend(true, defaults, settings);

        this.setTitle(defaults.title);

        if (!defaults.footerItems || !defaults.footerItems.length) {
            defaults.footerItems = [
                yesButton.setHandler(defaults.onYes).setText(defaults.yesText),
                noButton.setHandler(defaults.onNo).setText(defaults.noText)
            ];
        }
        this.setFooterItems(defaults.footerItems);
    };
    /**
     * @inheritdoc
     */
    ConfirmMessageWindow.prototype.createHTML = function () {
        ConfirmMessageWindow.superclass.prototype.createHTML.call(this);

        jQuery(this.dom.icon).addClass('pmui-messagewindow-icon-confirm');

        return this.html;
    };

    PMUI.extendNamespace('PMUI.ui.ConfirmMessageWindow', ConfirmMessageWindow);

    if (typeof exports !== "undefined") {
        module.exports = ConfirmMessageWindow;
    }
}());
(function () {
    /**
     * @class PMUI.ui.Button
     * @extend PMUI.core.Element
     *   * Usage example (only for subclasses since this is an abstract class):
     *
     *          @example
     *          //Remember, this is an abstract class so it shouldn't be instantiate,
     *          //anyway we are instantiating it just for this example
     *
     *          var a;
     *
     *           a = new PMUI.ui.Button({
     *               handler : function () {
     *                   alert("hi!");     
     *               },
     *               text : 'closed',
     *               iconClass:"pmui-gridpanel-pager_previous",
     *               iconPosition : "left",
     *               messageTooltip : 'this button to close the window'
     *           });
     *
     *           document.body.appendChild(a.getHTML());
     *           a.defineEvents();
     *
     * @constructor
     * Create a new instace of the Window class
     * @param {Object} settigs Constructor object
     */
    var Button = function (settings, parent) {
        Button.superclass.call(this, jQuery.extend(settings, {elementTag: "a"}));
        /**
         * @property icon for the Button, it can a string
         * @type {String}
         */
        this.icon = null;
        /**
         * @property {PMUI.event.Action} Defines the Action Object to handle he action windows
         * @type {Action}
         */
        this.action = null;
        /**
         * @property parent
         * @type {String}
         */
        this.parent = null;
        /**
         * @property text of the Button
         * @type {String}
         */
        this.text = null;
        /**
         * @property alias Button for idetified to alias
         * @type {String}
         */
        this.aliasButton = null;
        /**
         * [iconPosition description]
         * @type {[type]}
         */
        this.iconPosition = null;
        /**
         * [messageTooltip description]
         * @type {[type]}
         */
        this.messageTooltip = null;

        //this.buttonClass = null;

        //this.foreignClassName = null;

        this.linkStyle = null;

        this.labelVisible = null;
        this.iconVisible = null;
        this.disabled = null;
        this.dom = null;
        this.buttonType = null;

        Button.prototype.init.call(this, settings, parent);
    };

    PMUI.inheritFrom('PMUI.core.Element', Button);

    Button.prototype.type = 'Button';
    Button.prototype.family = 'Button';

    Button.prototype.init = function (settings, parent) {
        var defaults;

        defaults = {
            iconClass: '',
            aliasButton: null,
            parent: parent || null,
            height: "auto",
            width: "auto",
            handler: function () {
            },
            text: '[button]',
            iconPosition: "right",
            tooltip: false,
            messageTooltip: "",
            //buttonClass : '',
            iconVisible: true,
            labelVisible: true,
            disabled: false,
            //cls: 'pmui-button',
            //linkStyle: false,
            //foreignClassName : "",
            //useForeignClass : false
            buttonType: 'default'
        };
        this.dom = {};

        jQuery.extend(true, defaults, settings);

        this.setAliasButton(defaults.aliasButton)
            .setParent(defaults.parent)
            .setText(defaults.text)
            .setIcon(defaults.iconClass)
            .setWidth(defaults.width)
            .setHeight(defaults.height)
            .setIconPosition(defaults.iconPosition)
            .setHandler(defaults.handler)
            .setTooltipMessage(defaults.messageTooltip)
            .setDisabled(defaults.disabled)
            .setButtonType(defaults.buttonType);

    };

    Button.prototype.setButtonType = function (buttonType) {
        var classButton = "";

        if (typeof buttonType !== "string") {
            throw  new Error("setButtonType(): the type value is not valid");
        }
        this.buttonType = buttonType;
        this.style.removeClasses(["pmui-error", "pmui-warning", "pmui-success", "pmui-info", "pmui-link"]);
        classButton = 'pmui-' + this.buttonType;
        switch (this.buttonType) {
            case 'error':
                this.style.addClasses([classButton]);
                break;
            case 'warning':
                this.style.addClasses([classButton]);
                break;
            case 'success':
                this.style.addClasses([classButton]);
                break;
            case 'info':
                this.style.addClasses([classButton]);
                break;
            case 'link':
                this.style.addClasses([classButton]);
                break;
        }
        return this;
    };

    Button.prototype.showLabel = function () {
        this.labelVisible = true;
        if (this.html) {
            this.label.style.display = '';
        }
        return this;
    };

    Button.prototype.setDisabled = function (value) {
        if (typeof value !== 'boolean') {
            throw new Error('setDisabled(): the parameter is not valid, should be type boolean');
        }
        this.disabled = value;
        if (this.html) {
            if (this.disabled) {
                this.disable();
            } else {
                this.enable();
            }
        }
        return this;
    }

    Button.prototype.disable = function () {
        this.disabled = true;
        this.style.addClasses(['pmui-disabled']);
        if (this.eventsDefined) {
            this.defineEvents();
        }
        return this;
    };
    /**
     * @method enable
     * button can be enabled if it was disabled before
     * @chainable
     */
    Button.prototype.enable = function () {
        this.disabled = false;
        this.style.removeClasses(['pmui-disabled']);
        if (this.eventsDefined) {
            this.defineEvents();
        }
        return this;
    };

    /**
     * Performs the click action on the button.
     * @chainable
     */
    Button.prototype.click = function () {
        jQuery(this.html).trigger('click');
        return this;
    };

    /**
     * @method setHandler
     * Sets the handler for the button
     * @param {Function} handler is a Function.
     * @chainable
     */
    Button.prototype.setHandler = function (handler) {
        if (typeof handler !== "function" && handler !== null) {
            throw new Error("setHandler(): the parameter should be a function or null;");
        }
        this.handler = handler;
        this.action = new PMUI.event.Action({
            actionText: this.aliasButton,
            handler: this.handler
        });
        return this;
    };

    /**
     * @method removeEvent
     * Remove Event for the button
     * @param {Function} handler is a Function.
     * @chainable
     */
    Button.prototype.removeEvent = function (type) {
        jQuery(this.html).unbind(type);
        return this;
    };

    /**
     * @method removeEvents
     * Remove Events for the button
     * @param {Function} handler is a Function.
     * @chainable
     */
    Button.prototype.removeEvents = function () {
        var x;
        for (x in  this.events) {
            this.removeEvent(this.events[x].eventName);
        }
        this.events = {};
        return this;
    };

    /**
     * Sets the text for the label Button.
     * @param {String} is a String.
     * @chainable
     */
    Button.prototype.setText = function (text) {
        this.text = text;
        if (this.html && this.dom.spanText) {
            if (jQuery.trim(text) !== "") {
                this.dom.spanText.textContent = this.text;
            } else {
                this.dom.spanText.inerHTML = '&nbsp;';
            }
        }
        return this;
    };

    /**
     * Sets the icon for the Button.
     * @param {String} is a String.
     * @chainable
     */
    Button.prototype.setIcon = function (iconClass) {
        var icon;

        if (!(typeof iconClass == "string")) {
            throw new Error("setIcon(): the property should bo string");
        }
        this.icon = iconClass;
        if (this.html && this.dom.icon) {
            this.dom.icon.className = this.dom.icon.className + " " + this.icon;
        }
        return this;
    };

    /**
     * @method setAliasButton
     * Sets the alias for the label Button
     * @param {String} is a String. lue
     * @chainable
     */
    Button.prototype.setAliasButton = function (alias) {
        this.aliasButton = alias;
        return this;
    };

    /**
     * @method setParent
     * Sets the parent for the Button.
     * @param {Object} is a HTML Element.
     * @chainable
     */
    Button.prototype.setParent = function (parent) {
        this.parent = parent;
        return this;
    };
    /**
     * @method createHTML
     * Create HTML for the element
     * @return {HTMLElement} HTML Generated
     */

    Button.prototype.createHTML = function () {
        var spanText,
            icon;

        if (this.html) {
            return this.html;
        }
        Button.superclass.prototype.createHTML.call(this);
        this.html.href = '#';
        //***conatainer for text
        spanText = PMUI.createHTMLElement('span');
        spanText.className = "button-label";
        this.dom.spanText = spanText;
        this.html.appendChild(spanText);

        // container for icon
        icon = PMUI.createHTMLElement('span');
        icon.className = 'button-icon';
        this.dom.icon = icon;
        this.html.appendChild(icon);

        this.setIconPosition(this.iconPosition);
        this.setTooltipMessage(this.messageTooltip);

        this.setDisabled(this.disabled);
        this.setText(this.text);
        this.setIcon(this.icon);
        this.applyStyle();

        return this.html;
    };
    /**
     * @method defineEvents
     * Define events click for the Button, listening the function action
     */

    Button.prototype.defineEvents = function () {
        var that = this;

        Button.superclass.prototype.defineEvents.call(this);
        if (this.html) {
            if (this.disabled) {
                this.addEvent('click').listen(this.html, function (e) {
                    e.preventDefault();
                    e.stopPropagation();
                });
            } else {
                this.addEvent('click').listen(this.html, function (e) {
                    e.preventDefault();
                    e.stopPropagation();
                    if (typeof that.handler === 'function') {
                        that.handler(that);
                    }
                });
            }
        }
        return this;
    };

    /**
     * @method setHeight
     * Sets the height for the button.
     * @param {String, number} height
     * This parameter can take the following types
     * - number
     * - string
     */
    Button.prototype.setHeight = function (height) {
        if (typeof height === 'number') {
            this.height = height + "px";
        } else if (/^\d+(\.\d+)?px$/.test(height)) {
            this.height = height;
        } else if (/^\d+(\.\d+)?%$/.test(height)) {
            this.height = height;
        } else if (/^\d+(\.\d+)?em$/.test(height)) {
            this.height = height;
        } else if (height === 'auto' || height === 'inherit') {
            this.height = height;
        } else {
            throw new Error('setHeight: height param is not a number');
        }
        if (this.height !== 'auto') {
            this.style.addProperties({"line-height": parseInt(this.height, 10) + "px"});
        } else {
            this.style.addProperties({"line-height": 'normal'});
        }
        this.applyStyle();
        return this;
    };
    /**
     * @method setIconPosition
     * Creates the HTML element for the control.
     * @param {String} position
     * This parameter can take the following values: left, right
     */
    Button.prototype.setIconPosition = function (position) {
        if (position == 'right' || position == "left" || position == "top" || position == "bottom") {
            this.iconPosition = position;
            if (this.html) {
                if (this.iconPosition == 'left' || this.iconPosition == 'top') {
                    $(this.html).prepend(this.dom.icon);
                    if (this.iconPosition == 'left') {
                        this.dom.icon.style.display = 'inline-block';
                        this.dom.icon.style.marginLeft = '';

                    } else {
                        this.dom.icon.style.display = 'block';
                        this.dom.icon.style.marginLeft = '50%';
                    }
                } else {
                    $(this.html).append(this.dom.icon);
                    if (this.iconPosition == 'right') {
                        this.dom.icon.style.display = 'inline-block';
                        this.dom.icon.style.marginLeft = '';
                    } else {
                        this.dom.icon.style.display = 'block';
                        this.dom.icon.style.marginLeft = '50%';
                    }
                }
            }

        } else {
            throw new Error("setIconPosition(): the parameter is not valid, should be 'right','left', 'top' or 'bottom'");
        }
        return this;
    };
    /**
     * @method setTooltipMessage
     * Sets the tooltip message
     * @param {String} messageTooltip
     */
    Button.prototype.setTooltipMessage = function (messageTooltip) {
        if (typeof messageTooltip == "string") {
            this.messageTooltip = messageTooltip;
            if (this.html) {
                this.html.setAttribute('title', messageTooltip);
            }
        }
        return this;
    };

    PMUI.extendNamespace("PMUI.ui.Button", Button);

    if (typeof exports !== "undefined") {
        module.exports = Button;
    }
}());


(function () {
    /**
     * @class PMUI.ui.TooltipMessage
     * @extends PMUI.core.Element
     * Class to handle tooltip elements and alert messages
     *
     * Usage example (creates 4 TooltipMessage instances):
     *
     *      @example
     *      //usage example
     *      //creates 4 TooltipMessage instances:
     *      var a, b, c, d;
     *      $(function() {
     *          // Instantiates a simple info message
     *          a = new PMUI.ui.TooltipMessage({
     *              message: "Hi folks! this is a info",
     *              category: "info",
     *              displayMode: "block",
     *              mode: "normal"
     *          });
     *          //Instantiates an error tooltip with mouse tracking option enabled
     *          b = new PMUI.ui.TooltipMessage({
     *              message: "Hi folks! this is an error",
     *              category: "error",
     *              track: true,
     *              displayMode: "block"
     *          });
     *          //Instantiates a warning tooltip with a slide down effect when the tooltip is opened
     *          c = new PMUI.ui.TooltipMessage({
     *              message: "Hi folks! this is a warning",
     *              category: "warning",
     *              displayMode: "block",
     *              showEffect: {
     *                  effect: "slideDown",
     *                  delay: 250
     *              }
     *          });
     *          //Instantiates a help tooltip with a explode effect when the tooltip is hidden
     *          d = new PMUI.ui.TooltipMessage({
     *              message: "Hi folks! this is a help",
     *              category: "help",
     *              displayMode: "block",
     *              hideEffect: {
     *                  effect: "explode",
     *                  delay: 250
     *              }
     *          });
     *          document.body.appendChild(a.getHTML());
     *          document.body.appendChild(b.getHTML());
     *          document.body.appendChild(c.getHTML());
     *          document.body.appendChild(d.getHTML());
     *      });
     * @constructor
     * Create a new instace of the class 'TooltipMessage'
     * @param {Object} settings
     * A JSON object which can contain the following fields:
     *
     * @cfg {String} [message = ""] The message to show
     * @cfg {String} [category = 'help'] The object's category, it can be "info", "help", "warning" or "error"
     * @cfg {String} [displayMode = 'inline'] Determines if the object will have an "inline" or "block" displaying
     * @cfg {String} [mode="tooltip"] When is set to "tooltip" the object has a tooltip behavior, but when is set to "normal" it
     * has a regular message behavior
     * @cfg {String} [tooltipClass='pmui-tooltip-message'] the css class for the HTML element which will contain the message
     * @cfg {Object} [tooltipPosition = { 
                my: "left top+15", 
                at: "left bottom", 
                collision: "flipfit" 
            }] specifies the position for the tooltip, read the
     {@link PMUI.ui.TooltipMessage#setTooltipPosition .setTooltipPosition()} for more info
     * @cfg {Object} [showEffect=null] specifies the animation to apply when the tooltip is shown, read the
     {@link PMUI.ui.TooltipMessage#setShowEffect .setShowEffect()} for more info
     *  @cfg {Object} [hideEffect=null] specifies the animation to apply when the tooltip is hidden, read the
     {@link PMUI.ui.TooltipMessage#setHideEffect .setHideEffect()} for more info
     * @cfg {Function} [onOpen] a callback function to be invoked when the tooltip is shown
     * @cfg {Function} [onClose] a callback functio to be invoked when the tooltip is hidden
     * @cfg {Boolean} [track = false] a turns on/off the tooltip to follow the mouse movement
     * Note: tooltipClass, tooltipPosition, showEffect, hideEffect, onOpen, onClose and track options
     only are applied when the mode is set to "tooltip"
     */
    var TooltipMessage = function (settings) {
        TooltipMessage.superclass.call(this, settings);
        /**
         * @property {PMUI.core.Element} icon
         * An {@link PMUI.core.Element} which is used to put the icon in
         * @private
         */
        this.icon = null;
        /**
         * An Element
         * @type {PMUI.core.Element} which is used to display the message when the mode is set to "normal"
         * @private
         */
        this.messageArea = null;
        /**
         * @property {String} [message=""]
         * The message to be displayed
         * @readonly
         */
        this.message = null;
        /**
         * @property {String} [category="help"]
         * The category for the message
         * @readonly
         */
        this.category = null;
        /**
         * @property {String} [displayMode="inline"]
         * The displaying mode for the object's HTML element
         * @readonly
         */
        this.displayMode = null;
        /**
         * @property {String} [mode="tooltip"]
         * The object's behavior mode
         * @readonly
         */
        this.mode = null;
        /**
         * @property {Object} [tooltipPosition={ 
                my: "left top+15", 
                at: "left bottom", 
                collision: "flipfit" 
            }]
         * The tooltip position
         * @readonly
         */
        this.tooltipPosition = null;
        /**
         * @property {String} [tooltipClass="pmui-tooltip-message"]
         * The CSS class name for the tooltip HTML element
         * @readonly
         */
        this.tooltipClass = null;
        /**
         * @property {Object|Boolean|String|Number} [showEffect=null]
         * The effect to apply when the tooltip is shown
         */
        this.showEffect = null;
        /**
         * @property {Object|Boolean|String|Number} [hideEffect=null]
         * The effect to apply when the tooltip is hidden
         */
        this.hideEffect = null;
        /**
         * @property {Function} [onOpen=null]
         * The function callback to be invoked when the tooltip is shown
         */
        this.onOpen = null;
        /**
         * @property {Function} [onClose=null]
         * The function callback to be invoked when the tooltip is hidden
         * @type {Function}
         */
        this.onClose = null;
        /**
         * @property {Boolean} [track=false]
         * A boolean that specifies if the the tooltip element must follow the mouse position
         */
        this.track = null;
        TooltipMessage.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Element', TooltipMessage);

    TooltipMessage.prototype.type = 'PMUITooltipMessage';

    TooltipMessage.prototype.family = 'PMUITooltipMessage';

    TooltipMessage.prototype.init = function (settings) {
        var defaults = {
            message: "",
            category: "help",
            displayMode: "inline",
            mode: "tooltip",
            tooltipClass: 'pmui-tooltip-message',
            tooltipPosition: {
                my: "left top+15",
                at: "left bottom",
                collision: "flipfit"
            },
            showEffect: null,
            hideEffect: null,
            onOpen: null,
            onClose: null,
            track: false
        };

        $.extend(true, defaults, settings);

        this.onClose = defaults.onClose;
        this.onOpen = defaults.onOpen;

        this.setTooltipClass(defaults.tooltipClass)
            .setTooltipPosition(defaults.tooltipPosition)
            .setShowEffect(defaults.showEffect)
            .setHideEffect(defaults.hideEffect)
            .setTrack(defaults.track)
            .setMessage(defaults.message)
            .setCategory(defaults.category)
            .setDisplayMode(defaults.displayMode)
            .setMode(defaults.mode);
    };
    /**
     * Set the CSS class for the HTML element which will contain the tooltip message.
     * This only takes effect when the mode property is set to "tooltip"
     * @param {String} tooltipClass
     */
    TooltipMessage.prototype.setTooltipClass = function (tooltipClass) {
        this.tooltipClass = tooltipClass;
        return this;
    };
    /**
     * Establish if the HTML element which will be contain the tooltip message should track
     (follow) the mouse position when it appears
     * * This only takes effect when the mode property is set to "tooltip"
     * @param {Boolean} [track=true]
     */
    TooltipMessage.prototype.setTrack = function (track) {
        this.track = !!track;
        if (this.html) {
            this.setMode(this.mode);
        }
        return this;
    };
    /**
     * [setHideEffect description]
     * If and how to animate the hiding of the tooltip.
     * This only takes effect when the mode property is set to "tooltip"
     * @param {Object|Boolean|String|Number} effect
     * - Boolean: When set to false, no animation will be used and the tooltip will be hidden
     immediately. When set to true, the tooltip will fade out with the default duration and the
     default easing.
     * - Number: The tooltip will fade out with the specified duration and the default easing.
     * - String: The tooltip will be hidden using the specified effect. The value can either be the
     name of a built-in jQuery animation method, such as "slideUp", or the name of a jQuery UI effect,
     such as "fold". In either case the effect will be used with the default duration and the default easing.
     * - Object: If the value is an object, then effect, delay, duration, and easing properties may be
     provided. If the effect property contains the name of a jQuery method, then that method will be used;
     otherwise it is assumed to be the name of a jQuery UI effect. When using a jQuery UI effect that supports
     additional settings, you may include those settings in the object and they will be passed to the effect.
     If duration or easing is omitted, then the default values will be used. If effect is omitted,
     then "fadeOut" will be used. If delay is omitted, then no delay is used.
     */
    TooltipMessage.prototype.setHideEffect = function (effect) {
        this.hideEffect = effect;
        if (this.html) {
            this.setMode(this.mode);
        }
        return this;
    };
    /**
     * If and how to animate the showing of the tooltip.
     * This only takes effect when the mode property is set to "tooltip"
     * @param {Object|Boolean|String|Number} effect
     * - Boolean: When set to false, no animation will be used and the tooltip will be shown immediately.
     When set to true, the tooltip will fade in with the default duration and the default easing.
     * - Number: The tooltip will fade in with the specified duration and the default easing.
     * - String: The tooltip will be shown using the specified effect. The value can either be the name of
     a built-in jQuery animation method, such as "slideDown", or the name of a jQuery UI effect, such as
     "fold". In either case the effect will be used with the default duration and the default easing.
     * - Object: If the value is an object, then effect, delay, duration, and easing properties may be
     provided. If the effect property contains the name of a jQuery method, then that method will be used;
     otherwise it is assumed to be the name of a jQuery UI effect. When using a jQuery UI effect that
     supports additional settings, you may include those settings in the object and they will be passed
     to the effect. If duration or easing is omitted, then the default values will be used. If effect is
     omitted, then "fadeIn" will be used. If delay is omitted, then no delay is used.
     */
    TooltipMessage.prototype.setShowEffect = function (effect) {
        this.showEffect = effect;
        if (this.html) {
            this.setMode(this.mode);
        }
        return this;
    };
    /**
     * Identifies the position of the tooltip in relation to the associated target element.
     * The of option defaults to the target element, but you can specify another element to position
     against. You can refer to the [jQuery UI Position][1] utility for more details about the various options.
     * [1]: http://api.jqueryui.com/position
     * @param {Object} position
     */
    TooltipMessage.prototype.setTooltipPosition = function (position) {
        this.tooltipPosition = position;
        if (this.html) {
            this.setMode(this.mode);
        }
        return this;
    };
    /**
     * Sets the message to be shown in both normal and tooltip modes
     * @param {String} message
     */
    TooltipMessage.prototype.setMessage = function (message) {
        if (typeof message === 'string') {
            this.message = message;
            if (this.html) {
                if (this.messageArea) {
                    this.messageArea.getHTML().textContent = message;
                }

                if (this.mode === 'tooltip') {
                    this.icon.html.title = message;
                } else {
                    this.icon.html.title = "";
                }
            }
        } else {
            throw new Error("setMessage() method only accepts string values.");
        }
        return this;
    };
    /**
     * Set the category for the tooltip/message object
     * @param {String} category the value can be one of the following:
     *
     * - help
     * - info
     * - error
     * - warning
     */
    TooltipMessage.prototype.setCategory = function (category) {
        var validCategories = [
            "help", "info", "error", "warning"
        ];

        if (typeof category === 'string' && validCategories.indexOf(category) > -1) {
            this.category = category;
            if (this.icon && this.messageArea) {
                this.icon.style.removeAllClasses();
                this.icon.style.addClasses(['pmui-icon', 'pmui-icon-' + category]);
                this.messageArea.className = 'pmui-tooltip-message pmui-tooltip-' + category + '-message';
            }
            if (this.html) {
                if (this.category === "error") {
                    this.style.addClasses(["pmui-tooltip-category-error"]);
                } else {
                    this.style.removeClasses(["pmui-tooltip-category-error"]);
                }
            }
        } else {
            throw new Error('setCategory() method only accepts' +
                ' one of the following values: "help", "info", "warning", "info".');
        }

        return this;
    };
    /**
     * Sets the CSS displaying mode
     * @param {String} displayMode It can take the "block" or "inline" values
     */
    TooltipMessage.prototype.setDisplayMode = function (displayMode) {
        if (displayMode === 'block' || displayMode === 'inline') {
            this.displayMode = displayMode;
            if (this.html) {
                this.style.addProperties({"display": displayMode});
            }
        } else {
            throw new Error('setDisplayMode() method only accepts "inline" or "block" values');
        }

        return this;
    };
    /**
     * Sets the mode for the object, it determines if the current object has a tooltip or notification message behavior
     * @param {String} mode It only can take one of the following values:
     * - "tooltip"
     * - "normal"
     */
    TooltipMessage.prototype.setMode = function (mode) {
        if (mode === 'tooltip' || mode === 'normal') {
            this.mode = mode;
            if (this.html) {
                $(this.html).addClass('pmui-tooltip-mode-' + mode);
                if (mode === 'tooltip') {
                    this.messageArea.setVisible(false);
                    this.icon.html.title = this.message;
                    $(this.icon.html).tooltip({
                        tooltipClass: this.tooltipClass,
                        position: this.tooltipPosition,
                        show: this.showEffect,
                        hide: this.hideEffect,
                        open: this.onOpen,
                        track: this.track,
                        close: this.onClose
                    });
                } else {
                    try {
                        $(this.icon.html).tooltip('destroy');
                    } catch (ex) {
                    }
                    this.icon.html.title = "";
                    this.messageArea.setVisible(true);
                }
            }
        } else {
            throw new Error('setMode() method only accepts "tooltip" or "normal" values');
        }

        return this;
    };
    /**
     * It creates the HTML Element for the current object
     * @return {HTMLElement}
     */
    TooltipMessage.prototype.createHTML = function () {
        var html,
            messageArea,
            icon;

        if (this.html) {
            return this.html;
        }

        html = PMUI.createHTMLElement("span");
        html.className = 'pmui-tooltip';
        icon = new PMUI.core.Element({
            elementTag: 'span',
            width: 18,
            height: 18,
            style: {
                cssClasses: ['pmui-icon'],
                cssProperties: {
                    display: "inline-block",
                    "vertical-align": "middle"
                }
            }
        });
        messageArea = new PMUI.core.Element({
            elementTag: 'span',
            style: {
                cssClasses: ['pmui-tooltip-message']
            }
        });

        html.appendChild(icon.getHTML());
        html.appendChild(messageArea.getHTML());
        this.icon = icon;
        this.messageArea = messageArea;
        this.html = html;

        this.applyStyle();

        this.setCategory(this.category);
        this.setMessage(this.message);
        this.setMode(this.mode);

        return this.html;
    };

    PMUI.extendNamespace('PMUI.ui.TooltipMessage', TooltipMessage);

    if (typeof exports !== "undefined") {
        module.exports = TooltipMessage;
    }
}());

(function () {
    /**
     * @class PMUI.ui.TextLabel
     * A text label that can contain a plain text or an HTML formatted text.
     * @extends {PMUI.core.Element}
     *
     * Usage example:
     *
     *      @example
     *      var plainText, htmlText;
     *      plainText = new PMUI.ui.TextLabel({
     *          textMode: 'plain',
     *          text: 'Hello! this is a label with a plain text in it',
     *          style: {
     *              cssProperties: {
     *                  background: "red"
     *              }
     *          }
     *      });
     *      htmlText = new PMUI.ui.TextLabel({
     *          textMode: 'html',
     *          text: '<h1>Hello!</h1> this ia a label with a <b> HTML</b> formatted text'
     *      });
     *      document.body.appendChild(plainText.getHTML());
     *      document.body.appendChild(htmlText.getHTML());
     *
     * @constructor
     * Creates a new instance of the class.
     * @param {Object} [settings={}] An object literal with the config options to config the object.
     *
     * @cfg {String} [textMode='plain'] The rendering mode for the text in the label. Possible values:
     *
     * - "plain": for plain text rendering.
     * - "html": for html text rendering. Only the <b>, <i>, <h1> to <h6> tags are allowed, any other tags will be
     * removed.
     * @cfg {String} [elementTag='span'] The HTML tag to be used as the text label.
     * @cfg {String} [text=""] The label's text.
     */
    var TextLabel = function (settings) {
        TextLabel.superclass.call(this, settings);
        /**
         * The text rendering mode. Set by the {@link #cfg-textMode textMode config option} and the
         * {@link #method-setTextMode setTextMode() method}.
         * @type {String}
         * @readonly
         */
        this.textMode = null;
        /**
         * The current displayed label's text. Set by the {@link #cfg-text text config option} and the
         * {@link #method-setText setText() method}.
         * @type {String}
         * @readonly
         */
        this.text = null;
        /**
         * The original set text.
         * @type {String}
         * @private
         */
        this.rawText = null;
        TextLabel.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Element', TextLabel);
    /**
     * The object's type.
     * @type {String}
     */
    TextLabel.prototype.type = 'TextLabel';
    /**
     * Initializes the object.
     * @param  {Object} [settings={}] An object literal with the config options the new object will be initialized
     * with.
     * @chainable
     * @private
     */
    TextLabel.prototype.init = function (settings) {
        var defaults = {
            textMode: 'plain',
            elementTag: 'span',
            text: ''
        };

        jQuery.extend(true, defaults, settings);

        this.setElementTag(defaults.elementTag)
            .setTextMode(defaults.textMode)
            .setText(defaults.text);

        return this;
    };
    /**
     * Sets the label's text.
     * @param {String} text
     */
    TextLabel.prototype.setText = function (text) {
        var tags;

        if (typeof text !== 'string') {
            throw new Error('setText(): the parameter must be a string.');
        }
        this.text = this.rawText = text;
        if (this.textMode === 'html') {
            allowed = ("<b><i><h1><h2><h3><h4><h5><h6>".match(/<[a-z][a-z0-9]*>/g) || []).join('');
            tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,
                commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;
            this.text = text.replace(commentsAndPhpTags, '').replace(tags, function ($0, $1) {
                return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
            });
        }

        if (this.html) {
            if (this.textMode === 'html') {
                this.html.innerHTML = this.text;
            } else {
                this.html.textContent = this.text;
            }
        }
        return this;
    };
    /**
     * Sets the text rendering mode.
     * @param {String} textMode Possible values:
     *
     * - "plain": for plain text rendering.
     * - "html": for html text rendering. Only the <b>, <i>, <h1> to <h6> tags are allowed, any other tags will be
     * removed.
     */
    TextLabel.prototype.setTextMode = function (textMode) {
        if (textMode !== 'plain' && textMode !== 'html') {
            throw Error("setTextMode(): The parameter must be \"plain\" or \"html\".");
        }
        this.textMode = textMode;
        if (typeof this.rawText === 'string') {
            this.setText(this.rawText);
        }
        return this;
    };
    /**
     * Creates the label's HTML.
     * @return {HTMLElement}
     */
    TextLabel.prototype.createHTML = function () {
        if (this.html) {
            return this.html;
        }
        TextLabel.superclass.prototype.createHTML.call(this);
        this.setText(this.rawText);
        return this.html;
    };

    PMUI.extendNamespace('PMUI.ui.TextLabel', TextLabel);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = TextLabel;
    }
}());
(function () {
    /**
     * @class PMUI.ui.FlashMessage
     * A message to display for a while.
     * @extends {PMUI.core.Element}
     *
     * Usage example:
     *
     *      @example
     *      var message = new PMUI.ui.FlashMessage({
     *          message: "Hi!, this is a flash message!",
     *          duration: 5000,
     *          severity: 'info'
     *      });
     *
     *      message.show();
     *
     * @constructor
     * Creates a new instance of the class.
     * @param {Object} [settings] The config options.
     *
     * @cfg {String|Array} [message=""] The object's message. It can be a single string or an array of strings. In the
     * llatter case the mesage will be showed in a list format.
     * @cfg {Number} [duration=3000] The time in milliseconds the message will be displayed.
     * @cfg {HTMLElement|PMUI.ui.Element} [appendTo=document.body] The html element/PMUI's object the flash message
     * will be displayed.
     * @cfg {String} [display="absolute"] The display mode for the object's html.
     * @cfg {String} [position="absolute"] The position mode for the object's html.
     * @cfg {String} [severity="info"] The severity for the message. Valid values: 'info', 'success', 'error'.
     */
    var FlashMessage = function (settings) {
        FlashMessage.superclass.call(this, settings);
        /**
         * The object's message. Set by the {@link #cfg-message message} config option and the
         * {@link #method-setMessage setMessage()} method.
         * @type {String|Array}
         * @readonly
         */
        this.message = null;
        /**
         * The duration in milliseconds to show the message. Set by the {@link #cfg-duration duration} config option
         * and the {@link #method-setDuration setDuration()} method.
         * @type {Number}
         * @readonly
         */
        this.duration = null;
        /**
         * The html element/PMUI's object the message will be displayed in. Set by the {@link #cfg-appendTo appendTo}
         * config option and the {@link #method-setAppendTo setAppendTo()} method.
         * @type {HTMLElement|PMUI.core.Element}
         */
        this.appendTo = null;
        /**
         * The message's severity. Set by the {@link #cfg-severity severity} config option and the
         * {@link #method-setSeverity setSeverity()} method.
         * @type {String}
         * @readonly
         */
        this.severity = null;
        FlashMessage.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Element', FlashMessage);
    /**
     * The object's type.
     * @type {String}
     */
    FlashMessage.prototype.type = 'FlashMessage';
    /**
     * The initializer method.
     * @param  {Object} [settings] The config options
     * @private
     */
    FlashMessage.prototype.init = function (settings) {
        var defaults = {
            message: "",
            duration: 3000,
            appendTo: document.body,
            display: 'inline-block',
            positionMode: 'fixed',
            severity: 'info'
        };

        jQuery.extend(true, defaults, settings);
        this.setMessage(defaults.message)
            .setDuration(defaults.duration)
            .setAppendTo(defaults.appendTo)
            .setPositionMode(defaults.positionMode)
            .setDisplay(defaults.display)
            .setSeverity(defaults.severity);
    };
    /**
     * Sets the severity for the message.
     * @param {String} severity Valid values: "info", "success" or "error".
     * @chainable
     */
    FlashMessage.prototype.setSeverity = function (severity) {
        if (!(severity === 'success' || severity === 'error' || severity === 'info')) {
            throw new Error('setSeverity(): the parameter must be "success" or "error" or "info"');
        }
        this.severity = severity;
        this.style.removeClasses(['pmui-info', 'pmui-error', 'pmui-success']).addClasses(['pmui-' + severity]);

        return this;
    };
    /**
     * Sets the html element/PMUI's object in which the message will be displayed.
     * @param {HTMLElement|PMUI.core.Element} appendTo
     * @chainable
     */
    FlashMessage.prototype.setAppendTo = function (appendTo) {
        if (!(PMUI.isHTMLElement(appendTo) || appendTo instanceof PMUI.core.Element)) {
            throw new Error("setAppendTo(): The parameter must be a HTML element or an instance of PMUI.ui.Element.");
        }
        this.appendTo = appendTo;
        return this;
    };
    /**
     * Sets the duration for display the message.
     * @param {Number} duration The duration in milliseconds.
     * @chainable
     */
    FlashMessage.prototype.setDuration = function (duration) {
        if (typeof duration !== 'number') {
            throw new Error('setDuration(): The parameter must be a number.');
        }
        this.duration = duration;
        return this;
    };
    /**
     * Sets the message for the object.
     * @param {String|Array} message It can be a string or an array of strings, in the latter case the message will be
     * showed in a list format.
     * @chainable
     */
    FlashMessage.prototype.setMessage = function (message) {
        var ul,
            li,
            i;

        if (typeof message !== 'string' && !jQuery.isArray(message)) {
            throw new Error('setMessage(): The parameter must be a message.');
        }
        this.message = (typeof message === 'string') ? jQuery.trim(message) : message;
        if (this.html) {
            jQuery(this.html).empty();
            if (typeof message === 'string') {
                this.html.textContent = message;
            } else {
                ul = PMUI.createHTMLElement('ul');
                ul.className = 'pmui-flashmessage-list';
                ul.style.listStyleType = 'none';
                for (i = 0; i < message.length; i += 1) {
                    li = PMUI.createHTMLElement('li');
                    li.textContent = message[i];
                    ul.appendChild(li);
                }
                this.html.appendChild(ul);
            }

        }
        return this;
    };
    /**
     * Shows the message in the html element/PMUI's object specified by the {@link #cfg-appendTo appendTo} config
     * option or the {@link #method-setAppendTo setAppendTo()} method.
     * @chainable
     */
    FlashMessage.prototype.show = function () {
        var targetHTML = this.appendTo,
            html = this.html,
            top = 50,
            w,
            pw;

        if (!PMUI.isHTMLElement(targetHTML)) {
            targetHTML = targetHTML.html;
        }
        if (targetHTML) {
            if (!html) {
                html = this.getHTML();
            }
            jQuery(html).fadeTo(1, 0).get(0).style.top = top + "px";
            document.body.appendChild(html);
            w = jQuery(html).outerWidth();
            targetHTML.appendChild(html);
            this.style.addProperties({
                left: '50%',
                'margin-left': w / -2
            });
            jQuery(html).finish().css({
                'top': '50px'
            }).fadeTo(1, 0).animate({
                top: "-=" + top,
                opacity: 1
            }, 400, 'swing').delay(this.duration).animate({
                top: "+=" + top,
                opacity: 0,
                zIndex: '0'
            });
        }
        return this;
    };
    /**
     * @inheritDoc
     */
    FlashMessage.prototype.createHTML = function () {
        FlashMessage.superclass.prototype.createHTML.call(this);
        this.setMessage(this.message);
        return this.html;
    };

    PMUI.extendNamespace('PMUI.ui.FlashMessage', FlashMessage);

    if (typeof exports !== 'undefined') {
        module.exports = FlashMessage;
    }
}());
(function () {
    var Menu = function (settings) {
        settings = jQuery.extend(true, settings, {
            elementTag: 'ul',
            positionMode: 'absolute'
        });
        Menu.superclass.call(this, settings);
        this.targetElement = null;
        this.parent = null;
        this.onShow = null;
        this.onHide = null;
        this.displayed = null;
        this.factory = new PMUI.menu.MenuItemFactory();
        this.items = new PMUI.util.ArrayList();
        this.onOutsideClickHandler = null;
        Menu.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Element', Menu);

    Menu.prototype.type = 'Menu';

    Menu.prototype.init = function (settings) {
        var defaults = {
            targetElement: null,
            parent: null,
            onShow: null,
            onHide: null,
            items: []
        };

        jQuery.extend(true, defaults, settings);

        this.onShow = defaults.onShow;
        this.onHide = defaults.onHide;
        this.setParent(defaults.parent)
            .setItems(defaults.items)
            .setTargetElement(defaults.targetElement)
            .setOutsideClickHandler();
    };

    Menu.prototype.setOutsideClickHandler = function () {
        var that = this;

        this.onOutsideClickHandler = function (e) {
            if (!jQuery(e.currentTarget).parents('#' + that.id).length) {
                that.hide();
            }
        };
        return this;
    };

    Menu.prototype.setTargetElement = function (target) {
        var rootMenu,
            targetElement = (this.targetElement && this.targetElement.html) || document.body;

        $(targetElement).removeClass('pmui-contextmenu-target');
        if (target instanceof PMUI.core.Element || null) {
            rootMenu = this.getRootMenu();
            if (rootMenu.targetElement) {
                if (rootMenu.targetElement.menu === rootMenu) {
                    rootMenu.targetElement.menu = null;
                }
            }
            rootMenu.targetElement = target;
        }
        return this;
    };

    Menu.prototype.getTargetElement = function () {
        var rootMenu = this.getRootMenu();
        return rootMenu.targetElement;
    };

    Menu.prototype.addItem = function (item) {
        var newItem = this.factory.make(item);

        if (newItem) {
            newItem.setParent(this);
            this.items.insert(newItem);
            if (this.html) {
                this.html.appendChild(newItem.getHTML());
            }
        }
        return this;
    };

    Menu.prototype.removeItem = function (item) {
        var itemToRemove;

        if (typeof item === 'string') {
            itemToRemove = this.items.find('id', item);
        } else if (typeof item === 'number') {
            itemToRemove = this.items.get(item);
        } else if (item instanceof PMUI.item.MenuItem && this.items.contains(item)) {
            itemToRemove = item;
        }
        if (itemToRemove) {
            this.items.remove(itemToRemove);
            jQuery(itemToRemove.html).detach();
        }
        return this;
    };

    Menu.prototype.clearItems = function () {
        var index = 0;
        while (this.items.getSize()) {
            this.removeItem(index);
        }
        return this;
    };

    Menu.prototype.setItems = function (items) {
        var i;

        if (!jQuery.isArray(items)) {
            throw new Error('setItems(): The parameter must be an array.');
        }
        this.clearItems();
        for (i = 0; i < items.length; i += 1) {
            this.addItem(items[i]);
        }
        return this;
    };

    Menu.prototype.getItems = function () {
        var index = 0;
        return this.items.asArray().slice(index);
    };

    Menu.prototype.setParent = function (parent) {
        if (!(parent === null || parent instanceof PMUI.menu.MenuItem)) {
            throw new Error('setParent(): The parameter must be an instance of PMUI.item.MenuItem or null.');
        }
        this.parent = parent;
        return this;
    };

    Menu.prototype.defineEventListeners = function () {
        this.removeEvents();
        this.addEvent('contextmenu').listen(this.html, function (e) {
            e.stopPropagation();
            e.preventDefault();
        });
        this.addEvent('click').listen(this.html, function (e) {
            e.stopPropagation();
        });
        this.addEvent('mousedown').listen(this.html, function (e) {
            e.stopPropagation();
        });

        return this;
    };

    Menu.prototype.createHTML = function () {
        if (this.html) {
            return this.html;
        }

        Menu.superclass.prototype.createHTML.call(this);

        this.setItems(this.items.asArray().slice(0));

        this.defineEventListeners();

        return this.html;
    };

    Menu.prototype.isRoot = function () {
        return !this.parent;
    };

    Menu.prototype.getRootMenu = function () {
        if (this.isRoot()) {
            return this;
        } else {
            return this.parent.parent.getRootMenu();
        }
    };

    Menu.prototype.show = function (x, y) {
        var rootMenu = this.getRootMenu(),
            targetElement = (this.targetElement && this.targetElement.html) || document.body,
            zIndex = parseInt(targetElement.style.zIndex);
        x = x || 0;
        y = y || 0;
        rootMenu.setPosition({
            x: x,
            y: y
        });
        PMUI.removeCurrentMenu();
        rootMenu.setZOrder(zIndex + 1);
        $(targetElement).addClass('pmui-contextmenu-target');
        document.body.appendChild(rootMenu.getHTML());
        this.addEvent('mousedown', 'clickOutside').listen(document, this.onOutsideClickHandler);
        this.displayed = true;
        PMUI.currentContextMenu = this;
        if (typeof this.onShow === 'function') {
            this.onShow(this);
        }
        if (document.documentElement.clientHeight - y < jQuery(this.html).outerHeight()) {
            this.html.style.top = y - jQuery(this.html).outerHeight() + 'px'
        }
        return this;
    };

    Menu.prototype.hide = function () {
        var rootMenu = this.getRootMenu(),
            targetElement = (this.targetElement && this.targetElement.html) || document.body;

        this.removeEvent('clickOutside');
        $(targetElement).removeClass('pmui-contextmenu-target');
        jQuery(rootMenu.html).detach();
        rootMenu.displayed = false;
        PMUI.currentContextMenu = null;
        if (typeof rootMenu.onHide === 'function') {
            rootMenu.onHide(rootMenu);
        }
        return this;
    };

    Menu.prototype.setContextMenu = function () {
        return this;
    };

    PMUI.extendNamespace('PMUI.menu.Menu', Menu);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = Menu;
    }
}());
(function () {
    var MenuItem = function (settings) {
        MenuItem.superclass.call(this, settings);
        this.dom = {};
        this.parent = null;
        MenuItem.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Element', MenuItem);

    MenuItem.prototype.type = 'MenuItem';

    MenuItem.prototype.init = function (settings) {
        var defaults = {
            parent: null
        }

        jQuery.extend(true, defaults, settings);

        this.setParent(defaults.parent);
    };

    MenuItem.prototype.setParent = function (parent) {
        if (!(parent === null || parent instanceof PMUI.menu.Menu)) {
            throw new Error('setParent(): The parameter must be an instance of PMUI.ui.Menu or null.');
        }
        this.parent = parent;
        return this;
    };

    MenuItem.prototype.getParent = function () {
        return this.parent;
    };

    MenuItem.prototype.getRootMenu = function () {
        var parent = this.parent;

        if (this.parent) {
            return this.parent.getRootMenu();
        }
        return parent;
    };

    MenuItem.prototype.isLeaf = function () {
        throw new Error("isLeaf() is being called from an abstract class.");
    };

    MenuItem.prototype.getMenu = function () {
        return this.parent;
    };

    MenuItem.prototype.setContextMenu = function () {
        return this;
    };

    MenuItem.prototype.getMenuTargetElement = function () {
        var rootMenu = this.getRootMenu();

        if (rootMenu) {
            return rootMenu.getTargetElement();
        }
        return rootMenu
    };

    MenuItem.prototype.createHTML = function () {
        if (this.html) {
            return this.html;
        }
        MenuItem.superclass.prototype.createHTML.call(this);
        return this.html;
    };

    PMUI.extendNamespace('PMUI.menu.MenuItem', MenuItem);

}());
(function () {
    var MenuOption = function (settings) {
        MenuOption.superclass.call(this, settings);
        this.onClick = null;
        this.text = null;
        this.hideOnClick = null;
        this.disabled = null;
        MenuOption.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.menu.MenuItem', MenuOption);

    MenuOption.prototype.type = 'MenuOption';

    MenuOption.prototype.init = function (settings) {
        var defaults = {
            onClick: null,
            elementTag: 'li',
            text: '[option]',
            hideOnClick: true,
            disabled: false
        };

        jQuery.extend(true, defaults, settings);
        this.setElementTag(defaults.elementTag)
            .setText(defaults.text)
            .setOnClickHandler(defaults.onClick)
            .hideOnClick = !!defaults.hideOnClick;

        if (defaults.disabled) {
            this.disable();
        } else {
            this.enable();
        }
    };

    MenuOption.prototype.enable = function () {
        this.disabled = false;
        this.style.removeClasses(['pmui-disabled']);
        return this;
    };

    MenuOption.prototype.disable = function () {
        this.disabled = true;
        this.style.addClasses(['pmui-disabled']);
        return this;
    };

    MenuOption.prototype.setText = function (text) {
        if (typeof text !== 'string') {
            throw new Error('setText(): the parameter must be a srting.');
        }
        this.text = text;
        if (this.dom.textContainer) {
            this.dom.textContainer.textContent = text;
        }
        return this;
    };

    MenuOption.prototype.setOnClickHandler = function (onClick) {
        if (!(onClick === null || typeof onClick === 'function')) {
            throw new Error('setOnClickHandler(): The parameter must be a function or null.');
        }
        this.onClick = onClick;
        return this;
    };

    MenuOption.prototype.onClickHandler = function () {
        var that = this;

        return function (e) {
            e.preventDefault();
            e.stopPropagation();
            if (!that.disabled) {
                if (typeof that.onClick === 'function') {
                    that.onClick(that);
                }
                if (that.hideOnClick) {
                    that.parent.hide();
                }
            }
        };
    };

    MenuOption.prototype.remove = function () {
        this.parent.removeItem(this);
        return this;
    };

    MenuOption.prototype.defineEventListeners = function () {
        this.removeEvents();
        this.addEvent('click').listen(this.dom.title, this.onClickHandler());
        return this;
    };

    MenuOption.prototype.createHTML = function () {
        var link,
            textContainer,
            iconContainer;

        if (this.html) {
            return this.html;
        }
        MenuOption.superclass.prototype.createHTML.call(this);

        link = PMUI.createHTMLElement('a');
        link.href = '#';
        link.className = 'pmui-menuoption-title';

        textContainer = PMUI.createHTMLElement('span');
        textContainer.className = 'pmui-menuoption-text';

        iconContainer = PMUI.createHTMLElement('i');
        iconContainer.className = 'pmui-menuoption-text-icon';

        this.dom.title = link;
        this.dom.textContainer = textContainer;
        this.dom.iconContainer = iconContainer;

        link.appendChild(iconContainer);
        link.appendChild(textContainer);
        this.html.appendChild(link);

        this.setText(this.text);

        this.defineEventListeners();

        return this.html;
    };

    PMUI.extendNamespace('PMUI.menu.MenuOption', MenuOption);
}());
(function () {
    var MenuRegularOption = function (settings) {
        MenuRegularOption.superclass.call(this, settings);
        this.childMenu = null;
        MenuRegularOption.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.menu.MenuOption', MenuRegularOption);

    MenuRegularOption.prototype.init = function (settings) {
        var defaults = {
            items: []
        };

        jQuery.extend(true, defaults, settings);

        this.childMenu = new PMUI.menu.Menu({
            positionMode: 'absolute',
            parent: this
        });

        this.setItems(defaults.items);
    };

    MenuRegularOption.prototype.setItems = function (items) {
        this.childMenu.setItems(items);
        if (this.childMenu.getItems().length) {
            this.style.addClasses(['pmui-father']);
        }
        return this;
    };

    MenuRegularOption.prototype.getItems = function () {
        return this.childMenu.getItems();
    };

    MenuRegularOption.prototype.onClickHandler = function () {
        var that = this;
        return function (e) {
            e.preventDefault();
            e.stopPropagation();
            if (!that.disabled) {
                if (typeof that.onClick === 'function') {
                    that.onClick(that);
                }
                if (that.hideOnClick && that.childMenu.getItems().length === 0) {
                    that.parent.hide();
                }
            }
        };
    };

    MenuRegularOption.prototype.createHTML = function () {
        if (this.html) {
            return this.html;
        }
        MenuRegularOption.superclass.prototype.createHTML.call(this);
        this.html.appendChild(this.childMenu.getHTML());
        this.setItems(this.getItems());
        return this.html;
    };

    PMUI.extendNamespace('PMUI.menu.MenuRegularOption', MenuRegularOption);
}());
(function () {
    var MenuSeparatorItem = function (settings) {
        MenuSeparatorItem.superclass.call(this, jQuery.extend(true, settings, {
            elementTag: 'div'
        }));
        MenuSeparatorItem.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.menu.MenuItem', MenuSeparatorItem);

    MenuSeparatorItem.prototype.type = 'MenuSeparatorItem';

    MenuSeparatorItem.prototype.isLeaf = function () {
        return true;
    };

    PMUI.extendNamespace('PMUI.menu.MenuSeparatorItem', MenuSeparatorItem);
}());
(function () {
    var MenuItemFactory = function (settings) {
        MenuItemFactory.superclass.call(this, settings);
        MenuItemFactory.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.util.Factory', MenuItemFactory);

    MenuItemFactory.prototype.type = "MenuItemFactory";

    MenuItemFactory.prototype.init = function (settings) {
        var defaults = {
            products: {
                "menuRegularOption": PMUI.menu.MenuRegularOption,
                "menuSeparatorItem": PMUI.menu.MenuSeparatorItem
            },
            defaultProduct: "menuRegularOption"
        };

        this.setDefaultProduct(defaults.defaultProduct)
            .setProducts(defaults.products);
    };

    PMUI.extendNamespace('PMUI.menu.MenuItemFactory', MenuItemFactory);
}());
(function () {
    /**
     * @class PMUI.form.Field
     * Abstract class that encapsulates the field behavior
     * @extends PMUI.core.Element
     * @abstract
     *
     * @constructor
     * While it is true that this class must not be instantiated,
     * it is useful to mention the settings parameter for the constructor function
     * (which will be used for the non abstract subclasses).
     * @param {Object} [settings] A JSON object, it can be contain the following fields:
     *
     * - {@link PMUI.form.Field#cfg-name name}.
     * - {@link PMUI.form.Field#cfg-label label}.
     * - {@link PMUI.form.Field#cfg-value value}.
     * - {@link PMUI.form.Field#cfg-helper helper}.
     * - {@link PMUI.form.Field#cfg-showHelper showHelper}.
     * - {@link PMUI.form.Field#cfg-validators validators}.
     * - {@link PMUI.form.Field#cfg-valueType valueType}.
     * - {@link PMUI.form.Field#cfg-controlPositioning controlPositioning}.
     * - {@link PMUI.form.Field#cfg-labelWidth labelWidth}.
     * - {@link PMUI.form.Field#cfg-showColon showColon}
     *
     * @cfg {String} name The name for the field.
     * @cfg {String} label The text to be shown as the field's label
     * @cfg {String} value The initial value to be set to the control.
     * @cfg {String} helper The helper text to be shown in the helper tooltip
     * @cfg {Boolean} showHelper A boolean value which determines if the helper tootltip will be shown or not.
     * @cfg {Object} validators An array where each array's item is a JSON object (with the validator setting data)
     *  or a {@link PMUI.form.Validator Validator} object.
     *      {
     *          validators: [
     *              {
     *                  type: "maxlength",
     *                  criteria: 5
     *              },
     *              new LengthValidator({
     *                  min: 0,
     *                  max: 5
     *              })
     *          ]
     *      }
     * In example above, "validators" is an array in which their first element is an JSON object
     * and the second one is a {@link PMUI.form.Validator Validator} object.
     * @cfg {String} valueType A string which specifies the data type for the Field value.
     * @cfg {String} controlPositioning A formatted string that specifies the order output for the control(s).
     * A string  which specifies the output order for the field's controls.
     * Basically this string uses a wildcard with the format "[cx]", each one is replaced by the control:
     *      "[c0]-[c1]-[c2]"
     * If you apply the string above to the controlPositioning property, it will render
     * the first three field's controls, each one separated from the other by a "-".
     *
     * Another wildcard is [c*], this represents all the controls or the ones that haven't been included yet:
     *      "[c*]-"
     * The example above will render all the controls and at the end it will add a "-".
     *
     *      "[c2]-[c*]"
     * The example above will render first the second control and then the other ones (starting from the first one).
     * @cfg {String} labelWidth The width for the label.
     * The width label should be a String with the following format "X%"
     *
     * Note that this% should not exceed 80%, because 20% is the tooltip
     * @cfg {Boolean} showColon If a colon is shown after the label text.
     * @cfg {Boolean} [labelVisible=true] If the label is visible or not.
     * @cfg {String} [labelPosition="left"] Sets the position for the label, the value can take one of the following
     * options:
     *
     * - "left" (default).
     * - "right".
     * - "top".
     * - "bottom"
     *
     * @cfg {String|Number|Array} [controlsWidth] Determines the width for the field's controls, but the width of the
     * field must be a number or a string with the format "#px", otherwise the width for the controls will be set to
     * "auto".It can be:
     *
     * - A String, in this case the string must have the following format "##px", otherwise must be "auto".
     * - A Number, in this case the value received is parsed into a px units value.
     * - An Array in which each element can be a String or a Number (following the rules above).
     *
     * This will be applied respectively to each control in the field.
     * @cfg {Function} [onClick=null] Description here.
     * @cfg {Function|null} [onBeforeChange] The callback function to be executed before the field's value changes. To
     * avoid the change the callback function must return false. For info about the parameters sent to the calback
     * function please read the {@link #event-onBeforeChange onBeforeChange event} documentation.
     */
    var Field = function (settings) {
        Field.superclass.call(this, settings);
        /**
         * @property {String} [name=[The object's id]] The field's name.
         * @readonly
         */
        this.name = null;
        /**
         * @property {String} [label = "[field]"] The field's label text.
         * @readonly
         */
        this.label = null;
        /**
         * @property {String} [value=""] The field's value.
         * @readonly
         */
        this.value = null;
        /**
         * @property {PMUI.ui.TooltipMessage} [helper] The field's help tooltip
         * @readonly
         */
        this.helper = null;
        /**
         * @property {PMUI.ui.TooltipMessage} [message] A {@link PMUI.ui.TooltipMessage TooltipMessage}
         * object to show a message related to the field (i.e. validation error messages)
         * @private
         */
        this.message = null;
        /**
         * @property {Array} controls An array, it will contain all the necessary Control objects
         * @private
         */
        this.controls = [];
        /**
         * @property {Object} validators An JSON object which will contain all the
         * {@link PMUI.form.Validator Validators} object.
         * @private
         */
        this.validators = {};
        /**
         * @property {String} [controlPositioning="[c*]"] A formatted string that specifies
         * the order output for the field's controls.
         * @readonly
         */
        this.controlPositioning = null;
        /**
         * @property {Object} [dom] A JSON object which will contain important DOM object
         * for the Field object.
         * @private
         */
        this.dom = null;
        /**
         * @property {Boolean} [helperIsVisible=false] A Boolean that let us
         * know if the help tooltip will be shown or not.
         * @readonly
         */
        this.helperIsVisible = null;
        /**
         * @property {String} [labelWidth="30%"]
         * The width for the label. This property should be a String with the following format "X%"
         * @readonly
         */
        this.labelWidth = null;
        /**
         * @property {Boolean} [visibleColon=true]
         * If a colon is shown after the label text.
         * @readonly
         */
        this.visibleColon = null;
        /**
         * @property {Boolean} labelVisible
         * If the label is visible or not.
         * @readonly
         */
        this.labelVisible = null;
        /**
         * @property {String} [labelPosition="String"] The position for the field's label.
         * @readonly
         */
        this.labelPosition = null;
        /**
         * @event onBeforeChange
         * Fired before the field's value changes.
         * @param {String} newValue The new value to be set.
         * @param {String} oldValue The old field's value.
         */
        this.onBeforeChange = null;
        /**
         * @event onChange
         * Fired when the field's value changes.
         * @param {String} newValue The field's new value.
         * @param {String} oldValue The previous field's value.
         */
        this.onChange = null;
        /**
         * @property {String} valueType The value data type for the field.
         */
        this.valueType = null;
        /**
         * @property {PMUI.form.ValidatorFactory} validatorFactory The factory object for validator production.
         */
        this.validatorFactory = null;
        /**
         * @property {Boolean} [required] If the field is required or not
         */
        this.required = null;
        /**
         * @property {String} [requiredMessage="This field is required."]
         * The message to display when the validation for required property fails.
         */
        this.requiredMessage = null;
        /**
         * @property {Boolean} [validAtChange=true]
         * If the validation must be executed every time the field's value changes.
         */
        this.validAtChange = null;
        /**
         * The width for the control(s)
         * @type {Number|String|Array}
         * @readonly
         */
        this.controlsWidth = [];
        /**
         * @property {Number|String} controlsContainerWidth
         * @type {Number|String}
         */
        this.controlsContainerWidth = null;
        /**
         * @property {PMUI.form.Form} form
         * The form the field belongs to.
         */
        this.form = null;
        /**
         * @property {Boolean} eventsDefined
         * If the events for the object have been defined.
         */
        this.eventsDefined = null;
        /**
         * @property {PMUI.data.DataSet} data
         * The field's data object.
         * @private
         */
        this.data = null;
        /**
         * The initial value the field will be set when its reset() method is called.
         * @type {String}
         * @private
         */
        this.initialValue = null;
        /**
         * [onClick description]
         * @type {[type]}
         */
        this.onClick = null;
        /**
         * If the field is disabled or not. Notice that if it is disabled the validation won't be executed.
         * @type {Boolean}
         */
        this.disabled = null;

        /**
         * @protected
         * Valid Types
         * @type {Array}
         */
        this.dependentFields = [];
        this.dependencyHandler = null;

        this.validTypes = {
            'string': true,
            'number': true,
            'boolean': true,
            'date': true,
            'object': true,
            'integer': true,
            'float': true
        };
        Field.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Element', Field);

    Field.prototype.type = "Field";

    Field.prototype.init = function (settings) {
        var defaults = {
            name: this.id,
            label: '[field]',
            value: '',
            helper: '',
            showHelper: !!(settings && settings.helper),
            validators: [],
            valueType: 'string',
            controlPositioning: '[c*]',
            labelWidth: '23.5%',
            width: '100%',
            showColon: true,
            validatorFactory: new PMUI.form.ValidatorFactory(),
            onBeforeChange: null,
            onChange: null,
            required: false,
            validAtChange: true,
            requiredMessage: 'This field is required.'.translate(),
            labelVisible: true,
            labelPosition: 'left',
            form: null,
            controlsWidth: "auto",
            controlsContainerWidth: "70%",
            disabled: false,
            dependentFields: [],
            dependencyHandler: function () {
            },
            onClick: null
        };

        this.dependentFields = [];
        this.dependencyFields = new PMUI.util.ArrayList();
        $.extend(true, defaults, settings);

        this.initialValue = defaults.value;

        this.helper = new PMUI.ui.TooltipMessage({
            category: 'help'
        });

        this.dom = {};
        this.data = new PMUI.data.DataField();

        this.message = new PMUI.ui.TextLabel({
            text: 'This field is required.'.translate(),
            displayMode: 'block',
            mode: 'normal',
            visible: false
        });

        this.setForm(defaults.form)
            .setValidAtChange(defaults.validAtChange)
            .setRequired(defaults.required)
            .setRequiredMessage(defaults.requiredMessage)
            .setValidatorFactory(defaults.validatorFactory)
            .setValidators(defaults.validators)
            .setName(defaults.name)
            .setLabel(defaults.label)
            .setValue(defaults.value)
            .setHelper(defaults.helper)
            .setValueType(defaults.valueType)
            .setControlPositioning(defaults.controlPositioning)
            .setOnBeforeChangeHandler(defaults.onBeforeChange)
            .setOnChangeHandler(defaults.onChange)
            .setWidth(defaults.width)
            .setLabelWidth(defaults.labelWidth)
            .setLabelPosition(defaults.labelPosition)
            .setControlsWidth(defaults.controlsWidth)
            .setControlsContainerWidth(defaults.controlsContainerWidth)
            .setOnClickHandler(defaults.onClick)
            .setControls();
        this.setDependencyHandler(defaults.dependencyHandler);
        this.setDependentFields(defaults.dependentFields);

        if (defaults.showHelper) {
            this.showHelper();
        } else {
            this.hideHelper();
        }
        if (defaults.showColon) {
            this.showColon();
        } else {
            this.hideColon();
        }
        if (defaults.labelVisible) {
            this.showLabel();
        } else {
            this.hideLabel();
        }
        if (defaults.disabled) {
            this.disable();
        } else {
            this.enable();
        }
    };
    Field.prototype.onBeforeChangeHandler = function (handler) {
        var that = this;
        return function (newValue, oldValue) {
            var callbackResponse,
                futureValue = "",
                i;

            if (typeof that.onBeforeChange === 'function') {
                for (i = 0; i < that.controls.length; i += 1) {
                    if (that.controls[i] === this) {
                        futureValue += ' ' + newValue;
                    } else {
                        futureValue += ' ' + that.controls[i].getValue();
                    }
                }
                futureValue = futureValue.substr(1);
                callbackResponse = that.onBeforeChange(futureValue, that.getValue());
            }
            return callbackResponse;
        };
    };
    Field.prototype.setOnBeforeChangeHandler = function (handler) {
        if (!(handler === null || typeof handler === 'function')) {
            throw new Error("setOnBeforeChangeHandler(): The parameter must be a function or null.");
        }
        this.onBeforeChange = handler;
        return this;
    };
    Field.prototype.setOnClickHandler = function (handler) {
        if (!(handler === null || typeof handler === 'function')) {
            throw new Error("setOnClickHandler(): The parameter must be a function or null.");
        }
        this.onClick = handler;
        return this;
    };
    Field.prototype.setDependentFields = function (dependentFields) {
        if (!jQuery.isArray(dependentFields)) {
            throw new Error("setDependentFields(): The parameter must be an array.");
        }
        this.dependentFields = dependentFields;
        if (this.form) {
            this.form.updateDependencies();
        }
        return this;
    };

    Field.prototype.removeDependentField = function (FieldName) {
        var i;

        for (i = 0; this.dependentFields.length; i += 1) {
            if (this.dependentFields[i] === FieldName) {
                this.dependentFields.splice(i, 1);
                i--;
            }
        }
        return this;
    };

    Field.prototype.setDependencyHandler = function (handler) {
        if (typeof handler == 'function' || handler == null) {
            this.dependencyHandler = handler;
        }
        return this;
    };

    /**
     * Disables the field. Notice that when a field is disabled it is not validated and it is not returned when its
     * form's getData() method is invoked.
     * @chainable
     */
    Field.prototype.disable = function () {
        var i;

        this.disabled = true;
        this.style.addClasses(["pmui-disabled"]);
        for (i = 0; i < this.controls.length; i += 1) {
            this.controls[i].disable(true);
        }

        return this;
    };
    /**
     * Enables the field.
     * @chainable
     */
    Field.prototype.enable = function () {
        var i;

        this.disabled = false;
        this.style.removeClasses(["pmui-disabled"]);
        for (i = 0; i < this.controls.length; i += 1) {
            this.controls[i].disable(false);
        }

        return this;
    };
    /**
     * Resets the field to its original value.
     * @chainable
     */
    Field.prototype.reset = function () {
        this.setValue(this.initialValue);
        this.hideMessage();
        if (this.eventsDefined) {
            this.onChangeHandler();
        }
        return this;
    };
    /**
     * Determines the width for the field's controls, but the width of the field must be a number or a string with
     * the format "#px", otherwise the width for the controls will be set to "auto".
     * @param {Number|String|Array} width  It can be:
     *
     * - A String, in this case the string must have the following format "##px", otherwise must be "auto".
     * - A Number, in this case the value received is parsed into a px units value.
     * - An Array in which each element can be a String or a Number (following the rules above).
     *
     * This will be applied respectively to each control in the field.
     * @chainable
     */
    Field.prototype.setControlsWidth = function (width) {
        var i,
            labelWidthPx,
            auxWidth,
            autoGerateWidthForControls = false,
            fieldWidthIsAbsolute = false;

        if (!(width === 'auto' || typeof width === 'number' || jQuery.isArray(width) || /^\d+(\.\d+)?px/.test(width))) {
            throw new Error("setControlsWidth(): The parameter must be a string \"auto\", or a number or an array.");
        }

        this.controlsWidth = width;

        if (width === 'auto') {
            autoGerateWidthForControls = true;
        }

        if (typeof this.width === 'number' || /^\d+(\.\d+)?px/.test(this.width)) {
            fieldWidthIsAbsolute = true;
        }

        if (!autoGerateWidthForControls) {
            for (i = 0; i < this.controls.length; i += 1) {
                if (jQuery.isArray(width)) {
                    this.controls[i].setWidth(width[i] || "auto");
                } else {
                    this.controls[i].setWidth(width);
                }
            }
        } else if (fieldWidthIsAbsolute) {
            auxWidth = parseInt(this.width, 10);
            labelWidthPx = auxWidth * (parseInt(this.labelWidth, 10) / 100) + 4;
            labelWidthPx = auxWidth - 83 - labelWidthPx;
            for (i = 0; i < this.controls.length; i += 1) {
                this.controls[i].setWidth(labelWidthPx);
            }
        } else {
            for (i = 0; i < this.controls.length; i += 1) {
                this.controls[i].setWidth("auto");
            }
        }

        return this;
    };
    /**
     * Sets the width for the controls container.
     * @param width
     */
    Field.prototype.setControlsContainerWidth = function (width) {
        if (!(typeof width === 'number' || jQuery.isArray(width) || /^\d+(\.\d+)?(px|\%)/.test(width))) {
            throw new Error("setControlsContainerWidth(): The parameter must be a string (with a numeric or percentage value), or a number or an array.");
        }
        this.controlsContainerWidth = width;
        if (this.dom && this.dom.controlContainer) {
            $(this.dom.controlContainer).css('width', width);
        }
        return this;
    };
    /**
     * Sets the form the field belongs to.
     * @param {PMUI.form.Form} form
     * @chainable
     */
    Field.prototype.setForm = function (form) {
        if (form instanceof PMUI.form.Form) {
            this.form = form;
        }

        return this;
    };
    /**
     * Turns on/off the validation when the field's value changes.
     * @param {Boolean} validAtChange
     * @chainable
     */
    Field.prototype.setValidAtChange = function (validAtChange) {
        this.validAtChange = !!validAtChange;
        return this;
    };
    /**
     * Sets the position for the label.
     * @param {String} position It can take one of the following values: "top", "right", "bottom", "left".
     * @chainable
     */
    Field.prototype.setLabelPosition = function (position) {
        if (position === 'top' || position === 'right' || position === 'bottom' || position === 'left') {
            this.labelPosition = position;
            if (this.html) {
                if (position === 'top' || position === 'left') {
                    this.html.insertBefore(this.dom.labelTextContainer, this.dom.controlContainer);
                    this.dom.labelTextContainer.style.display = position === 'top' ? 'block' : 'inline-block';
                    $(this.dom.controlContainer).css("float", '');
                } else {
                    this.html.insertBefore(this.dom.controlContainer, this.dom.labelTextContainer);
                    this.dom.labelTextContainer.style.display = position === 'bottom' ? 'block' : 'inline-block';
                    $(this.dom.labelTextContainer).css("float", '');
                }
            }
        } else {
            throw new Error('setLabelPosition(): it only accepts "top", "rigth", "left" or "bottom" as value for ' +
                'the parameter');
        }

        return this;
    };
    /**
     * Sets the factory which must be produce {@link PMUI.form.Validator Validator} objects.
     * @param {PMUI.util.Factory|Object} factory
     * It can be a:
     *
     * - a {@link PMUI.util.Factory} object
     * - a JSON object: in this case a new {@link PMUI.form.ValidatorFactory ValidatorFactory} will be created
     * using the JSON object as the constructor parameter.
     * @private
     * @chainable
     */
    Field.prototype.setValidatorFactory = function (factory) {
        if (factory instanceof PMUI.util.Factory) {
            this.validatorFactory = factory;
        } else {
            this.validatorFactory = new PMUI.form.ValidatorFactory(factory);
        }

        return this;
    };
    /**
     * Sets the message to show when the required field validation fails.
     * @param {String} requiredMessage
     * @chainable
     */
    Field.prototype.setRequiredMessage = function (requiredMessage) {
        if (typeof requiredMessage === 'string') {
            this.requiredMessage = requiredMessage;
        } else {
            throw new Error("the setRequiredMessage() method only accepts string values.");
        }

        return this;
    };
    /**
     * Sets if the fields is required or not.
     * @param {Boolean} required
     * @chainable
     */
    Field.prototype.setRequired = function (required) {
        this.required = !!required;
        if (this.dom.fieldRequired) {
            if (this.required) {
                this.showRequired();
            } else {
                this.hideRequired();
            }
        }
        return this;
    };

    Field.prototype.hideRequired = function () {
        this.dom.fieldRequired.style.display = 'none';
        return this;
    };

    Field.prototype.showRequired = function () {
        this.dom.fieldRequired.style.display = 'inline-block';
        return this;
    };
    /**
     * Removes all the validators from the field.
     * @chainable
     */
    Field.prototype.clearValidators = function () {
        var key;

        for (key in this.validators) {
            if (this.validators.hasOwnProperty(key)) {
                this.validators[key] = null;
                delete this.validators[key];
            }
        }

        return this;
    };
    /**
     * Adds a validator to the field.
     * @param {String|PMUI.form.Validator|Object} validator
     * It can be:
     *
     * - a String: it must be on of the supported pmTypes by the
     * {@link PMUI.form.ValidatorFactory ValidatorFactory} class.
     * - a {@link PMUI.form.Validator Validator} object.
     * - a JSON object: in this case the JSON structure must be the same as the needed one to create
     * the desired validator, additionally must have the respective pmType.
     *
     * Note. All the supported pmTypes are specified in the
     * {@link PMUI.form.ValidatorFactory ValidatorFactory documentation}.
     * @chainable
     */
    Field.prototype.addValidator = function (validator) {
        var newValidator;

        if (this.validatorFactory) {
            if (this.validatorFactory.isValidClass(validator) || this.validatorFactory.isValidName(validator.pmType)) {
                newValidator = this.validatorFactory.make(validator);
            } else {
                throw new Error('Invalid validator to add.');
            }
        }

        if (newValidator && newValidator instanceof PMUI.form.Validator) {
            newValidator.setParent(this);
            this.validators[newValidator.type] = newValidator;
        }

        return this;
    };
    /**
     * Sets the validators for the field.
     * @param {Array} validators An array in which each element can be one of the accepted types in the
     * {@link PMUI.form.Field#addValidator addValidator() method}.
     * @chainable
     */
    Field.prototype.setValidators = function (validators) {
        var i = 0;

        if (jQuery.isArray(validators)) {
            this.clearValidators();
            for (i = 0; i < validators.length; i += 1) {
                this.addValidator(validators[i]);
            }
        }

        return this;
    };
    /**
     * Shows a colon after the label text.
     * @chainable
     */
    Field.prototype.showColon = function () {
        this.visibleColon = true;
        if (this.dom.fieldColon) {
            this.dom.fieldColon.style.display = '';
        }
    };
    /**
     * Hides the colon after the label text.
     * @chainable
     */
    Field.prototype.hideColon = function () {
        this.visibleColon = false;
        if (this.dom.fieldColon) {
            this.dom.fieldColon.style.display = "none";
        }
        return this;
    }
    /**
     * Sets the width for the label.
     * @param {String} width It can be a Number or a String.
     * This parameter should be a String with the following format "X%";
     * @chainable
     */
    Field.prototype.setLabelWidth = function (width) {
        if (/^\d+(\.\d+)?(px|em|%)$/.test(width)) {
            this.labelWidth = width;
            this.setControlsWidth(this.controlsWidth);
        } else {
            throw new Error('setLabelWidth(): invalid "width" parameter');
        }
        if (this.dom.labelTextContainer) {
            this.dom.labelTextContainer.style.width = this.labelWidth;
            if (this.labelVisible && this.labelPosition != "top")
                $(this.dom.messageContainer).css({"margin-left": parseInt(this.labelWidth) + 1 + "%"});
        }
        return this;
    };
    /**
     * Sets the callback function to be called when the field's value changes.
     * @param {Function} handler
     * @chainable
     */
    Field.prototype.setOnChangeHandler = function (handler) {
        if (typeof handler === 'function') {
            this.onChange = handler;
        }

        return this;
    };
    /**
     * Sets the helper tooltip visible.
     * @chainable
     */
    Field.prototype.showHelper = function () {
        this.helperIsVisible = true;
        this.helper.setVisible(true);
        return this;
    };
    /**
     * Sets the helper tooltip non visible.
     * @chainable
     */
    Field.prototype.hideHelper = function () {
        this.helperIsVisible = false;
        this.helper.setVisible(false);
        return this;
    };
    /**
     * Returns an array with all the field's controls.
     * @return {Array}
     */
    Field.prototype.getControls = function () {
        return this.controls;
    };
    /**
     * Returns an index based field's control.
     * @param  {Number} index An integer value.
     * @return {PMUI.control.Control}
     */
    Field.prototype.getControl = function (index) {
        index = index || 0;
        return this.controls[index];
    };
    /**
     * Sets the controlPositioning property.
     * @param {String} positioning The string must have the same format that the
     * {@link PMUI.form.Field#cfg-controlPositioning controlPositioning} config option.
     */
    Field.prototype.setControlPositioning = function (positioning) {
        var pos,
            controlPos,
            i,
            j,
            k,
            controls,
            span,
            addControl,
            that = this;

        if (typeof positioning === 'string') {
            this.controlPositioning = positioning;
            if (this.html && this.controls.length) {
                for (i = 0; i < this.controls.length; i += 1) {
                    jQuery(this.controls[i].getHTML()).detach();
                }
                $(this.dom.controlContainer).empty();
                if (positioning !== "") {
                    controls = this.controls.slice();
                    pos = positioning.split(/\[c[\d|\*]\]/);
                    controlPos = positioning.match(/\[c[\d|\*]\]/g);
                    addControl = function (c) {
                        var k;
                        if (c === '[c*]') {
                            for (k = 0; k < controls.length; k += 1) {
                                if (controls[k] !== null) {
                                    that.dom.controlContainer.appendChild(controls[k].getHTML());
                                    controls[k] = null;
                                }
                            }
                        } else {
                            k = c.match(/\d+/);
                            k = parseInt(k[0], 10);
                            if (controls[k] !== null) {
                                that.dom.controlContainer.appendChild(controls[k].getHTML());
                                controls[k] = null;
                            }
                        }
                    };

                    j = 0;
                    for (i = 0; i < pos.length; i += 1) {
                        if (pos[i] === "" && j < controlPos.length) {
                            addControl(controlPos[j]);
                            j++;
                        } else {
                            span = PMUI.createHTMLElement('span');
                            span.textContent = pos[i];
                            this.dom.controlContainer.appendChild(span);
                        }
                        if (j < controlPos.length) {
                            addControl(controlPos[j]);
                            j++;
                        }
                    }
                    this.dom.labelTextContainer.setAttribute('for', this.controls[0].id);
                    this.dom.controlContainer.appendChild(this.helper.getHTML());
                }
            }
        } else {
            throw new Error("The setControlPositioning() method only accepts string values.");
        }

        return this;
    };
    /**
     * Sets the controls for the field.
     *
     * Since this is an abstract method, it must be implemented in its non-abstract subclasses
     * @abstract
     * @private
     */
    Field.prototype.setControls = function () {
    };
    /**
     * Sets the name for the Field
     * @param {String} name
     */
    Field.prototype.setName = function (name) {
        if (typeof name === 'string') {
            this.name = name;
            this.data.setKey(name);
        } else {
            throw new Error('The setName() method only accepts string values!');
        }
        return this;
    };
    /**
     * Returns the field's name
     * @return {String}
     */
    Field.prototype.getName = function () {
        return this.name;
    };
    /**
     * Sets the text for the field's label.
     * @param {String} label
     */
    Field.prototype.setLabel = function (label) {
        if (typeof label === 'string') {
            this.label = label;
        } else {
            throw new Error("The setLabel() method only accepts string values!");
        }
        if (this.dom.fieldTextLabel) {
            this.dom.fieldTextLabel.textContent = this.label;
        }
        if (this.dom.fieldRequired) {
            this.dom.fieldRequired.textContent = '*';
            this.setRequired(this.required);
        }
        if (this.dom.fieldColon) {
            this.dom.fieldColon.textContent = ":";
        }
        return this;
    };
    /**
     * Returns the text from the field's label.
     * @return {String}
     */
    Field.prototype.getLabel = function () {
        return this.label;
    };
    /**
     * Sets the value to the field's controls.
     * @protected
     * @param {String} value
     * @chainable
     */
    Field.prototype.setValueToControls = function (value) {
        var i;

        for (i = 0; i < this.controls.length; i += 1) {
            this.controls[i].setValue(value);
        }
        return this;
    };
    /**
     * Returns the current value from the field's controls without affect the field's value.
     * @return {String}
     * @private
     */
    Field.prototype.getValueFromControls = function () {
        var value = '',
            i;

        for (i = 0; i < this.controls.length; i += 1) {
            value += ' ' + this.controls[i].getValue();
        }

        return value.substr(1);
    };
    /**
     * Update the field's value property from the controls
     * @protected
     * @chainable
     */
    Field.prototype.updateValueFromControls = function () {
        var value = this.getValueFromControls();

        this.value = value;
        this.data.setValue(value);

        return this;
    };
    /**
     * Sets the field's value.
     * @param {String} value
     */
    Field.prototype.setValue = function (value) {
        if (typeof value === 'number') {
            value = value.toString();
        }
        if (typeof value === 'string') {
            this.value = value;
        } else {
            throw new Error("The setValue() method only accepts string values!");
        }
        this.data.setValue(this.value);
        this.setValueToControls(this.value);
        return this;
    };
    /**
     * Returns the field's value.
     * @param [format] Defines the return format
     * @returns {*}
     */
    Field.prototype.getValue = function (format) {
        var castFormat = format || this.valueType;
        return PMUI.castValue(this.value, castFormat);
    };
    /**
     * Sets the helper text.
     * @param {String} helper
     */
    Field.prototype.setHelper = function (helper) {
        this.helper.setMessage(helper);

        return this;
    };
    /**
     * Shows a message below the control.
     * @param  {String} message  The message
     * @param  {String} [category="info"] The message category,
     * It can be one of the accepted values for the
     * {@link PMUI.ui.TooltipMessage#setCategory TooltipMessage's setCategory() method}, it defaults to "info".
     * @chainable
     */
    Field.prototype.showMessage = function () {
        this.message.setVisible(true)
        return this;
    };
    /**
     * Hides the message below the control.
     * @chainable
     */
    Field.prototype.hideMessage = function () {
        this.message.setVisible(false);

        return this;
    };
    /**
     * Sets the value type for the field.
     * @param {String} type
     */
    Field.prototype.setValueType = function (type) {
        var newType;
        if (typeof type === 'string') {
            newType = this.validTypes[type] ? type : 'object';
            this.valueType = newType;
            this.data.setType(newType);
        } else {
            throw new Error("The setValueType() method only accepts string values!");
        }

        return this;
    };
    /**
     * Returns the field's value type.
     * @return {String}
     */
    Field.prototype.getValueType = function () {
        return this.valueType;
    };
    /**
     * The onChange event handler constructor.
     * @private
     * @return {Function} The handler.
     */
    Field.prototype.onChangeHandler = function () {
        var that = this,
            i,
            dependentFields = this.dependentFields,
            form = this.form;

        return function () {
            var previousValue = that.value,
                newValue = that.getValueFromControls();

            if (newValue === previousValue) {
                return this;
            }

            that.value = newValue;
            that.data.setValue(newValue);

            if (that.validAtChange) {
                that.isValid();
            }

            if (typeof that.onChange === 'function') {
                that.onChange(that.getValue(), previousValue);
            }
            if (that.form) {
                (that.form.onChangeHandler())(that, that.value, previousValue);
                //PMUI call a event for dependency [dependencyHandler]
                for (i = 0; i < dependentFields.length; i += 1) {
                    dependentField = form.getField(dependentFields[i]);
                    if (!dependentField) {
                        that.removeDependentField(dependentFields[i]);
                        continue;
                    }
                    dependentField.fireDependencyHandler();
                }
            }
        };
    };

    Field.prototype.fireDependencyHandler = function () {
        var form = this.form,
            dependsOf,
            obj = {},
            i,
            that = this;

        if (form) {
            dependsOf = form.dependencies[that.name];
            for (i = 0; i < dependsOf.length; i += 1) {
                obj[dependsOf[i].name] = dependsOf[i];
            }
            if (typeof this.dependencyHandler === 'function') {
                this.dependencyHandler(that, obj);
            }
        }
        return this;
    };
    /**
     * Attach the event listeners to the HTML element
     * @private
     * @chainable
     */
    Field.prototype.defineEvents = function () {
        var i,
            that = this;

        Field.superclass.prototype.defineEvents.call(this);
        for (i = 0; i < this.controls.length; i += 1) {
            this.controls[i].setOnChangeHandler(this.onChangeHandler())
                .setOnBeforeChangeHandler(this.onBeforeChangeHandler())
                .defineEvents();
        }
        if (this.onClick) {
            this.addEvent('click').listen(this.html, function (e) {
                if (typeof that.onClick === 'function') {
                    that.onClick(that);
                }
            });
            this.addEvent('click').listen(this.dom.labelTextContainer, function (e) {
                e.stopPropagation();
            });
        }
        this.eventsDefined = true;

        return this;
    };
    /**
     * Returns true if the label is visible, otherwise returns false.
     * @return {Boolean}
     */
    Field.prototype.isLabelVisible = function () {
        return this.labelVisible;
    };
    /**
     * Hides the field's label.
     * @chainable
     */
    Field.prototype.hideLabel = function () {
        jQuery(this.dom.labelTextContainer).css('visibility', 'hidden');
        this.labelVisible = false;
        return this;
    };
    /**
     * Shows the field's label.
     * @chainable
     */
    Field.prototype.showLabel = function () {
        jQuery(this.dom.labelTextContainer).css('visibility', '');
        this.labelVisible = true;
        return this;
    };
    /**
     * Evaluates the required validation.
     * @return {Boolean} Returns true if the validation passes otherwise returns false.
     */
    Field.prototype.evalRequired = function () {
        var valid = true;
        if (this.required) {
            if (this.valueType !== 'number') {
                if (this.getValue() === "" || this.getValue() === "[]") {
                    valid = false;
                }
            } else {
                if (this.getValue().toString() === 'NaN') {
                    valid = false;
                }
            }
            if (valid) {
                this.hideMessage();
            } else {
                this.showMessage();
            }
        }
        return valid;
    };
    /**
     * Executes the validations and returns true if all of them passes, otherwise returns false.
     * @return {Boolean}
     */
    Field.prototype.isValid = function () {
        var valid = true, validator;
        valid = valid && this.evalRequired();
        if (!valid) {
            this.controls[0].style.addClasses(['error']);
            return valid;
        }
        this.controls[0].style.removeClasses(['error']);
        for (validator in this.validators) {
            if (this.validators.hasOwnProperty(validator)) {
                valid = valid && this.validators[validator].isValid();
                if (!valid) {
                    this.message.setText(this.validators[validator].errorMessage);
                    return valid;
                }
            }
        }
        return valid;
    };
    /**
     * Create the HTML Element for the Field.
     * @protected
     * @return {HTMLElement}
     */
    Field.prototype.createHTML = function () {
        var html, label,
            labelTextContainer,
            controlContainer,
            messageContainer,
            fieldColon,
            fieldRequired,
            fieldTextLabel;

        if (!this.html) {
            this.style.addClasses(['pmui-field']);
            Field.superclass.prototype.createHTML.call(this);


            labelTextContainer = PMUI.createHTMLElement("label");

            // contenedor de todos los labels
            labelTextContainer.className = 'pmui-field-label';

            //labelText
            fieldTextLabel = PMUI.createHTMLElement('span');
            fieldTextLabel.className = "pmui-field-textLabel";
            //asterisk for required
            fieldRequired = PMUI.createHTMLElement ('span');
            fieldRequired.className = "pmui-field-required";
            //colon
            fieldColon = PMUI.createHTMLElement('span');
            fieldColon.className = "pmui-field-colon";


            controlContainer = PMUI.createHTMLElement("span");
            controlContainer.className = 'pmui-field-control';
            messageContainer = PMUI.createHTMLElement("span");
            messageContainer.className = 'pmui-field-message';
            messageContainer.style.display = "block"


            //labelTextContainer append tree childs label, * and :
            labelTextContainer.appendChild(fieldTextLabel);
            labelTextContainer.appendChild(fieldRequired);
            labelTextContainer.appendChild(fieldColon);

            //messageContainer append the helper
            messageContainer.appendChild(this.message.getHTML());

            //this html append label, control and helper tag
            this.html.appendChild(labelTextContainer);
            this.html.appendChild(controlContainer);
            this.html.appendChild(messageContainer);

            this.dom.labelTextContainer = labelTextContainer;
            this.dom.fieldTextLabel = fieldTextLabel;
            this.dom.fieldColon = fieldColon;
            this.dom.fieldRequired = fieldRequired;
            this.dom.controlContainer = controlContainer;
            this.dom.messageContainer = messageContainer;

            this.setControlPositioning(this.controlPositioning);
            this.setLabelWidth(this.labelWidth);
            this.setLabel(this.label);
            this.setValue(this.value);
            this.setLabelPosition(this.labelPosition);
            if (this.labelVisible) {
                this.showLabel();
            } else {
                this.hideLabel();
            }
            if (this.visibleColon) {
                this.showColon();
            } else {
                this.hideColon();
            }
        }

        return this.html;
    };
    /**
     * Set the width for the field.
     * @param {Number|String} width height it can be a number or a string.
     * In case of using a String you only can use 'auto' or ##px or ##% or ##em when ## is a number.
     * @chainable
     */
    Field.prototype.setWidth = function (width) {
        Field.superclass.prototype.setWidth.call(this, width);
        if (this.labelWidth) {
            this.setLabelWidth(this.labelWidth);
        }
        return this;
    };
    /**
     * @method setFocus
     * set the focus on field for to control
     * @param {Number} [index] if the field has more than one control and needs to focus on a specific Control
     * @chainable
     */
    Field.prototype.setFocus = function (index) {
        var i = index || 0,
            j,
            controls = this.getControls();

        if (this.controls[i] && this.controls[i].setFocus) {
            this.controls[i].setFocus();
        }
        return this;
    };

    PMUI.extendNamespace('PMUI.form.Field', Field);

    if (typeof exports !== "undefined") {
        module.exports = Field;
    }
}());

(function () {
    /**
     * @class PMUI.field.TextField
     * @extends PMUI.form.Field
     * Class to handle a {@link PMUI.control.TextControl} field.
     *
     * Usage example:
     *
     *      @example
     *          var a;
     *          $(function() {
     *              a = new PMUI.field.TextField({
     *                  label: "Some Text",
     *                  placeholder: 'a text here',
     *                  required: true,
     *                  helper: "Introduce a text (6 chars. max.)",
     *                  validators: [
     *                      {
     *                          pmType: 'textLength',
     *                          criteria: {
     *                              maxLength: 6
     *                          }
     *                      }
     *                  ]
     *              });
     *              document.body.appendChild(a.getHTML());
     *              a.defineEvents();
     *          });
     *
     *
     * The code above will generate a required TextField with "Some Text" as label,
     * the control will have a placeholder with the text "a text here".
     * Also the field will contain a helper with the text "Introduce a text (6 chars. max.)"
     * and a validator that will control the maximum length for the text to be entered.
     *
     * @cfg {String} [placeholder=""] The text to show as placeholder for the field's control
     * @cfg {Number} [maxLength=0] An integer that specifies the maximum character length for the text to be entered.
     * 0 means no max length.
     *
     * #Note: This setting doesn't act as a validator, it simply set the max character length
     * for the field's control.
     * @cfg {Boolean} [trimOnBlur=true] A boolean that specifies if the value entered will be trimmed
     * when the field loses focus.
     */
    var TextField = function (settings) {
        TextField.superclass.call(this, settings);
        /**
         * @property {Boolean} [trimOnBlur=true] If the field's value must be trimmed every time it loses focus.
         */
        this.trimOnBlur = null;
        TextField.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.form.Field', TextField);

    TextField.prototype.type = "TextField";

    TextField.prototype.init = function (settings) {
        var defaults = {
            placeholder: "",
            maxLength: 0,
            trimOnBlur: true,
            readOnly: false
        };

        $.extend(true, defaults, settings);

        this.setPlaceholder(defaults.placeholder)
            .setMaxLength(defaults.maxLength)
            .setTrimOnBlur(defaults.trimOnBlur)
            .setReadOnly(defaults.readOnly)
            .setDependentFields(this.dependentFields);
    };
    /**
     * Sets the value for the field.
     * @param {String} value
     * @chainable
     */
    TextField.prototype.setValue = function (value) {
        if (this.trimOnBlur) {
            value = jQuery.trim(value);
        }
        TextField.superclass.prototype.setValue.call(this, value);

        return this;
    };
    /**
     * Sets the placeholder for the control. Note that this feature is only supported
     * by browsers which support the "placeholder" for input elements.
     * @param {String} placeholder
     * @chainable
     */
    TextField.prototype.setReadOnly = function (readonly) {
        this.controls[0].setReadOnly(readonly);
        return this;
    };
    /**
     * [setPlaceholder description]
     * @param {[type]} placeholder [description]
     */
    TextField.prototype.setPlaceholder = function (placeholder) {
        this.controls[0].setPlaceholder(placeholder);
        return this;
    };
    /**
     * [getPlaceholder description]
     * @return {[type]} [description]
     */
    TextField.prototype.getPlaceholder = function () {
        return this.controls[0].getPlaceholder();
    };
    /**
     * [setMaxLength description]
     * @param {[type]} maxLength [description]
     */
    TextField.prototype.setMaxLength = function (maxLength) {
        this.controls[0].setMaxLength(maxLength);
        return this;
    };
    /**
     * Set the parameter disabled for the control.
     * @param {[disabled]} value 
     * @chainable
     */
     TextField.prototype.setDisabled = function (value) {
        if (typeof value === 'boolean') {
            this.controls[0].setDisabled(value);
        }
        return this;
    };
    /**
     * [getMaxLength description]
     * @return {[type]} [description]
     */
    TextField.prototype.getMaxLength = function () {
        return this.controls[0].getMaxLength();
    };
    /**
     * [isReadOnly description]
     * @return {Boolean} [description]
     */
    TextField.prototype.isReadOnly = function () {
        return this.controls[0].isReadOnly();
    };
    /**
     * Switches on/off the value trimming for the field's value when it loses focus.
     * @param {Boolean} trimOnBlur Send true for switch on or false to switch off.
     * @chainable
     */
    TextField.prototype.setTrimOnBlur = function (trimOnBlur) {
        this.trimOnBlur = !!trimOnBlur;
        return this;
    };
    /**
     * Returns a boolean value that indicates if the trimming function is enabled/disabled
     * @return {Boolean}
     */
    TextField.prototype.getTrimOnBlur = function () {
        return this.trimOnBlur;
    };
    /**
     * Sets the control for the TextField
     * @chainable
     * @private
     */
    TextField.prototype.setControls = function () {
        if (this.controls.length) {
            return this;
        }

        this.controls.push(new PMUI.control.TextControl());

        return this;
    };
    /**
     * Establish the handler function for the internal onChange event
     * @return {Function}
     * @private
     */
    TextField.prototype.onChangeHandler = function () {
        var that = this,
            i,
            dependentFields = this.dependentFields,
            form = this.form;

        return function () {
            var previousValue = that.value,
                value;

            if (that.trimOnBlur) {
                value = that.controls[0].getValue();
                value = jQuery.trim(value);
                that.controls[0].setValue(value);
            }
            that.updateValueFromControls();
            if (that.validAtChange) {
                that.isValid();
            }
            if (typeof that.onChange === 'function') {
                that.onChange(that.getValue(), previousValue);
            }
            if (that.form) {
                (that.form.onChangeHandler())(that, that.getValue(), previousValue);
            } else if (that.parent && that.parent.form) {
                (that.parent.form.onChangeHandler())(that, that.getValue(), previousValue);
            } else if (that.parent.parent && that.parent.parent.form) {
                (that.parent.parent.form.onChangeHandler())(that, that.getValue(), previousValue);
            } else if (that.parent.parent.parent && that.parent.parent.parent.form) {
                (that.parent.parent.parent.form.onChangeHandler())(that, that.getValue(), previousValue);
            } else {
                return;
            }
            if (that.form) {
                for (i = 0; i < dependentFields.length; i += 1) {
                    dependentField = form.getField(dependentFields[i]);
                    if (!dependentField) {
                        that.removeDependentField(dependentFields[i]);
                        continue;
                    }
                    dependentField.fireDependencyHandler();
                }
            }
        };
    };

    PMUI.extendNamespace('PMUI.field.TextField', TextField);

    if (typeof exports !== "undefined") {
        module.exports = TextField;
    }
}());
(function () {
    /**
     * @class PMUI.field.PasswordField
     * @extends PMUI.form.Field
     * Class to handle a {@link PMUI.control.PasswordControl} field.
     *
     * Usage example:
     *
     *      @example
     *          var a;
     *          $(function() {
     *              a = new PMUI.field.PasswordField({
     *                  label: "Some Text",
     *                  required: true,
     *                  helper: "Introduce a text (6 chars. max.)",
     *                  validators: [
     *                      {
     *                          pmType: 'textLength',
     *                          criteria: {
     *                              maxLength: 6
     *                          }
     *                      }
     *                  ]
     *              });
     *              document.body.appendChild(a.getHTML());
     *              a.defineEvents();
     *          });
     *
     *
     * The code above will generate a required PasswordField with "Some Text" as label,
     * the control will have a placeholder with the text "a text here".
     * Also the field will contain a helper with the text "Introduce a text (6 chars. max.)"
     * and a validator that will control the maximum length for the text to be entered.
     *
     * @cfg {Number} [maxLength=0] An integer that specifies the maximum character length for the text to be entered.
     * 0 means no max length.
     *
     * #Note: This setting doesn't act as a validator, it simply set the max character length
     * for the field's control.
     * @cfg {Boolean} [trimOnBlur=true] A boolean that specifies if the value entered will be trimmed
     * when the field loses focus.
     */
    var PasswordField = function (settings) {
        PasswordField.superclass.call(this, settings);
        /**
         * @property {Boolean} [trimOnBlur=true] If the field's value must be trimmed every time it loses focus.
         */
        this.trimOnBlur = null;
        PasswordField.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.form.Field', PasswordField);

    PasswordField.prototype.type = "PasswordField";

    PasswordField.prototype.init = function (settings) {
        var defaults = {
            maxLength: 0,
            trimOnBlur: true
        };

        $.extend(true, defaults, settings);

        this.setMaxLength(defaults.maxLength)
            .setTrimOnBlur(defaults.trimOnBlur);
    };
    /**
     * Sets the value for the field.
     * @param {String} value
     * @chainable
     */
    PasswordField.prototype.setValue = function (value) {
        if (this.trimOnBlur) {
            value = jQuery.trim(value);
        }
        PasswordField.superclass.prototype.setValue.call(this, value);

        return this;
    };

    /**
     * Set MaxLength
     * @param maxLength
     * @returns {PMUI.field.PasswordField}
     */
    PasswordField.prototype.setMaxLength = function (maxLength) {
        this.controls[0].setMaxLength(maxLength);
        return this;
    };

    /**
     * Get Max Length
     * @returns {*}
     */
    PasswordField.prototype.getMaxLength = function () {
        return this.controls[0].getMaxLength();
    };

    /**
     * Switches on/off the value trimming for the field's value when it loses focus.
     * @param {Boolean} trimOnBlur Send true for switch on or false to switch off.
     * @chainable
     */
    PasswordField.prototype.setTrimOnBlur = function (trimOnBlur) {
        this.trimOnBlur = !!trimOnBlur;
        return this;
    };
    /**
     * Returns a boolean value that indicates if the trimming function is enabled/disabled
     * @return {Boolean}
     */
    PasswordField.prototype.getTrimOnBlur = function () {
        return this.trimOnBlur;
    };
    /**
     * Sets the control for the PasswordField
     * @chainable
     * @private
     */
    PasswordField.prototype.setControls = function () {
        if (this.controls.length) {
            return this;
        }

        this.controls.push(new PMUI.control.PasswordControl());

        return this;
    };
    /**
     * Establish the handler function for the internal onChange event
     * @return {Function}
     * @private
     */
    PasswordField.prototype.onChangeHandler = function () {
        var that = this;

        return function () {
            var previousValue = that.value,
                value;

            if (that.trimOnBlur) {
                value = that.controls[0].getValue();
                value = jQuery.trim(value);
                that.controls[0].setValue(value);
            }
            that.updateValueFromControls();
            if (that.validAtChange) {
                that.isValid();
            }
            if (typeof that.onChange === 'function') {
                that.onChange(that.getValue(), previousValue);
            }
            if (that.form) {
                (that.form.onChangeHandler())(that, that.getValue(), previousValue);
            }
        };
    };

    PMUI.extendNamespace('PMUI.field.PasswordField', PasswordField);

    if (typeof exports !== "undefined") {
        module.exports = PasswordField;
    }
}());
(function () {
    /**
     * @class PMUI.field.TextAreaField
     * @extends PMUI.field.TextField
     * Class to handle a {@link PMUI.control.TextAreaControl}.
     *
     * Usage example:
     *
     *      @example
     *           var a;
     *           $(function() {
     *                a = new PMUI.field.TextAreaField({
     *                     id:'12345',
     *                     label: "coment",
     *                     cols: 300,   
     *                     rows: 200,
     *                     name: 'coments',
     *                     placeholder: 'make your comment here...........',
     *                     required: true,
     *                     labelposition:'left',
     *                     labelVerticalPosition:'top',
     *                     tooltipPosition:'bottom',
     *                     helper: "Introduce a text (200 chars. max.)",
     *                     validators: [
     *                         {
     *                             pmType: 'textLength',
     *                             criteria: {
     *                                 maxLength: 200
     *                             }
     *                         }
     *                     ]
     *                 });
     *                 document.body.appendChild(a.getHTML());
     *             });
     *
     *
     * TextAreaField, is a field for very long text strings
     * to generate the above code on TextAreaField requires, one id, label, name, placeholder, required,
     labelposition,
     * labelVerticalPosition, tooltipPosition, helper.
     *
     * In case to modified label position or tooltip position it can contain the next properties:
     * @cfg {'string'} [labelVerticalPosition='top'] in case that the position of the label left or right we
     fixed this
     * property in three cases ('top', 'center', 'bottom')
     * @cfg {'string'} [tooltipPosition='top'] this property can set the tooltip in two cases ('top' or 'bottom')
     for TextAreaField
     */

    var TextAreaField = function (settings) {

        TextAreaField.superclass.call(this, settings);
        /**
         * @property {string} [labelVerticalPosition='top'], in case that the position of the label left
         or right we fixed this
         * property in three cases ('top', 'center', 'bottom')
         * @readonly
         */
        this.labelVerticalPosition = null;
        /**
         * @property {string} [tooltipPosition='top'] this property can set the tooltip in two cases ('top' or
         'bottom').
         * @readonly
         */
        this.tooltipPosition = null;
        this.cols = null;
        this.rows = null;

        TextAreaField.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.field.TextField', TextAreaField);

    TextAreaField.prototype.type = "TextAreaField";

    TextAreaField.prototype.init = function (settings) {
        var defaults = {
            labelVerticalPosition: 'top',
            tooltipPosition: 'top',
            cols: 'auto',
            rows: 'auto'
        };

        jQuery.extend(true, defaults, settings);

        this.setLabelVerticalPosition(defaults.labelVerticalPosition);
        this.setTooltipPosition(defaults.tooltipPosition);
        this.setRows(defaults.rows);
        this.setCols(defaults.cols);

    };

    /**
     * Sets the labelVerticalPosition for the Label of the TextAreaField.
     * input values ​​(top, center and bottom) only affect the field if the label of TextAreaField is in
     * left or right position.
     * @param {String} labelVerticalPosition
     * @chainable
     */

    TextAreaField.prototype.setLabelVerticalPosition = function (value) {
        var x,
            result;

        if (value === 'top' || value === 'bottom' || value === 'center') {
            this.labelVerticalPosition = value;

            if (this.controls[0] && this.html) {
                this.dom.labelTextContainer.style.position = 'relative';
                if (this.labelPosition === 'left' || this.labelPosition === 'right') {
                    if (this.labelVerticalPosition === 'top') {
                        this.dom.labelTextContainer.style.top = '0px';
                        this.dom.labelTextContainer.style.verticalAlign = 'top';
                    }
                    if (this.labelVerticalPosition === 'bottom') {
                        this.dom.labelTextContainer.style.top = '0px';

                        this.dom.labelTextContainer.style.verticalAlign = 'bottom';
                    }
                    if (this.labelVerticalPosition === 'center') {

                        this.dom.labelTextContainer.style.verticalAlign = 'top';
                        result = ($(this.controls[0].getHTML()).outerHeight() / 2) -
                            ($(this.dom.labelTextContainer).outerHeight() / 2);

                        this.dom.labelTextContainer.style.top = (result) + 'px';

                    }
                }
            }
        }
        else {
            throw new Error("The value is not 'top' or 'bottom' please enter these values");
        }

        return this;
    };
    /**
     * Sets the label position
     * input values(top, bottom, left, right)
     * @param {String} labelPosition
     * @chainable
     */
    TextAreaField.prototype.setLabelPosition = function (position) {
        if (this.html && (position === 'top' || position === 'bottom')) {
            this.dom.labelTextContainer.style.top = '0px';
        }
        TextAreaField.superclass.prototype.setLabelPosition.call(this, position);

        return this;
    };
    /**
     * Sets the tooltipPosition for the Label of the TextAreaField.
     * input values ​​(top and bottom) only affect the field if the label of TextAreaField
     * @param {String} tooltipPosition
     * @chainable
     */
    TextAreaField.prototype.setTooltipPosition = function (value) {

        if (value === 'top' || value === 'bottom') {
            this.tooltipPosition = value;

            if (this.controls[0] && this.html) {


                if (this.tooltipPosition === 'top') {
                    this.helper.icon.style.addProperties({'vertical-align': 'top'});
                }
                if (this.tooltipPosition === 'bottom') {

                    this.helper.icon.style.addProperties({'vertical-align': 'bottom'});
                }
            }
        }
        else {
            throw new Error("The value is not 'top' or 'bottom' please enter these values");
        }

        return this;
    };
    /**
     * Sets the  setHeight for the {PMUI.controls.TextAreaControl}.
     * @param {String|number} [PMUI.controls.TextAreaControl]
     * @chainable
     */
    TextAreaField.prototype.setControlHeight = function (height) {
        this.controls[0].setHeight(height);
        return this;
    };
    /**
     * Sets the  setWidth for the {PMUI.controls.TextAreaControl}.
     * @param {String|number} [PMUI.controls.TextAreaControl]
     * @chainable
     */
    TextAreaField.prototype.setControlWidth = function (width) {
        this.controls[0].setWidth(width);
        return this;
    };

    /**
     * Sets the text for the TextAreaField label.
     * @param {String} label
     */

    TextAreaField.prototype.setLabel = function (label) {
        if (typeof label === 'string') {
            this.label = label;
        } else {
            throw new Error("The setLabel() method only accepts string values!");
        }
        if (this.dom.fieldTextLabel) {
            this.dom.fieldTextLabel.textContent = this.label;
        }
        if (this.dom.fieldRequired) {
            this.dom.fieldRequired.textContent = '*';
            this.setRequired(this.required);
        }
        if (this.dom.fieldColon) {
            this.dom.fieldColon.textContent = ":";
        }
        return this;
    };
    /**
     * Sets the control for the TextField
     * @chainable
     * @private
     */
    TextAreaField.prototype.setControls = function () {
        if (this.controls.length) {
            return this;
        }

        this.controls.push(new PMUI.control.TextAreaControl());

        return this;
    };

    /**
     * Disables the field
     * @param {Boolean} Disable, if the field, then control is activated, if the control is enabled
     * @chainable
     */
    TextAreaField.prototype.disableField = function () {
        this.controls[0].disable(true);
        return this;
    };
    /**
     * Disables/enables the control
     * @param {Boolean} Enable, if the field, then control is activated, if the control is diabled
     * @chainable
     */
    TextAreaField.prototype.enableField = function () {
        this.controls[0].disable(false);
        return this;
    };
    /**
     * Creates the HTML element for the textAreaField
     * @return {HTMLElement}
     */
    TextAreaField.prototype.createHTML = function () {
        TextAreaField.superclass.prototype.createHTML.call(this);
        this.setLabelVerticalPosition(this.labelVerticalPosition);
        this.setTooltipPosition(this.tooltipPosition);
        return this.html;
    };
    /**
     * assign hight as rows to textArea,
     * @param {number|String} rows will be assigned as high in PX, the entered values ​​can
     * be numbers, 'number + px', 'auto' or percentage
     * @chainable
     */
    TextAreaField.prototype.setRows = function (rows) {
        this.controls[0].setHeight(rows);
        return this;
    };
    /**
     * assign width as Cols to textArea,
     * @param {number|String} Cols will be assigned as high in PX, the entered values ​​can
     * be numbers, 'number + px', 'auto' or percentage
     * @chainable
     */
    TextAreaField.prototype.setCols = function (cols) {
        this.controls[0].setWidth(cols);
        return this;
    };

    PMUI.extendNamespace('PMUI.field.TextAreaField', TextAreaField);

    if (typeof exports !== "undefined") {
        module.exports = TextAreaField;
    }
}());
(function () {
    /**
     * @class PMUI.field.DropDownListField
     * @extends PMUI.form.Field
     * Class to handle a {@link PMUI.control.DropDownListControl} field.
     *
     * Usage example:
     *
     *      @example
     *              var settings, list;
     *              $(function(){
     *                  settings = {
     *                   id : '123',
     *                  label : "Pais",
     *                  name : 'List Select',
     *                  required: true,
     *                  helper: "selected one opcion of the list",
     *                  options: [                                
     *                  {
     *                   label: "BOLIVIA",
     *                        options: [
     *                        {
     *                              label: "La Paz",
     *                              value: 1,
     *                              disabled : true
     *                          },
     *                          {
     *                              label: "Cochabamba",
     *                              value: 2
     *                          },
     *                          {
     *                              label: "SantaCruz",
     *                              value: 3
     *                          },
     *                      ]
     *                   },
     *                   {
     *                        label: "ARGENTINA",
     *                      options: [
     *                          {
     *                              label: "Buenos Aires",
     *                              value: 4
     *                          },
     *                          {
     *                              label: "Santa Fe",
     *                              value: 5
     *                          },
     *                          {
     *                                  value: "Cordoba"
     *                          }
     *                      ]
     *                   },
     *                   {
     *                        label: "CHILE",
     *                      options: [
     *                          {
     *                              label: "Arica",
     *                              value: 6
     *                          },
     *                          {
     *                              label: "Santiago",
     *                              value: 7
     *                          },
     *                          {
     *                                  value: "Concepcion"
     *                          }
     *                      ]
     *                   },
     *                   {
     *                        label: "New York",
     *                        value: 8
     *                   },
     *                   {
     *                        label: "Mexico D.F.",
     *                        value: 9
     *                   }
     *               ]
     *           }
     *           list = new PMUI.field.DropDownListField(settings);
     *           document.body.appendChild(list.getHTML());
     *         });
     *
     *
     * DropDownListField typo is a selection field, enclose the values â€‹â€‹that we can choose from a list
     * of options. The attributes that accompany the opening tag are:
     * The code above will generate a DropDownListField, required one id, name, requiere,
     * helper, text  for list the options, one opcion array  with the list options
     *
     * @constructor Creates an instance of the class DropDownListField.
     * @param {Object} options Initialization options.
     * @cfg {Array} [options=[]] An array with all the options to be contained by the
     {PMUI.control.DropDownListControl}.
     *
     * Each element in the array is a JSON object, this JSON object can represent an option group
     * or an option item.
     *
     * In case to represent an option item it can contain the next properties:
     *
     * - value {String} (required): the value for the option.
     * - label {String} (optional): the label for the option, if isn't specified the value is used instead.
     * - selected {Boolean} (optional): if the option is selected. #Note. If the configuration object has the
     * "value" propery set then this "selected" property will be
     * - disabled {Boolean} (optional): if the option is disabled or not.
     *
     * On the other hand, in case to represent an option group, it can contain the next properties:
     *
     * - label {String} (required): The name for the option group.
     * - disabled {Boolean} (optional): If the group is disabled or not.
     * - options {Array} (required): An array in which each element is a JSON object representing an option item,
     * so every item must have the structure explained above (for represent option items). #Note. This propery makes
     * the difference between an option and a option group. If the "options" property is not specified or if it isn't
     * an array then it will treated like a option item.
     * @cfg {String|Number} [listWidth='auto'] This value sets the width of the list
     */


    var DropDownListField = function (settings) {

        DropDownListField.superclass.call(this, settings);

        /**
         * @property {Boolean} [disabled=false] If the field is disabled or not.
         * @readonly
         */

        this.listWidth = null;
        //this.defaultValue = null;

        DropDownListField.prototype.init.call(this, settings);

    };

    PMUI.inheritFrom("PMUI.form.Field", DropDownListField);

    DropDownListField.prototype.type = "DropDownListField";

    DropDownListField.prototype.init = function (settings) {
        var defaults = {
            options: [],
            listWidth: 'auto',
            value: null
        };

        jQuery.extend(true, defaults, settings);

        this.setOptions(defaults.options);
        this.setListWidth(defaults.listWidth);
        this.updateValueFromControls();

        if (defaults.value !== null) {
            this.setValue(defaults.value);
        }
    };
    /**
     * Sets the options/option groups for the control.
     * @param {Array} options An array with the same structure that the
     * {@link PMUI.field.DropDownListField#cfg-options "options"} property in the
     * Config settings section.
     */
    DropDownListField.prototype.setOptions = function (options) {
        var i;
        if (jQuery.isArray(options)) {
            this.controls[0].setOptions(options);
        }
        if (!this.value && options) {
            for (i = 0; i < options.length; i += 1) {
                if (options[i].selected) {
                    this.initialValue = options[i].value || options[i].label || "";
                    break;
                }
            }
        }
        this.value = this.controls[0].getValue();
        return this;
    };
    /**
     * Returns the options/option groups from the field
     * @param  {Boolean} [includeGroups=false] If it's evaluated as true then it will include
     * the option groups with its child elements, otherwise it will return only the option items.
     * @return {Array}
     *
     *         example
     *
     *          list.getOptions(false)
     *         [La Paz][Cochabamba][Santa Cruz][Buenos Aires][Santa Fe][Cordoba][Santiago][.][.][Mexico D.F]
     *
     *        list.getOptions(true)
     *          [BOLIVIA
     *              [La Paz][Cochabamba][SantaCruz]
     *          ]
     *          [ARGENTINA
     *              [Buenos Aires][Santa Fe][Cordoba]
     *           ]
     *          [CHILE
     *             [x][y][z]
     *          ]
     *          [New York]
     *          [Mexico D.F.]
     *
     * @return {Array}
     */
    DropDownListField.prototype.getOptions = function (includeGroups) {
        return this.controls[0].getOptions(includeGroups);
    };
    /**
     * Clear all the options from the control.
     * @chainable
     */
    DropDownListField.prototype.clearOptions = function () {
        this.controls[0].clearOptions();
        this.value = this.controls[0].value;
        return this;
    };

    /**
     * Disables one or more options/option groups.
     * @param  {String|Number|Object} option It can be a string, a number or a JSON object.
     *
     * - In case to be a String, it will be disabled the options that match the string in its value and the option
     * groups which match the string in its label. In this case more than one single item can be disabled.
     * - In case to be a Number, it will be disabled the option/option group which index position matches the number.
     * Obviously, in this case only one item will be disabled.
     * - In case to be an object you can specify if the change will be applied only to options or option groups,
     * it should have two properties:
     *     - criteria {String}: The value (in case of options) or the label (in case of option groups) the items
     needs
     *     to match for apply the changes.
     *     - applyTo {String} (optional), it can take the following values:
     *         - "groups", the change will be applied only to the option groups.
     *         - "options", the change will be applied only to the options (direct child of the object).
     *         - [any other string value], the default value, it indicates that the change will be applied to both
     *         options/option groups that matches the criteria in its value/label respectly.
     *
     * @param  {String} [group] It it is specified must be an String making reference to an existing option group
     label.
     * Using this parameter, the elements to be match by the first parameter will be search only in the option groups
     * that match this parameter in its label.
     *
     *         example
     *              list =    [a
     *                             [a0]
     *                             [a1]
     *                        ]
     *                        [b
     *                             [b0]
     *                             [b1]
     *                        ]
     *                        [c]
     *   //disabling sending a number
     *              list.disableOption(0); -->disabled group a
     *              list.disableOption(4); -->disabled option b[b0]
     *
     *    //disabling sending a string
     *             list.disableOption('a') --> disabled group a
     *             list.disableOption('b0') --> disabled option b[b0]

     *   //disabling sending a objects
     list.disableOption({criteria:'a', applyTo:'groups'}) --> disabled group a
     *
     * @chainable
     */

    DropDownListField.prototype.disableOption = function (option, group) {
        this.controls[0].disableOption(option, group);
        return this;
    };

    /**
     * Enables one or more options/option groups.
     * @param  {String|Number|Object} option It can be a string, a number or a JSON object.
     *
     * - In case to be a String, it will be enabled the options that match the string in its value and the option
     * groups which match the string in its label. In this case more than one single item can be enabled.
     * - In case to be a Number, it will be enabled the option/option group which index position matches the number.
     * Obviously, in this case only one item will be enabled.
     * - In case to be an object you can specify if the change will be applied only to options or option groups,
     * it should have two properties:
     *     - criteria {String}: The value (in case of options) or the label (in case of option groups) the items needs
     *     to match for apply the changes.
     *     - applyTo {String} (optional), it can take the following values:
     *         - "groups", the change will be applied only to the option groups.
     *         - "options", the change will be applied only to the options (direct child of the object).
     *         - [any other string value], the default value, it indicates that the change will be applied to both
     *         options/option groups that matches the criteria in its value/label respectly.
     *
     * @param  {String} [group] It it is specified must be an String making reference to an existing option group
     label.
     * Using this parameter, the elements to be match by the first parameter will be search only in the option groups
     * that match this parameter in its label.
     * @chainable
     */

    DropDownListField.prototype.enableOption = function (option, group) {
        this.controls[0].enableOption(option, group);
        return this;
    };

    /**
     * Removes one or more option/option groups.
     * @param  {String|Number|Object} option It can be a string, a number or a JSON object.
     *
     * - In case to be a String, it will be removed the options that match the string in its value and the option
     * groups which match the string in its label. In this case more than one single item can be removed.
     * - In case to be a Number, it will be removed the option/option group which index position matches the number.
     * Obviously, in this case only one item will be removed.
     * - In case to be an object you can specify if the change will be applied only to options or option groups,
     * it should have two properties:
     *     - criteria {String}: The value (in case of options) or the label (in case of option groups) the items
     needs
     *     to match for apply the changes.
     *     - applyTo {String} (optional), it can take the following values:
     *         - "groups", the change will be applied only to the option groups.
     *         - "options", the change will be applied only to the options (direct child of the object).
     *         - [any other string value], the default value, it indicates that the change will be applied to both
     *         options/option groups that matches the criteria in its value/label respectly.
     *
     * @param  {String} [group] It it is specified must be an String making reference to an existing option group
     label.
     * Using this parameter, the elements to be match by the first parameter will be search only in the option groups
     * that match this parameter in its label.
     *
     * ##Note. Removing an option group implies removing all its child options.
     * @chainable
     */
    DropDownListField.prototype.removeOption = function (option, group) {
        this.controls[0].removeOption(option, group);
        return this;
    };
    /**
     * Adds a new option group to the Field
     * @param {Object} optionGroup A JSON object with the following properties:
     *
     * - label {String} (required): the label for the option group.
     * - disabled {Boolean}(optional): if the option group will be disabled or not.
     * it defaults to false.
     * - options {Array} (optional): An array of JSON object, each one represents an option and
     * should have the same structure than the "option" paremeter for the
     * {@link PMUI.field.DropDownListField#addOption addOption() method}.
     */
    DropDownListField.prototype.addOptionGroup = function (optionGroup) {
        this.controls[0].addOptionGroup(optionGroup);
        return this;
    };

    /**
     * Adds a new option to the list of the DropDownListField or to an option group.
     * @param {Object} option An object with ther settings for the new option.
     * this object can have the following properties:
     *
     * - value {String} (required): the value for the option.
     * - label {String} (optional): the label for the option, if isn't specified the value is used instead.
     * - selected {Boolean} (optional): if the option is selected. #Note. If the configuration object has the
     * "value" propery set then this "selected" property will be
     * - disabled {Boolean} (optional): if the option is disabled or not.
     *
     * @param {String} group The name of the option group in which the new option will be added. If it doesn't exist
     * it will be created.
     *
     *         usage
     *         // add option a groupÃ§
     *
     *         list.addOption({value:'5', label:'Pando', selected: true, disabled:true },'BOLIVIA')
     *
     *         //or add option
     *
     *         list.addOption({value:'paris', label:'Paris'})
     *
     * @chainable
     */
    DropDownListField.prototype.addOption = function (option, group) {
        this.controls[0].addOption(option, group);
        if (this.getOptions().length == 1) {
            this.value = this.controls[0].value;
        }
        return this;
    };

    /**
     * Returns the label from the option currently selected.
     * @return {String}
     */
    DropDownListField.prototype.getSelectedLabel = function () {
        return this.controls[0].getSelectedLabel();
    };
    /**
     * Sets the control for the DropDownListField
     * @chainable
     * @private
     */
    DropDownListField.prototype.setControls = function () {
        if (this.controls.length) {
            return this;
        }
        this.controls.push(new PMUI.control.DropDownListControl());

        return this;
    };
    /**
     * Set the width for the HTML DropDownListControl(select)
     * @param {Number|String} width height it can be a number or a string.
     In case of using a String you only can use 'auto' or 'inherit' or ##px or ##% or ##em when ## is a number
     * @chainable
     */

    DropDownListField.prototype.setListWidth = function (width) {
        this.listWidth = width;
        this.controls[0].setWidth(this.listWidth);
        return this;
    };


    DropDownListField.prototype.getListWidth = function (width) {
        return this.listWidth;
    };

    DropDownListField.prototype.setValue = function (value) {
        var val;
        if (this.controls[0]) {
            val = this.controls[0].setValue(value).getValue();
        }
        DropDownListField.superclass.prototype.setValue.call(this, val || value);
        return this;
    };

    PMUI.extendNamespace('PMUI.field.DropDownListField', DropDownListField);

    if (typeof exports !== "undefined") {
        module.exports = DropDownListField;
    }
}());
(function () {
    /**
     * @class  PMUI.field.CheckBoxGroupField
     * Field where the values ​​can be selected from a group of options.
     * @extends PMUI.form.Field
     *
     *  Usage example:
     *
     *      @example
     *             var a;
     *             $(function() {
     *                 a = new PMUI.field.CheckBoxGroupField({
     *                     label: "development tools",
     *                     controlPositioning: 'vertical',
     *                     maxDirectionOptions: 2,
     *                     required: true,
     *                     value: '1',
     *                     helper: "what lanaguages you know?. Select others besides javascript",
     *                     options : [
     *                         {  label: 'Java',
     *                            disabled: false,
     *                            value: 'java',
     *                            selected :true
     *                         },
     *                         { label: 'JavaScript',
     *                            disabled: true,
     *                            value: 'javaScript',
     *                            selected :true
     *                         },
     *                         { label: 'Python',
     *                            disabled: false,
     *                            value: 'python'
     *                         },
     *                          {  label: 'C#',
     *                            disabled: true,
     *                            value: 'visualbasic'
     *                         }
     *                     ],
     *                      onChange: function(newVal, oldVal) {
     *                         console.log("The value for the field  \"" + this.getLabel() + 
     *                         "\": has change from \"" + oldVal + "\" to \"" + newVal + "\"");
     *                     },
     *                 });
     *                 document.body.appendChild(a.getHTML());
     *             });
     *
     *
     * The example above will generate a field with 4 CheckBox options.
     *
     * @constructor Creates an instance of the class CheckBoxGroupField.
     * @param {Object} options Initialization options.
     * @cfg {Array} [options=[]] An array in which every element is a JSON object with the same
     structure required in the
     * {@link PMUI.field.CheckBoxGroupField#addOption addOption() method}.
     * @cfg {String} [controlPositioning="vertical"] A string that determines the direction for the options to be added
     * in the field. This string can have one of the following values:
     *
     * - "horizontal" (default), in this case all the options will be included  in horizontal order.
     * - "vertical", in this case all the options will be included in vertical order.
     * @cfg {Number} [maxDirectionOptions=1] The maximum number of options to be added in the current direction
     * (set by the {@link PMUI.control.CheckBoxGroupField#cfg-controlPositioning controlPositioning config option}).
     *
     * @cfg {String} [value='[]'] The value that determines which options will be checked. the String must have an
     * array format in which each element is the value of the options that are wanted to be checked.
     */
    var CheckBoxGroupField = function (settings) {
        CheckBoxGroupField.superclass.call(this, settings);
        /**
         * @property {String} controlPositioning The direction for the options to be added in the field.
         * @readonly
         */
        this.controlPositioning = null;
        /**
         * @property {Number} maxDirectionOptions The max number of options that can be in the current options
         * direction.
         * @readonly
         */
        this.maxDirectionOptions = null;
        /**
         * The status if the controls to be saved when the field is switch between enabled/disabled.
         * @type {Object}
         * @private
         */
        this.auxControlStates = {};

        this.controlTable = {};
        CheckBoxGroupField.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.form.Field', CheckBoxGroupField);

    CheckBoxGroupField.prototype.type = "CheckBoxGroupField";

    CheckBoxGroupField.prototype.init = function (settings) {
        var defaults = {
            options: [],
            controlPositioning: "vertical",
            maxDirectionOptions: 1,
            value: '[]'
        };

        jQuery.extend(true, defaults, settings);
        this.setOptions(defaults.options)
            .setMaxDirectionOptions(defaults.maxDirectionOptions)
            .setControlPositioning(defaults.controlPositioning);
    };
    /**
     * Enables the field. Notice that the controls that initially were disabled will continue being disabled.
     * @chainable
     */
    CheckBoxGroupField.prototype.enable = function () {
        var key,
            i,
            controlsLength = this.controls.length,
            controls = this.controls;
        CheckBoxGroupField.superclass.prototype.enable.call(this);
        this.disabled = false;
        for (i = 0; i < controlsLength; i += 1) {
            controls[i].disable(this.auxControlStates[controls[i].id]);
        }
        return this;
    };
    /**
     * Sets the max number of controls that can be in the current direction (the direction is set by the
     * CheckBoxGroupField's
     * {@link PMUI.field.CheckBoxGroupField#setControlPositioning setControlPositioning() method}).
     * @param {Number} max It should be an integer, otherwise it will be floor rounded. If the value is equal or minor
     * than 0 it means there's no limit.
     */
    CheckBoxGroupField.prototype.setMaxDirectionOptions = function (max) {
        if (typeof max === 'number') {
            this.maxDirectionOptions = Math.floor(max);
            if (this.html) {
                this.setControlPositioning(this.controlPositioning);
            }
        } else {
            throw new Error("setMaxDirectionOptions(): it only accepts number values.");
        }

        return this;
    };
    /**
     * It overrides the Field's {@link PMUI.form.Field#setControlPositioning setControlPositioning() method}.
     * This new implementation sets the direction for the control addition: horizontal or vertical.
     * @param {String} positioning It can be "horizontal" or "vertical".
     */
    CheckBoxGroupField.prototype.setControlPositioning = function (positioning) {
        var errorMessage = "The setControlPositioning() method only accepts \"horizontal\" or \"vertical\" as value.",
            table,
            tbody,
            cell,
            row,
            i,
            column,
            rowIndex;
        if (typeof positioning === 'string') {
            if (!(positioning === 'horizontal' || positioning === 'vertical')) {
                return this;
            }
            this.controlPositioning = positioning;
            if (this.html && this.controls) {
                for (i = 0; i < this.controls.length; i += 1) {
                    jQuery(this.controls[i].getHTML()).detach();
                }
                $(this.dom.controlContainer).empty();
                table = PMUI.createHTMLElement("table");
                table.className = 'pmui-field-control-table';
                tbody = PMUI.createHTMLElement("tbody");
                if (positioning === 'horizontal') {
                    row = PMUI.createHTMLElement("tr");
                    for (i = 0; i < this.controls.length; i += 1) {
                        cell = PMUI.createHTMLElement('td');
                        this.controls[i].getHTML();
                        this.controls[i].control.tabIndex = i;
                        cell.appendChild(this.controls[i].getHTML());
                        row.appendChild(cell);
                        if (this.maxDirectionOptions > 0 && (i + 1) % this.maxDirectionOptions === 0) {
                            tbody.appendChild(row);
                            row = PMUI.createHTMLElement("tr");
                        }
                    }
                    tbody.appendChild(row);
                } else {
                    column = 0;
                    for (i = 0; i < this.controls.length; i += 1) {
                        cell = PMUI.createHTMLElement('td');
                        this.controls[i].getHTML();
                        this.controls[i].control.tabIndex = i;
                        cell.appendChild(this.controls[i].getHTML());
                        rowIndex = this.maxDirectionOptions === 0 ? i : i % this.maxDirectionOptions;

                        row = jQuery(tbody).find('tr').eq(rowIndex).get(0);
                        if (!row) {
                            row = PMUI.createHTMLElement('tr');
                            tbody.appendChild(row);
                        }
                        row.appendChild(cell);
                    }
                }
                this.controlTable = table;
                table.appendChild(tbody);
                this.dom.controlContainer.appendChild(table);
                this.dom.controlContainer.appendChild(this.helper.getHTML());
            }
        }

        return this;
    };
    /**
     * Sets all the options for the control for checkbox button.
     * @param {Array} options An array in which every element is a JSON object with the
     same structure required in the
     * {@link PMUI.field.CheckBoxGroupField#addOption addOption() method}.
     * @chainable
     */
    CheckBoxGroupField.prototype.setOptions = function (options) {
        var i = 0;

        this.value = [];
        this.value = JSON.stringify(this.value);

        if (jQuery.isArray(options)) {
            for (i = 0; i < options.length; i += 1) {
                this.addOption(options[i]);
            }
        } else {
            throw new Error("setOptions(): the supplied argument must be an array.");
        }

        return this;
    };
    /**
     * Adds a new option to the CheckBoxGroupField.
     * @param {Object} option A JSON object, which can have the same properties that the config options
     * for the {@link PMUI.control.SelectableControl SelectableControl class} except by "mode",
     it will always be override by the value "checkBox".
     * @chainable
     */
    CheckBoxGroupField.prototype.addOption = function (option) {
        var newOption;

        newOption = new PMUI.control.SelectableControl(jQuery.extend(true, option, {
            mode: 'checkbox',
            name: this.controls.length + 1,
            selected: option.selected
        }));

        if (newOption.isSelected()) {
            this.value = JSON.parse(this.value);
            this.value.push(newOption.getValue());
            this.value = JSON.stringify(this.value);
        }
        if (this.eventsDefined) {
            newOption.setOnChangeHandler(this.onChangeHandler()).getHTML();
            newOption.setOnBeforeChangeHandler(this.onBeforeChangeHandler());
            newOption.defineEvents();
        }
        this.auxControlStates[newOption.id] = newOption.disabled;
        this.controls.push(newOption);
        this.setControlPositioning(this.controlPositioning);

        return this;
    };
    /**
     * Removes one or more options (checkBox Button).
     * @param  {String|Number|PMUI.control.SelectableControl} option It can be:
     *
     *  - A number, in that case the parameter is used as the index of the item to be removed.
     *  - A String, in that case the parameter is used as the item's value,
     *  that means that everyone item that has that value will be removed.
     *  - An instance of {@link PMUI.control.SelectableControl SelectableControl} which must be a control of the field.
     * @chainable
     */
    CheckBoxGroupField.prototype.removeOption = function (item) {
        var itemToRemove,
            i;
        if (item instanceof PMUI.control.SelectableControl) {
            for (i = 0; i < this.controls.length; i += 1) {
                if (this.controls[i] === item) {
                    itemToRemove = i;
                    break;
                }
            }
        } else if (typeof item === 'string') {
            for (i = 0; i < this.controls.length; i += 1) {
                if (this.controls[i].id === item) {
                    itemToRemove = this.controls[i];
                    break;
                }
            }
        } else {
            itemToRemove = item;
        }
        if (typeof itemToRemove === 'number') {
            itemToRemove = this.controls[itemToRemove];
            delete this.auxControlStates[itemToRemove.id];
            jQuery(itemToRemove.html).detach();
            this.controls.splice(itemToRemove, 1);
            this.setControlPositioning(this.controlPositioning);
        }
        return this;
    };
    /**
     * It clears all the options from the field.
     * @chainable
     */
    CheckBoxGroupField.prototype.clearOptions = function () {
        while (this.controls.length) {
            this.removeOption(0);
        }

        return this;
    };
    /**
     * Update the field's value property from the controls
     * the parameter value is a clean array;
     * @protected
     * @chainable
     */
    CheckBoxGroupField.prototype.updateValueFromControls = function () {
        var value = [], i;

        for (i = 0; i < this.controls.length; i += 1) {
            if (this.controls[i].isSelected()) {
                value.push(this.controls[i].getValue());
            }
        }

        this.value = JSON.stringify(value);
        this.data.setValue(this.value);

        return this;
    };
    /**
     * Sets the value to the field's controls.
     * @protected
     * @param {String} value
     * @chainable
     */
    CheckBoxGroupField.prototype.setValueToControls = function (value) {
        var i, j, values, controls, controlsLength;

        try {
            value = value.replace(/'/g, "\"");
            values = JSON.parse(value);
        } catch (e) {
            values = '[]';
        }

        controlsLength = (controls = this.controls.slice(0)).length;
        for (i = 0; i < controlsLength; i += 1) {
            controls[i].deselect();
        }
        for (i = 0; i < values.length; i += 1) {
            for (j = 0; j < controlsLength; j += 1) {
                if (controls[j].getValue() === values[i]) {
                    controls[j].select();
                    controls.splice(j, 1);
                    controlsLength -= 1;
                    j -= 1;
                }
            }
        }
        return this;
    };
    /**
     * Evaluates the required validation.
     * @return {Boolean} Returns true if the validation passes otherwise returns false.
     */
    CheckBoxGroupField.prototype.evalRequired = function () {
        var valid = true, value;
        value = JSON.parse(this.getValue());
        if (this.required && value.length === 0) {
            this.showMessage(this.requiredMessage, "error");
            valid = false;
        } else {
            this.hideMessage();
        }
        return valid;
    };

    CheckBoxGroupField.prototype.isValid = function () {
        var valid = true,
            validator;

        valid = valid && this.evalRequired();
        if (!valid) {
            $(this.controlTable).addClass('error');
            return valid;
        }
        $(this.controlTable).removeClass('error');
        for (validator in this.validators) {
            if (this.validators.hasOwnProperty(validator)) {
                valid = valid && this.validators[validator].isValid();
                if (!valid) {
                    this.message.setText(this.validators[validator].errorMessage);
                    return valid;
                }
            }
        }
        return valid;
    };
    /**
     * @inheritdoc
     */
    CheckBoxGroupField.prototype.getValue = function (format) {
        var castFormat = format || this.valueType,
            controls = this.controls,
            i,
            res = [];

        for (i = 0; i < controls.length; i += 1) {
            if (controls[i].isSelected()) {
                res.push(PMUI.castValue(controls[i].getValue(), castFormat));
            }
        }
        return JSON.stringify(res);
    };
    /**
     * Disable the option
     * @param {number|string} disable If the value is part of group CheckBox
     * @chainable
     */
    CheckBoxGroupField.prototype.disableOption = function (value) {
        var i;
        for (i = 0; i < this.controls.length; i += 1) {
            if (this.controls[i].value == value || this.controls[i].label == value || value == i) {
                this.controls[i].disable(true);
                return this;
            }
        }
        throw new Error('the value send is not part of group CheckBox');
    };
    /**
     * Enable the option
     * @param {number|string} disable If the value is part of group CheckBox
     * Enables a field's option.
     */
    CheckBoxGroupField.prototype.enableOption = function (value) {
        var i;
        for (i = 0; i < this.controls.length; i += 1) {
            if (this.controls[i].value == value || this.controls[i].label == value || value == i) {
                this.controls[i].disable(false);
                return this;
            }
        }
        throw new Error('the value send is not part of group CheckBox');
    };

    CheckBoxGroupField.prototype.createHTML = function () {
        if (this.html) {
            return this.html;
        }
        CheckBoxGroupField.superclass.prototype.createHTML.call(this)
        if (this.disabled) {
            this.disable();
        }
        return this.html;
    };

    CheckBoxGroupField.prototype.checkedOption = function (option) {
        var i;
        for (i = 0; i < this.controls.length; i += 1) {
            if (option === i || option === this.controls[i].label || option === this.controls[i].value) {
                if (!this.controls[i].selected) {
                    this.controls[i].select();
                } else {
                    this.controls[i].deselect();
                }
                return this;
            }
        }
        return this;
    };

    CheckBoxGroupField.prototype.defineEvents = function () {
        var that = this;
        CheckBoxGroupField.superclass.prototype.defineEvents.call(this);
        this.addEvent('click').listen(this.dom.labelTextContainer, function (e) {
            if (that.controls.length) {
                $(that.controls[0].html).find('input')[0].focus();
            }
        });
        return this;
    };
    /**
     * @method getOptions
     * @ obtains under each option and configuration
     * @chainable
     */
    CheckBoxGroupField.prototype.getOptions = function () {
        var i,
            options = [],
            option;

        for (i = 0; i < this.controls.length; i += 1) {
            option = {};
            option["label"] = this.controls[i].label;
            option["value"] = this.controls[i].value;
            option["disabled"] = this.controls[i].disabled;
            option["selected"] = this.controls[i].selected;
            options.push(option);
        }
        return options;
    };

    CheckBoxGroupField.prototype.getValueFromControls = function () {
        var value = [],
            i;

        for (i = 0; i < this.controls.length; i += 1) {
            if (this.controls[i].isSelected()) {
                value.push(this.controls[i].getValue());
            }
        }

        this.value = JSON.stringify(value);
        this.data.setValue(this.value);
        return this.value;
    };
    PMUI.extendNamespace('PMUI.field.CheckBoxGroupField', CheckBoxGroupField);

    if (typeof exports !== "undefined") {
        module.exports = CheckBoxGroupField;
    }
}());
(function () {
    /**
     * @class  PMUI.field.RadioButtonGroupField
     * Field whose value can be selected from a group of options.
     * @extends PMUI.form.Field
     *
     *      Usage example:
     *
     *      @example
     *      var a;
     *      $(function() {
     *          a = new PMUI.field.RadioButtonGroupField({
     *              label: "Some Text",
     *              controlPositioning: 'vertical',
     *              maxDirectionOptions: 2,
     *              options: [
     *                  {
     *                      label: "opt1",
     *                      value: "1"
     *                  },
     *                  {
     *                      label: "opt2",
     *                      value: "2"
     *                  },
     *                  {
     *                      label: "opt3",
     *                      value: "3"
     *                  }
     *              ],
     *              onChange: function(newVal, oldVal) {
     *                  console.log("The value for the field  \"" + this.getLabel() 
     *                      + "\": has change from \"" + oldVal + "\" to \"" + newVal + "\"");
     *              },
     *              required: true,
     *              value: "2"
     *          });
     *          document.body.appendChild(a.getHTML());
     *      });
     *
     *
     * The example above will generate a field with 3 radio buttons.
     *
     * @cfg {Array} [options=[]] An array in which every element is a JSON object with the same structure required in
     * the {@link PMUI.field.RadioButtonGroupField#addOption addOption() method}.
     * @cfg {String} [controlPositioning="vertical"] A string that determines the direction for the options to be added
     * in the field. This string can have one of the following values:
     *
     * - "horizontal" (default), in this case all the options will be included  in horizontal order.
     * - "vertical", in this case all the options will be included in vertical order.
     * @cfg {Number} [maxDirectionOptions=1] The maximum number of options to be added in the current direction
     * (set by the {@link PMUI.control.RadioButtonGroupField#cfg-controlPositioning controlPositioning config option}).
     */
    var RadioButtonGroupField = function (settings) {
        RadioButtonGroupField.superclass.call(this, settings);
        /**
         * @property {String} controlPositioning The direction for the options to be added in the field.
         * @readonly
         */
        this.controlPositioning = null;
        /**
         * @property {Number} maxDirectionOptions The max number of options that can be in the current options
         * direction.
         * @readonly
         */
        this.maxDirectionOptions = null;
        /**
         * The status if the controls to be saved when the field is switch between enabled/disabled.
         * @type {Object}
         * @private
         */
        this.auxControlStates = {};
        this.controlTable = null;
        RadioButtonGroupField.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.form.Field', RadioButtonGroupField);

    RadioButtonGroupField.prototype.type = "RadioButtonGroupField";

    RadioButtonGroupField.prototype.init = function (settings) {
        var defaults = {
            options: [],
            controlPositioning: "vertical",
            maxDirectionOptions: 1
        };

        jQuery.extend(true, defaults, settings);

        this.setOptions(defaults.options)
            .setMaxDirectionOptions(defaults.maxDirectionOptions)
            .setControlPositioning(defaults.controlPositioning);
    };
    /**
     * Enables the field. Notice that the controls that initially were disabled will continue being disabled.
     * @chainable
     */
    RadioButtonGroupField.prototype.enable = function () {
        var key,
            i,
            controlsLength = this.controls.length,
            controls = this.controls;

        RadioButtonGroupField.superclass.prototype.enable.call(this);
        this.disabled = false;
        for (i = 0; i < controlsLength; i += 1) {
            controls[i].disable(this.auxControlStates[controls[i].id]);
        }
        return this;
    };
    /**
     * Sets the max number of controls that can be in the current direction (the direction is set by the
     * RadioButtonGroupField's
     * {@link PMUI.field.RadioButtonGroupField#setControlPositioning setControlPositioning() method}).
     * @param {Number} max It should be an integer, otherwise it will be floor rounded. If the value is equal or minor
     * than 0 it means there's no limit.
     */
    RadioButtonGroupField.prototype.setMaxDirectionOptions = function (max) {
        if (typeof max === 'number') {
            this.maxDirectionOptions = Math.floor(max);
            if (this.html) {
                this.setControlPositioning(this.controlPositioning);
            }
        } else {
            throw new Error("setMaxDirectionOptions(): it only accepts number values.");
        }

        return this;
    };
    /**
     * Removes one or more options (radio button)
     * @param  {String|Number|PMUI.control.SelectableControl} option It can be:
     *
     *  - A number, in that case the parameter is used as the index of the item to be removed.
     *  - A String, in that case the parameter is used as the item's value,
     *  that means that everyone item that has that value will be removed.
     *  - An instance of {@link PMUI.control.SelectableControl SelectableControl} which must be a control of the field.
     * @chainable
     */
    RadioButtonGroupField.prototype.removeOption = function (item) {
        var itemToRemove,
            i;

        if (item instanceof PMUI.control.SelectableControl) {
            for (i = 0; i < this.controls.length; i += 1) {
                if (this.controls[i] === item) {
                    itemToRemove = i;
                    break;
                }
            }
        } else if (typeof item === 'string') {
            for (i = 0; i < this.controls.length; i += 1) {
                if (this.controls[i].id === item) {
                    itemToRemove = this.controls[i];
                    break;
                }
            }
        } else {
            itemToRemove = item;
        }
        if (typeof itemToRemove === 'number') {
            itemToRemove = this.controls[itemToRemove];
            delete this.auxControlStates[itemToRemove.id];
            jQuery(itemToRemove.html).detach();
            this.controls.splice(itemToRemove, 1);
            this.setControlPositioning(this.controlPositioning);
        }
        return this;
    };
    /**
     * It clears all the options from the field.
     * @chainable
     */
    RadioButtonGroupField.prototype.clearOptions = function () {
        while (this.controls.length) {
            this.removeOption(0);
        }

        return this;
    };
    /**
     * It overrides the Field's {@link PMUI.form.Field#setControlPositioning setControlPositioning() method}.
     * This new implementation sets the direction for the control addition: horizontal or vertical.
     * @param {String} positioning It can be "horizontal" or "vertical".
     */
    RadioButtonGroupField.prototype.setControlPositioning = function (positioning) {
        var errorMessage = "The setControlPositioning() method only accepts \"horizontal\" or \"vertical\" as value.",
            table,
            tbody,
            cell,
            row,
            i,
            column,
            rowIndex;

        if (typeof positioning === 'string') {
            if (!(positioning === 'horizontal' || positioning === 'vertical')) {
                return this;
            }
            this.controlPositioning = positioning;
            if (this.html && this.controls) {
                for (i = 0; i < this.controls.length; i += 1) {
                    jQuery(this.controls[i].getHTML()).detach();
                }
                $(this.dom.controlContainer).empty();
                table = PMUI.createHTMLElement("table");
                table.className = 'pmui-field-control-table';
                tbody = PMUI.createHTMLElement("tbody");
                if (positioning === 'horizontal') {
                    row = PMUI.createHTMLElement("tr");
                    for (i = 0; i < this.controls.length; i += 1) {
                        cell = PMUI.createHTMLElement('td');
                        this.controls[i].getHTML();
                        this.controls[i].control.tabIndex = i;
                        cell.appendChild(this.controls[i].getHTML());
                        row.appendChild(cell);
                        if (this.maxDirectionOptions > 0 && (i + 1) % this.maxDirectionOptions === 0) {
                            tbody.appendChild(row);
                            row = PMUI.createHTMLElement("tr");
                        }
                    }
                    tbody.appendChild(row);
                } else {
                    column = 0;
                    for (i = 0; i < this.controls.length; i += 1) {
                        cell = PMUI.createHTMLElement('td');
                        this.controls[i].getHTML();
                        this.controls[i].control.tabIndex = i;
                        cell.appendChild(this.controls[i].getHTML());
                        rowIndex = this.maxDirectionOptions === 0 ? i : i % this.maxDirectionOptions;

                        row = jQuery(tbody).find('tr').eq(rowIndex).get(0);
                        if (!row) {
                            row = PMUI.createHTMLElement('tr');
                            tbody.appendChild(row);
                        }
                        row.appendChild(cell);
                    }
                }
                this.controlTable = table;
                table.appendChild(tbody);
                this.dom.controlContainer.appendChild(table);
                this.dom.controlContainer.appendChild(this.helper.getHTML());
            }
        }

        return this;
    };
    /**
     * Adds a new option to the field.
     * @param {Object} option A JSON object, which can have the same properties that the config options
     * for the {@link PMUI.control.SelectableControl SelectablecControl class} except by "mode",
     * it will always be override by the value "radio".
     * @chainable
     */
    RadioButtonGroupField.prototype.addOption = function (option) {
        var newOption,
            settings = {
                mode: 'radio',
                name: this.name,
                selected: option.selected
            };

        newOption = new PMUI.control.SelectableControl(jQuery.extend(true, option, settings));
        if (this.eventsDefined) {
            newOption.setOnChangeHandler(this.onChangeHandler()).getHTML();
            newOption.defineEvents();
        }
        this.auxControlStates[newOption.id] = newOption.disabled;
        this.controls.push(newOption);
        this.setControlPositioning(this.controlPositioning);

        return this;
    };
    /**
     * Sets all the options for the control.
     * @param {Array} options An array in which every element is a JSON object with the same structure required in the
     * {@link PMUI.field.RadioButtonGroupField#addOption addOption() method}.
     * @chainable
     */
    RadioButtonGroupField.prototype.setOptions = function (options) {
        var i = 0;
        if (jQuery.isArray(options)) {
            for (i = 0; i < options.length; i += 1) {
                this.addOption(options[i]);
            }
        } else {
            throw new Error("setOptions(): the supplied argument must be an array.");
        }

        return this;
    };
    /**
     * Update the field's value property from the controls
     * @protected
     * @chainable
     */
    RadioButtonGroupField.prototype.updateValueFromControls = function () {
        var value = '',
            i;

        for (i = 0; i < this.controls.length; i += 1) {
            if (this.controls[i].isSelected()) {
                value = this.controls[i].getValue();
                break;
            }
        }

        this.value = value;
        this.data.setValue(this.value);

        return this;
    };
    /**
     * Sets the value to the field's controls.
     * @protected
     * @param {String} value
     * @chainable
     */
    RadioButtonGroupField.prototype.setValueToControls = function (value) {
        var i;

        for (i = 0; i < this.controls.length; i += 1) {
            if (this.controls[i].getValue() === value) {
                this.controls[i].select();
            }
        }
        return this;
    };
    /**
     * @inheritdoc
     */
    RadioButtonGroupField.prototype.getValueFromControls = function () {
        var value = '',
            i;

        for (i = 0; i < this.controls.length; i += 1) {
            if (this.controls[i].isSelected()) {
                value += ' ' + this.controls[i].getValue();
            }
        }
        return value.substr(1);
    };
    RadioButtonGroupField.prototype.createHTML = function () {
        if (this.html) {
            return this.html;
        }
        RadioButtonGroupField.superclass.prototype.createHTML.call(this)
        if (this.disabled) {
            this.disable();
        }
        return this.html;
    };

    /**
     * @method selection Option
     * @param  {Number|String} value This is the index or the name of the option that will be selected
     * @chainable
     */
    RadioButtonGroupField.prototype.selectOption = function (value) {
        var i;

        for (i = 0; i < this.controls.length; i += 1) {
            if (value == this.controls[i].label || value == i) {
                this.setValue(this.controls[i].value);
                return this;
            }
        }
        return this;
    };

    RadioButtonGroupField.prototype.disableOption = function (value) {
        var i;

        for (i = 0; i < this.controls.length; i += 1) {
            if (this.controls[i].value === value || this.controls[i].label === value || value === i) {
                this.controls[i].disable(true);
                return this;
            }
        }
        throw new Error('the value send is not part of group Radio');
    };

    RadioButtonGroupField.prototype.enableOption = function (value) {
        var i;

        for (i = 0; i < this.controls.length; i += 1) {
            if (this.controls[i].value === value || this.controls[i].label === value || value === i) {
                this.controls[i].disable(false);
                return this;
            }
        }
        throw new Error('the value send is not part of group Radio');
    };

    RadioButtonGroupField.prototype.defineEvents = function () {
        var that = this;

        if (this.html && !this.eventsDefined) {
            RadioButtonGroupField.superclass.prototype.defineEvents.call(this);
            this.addEvent('click').listen(this.dom.labelTextContainer, function (e) {
                if (that.controls.length) {
                    $(that.controls[0].html).find('input')[0].focus();
                }
            });
            this.eventsDefined = true;
        }
        return this;
    };
    RadioButtonGroupField.prototype.isValid = function () {
        var valid = true,
            validator;

        valid = valid && this.evalRequired();
        if (!valid) {
            $(this.controlTable).addClass('error');
            return valid;
        }
        $(this.controlTable).removeClass('error');
        for (validator in this.validators) {
            if (this.validators.hasOwnProperty(validator)) {
                valid = valid && this.validators[validator].isValid();
                if (!valid) {
                    this.message.setText(this.validators[validator].errorMessage);
                    return valid;
                }
            }
        }
        return valid;
    };
    /**
     * @method getOptions
     * @ obtains under each option and configuration
     * @chainable
     */
    RadioButtonGroupField.prototype.getOptions = function () {
        var i,
            options = [],
            option;

        for (i = 0; i < this.controls.length; i += 1) {
            option = {};
            option["label"] = this.controls[i].label;
            option["value"] = this.controls[i].value;
            option["disabled"] = this.controls[i].disabled;
            option["selected"] = this.controls[i].selected;
            options.push(option);
        }
        return options;
    };

    RadioButtonGroupField.prototype.getValueFromControls = function () {
        var value = '', i;

        for (i = 0; i < this.controls.length; i += 1) {
            if (this.controls[i].isSelected()) {
                value = this.controls[i].getValue();
                break;
            }
        }

        this.value = value;
        this.data.setValue(this.value);

        return this.value;
    };

    PMUI.extendNamespace('PMUI.field.RadioButtonGroupField', RadioButtonGroupField);

    if (typeof exports !== "undefined") {
        module.exports = RadioButtonGroupField;
    }
}());
(function () {
    /**
     * @class  PMUI.field.DateTimeField
     * @extends {PMUI.form.Field}
     * This field is to manipulate the [PMI.control.DateTimeControl]
     *
     * example:
     *
     *      @example
     *      var dateTimePicker;
     *          $(function() {
     *              dateTimePicker = new PMUI.field.DateTimeField(
     *                  {
     *                      label:'Calendar',
     *                      helper: 'This is calendar Gregorian',
     *                      value: new Date(),
     *                      datetime : true,
     *                      dateFormat: 'M dd yy',
     *                      minDate: -90,
     *                      maxDate: "+1y -1m -4d",
     *                      firstDay: 1,
     *                      months: {
     *                          "january": {
     *                              name: "Enero",
     *                              shortname: "Ene"
     *                          },
     *                          "february": {
     *                              name: "Febrero",
     *                              shortname: "Feb"
     *                          },
     *                          "march": {
     *                              name: "Marzo",
     *                              shortname: "Mar"
     *                          },
     *                          "april": {
     *                              name: "Abril",
     *                              shortname: "Abr"
     *                          },
     *                          "may": "May",
     *                          "june": "Junio",
     *                          "july": "July",
     *                          "august": "Agosto",
     *                          "september": "Septiembre",
     *                          "october": "Octubre",
     *                          "november": "Noviembre",
     *                          "december": "Diciembre"
     *                      },
     *                      days: {
     *                          "sunday": {
     *                              name: "Domingo",
     *                              shortname: "Do"
     *                          },
     *                          "monday": {
     *                              name: "Lunes",
     *                              shortname: "Lu"
     *                          },
     *                          "tuesday": {
     *                              name: "Martes",
     *                              shortname: "Ma"
     *                          },
     *                          "wednesday": {
     *                              name: "Miércoles",
     *                              shortname: "Mi"
     *                          },
     *                          "thursday": {
     *                              name: "Jueves",
     *                              shortname: "Ju"
     *                          },
     *                          "friday": "Viernes",
     *                          "saturday": "Sábado"
     *                      }
     *                  }
     *              );
     *              document.body.appendChild(dateTimePicker.getHTML());
     *              dateTimePicker.defineEvents();
     *          });
     *
     * @constructor Creates an instance of the class DateTimeField.
     * @param {Object} options Initialization options.
     * @cfg {Boolean} [datetime=false] If the [PMUI.control.DateTimeControl] You can see more about the configuration
     * in {@link PMUI.control.DateTimeControl#cfg-datetime datetime}
     * @cfg {String} [dateFormat="yy-mm-dd H:i:s"|"yy-mm-dd"],necessary to set the date format property of the control
     * [PMUI.control.DateTimeControl]
     * You can see more about the configuration in  {@link PMUI.control.DateTimeControl#cfg-dateFormat dateFormat}
     * @cfg {Object} [months={"january": "January", "february": "February", "march": "March", "april": "April",
     * "may": "May", "june": "June", "july": "July", "august": "August", "september": "September",
     * "october": "October", "november": "November", "december": "December"}], A JSON object to set the names and
     * shortnames for every month in year.
     * You can see more about the configuration in {@link PMUI.control.DateTimeControl#cfg-months months}
     * @cfg {Object} [days={"sunday": "Sunday","monday": "Monday","tuesday": "Tuesday","wednesday": "Wednesday",
     * "thursday": "Thursday","friday": "Friday","saturday": "Saturday"}], A JSON object to set the name and shortname
     * for every day of week.
     * You can see more about the configuration in {@link PMUI.control.DateTimeControl#cfg-days days}
     * @cfg {String|Number} [minDate=-365] A value which sets the min selectable date for the calendar. You can see
     * more about the configuration in
     * {@link PMUI.control.DateTimeControl#cfg-minDate minDate}
     * @cfg {String|Number} [maxDate=365] A value which sets the max selectable date for the calendar.
     * @cfg {Number} [firstDay=0] Sets the first day of week. You can use numbers from 0 to 6, 0 means Sunday, 1 means
     * Monday and so on.
     * @cfg {String} [returnFormat="UTC"] Specifies the date format that will be used for the returning date.
     * The valid values are:
     *
     * - "UTC", returns a date in the format: yyyy-mm-ddTHH:ii:ss-HH:mm (i.e. 2013-08-31T00:08:00+04:00).
     * - "@" or "timestamp", returns a date in timestamp format.
     * - [any other valid format string], will return the date using the string as the dateformat. This string can
     * contain any of the wilcards specified in the
     * {@link PMUI.control.DateTimeControl#cfg-dateFormat dateFormat config option}.
     */
    var DateTimeField = function (settings) {
        DateTimeField.superclass.call(this, settings);
        /**
         * The default date format to be used in the returning date. Set by the {@link #cfg-returnFormat} config option
         * and the {@link #method-setReturnFormat setReturnFormat()}  method.
         * @type {String}
         */
        this.returnFormat = null;
        DateTimeField.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.form.Field', DateTimeField);

    DateTimeField.prototype.type = "DateTimeField";

    DateTimeField.prototype.init = function (settings) {
        var defaults = {
            datetime: false,
            dateFormat: settings && settings.datetime ? 'yy-mm-dd H:i:s' : 'yy-mm-dd',
            months: {
                "january": "January".translate(),
                "february": "February".translate(),
                "march": "March".translate(),
                "april": "April".translate(),
                "may": "May".translate(),
                "june": "June".translate(),
                "july": "July".translate(),
                "august": "August".translate(),
                "september": "September".translate(),
                "october": "October".translate(),
                "november": "November".translate(),
                "december": "December".translate()
            },
            days: {
                "sunday": "Sunday".translate(),
                "monday": "Monday".translate(),
                "tuesday": "Tuesday".translate(),
                "wednesday": "Wednesday".translate(),
                "thursday": "Thursday".translate(),
                "friday": "Friday".translate(),
                "saturday": "Saturday".translate()
            },
            minDate: -365,
            maxDate: 365,
            firstDay: 0,
            returnFormat: 'UTC'
        };

        jQuery.extend(true, defaults, settings);

        this.setDateFormat(defaults.dateFormat)
            .setMonths(defaults.months)
            .setDays(defaults.days)
            .setMinDate(defaults.minDate)
            .setMaxDate(defaults.maxDate)
            .setFirstDay(defaults.firstDay)
            .setReturnFormat(defaults.returnFormat)
            .setValue(defaults.value)
            .visibleDateTime(defaults.datetime);
    };
    /**
     * Enables/disabled the calendar's [PMUI.control.DateTimeControl] time supporting.
     * @param  {Boolean} visible If it's true, then the time supporting is enabled, otherwise it's disabled.
     * @chainable
     */
    DateTimeField.prototype.visibleDateTime = function (visible) {
        this.controls[0].visibleDateTime(visible);
        return this;
    };
    /**
     * Returns the [PMUI.control.DateTimeControl] maximum selectable date.
     * @param {String} [format="UTC"] The format to applied to the returning date.
     * @return {String} The maximum selectable date in string format.
     */
    DateTimeField.prototype.getMaxDate = function (format) {
        return this.controls[0].getMaxDate(format);
    };
    /**
     * Returns the [PMUI.control.DateTimeControl] minimun selectable date.
     * @param {String} [format="UTC"] The format to applied to the returning date.
     * @return {String} The maximum selectable date in string format.
     */
    DateTimeField.prototype.getMinDate = function (format) {
        return this.controls[0].getMinDate(format);
    };
    /**
     * Sets the date formatr to be used when you get the value from the object.
     * @param {String} format The valid values are:
     *
     * - "UTC", returns a date in the format: yyyy-mm-ddTHH:ii:ss-HH:mm (i.e. 2013-08-31T00:08:00+04:00).
     * - "@" or "timestamp", returns a date in timestamp format.
     * - [any other valid format string], will return the date using the string as the dateformat. This string can
     * contain any of the wilcards specified in the
     * {@link PMUI.control.DateTimeControl#cfg-dateFormat dateFormat config option}.
     * @chainable
     */
    DateTimeField.prototype.setReturnFormat = function (format) {
        if (typeof format === 'string') {
            this.returnFormat = format;
        } else {
            throw new Error("setReturnFormat(): the parameter must be a string.");
        }

        return this;
    };
    /**
     * Returns the day index of the first day of week.
     * @return {Number} A number refering a day: 0 for Sunday, 1 for Monday and so on.
     */
    DateTimeField.prototype.getFirstDay = function () {
        return this.controls[0].getFirstDay();
    };
    /**
     * Sets the value to the field's controls.
     * @protected
     * @param {String} value
     * @param {String} utc
     * @chainable
     */
    DateTimeField.prototype.setValueToControls = function (value, utc) {
        this.controls[0].setValue(value, utc);
        return this;
    };
    /**
     * Sets the field's value.
     * @param {String} value
     * @param {String} utc
     */
    DateTimeField.prototype.setValue = function (value, utc) {
        value = value || "";
        if (value instanceof Date || typeof value === 'string' || typeof value === 'number') {
            if (this.controls[0]) {
                this.setValueToControls(value, utc);
                this.value = this.controls[0].getValue(this.returnFormat);
                this.data.setValue(this.value);
            }
        } else {
            throw new Error("The setValue() method only accepts string values!");
        }
        return this;
    };
    /**
     * Sets the first day of week.
     * @param {Number} day Use 0 for Sunday, 1 for Monday, 2 for Tuesday and so on!.
     */
    DateTimeField.prototype.setFirstDay = function (day) {
        this.controls[0].setFirstDay(day);
        return this;
    };
    /**
     * Sets the format for the date to be displayed in the control's textbox control.
     * You can see more about here [PMUI.control.DateTimeControl] in method
     * {@link PMUI.control.DateTimeControl#setDateFormat setDateFormat}
     **/
    DateTimeField.prototype.setDateFormat = function (dateFormat) {
        this.controls[0].setDateFormat(dateFormat);
        return this;
    };
    /**
     * Sets the minimum date the [PMUI.control.DateTimeControl] can accept as a selectable one.
     * You can see more about here [PMUI.control.DateTimeControl] in method
     * {@link PMUI.control.DateTimeControl#setMinDate setMinDate}
     */
    DateTimeField.prototype.setMinDate = function (date) {
        this.controls[0].setMinDate(date);
        return this;
    };
    /**
     * Sets the max date the [PMUI.control.DateTimeControl] can accept as a selectable one.
     * You can see more about here in the class [PMUI.control.DateTimeControl] in method
     {@link PMUI.control.DateTimeControl#setMaxDate setMaxDate}
     */
    DateTimeField.prototype.setMaxDate = function (date) {
        this.controls[0].setMaxDate(date);
        return this;
    };
    /**
     * Sets the months the [PMUI.control.DateTimeControl] names/shortnames to be used by the calendar.
     * @param {Object} months An object with the same structure that the [PMUI.control.DateTimeControl]
     * method {@link PMUI.control.DateTimeControl#setMonths setMonths}.
     * @chainable
     */
    DateTimeField.prototype.setMonths = function (months) {
        this.controls[0].setMonths(months);
        return this;
    };
    /**
     * Sets the [PMUI.control.DateTimeControl] name/shortnames for the days to be used in the calendar.
     * @param {Object} days A JSON object with the same structure than the [PMUI.control.DateTimeControl]
     * method {@link PMUI.control.DateTimeControl#setDays setDays}
     */
    DateTimeField.prototype.setDays = function (days) {
        this.controls[0].setDays(days);
        return this;
    };
    /**
     * Returns the formated date.
     * @return {String}
     */
    DateTimeField.prototype.getFormatedDate = function () {
        return this.controls[0].getFormatedDate();
    };
    /**
     * Returns the field's value.
     * @param  {String} [format] Specifies the date format that will be used for the returning date. If it isn't
     * specified, the {@link #property-returnFormat returnFormat} will be used instead.
     *
     * The valid values are:
     *
     * - "UTC", returns a date in the format: yyyy-mm-ddTHH:ii:ss-HH:mm (i.e. 2013-08-31T00:08:00+04:00).
     * - "@" or "timestamp", returns a date in timestamp format.
     * - [any other valid format string], will return the date using the string as the dateformat. This string can
     * contain any of the wilcards specified in the
     * {@link PMUI.control.DateTimeControl#cfg-dateFormat dateFormat config option}.
     * @return {String}
     */
    DateTimeField.prototype.getValue = function (format) {
        return this.controls[0].getValue(format || this.returnFormat);
    };
    /**
     * Sets the controls for the field.
     * Since this is an abstract method, it must be implemented in its non-abstract subclasses
     * @abstract
     * @private
     */
    DateTimeField.prototype.setControls = function () {
        if (this.controls.length) {
            return this;
        }
        this.controls.push(new PMUI.control.DateTimeControl());
        return this;
    };
    /**
     * Updates the current value from the controls which are part of the field
     * @chainable
     */
    DateTimeField.prototype.updateValueFromControls = function () {
        this.value = this.controls[0].getValue(this.returnFormat);
        this.data.setValue(this.value);
        return this;
    };

    PMUI.extendNamespace('PMUI.field.DateTimeField', DateTimeField);

    if (typeof exports !== "undefined") {
        module.exports = DateTimeField;
    }
}());

(function () {
    var TextAnnotationField = function (settings) {
        TextAnnotationField.superclass.call(this, settings);
        this.text = null;
        this._textType = null;
        this.text_Align = null;
        this._labelTag = null;
        TextAnnotationField.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.form.Field', TextAnnotationField);

    TextAnnotationField.prototype.type = "TextAnnotationField";

    TextAnnotationField.TEXT_TYPES = {
        Plain: 0,
        HTML: 1
    };

    TextAnnotationField.prototype.init = function (settings) {
        var defaults = {
            maxLength: 0,
            text: "",
            textType: TextAnnotationField.TEXT_TYPES.Plain,
            text_Align: "left"
        };

        $.extend(true, defaults, settings);
        this.setText(defaults.text);
        this.setTextType(defaults.textType);
        this.setText_Align(defaults.text_Align);
    };

    TextAnnotationField.prototype.setTextType = function (type) {
        switch (type) {
            case TextAnnotationField.TEXT_TYPES.Plain:
            case TextAnnotationField.TEXT_TYPES.HTML:
                this._textType = type;
                break;
            default:
                throw new Error("setTextType(): Invalid argument.");
        }
        this.setText(this.text);
        return this;
    };

    TextAnnotationField.prototype.setText = function (text) {
        if (typeof text !== "string") {
            throw new Error("setText(): The parameter is not valid");
        }
        this.text = text;
        if (this.html) {
            if (this._textType == TextAnnotationField.TEXT_TYPES.HTML) {
                jQuery(this._labelTag).html(this.text);
            } else {
                jQuery(this._labelTag).text(this.text);
            }
        }
        return this;
    };

    TextAnnotationField.prototype.setText_Align = function (value) {
        if (typeof value === "string") {
            this.text_Align = value;
        } else {
            throw new Error("the enter parameter is no valid");
        }
        return this;
    };

    TextAnnotationField.prototype.setValue = function () {
        return this;
    };

    TextAnnotationField.prototype.setControls = function () {
        return this;
    };

    TextAnnotationField.prototype.setHelper = function () {
        return this;
    };

    TextAnnotationField.prototype.createHTML = function () {
        var labelTag, textbefore, spanRequired, sp = "&nbsp;";
        if (this.html) {
            return this.html;
        }
        TextAnnotationField.superclass.superclass.prototype.createHTML.call(this);
        labelTag = PMUI.createHTMLElement('span');
        labelTag.className = 'pmui-field-label';
        labelTag.style.width = "100%";
        labelTag.style.textAlign = this.text_Align;
        this._labelTag = labelTag;
        this.html.appendChild(labelTag);

        this.setText(this.text);
        return this.html;
    };

    TextAnnotationField.prototype.isValid = function () {
        return true;
    };

    PMUI.extendNamespace('PMUI.field.TextAnnotationField', TextAnnotationField);

    if (typeof exports !== "undefined") {
        module.exports = TextAnnotationField;
    }

}());
(function () {
    /**
     * @class  PMUI.form.FormItemFactory
     * @extend PMUI.util.Factory
     * Extends the factory class to produce objects to be included in containers for forms.
     *
     * Its default products are:
     *
     * - {@link PMUI.form.FormPanel FormPanel} objects: using "panel".
     * - {@link PMUI.field.TextField TextField} objects: using "text".
     * - {@link PMUI.field.DropDownListField DropDownListField} objects: using "dropdown".
     * - {@link PMUI.field.RadioButtonGroupField RadioButtonGroupField} objects: using "radio".
     * - {@link PMUI.field.CheckBoxGroupField CheckBoxGroupField} objects: using "checkbox".
     * - {@link PMUI.field.TextAreaField TextAreaField} objects: using "textarea".
     * - {@link PMUI.field.DateTimeField DateTimeField} objects: using "datetime".
     *
     * @constructor
     * Creates a new instance od the class
     */
    var FormItemFactory = function () {
        FormItemFactory.superclass.call(this);
        FormItemFactory.prototype.init.call(this);
    };

    PMUI.inheritFrom('PMUI.util.Factory', FormItemFactory);

    FormItemFactory.prototype.init = function () {
        var defaults = {
            products: {
                "field": PMUI.form.Field,
                "panel": PMUI.form.FormPanel,
                "text": PMUI.field.TextField,
                "password": PMUI.field.PasswordField,
                "dropdown": PMUI.field.DropDownListField,
                "radio": PMUI.field.RadioButtonGroupField,
                "checkbox": PMUI.field.CheckBoxGroupField,
                "textarea": PMUI.field.TextAreaField,
                "datetime": PMUI.field.DateTimeField,
                "optionsSelector": PMUI.field.OptionsSelectorField,
                "upload": PMUI.field.UploadField,
                "buttonField": PMUI.field.ButtonField,
                "annotation": PMUI.field.TextAnnotationField
            },
            defaultProduct: "panel"
        };
        this.setProducts(defaults.products)
            .setDefaultProduct(defaults.defaultProduct);
    };

    PMUI.extendNamespace('PMUI.form.FormItemFactory', FormItemFactory);

}());
(function () {
    /**
     * @class PMUI.form.FormPanel
     * @extends PMUI.core.Panel
     * Class to handle form containers for {@link PMUI.form.Field Field objects},
     * {@link PMUI.form.FormPanel FormPanel objects}, and {@link PMUI.form.TabPanel TabPanel objects}.
     *
     *      @example
     *      var p;
     *      $(function() {
     *          p = new PMUI.form.FormPanel({
     *              width: 600, 
     *              height: 130,
     *              fieldset: true,
     *              legend: "my fieldset panel",
     *              items: [
     *                  {
     *                      pmType: "text",
     *                      label: "Name",
     *                      id: "123",
     *                      value: "",
     *                      placeholder: "insert your name",
     *                      name: "name"
     *                  },{
     *                      pmType: "text",
     *                      label: "Last name",
     *                      value: "",
     *                      placeholder: "your lastname here asshole!",
     *                      name: "lastname"
     *                  }, {
     *                      pmType: "panel",
     *                      layout: 'hbox',
     *                      items: [
     *                          {
     *                              pmType: "text",
     *                              label: "E-mail",
     *                              value: "",
     *                              name: "email"
     *                          },{
     *                              pmType: "text",
     *                              label: "Phone",
     *                              value: "555",
     *                              name: "phone"
     *                          }
     *                      ]
     *                  }
     *              ],
     *              layout: "vbox"
     *          });
     *          document.body.appendChild(p.getHTML());
     *          console.log("haha");
     *      });
     *
     * @cfg {PMUI.form.Form} [form=null] The {@link PMUI.form.Form Form} the object belongs to.
     * @cfg {Array} [items=[]] The array with the items to be contained by the object.
     *      //example
     *      {
     *         ......
     *         items: [
     *             {
     *                 pmType: "text",
     *                 label: "Name",
     *                 id: "123",
     *                 value: "",
     *                 placeholder: "insert your name",
     *                 name: "name"
     *             }, {
     *                 pmType: "text",
     *                 label: "Last name",
     *                 value: "",
     *                 placeholder: "your lastname here asshole!",
     *                 name: "lastname"
     *             }, {
     *                 pmType: "panel",
     *                 layout: 'hbox',
     *                 items: [
     *                     {
     *                         pmType: "text",
     *                         label: "E-mail",
     *                         value: "",
     *                         name: "email"
     *                     },{
     *                         pmType: "text",
     *                         label: "Phone",
     *                         value: "555",
     *                         name: "phone"
     *                     }
     *                 ]
     *             }
     *         ],
     *         ......
     *     });
     * @cfg {Boolean} [fieldset=false] If the panel will apply the fieldset behavior.
     * @cfg {String} [legend=""] The text for the legend to show in case the object applies the fieldset behavior.
     * (only applicable if the the object will apply the fieldset behavior).
     * @cfg {Number} [legendSize=12] The size for the text in the fieldset's legend.
     */
    var FormPanel = function (settings) {
        /**
         * @property {PMUI.util.ArrayList} fields
         * Object that contains all the object's direct children that are {@link PMUI.form.Field Field} objects.
         * @private
         */
        this.fields = new PMUI.util.ArrayList();
        /**
         * @property {PMUI.util.ArrayList} formPanels
         * Object that contains all the object's direct children that are {@link PMUI.form.FormPanel FormPanel}
         objects.
         * @private
         */
        this.formPanels = new PMUI.util.ArrayList();
        /**
         * @property {PMUI.util.ArrayList} tabPanels
         * Object that contains all the object's direct children that are {@link PMUI.form.TabPanel TabPanel} objects.
         * @private
         */
        this.tabPanels = new PMUI.util.ArrayList();

        FormPanel.superclass.call(this, settings);
        /**
         * @property {Boolean} [fieldset]
         * If the panel has the fieldset behavior.
         * @readonly
         */
        this.fieldset = null;
        /**
         * @property {PMUI.form.Form} [form=null] The {@link PMUI.form.Form Form} the object belongs to.
         */
        this.form = null;
        /**
         * @property {String} [legend=""] The text for the legend to show in case of the fieldset behavior
         will be applied.
         */
        this.legend = null;
        /**
         * @property {String} [legendElement=""] The HTML element for containt the legend to show in case of the
         fieldset behavior will be applied.
         */
        this.legendElement = null;
        /**
         * @property {Number} [fieldsetLegendSize] The size for the text showed in the fieldset legend.
         * @readonly
         */
        this.legendSize = null;
        this.onChange = null;
        this._onAvailabilityChange = null;

        FormPanel.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Panel', FormPanel);

    FormPanel.prototype.type = 'FormPanel';

    FormPanel.prototype.init = function (settings) {
        var defaults = {
            form: null,
            fieldset: false,
            legend: "",
            padding: (settings && settings.fieldset) ? "5px 10px" : "10px",
            borderWidth: (settings && settings.fieldset) ? 1 : "",
            legendSize: 12,
            onChange: null,
            onAvailabilityChange: null
        };

        jQuery.extend(true, defaults, settings);

        this.setPadding(defaults.padding)
            .setBorderWidth(defaults.borderWidth)
            .setForm(defaults.form)
            .setOnAvailabilityChange(defaults.onAvailabilityChange);

        this.form = defaults.form;
        this.fieldset = defaults.fieldset;
        this.legend = defaults.legend;
        this.legendSize = defaults.legendSize;
        this.onChange = defaults.onChange;

        if (this.fieldset) {
            this.setElementTag("fieldset");
        }
    };
    /**
     * Sets the function to be called when the availability on any contained item changes.
     * @param handler
     */
    FormPanel.prototype.setOnAvailabilityChange = function (handler) {
        if (!(typeof handler === 'function' || handler === null)) {
            throw new Error("onAvailabilityChange(): The parameter must be a function or NULL.");
        }
        this._onAvailabilityChange = handler;
        return this;
    };
    /**
     * Returns a field from the form panel.
     * @param  {String} name The name of the field to find.
     * @return {PMUI.form.Field|null}
     */
    FormPanel.prototype.getField = function (name) {
        var i,
            items = this.getItems(),
            field = null;

        for (i = 0; i < items.length; i += 1) {
            if (items[i] instanceof PMUI.form.Field) {
                if (items[i].getName() === name) {
                    return items[i];
                }
            } else {
                field = items[i].getField(name);
                if (field) {
                    return field;
                }
            }
        }
        return field;
    };
    /**
     * Returns an array of items depending on the parameters the method receives.
     * @param  {String} [filter=undefined]
     * A string which specifiest the bunch of items that will be returned.
     * It defaults to {undefined}, that means that only the direct child items will be in the returned array.
     *
     * Alternatively this param can take one of the following values:
     *
     * - 'fields': it will return only the child {@link PMUI.form.Field Field} items.
     * - 'formPanels': it will return only the child {@link PMUI.form.FormPanel FormPanel} items.
     * - 'tabPanels': it will return only the child {@link PMUI.form.TabPanel TabPanel} items.
     *
     * @param  {Boolean} [includeChildren=false]
     *
     * If the value is evaluated as false only the direct child items will be returned,
     * otherwise additionaly will be added the items for all child items.
     *
     * Note: This parameter only has effect when the [filter] parameter is provided.
     * @return {Array}
     */
    FormPanel.prototype.getItems = function (filter, includeChildren) {
        var res = [],
            items,
            size,
            i,
            targetArray,
            the_class;

        switch (filter) {
            case 'fields':
                the_class = PMUI.form.Field;
                targetArray = this.fields;
                break;
            case 'formPanels':
                the_class = PMUI.form.FormPanel;
                targetArray = this.formPanels;
                break;
            case 'tabPanels':
                the_class = PMUI.form.TabPanel;
                targetArray = this.tabPanels;
                break;
            default:
                return FormPanel.superclass.prototype.getItems.call(this);
        }

        if (includeChildren) {
            if (the_class) {
                items = this.items.asArray();
                size = items.length;
                for (i = 0; i < size; i += 1) {
                    if (items[i] instanceof the_class) {
                        res.push(items[i]);
                    } else {
                        res = res.concat(items[i].getItems(filter, true));
                    }
                }
            } else {
                throw new Error('getItems(): The valid values for the "filter" parameter are: "fields",' +
                    '"formPanels" or "tabPanels", received: ' + filter);
            }
        } else {
            res = targetArray.asArray().slice(0);
        }
        return res;
    };
    /**
     * Sets the form the form panel belongs to.
     * @param {PMUI.form.Form} form
     * @chainable
     */
    FormPanel.prototype.setForm = function (form) {
        if (form instanceof PMUI.form.Form) {
            this.form = form;
        }

        return this;
    };
    /**
     * Sets the {@link PMUI.util.Factory Factory} object for the FormPanel
     * @param {PMUI.util.Factory|Object} factory It can be a {@link PMUI.util.Factory Factory} object,
     or a JSON object.
     * If it is a JSON object then a {@link PMUI.form.FormItemFactory FormItemFactory}
     * is created using the JSON object as the config options.
     * @chainable
     * @private
     */
    FormPanel.prototype.setFactory = function (factory) {
        if (factory instanceof PMUI.util.Factory) {
            this.factory = factory;
        } else {
            this.factory = new PMUI.form.FormItemFactory(factory);
        }
        return this;
    };
    /**
     * Clear all the items.
     * @chainable
     */
    FormPanel.prototype.clearItems = function () {
        FormPanel.superclass.prototype.clearItems.call(this);
        if (this.fields) {
            this.fields.clear();
            this.formPanels.clear();
            this.tabPanels.clear();
        }
        return this;
    };
    /**
     * Sets the text for the legend to be displayed just in case of the fieldset behavior is beign applied.
     * @param {String} legend
     * @chainable
     */
    FormPanel.prototype.setLegend = function (legend) {
        if (typeof legend === 'string') {
            this.legend = legend;
            if (this.legendElement) {
                this.legendElement.textContent = legend;
            }
        } else {
            throw new Error("setLegend(): this method accepts string values as only parameter.");
        }

        return this;
    };
    /**
     * Creates the HTML element for the FormPanel.
     * @chainable
     */
    FormPanel.prototype.createHTML = function () {
        var legendElement,
            html;

        if (this.html) {
            return this.html;
        }

        FormPanel.superclass.prototype.createHTML.call(this);

        if (this.fieldset) {
            legendElement = PMUI.createHTMLElement('legend');
            legendElement.textContent = this.legend;
            legendElement.className = 'pmui-formpanel-legend';
            legendElement.style.fontSize = this.legendSize + "px";
            this.legendElement = legendElement;
            jQuery(this.html).prepend(legendElement);
        }
        return this.html;
    };
    /**
     * Returns the object's usable height.
     * @return {Number|String}
     */
    FormPanel.prototype.getUsableHeight = function () {
        var height = FormPanel.superclass.prototype.getUsableHeight.call(this);
        if (isNaN(height)) {
            return height;
        }
        if (this.fieldset) {
            height -= this.legendSize;
        }

        return height;
    };
    /**
     * Sets the internal handler function for the onChange Event
     * @return {Function} The handler function.
     * @private
     */
    FormPanel.prototype.onChangeHandler = function () {
        var that = this;
        return function (a, b, c) {
            var target,
                newValue,
                previousValue;

            if (this instanceof PMUI.form.Field) {
                target = this;
                newValue = a;
                previousValue = b;
            } else {
                target = a;
                newValue = b;
                previousValue = c;
            }
            if (typeof that.onChange === 'function') {
                that.onChange(target, newValue, previousValue);
            }
            if (this instanceof PMUI.form.Field && this.form) {
                (this.form.onChangeHandler())(target, newValue, previousValue);
            }
        };
    };
    FormPanel.prototype._getOnAvailabilityChangeHandler = function () {
        var that = this;
        return function (panel, enabled) {
            if (typeof that._onAvailabilityChange === 'function') {
                that._onAvailabilityChange(panel, enabled);
            }
        };
    };
    /**
     * Add an item to the object.
     * @param {Object|PMUI.form.Field|PMUI.form.FormPanel|PMUI.form.TabPanel} item
     * The item parameter can be:
     *
     * - A JSON object, in that case, it must have at least a "pmType" property
     * which can have any of the values specifed in the {@link PMUI.form.FormItemFactory FormItemFactory 
     documentation}.
     * - A {@link PMUI.form.Field Field} object.
     * - A {@link PMUI.form.FormPanel FormPanel} object.
     * - A {@link PMUI.form.TabPanel TabPanel} object.
     */
    FormPanel.prototype.addItem = function (item, index) {
        var itemToBeAdded;

        if (this.factory) {
            if (typeof item === 'object' && !item.layout) {
                item.layout = (this.layout && this.layout.type.toLowerCase()) || "vbox";
            }
            if (this.factory.isValidClass(item) || this.factory.isValidName(item.pmType)) {
                itemToBeAdded = this.factory.make(item);
            } else {
                throw new Error('Invalid item to add.');
            }
        }
        if (itemToBeAdded) {
            itemToBeAdded.setForm(this.form);
            itemToBeAdded.setDisplay("inline-block");
            if (itemToBeAdded instanceof PMUI.form.FormPanel) {
                itemToBeAdded.setOnAvailabilityChange(this._getOnAvailabilityChangeHandler());
                this.formPanels.insert(itemToBeAdded);
            } else if (itemToBeAdded instanceof PMUI.form.Field) {
                this.fields.insert(itemToBeAdded);
                if (this.form) {
                    this.form.data.addItem(itemToBeAdded.data);
                }
            }
            FormPanel.superclass.prototype.addItem.call(this, itemToBeAdded, index);
        }

        return this;
    };
    /**
     * Removes an item from the form panel.
     * @param  {String|Number|PMUI.core.Element} item
     * It can be:
     * - a String: in this case this string must be the id of the element to be removed.
     * - a Number: in this case this number must be the index of the element to be removed.
     * - a {@link PMUI.core.Element Element} object: in this case it will be removed
     * (if it is contained by the current form panel).
     * @chainable
     */
    FormPanel.prototype.removeItem = function (item) {
        var itemToRemove;

        if (item instanceof PMUI.core.Element) {
            itemToRemove = item;
        } else {
            if (typeof item === 'string') {
                itemToRemove = this.items.find("id", item.id);
            } else if (typeof item === 'number') {
                itemToRemove = this.items.get(item);
            }
        }
        if (itemToRemove) {
            if (itemToRemove instanceof PMUI.form.Field) {
                if (this.form) {
                    this.form.data.removeItem(itemToRemove.data);
                }
            }
            FormPanel.superclass.prototype.removeItem.call(this, itemToRemove);
        }

        return this;
    };
    /**
     * Returns true if the validation passes otherwise it returns false.
     * @return {Boolean}
     */
    FormPanel.prototype.isValid = function () {
        var items = this.items.asArray(),
            i,
            valid = true;

        for (i = 0; i < items.length; i += 1) {
            if (items[i].disabled) {
                continue;
            }
            valid = valid && items[i].isValid();
            if (!valid) {
                if (items[i] instanceof PMUI.form.Field) {
                    items[i].setFocus();
                }
                return valid;
            }
        }

        return valid;
    };

    FormPanel.prototype.disable = function () {
        FormPanel.superclass.prototype.disable.call(this);
        if (typeof this._onAvailabilityChange === 'function') {
            this._onAvailabilityChange(this, false);
        }
        return this;
    };

    FormPanel.prototype.enable = function () {
        FormPanel.superclass.prototype.enable.call(this);
        if (typeof this._onAvailabilityChange === 'function') {
            this._onAvailabilityChange(this, true);
        }
        return this;
    };

    PMUI.extendNamespace('PMUI.form.FormPanel', FormPanel);

    if (typeof exports !== "undefined") {
        module.exports = FormPanel;
    }
}());
(function () {
    /**
     * @class PMUI.form.Form
     * @extends PMUI.core.Panel
     *
     *  Usage example:
     *
     *      @example
     *      var f;
     *      $(function() {
     *          f = new PMUI.form.Form({
     *              onSubmit: function() {
     *                  console.log("submitting...");
     *              },
     *              onChange: function(field, newValue, previousValue) {
     *                  console.log("The field " + field.getName() + " has changed from \"" + previousValue + "\" to \"" + newValue + "\"");
     *              },
     *              items: [
     *                  {
     *                      pmType: "text",
     *                      label: "Name",
     *                      id: "123",
     *                      value: "",
     *                      placeholder: "insert your name",
     *                      name: "name",
     *                      helper: "Introduce your name",
     *                      required : true,
     *                      controlsWidth: 200
     *                  }, 
     *                  {
     *                      pmType: "datetime",
     *                      visible : false,
     *                      label: "birth date",
     *                      name: "birthdate"
     *                  },
     *                  {
     *                      pmType: "dropdown",
     *                      label: "age",
     *                      options: [
     *                          {
     *                              label: "",
     *                              value: ""
     *                          },
     *                          {
     *                              label: "from 0 to 7 years old",
     *                              value: "0-7"
     *                          },
     *                          {
     *                              label: "from 8 to 13 years old",
     *                              value: "8-13"
     *                          },
     *                          {
     *                              label: "from 14 to 19 years old",
     *                              value: "14-19"
     *                          },
     *                          {
     *                              label: "from 20 to 30 years old",
     *                              value: "20-30"
     *                          },
     *                          {
     *                              label: "from 31 to 45 years old",
     *                              value: "31-45"
     *                          },
     *                          {
     *                              label: "older than 45",
     *                              value: "46+"
     *                          }
     *                      ],
     *                      name: "age",
     *                      helper: "Select one of the options",
     *                      required: true,
     *                      onChange: function() {
     *                          console.log("The value has been changed");
     *                      },
     *                      controlsWidth: 100
     *                  },
     *                  {
     *                      pmType: "radio",
     *                      label: "Gender",
     *                      value: "m",
     *                      name: "gender",
     *                      required: true,
     *                      options: [
     *                          {
     *                              label: "Male",
     *                              value: "m"
     *                          },
     *                          {
     *                              label: "Female",
     *                              value: "f"
     *                          }
     *                      ]
     *                  }, 
     *                  {
     *                      pmType: "text",
     *                      label: "E-mail",
     *                      value: "",
     *                      name: "email",
     *                      helper: "you email here",
     *                      required: true,
     *                      validators: [
     *                          {
     *                              pmType: "regexp",
     *                              criteria: /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
     *                              errorMessage: "Please enter a valid email address"
     *                          }
     *                      ]
     *                  }, 
     *                  {
     *                      pmType: "checkbox",
     *                      label: "Things you like to do",
     *                      value: "[\"music\"]",
     *                      name: "hobbies",
     *                      helper: "Check up to 3 options",
     *                      options: [
     *                          {
     *                              label: "Music",
     *                              value: "music"
     *                          },
     *                          {
     *                              label: "Programming",
     *                              value: "programming"
     *                          },
     *                          {
     *                              label: "Bike ridding",
     *                              value: "bike"
     *                          },
     *                          {
     *                              label: "Gastronomy",
     *                              value: "gastronomy"
     *                          },
     *                          {
     *                              label: "Movies",
     *                              value: "movies"
     *                          }
     *                      ],
     *                      validators: [],
     *                      required: true
     *                  },
     *                  {
     *                      pmType: "panel",
     *                      fieldset: true,
     *                      legend: "Another Info",
     *                      layout: "vbox",
     *                      items: [
     *                          {
     *                              pmType: "panel",
     *                              layout: 'hbox',
     *                              items: [
     *                                  {
     *                                      pmType: "text",
     *                                      label: "Country",
     *                                      value: "Bolivia",
     *                                      name: "country"
     *                                  },
     *                                  {
     *                                      pmType: "text",
     *                                      label: "City",
     *                                      name: "city"
     *                                  }
     *                              ]
     *                          },
     *                          {
     *                              pmType: "panel",
     *                              layout: 'hbox',
     *                              items: [
     *                                  {
     *                                      pmType: "text",
     *                                      label: "Address",
     *                                      name: "address",
     *                                      placeholder: "Your address here"
     *                                  },
     *                                  {
     *                                      pmType: "text",
     *                                      label: "Zip Code",
     *                                      name: "zip"
     *                                  }
     *                              ]
     *                          }, 
     *                          {
     *                              pmType: "panel",
     *                              layout: 'vbox',
     *                              items: [
     *                                  {
     *                                      pmType: "textarea",
     *                                      label: "About you",
     *                                      name: "about"
     *                                  }
     *                              ]
     *                          }
     *                      ]
     *                  }
     *              ],
     *              footerItems: [
     *                  {
     *                      pmType: "button",
     *                      text: "Submit",
     *                      handler: function() {
     *                          f.submit();
     *                          console.log("submitting form...");
     *                      }
     *                  }, {
     *                      pmType: "button",
     *                      text: "Reset",
     *                      handler: function() {
     *                          f.reset();
     *                      }
     *                  }
     *              ]
     *          });
     *          document.body.appendChild(f.getHTML());
     *      });
     *
     * The example above will generate a form with 8 fields:
     * Name, Last Name, E-mail, Phone, Country, City, Address, Zip Code.
     * The first four ones are required and each one has a helper.
     * The email and Phone fields also have a validator for control the input.
     *
     * The latest four ones are contained by a fieldset.
     *
     * @cfg {String} title The title for the form.
     * @cfg {name} [name=<the object's id>] The name for the form.
     * @cfg {String} [encType="application/x-www-form-urlencoded"] The value for the form's encType attribute .
     * @cfg {Array} [items=[]] An array in which every element can be
     * a JSON Object or a supported object by Form:
     *
     * - In case to be a JSON object, it should includes a "pmType" property with a valid value.
     * The valid values for the pmType property are specified in the
     * {@link PMUI.form.FormItemFactory FormItemFactory documentation}.
     *
     * - In case of objects, use an object that the {@link PMUI.form.FormItemFactory FormItemFactory} can produce.
     *
     * Example:
     *     var form, items;
     *
     *     items = [
     *          {
     *              pmType: "text",
     *              label: "Name",
     *              id: "123",
     *              value: "",
     *              placeholder: "insert your name",
     *              name: "name",
     *              helper: "Introduce your name",
     *              required: true
     *          },
     *          {
     *              pmType: "text",
     *              label: "Last name",
     *              value: "",
     *              placeholder: "your lastname here asshole!",
     *              name: "lastname",
     *              helper: "Introduce your lastname",
     *              required: true
     *          }
     *      ];
     *
     *     form = new PMUI.form.Form({
     *         name: "My form",
     *         items: items
     *     });
     *
     * Depending on the type of element you add, it can contain a nested items property to define its child items:
     *
     *      f = new PMUI.form.Form({
     *          items: [
     *              .......
     *              {
     *                  pmType: "panel",
     *                  fieldset: true,
     *                  legend: "Another Info",
     *                  layout: "vbox",
     *                  items: [
     *                      {
     *                          pmType: "panel",
     *                          layout: 'hbox',
     *                          items: [
     *                              {
     *                                  pmType: "text",
     *                                  label: "Country",
     *                                  value: "Bolivia",
     *                                  name: "country"
     *                              },
     *                              {
     *                                  pmType: "text",
     *                                  label: "City",
     *                                  name: "city"
     *                              }
     *                          ]
     *                      },
     *                      {
     *                          pmType: "panel",
     *                          layout: 'hbox',
     *                          items: [
     *                              {
     *                                  pmType: "text",
     *                                  label: "Address",
     *                                  name: "address",
     *                                  placeholder: "Your address here"
     *                              },
     *                              {
     *                                  pmType: "text",
     *                                  label: "Zip Code",
     *                                  name: "zip"
     *                              }
     *                          ]
     *                      }
     *                  ]
     *              }
     *          ]
     *      });
     *
     * As you can see in the example above, only the container type objects can have an "item" property.
     *
     * @cfg {Number} [fontSize=12] The size to be use for the font in the form.
     * @cfg {Number} [width=600] The width for the form.
     * In case of using a String you only can use 'auto' or 'inherit' or ##px or ##% or ##em when ## is a number.
     * @cfg {Number} [height=400] The height for the form.
     * In case of using a String you only can use 'auto' or 'inherit' or ##px or ##% or ##em when ## is a number.
     * @cfg {Boolean} [visibleHeader=true] If the header is visible or not.
     * @cfg {String} [layout='vbox'] The layout type to apply to the form.
     * @cfg {Array} [buttons=[]] An array in which each element is JSON Object or a
     * {@link Form.ui.Button Button object}
     * @deprecated This config option will be removed soon, please use the {@link #cfg-footerItems footerItems} config
     * option instead.
     * @cfg {Function} [onChange=null] A callback function to be called everytime a form's field changes.
     * This callback function will be called in the current object context and will receive three parameters:
     *
     * - The field that changed.
     * - The new field's value.
     * - The previous field's value.
     *
     *      var f = new PMUI.form.Form({
     *          onChange: function(field, newValue, previousValue) {
     *              console.log("Action in the form named \"" + this.getName() + "\":");
     *              console.log("The field " + field.getName() + " has changed from \"" + previousValue + 
     "\" to \"" + newValue + "\"");
     *          },
     *          ......
     *      };
     *
     * @cfg {Function} [onSubmit=null] A callback function to be called when the form is submitted.
     * This callback function will be called in the form object context.
     *
     *      var f = new PMUI.form.Form({
     *          onSubmit: function() {
     *              console.log("Submitting the form named \"" + this.getName() + "\"...");
     *          },
     *          ......
     *      };
     * @cfg {Array} [footerItems=[]] Sets the elements in the window footer, this elements can be instances of Button
     * and/or instances of Label. The value for this config option must be an array in which each element can be:
     *
     * - An object literal, in this case the object literal must have the property "pmType" with its value set to
     * "button" (if you want the element be a {@link PMUI.ui.Button Button}) or "label" (if you want the element be a
     * {@link PMUI.ui.TextLabel Label}). Optionally you can add the respective config options for each case.
     *
     * - A PMUI object, in this case it must be an instance of {@link PMUI.ui.Button Button} or an instance of
     * {@link PMUI.ui.TextLabel Label}.
     * @cfg {Function} [onLoad=null] The callback function to be executed when the form is ready to use. for info about
     * the parameters received by the callback please read the {@link #event-onLoad onLoad event} documentation.
     * @cfg {Srting} [buttonPanelPosition='bottom'] The position for the form's footer. It can be 'bottom' or 'top'.
     */
    var Form = function (settings) {
        Form.superclass.call(this, settings);
        /**
         * The name for the form
         * @type {String}
         * @readonly
         */
        this.name = null;
        /**
         * The form's encType property
         * @type {String}
         * @readonly
         */
        this.encType = null;
        /**
         * The text for the form's title.
         * @type {String}
         * @readonly
         */
        this.title = null;
        /**
         * The size for the font in the form.
         * @type {Number}
         * @readonly
         */
        this.fontSize = null;
        /**
         * If the header is visible or not.
         * @type {Boolean}
         * @readonly
         */
        this.visibleHeader = null;
        /**
         * The callback function to be called when a form's field changes.
         * @type {Function}
         */
        this.onChange = null;
        /**
         * The callback functionto be called when the form is submitted.
         * @type {Function}
         */
        this.onSubmit = null;
        /**
         * The form's data object.
         * @type {PMUI.data.DataSet}
         * @private
         */
        this.data = null;
        /**
         * The DOM object that plays the role of title container in the form.
         * @type {HTMLElement}
         * @private
         */
        this.header = null;
        /**
         * The form footer.
         * @type {PMUI.panel.ButtonPanel}
         * @private
         */
        this.footer = null;
        /**
         * The height for the footer area in the Form
         * @type {Number}
         * @readonly
         */
        this.footerHeight = null;
        /**
         * @event onLoad
         * Fired when the form is ready to use.
         * @param {PMUI.form.Form} form The form.
         */
        this.onLoad = null;
        /**
         * If the form is dirty.
         * @type {Boolean}
         */
        this.dirty = null;
        this._requiredFieldNotification = null;
        /**
         * The position for the form's footer.
         * @type {String}
         */
        this.buttonPanelPosition = null;
        this.dependencies = {};
        this.alignmentButtons = null;
        Form.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Panel', Form);

    Form.prototype.type = 'PMForm';

    Form.prototype.family = 'PMForm';

    Form.prototype.init = function (settings) {
        var defaults = {
            title: "Untitled form",
            name: this.id,
            encType: "application/x-www-form-urlencoded",
            items: [],
            fontSize: 12,
            width: 600,
            height: 400,
            visibleHeader: true,
            layout: 'vbox',
            buttons: [],
            footerItems: [],
            buttonPanelPosition: 'bottom',
            onChange: null,
            onSubmit: null,
            onLoad: null,
            alignmentButtons: 'right'
        };

        $.extend(true, defaults, settings);

        this.panel = new PMUI.form.FormPanel({
            form: this,
            width: defaults.width,
            layout: defaults.layout,
            style: {
                cssProperties: {
                    "overflow": "auto"
                }
            },
            onChange: this.onChangeHandler()
        });

        this.footer = new PMUI.panel.ButtonPanel({
            style: {
                cssClasses: ['pmui-form-footer']
            }
        });

        this.data = new PMUI.data.DataSet();

        this.buttons = new PMUI.util.ArrayList();

        this.visibleHeader = defaults.visibleHeader;
        this.onChange = defaults.onChange;
        this.onSubmit = defaults.onSubmit;

        this.setLayout(defaults.layout)
            .setFontSize(defaults.fontSize)
            .setWidth(defaults.width)
            .setTitle(defaults.title)
            .setName(defaults.name)
            .setEncType(defaults.encType)
            .setItems(defaults.items)
            .setOnLoadHandler(defaults.onLoad)
            .setButtonPanelPosition(defaults.buttonPanelPosition)
            .setAlignmentButtons(defaults.alignmentButtons);

        //TODO tell the guys about the deprecated methods
        if (defaults.footerItems && defaults.footerItems.length) {
            this.setFooterItems(defaults.footerItems);
        } else {
            this.setButtons(defaults.buttons);
        }
        if (typeof this.onLoad === 'function') {
            this.onLoad(this);
        }
    };

    Form.prototype.updateDependencies = function () {
        var dependencies = {},
            i,
            j,
            fields = this.getFields(),
            dependent,
            dependents;

        for (i = 0; i < fields.length; i += 1) {
            dependents = fields[i].dependentFields;
            for (j = 0; j < dependents.length; j += 1) {
                if (this.getField(dependents[j])) {
                    if (!dependencies[dependents[j]]) {
                        dependencies[dependents[j]] = [];
                    }
                    dependencies[dependents[j]].push(fields[i]);
                }
            }
        }
        this.dependencies = dependencies;
        return this;
    };


    /**
     * Sets the position for the form's footer.
     * @param {String} position A string with the text 'bottom' or 'top', this sets the position for the form's footer.
     */
    Form.prototype.setButtonPanelPosition = function (position) {
        if (position === 'top' || position === 'bottom') {
            this.buttonPanelPosition = position;
            if (this.html) {
                if (position === 'top') {
                    this.html.insertBefore(this.footer.html, this.panel.html);
                } else {
                    this.html.appendChild(this.footer.html);
                }

            }
        }
        return this;
    };
    /**
     * Sets the callback to be executed when the {@link #event-onLoad onLoad event} is fired.
     * @param {Function|null} handler The callback function or the null constant, in the latter case no callback
     * function will be executed.
     * @chainable
     */
    Form.prototype.setOnLoadHandler = function (handler) {
        if (!(typeof handler === 'function' || handler === null)) {
            throw new Error('setOnLoadHandler(): the parameter must be a function or null.');
        }
        this.onLoad = handler;
        return this;
    }
    /**
     * Removes an iten from the form's footer.
     * @param  {PMUI.core.Element|String|Number} item It can be a string (id of the child to remove),
     a number (index of the child to remove) or a {Element} object.
     * @chainable
     */
    Form.prototype.removeFooterItem = function (item) {
        this.footer.removeItem(item);
        return this;
    };
    /**
     * Removes all the items in the form's footer.
     * @chainable
     */
    Form.prototype.clearFooterItems = function () {
        this.footer.clearItems();
        return this;
    };
    /**
     * Adds an item into the forms footer.
     * @param {Object|PMUI.ui.Button|PMUI.ui.Button} item It can be:
     *
     * - An object literal, in this case it can have the config options for create a {@link PMUI.ui.Button Button} or a
     * {@link PMUI.ui.TextLabel Label}, additionally it must include the respective pmType ('button' for Button and 'label'
     * for Label).
     *
     * -A PMUI object, in this case it must be an instance of {@link PMUI.ui.Button Button} or an instance of {@link
        * PMUI.ui.TextLabel Label}.
     */
    Form.prototype.addFooterItem = function (item) {
        this.footer.addItem(item);
        this.showFooter();
        return this;
    };
    /**
     * Sets the items to be shown in the form's footer.
     * @param {Array} items An array in which each element can be:
     *
     * - An object literal, in this case it can have the config options for create a {@link PMUI.ui.Button Button} or a
     * {@link PMUI.ui.TextLabel Label}, additionally it must include the respective pmType ('button' for Button and 'label'
     * for Label).
     *
     * -A PMUI object, in this case it must be an instance of {@link PMUI.ui.Button Button} or an instance of {@link
        * PMUI.ui.TextLabel Label}.
     */
    Form.prototype.setFooterItems = function (items) {
        var i;

        if (!jQuery.isArray(items)) {
            throw new Error('setFooterItems(): The parameter must be an array.');
        }
        this.clearFooterItems();
        this.hideFooter();
        for (i = 0; i < items.length; i += 1) {
            this.addFooterItem(items[i]);
        }

        return this;
    };
    /**
     * The method which sets the internal callback function for the onChange event.
     * @return {Function}
     * @private
     */
    Form.prototype.onChangeHandler = function () {
        var that = this;
        return function (field, newValue, previousValue) {
            if (field.initialValue === newValue) {
                that.dirty = false;
            } else {
                that.dirty = true;
            }
            if (typeof that.onChange === 'function') {
                that.onChange(field, newValue, previousValue);
            }
        };
    };
    /**
     * Sets the form to dirty/clean.
     * @param {Boolean} dirty True to set the form to dirty, False to set it to clean.
     * @chainable
     */
    Form.prototype.setDirty = function (dirty) {
        this.dirty = !!dirty;
        return this;
    };
    /**
     * Check if the form is dirty
     * @return {Boolean} Returns true if the for is dirty, otherwise it returns false.
     */
    Form.prototype.isDirty = function () {
        return this.dirty;
    };
    /**
     * Removes any button previously added to the form.
     * @param  {String|Number|PMUI.ui.Button} button
     * This param can be:
     *  - String: in that case the string must be the id of the button to remove.
     *  - Number: in that case the number is evaluated as the index of the button to remove.
     *  - Button: the button to remove.
     * @chainable
     * @deprecated This method will be removed soon, please use the {@link #method-removeFooterItem removeFooterItem()}
     * method instead.
     */
    Form.prototype.removeButton = function (button) {
        return this.removeFooterItem(button);
    };
    /**
     * Remove all the buttons from the form.
     * @chainable
     * @deprecated This method will be removed soon, please use the {@link #method-clearFooterItems clearFooterItems()}
     * method instead.
     */
    Form.prototype.clearButtons = function () {
        return this.clearFooterItems();
    };
    /**
     * Adds a button into the form. The button will be added to the form's footer.
     * @param {Object|PMUI.ui.Button} button
     * It can be a:
     *
     * - {@link PMUI.ui.Button Button} object.
     * - JSON Object: use the same JSON structure for create {@link PMUI.ui.Button Button} objects.
     *
     * @chainable
     * @deprecated This method will be removed soon, please use the {@link #method-addFooterItem addFooterItem()}
     * method instead.
     */
    Form.prototype.addButton = function (button) {
        return this.addFooterItem(button);
    };
    /**
     * Sets the buttons for the form. They will be added to the footer.
     * @param {Array} buttons An array in which each element is a JSON object or a {@link PMUI.ui.Button Button}.
     * @chainable
     * @deprecated This method will be removed soon, please use the {@link #method-setFooterItems setFooterItems()}
     * method instead.
     */
    Form.prototype.setButtons = function (buttons) {
        return this.setFooterItems(buttons);
    };
    /**
     * Updates the dimensions for the form's areas.
     * @chainable
     */
    Form.prototype.updateDimensions = function () {
        var headerHeight = 0,
            footerHeight = 0,
            bodyHeight;

        if (!this.panel) {
            return this;
        }
        if (this.getHeight() !== 'auto') {
            if (this.visibleHeader && this.title) {
                headerHeight = 2 * this.fontSize;
            }
            if (this.footer.getItems().length) {
                footerHeight = (1.7 * this.fontSize) + 12;
            }
            bodyHeight = this.getHeight() - headerHeight - footerHeight;
        }
        return this;
    };
    /**
     * Sets the form's height.
     * @param {Number|String} height it can be a number or a string.
     * In case of using a String you only can use 'auto' or 'inherit' or ##px or ##% or ##em when ## is a number
     * @chainable
     */
    Form.prototype.setHeight = function (height) {
        Form.superclass.prototype.setHeight.call(this, height);
        this.updateDimensions();
        return this;
    };
    /**
     * Sets the form's width.
     * @param {Number|String} width it can be a number or a string.
     * In case of using a String you only can use 'auto' or 'inherit' or ##px or ##% or ##em when ## is a number
     * @chainable
     */
    Form.prototype.setWidth = function (width) {
        Form.superclass.prototype.setWidth.call(this, width);
        this.updateDimensions();
        return this;
    };
    /**
     * Sets the size for the font toi be used in the form.
     * @param {Number} fontSize
     */
    Form.prototype.setFontSize = function (fontSize) {
        if (typeof fontSize === 'number') {
            this.fontSize = fontSize;
            this.style.addProperties({
                "font-size": fontSize + "px"
            });
            this.updateDimensions();
        } else {
            throw new Error("setFontSize(): this method only accepts a number as parameter.");
        }

        return this;
    };
    /**
     * Returns a field from the form.
     * @param  {String} name The name of the field to find.
     * @return {PMUI.form.Field|null}
     */
    Form.prototype.getField = function (name) {
        return this.panel.getField(name);
    };
    /**
     * Returns all the fields contained by the form.
     * @return {Array} An array in which each element is a {PMUI.form.Field}.
     */
    Form.prototype.getFields = function () {
        return this.panel.getItems("fields", true);
    };
    /**
     * Returns the items from the form's body.
     * @return {Array} An array with all the form's items.
     */
    Form.prototype.getItems = function () {
        return this.panel.getItems();
    };
    /**
     * Sets the layout mode for the form's root panel.
     * @param {String} layout it can take one of the following values:
     *
     * - "vbox" for vertical positioning.
     * - "hbox" for horizontal positioning.
     * - "box" without layout.
     */
    Form.prototype.setLayout = function (layout) {
        var factory;

        if (this.panel) {
            factory = new PMUI.layout.LayoutFactory();
            this.panel.layout = factory.make(layout);
            this.panel.layout.setContainer(this.panel);
            if (this.html) {
                this.panel.layout.applyLayout();
            }
        }
        return this;
    };
    /**
     * Adds an item to the form's root panel. It only creates one of
     * the {@link PMUI.form.FormItemFactory FormItemFactory} supported products.
     * @param {String|Object|PMUI.form.FormPanel|PMUI.form.Field} item
     * It can accept one of the following data types:
     *
     * - a String: one of the supported pmTypes by the {@link PMUI.form.FormItemFactory FormItemFactory} object.
     * - a JSON Object: use the JSON structure the object's constructor needs, additionally add the pmType property,
     * you can use any of the pmTypes supported by the {@link PMUI.form.FormItemFactory FormItemFactory}.
     * - a {@link PMUI.form.FormPanel FormPanel} object.
     * - a{@link PMUI.form.Field Field} object
     */
    Form.prototype.addItem = function (item) {
        if (this.panel) {
            this.panel.addItem(item);
            this.updateDependencies();
        }
        return this;
    };
    /**
     * Set items for the form's root panel.
     * @param {Array} items An array in which each element can be one of the accepted parameters
     * in the {@link PMUI.form.Form#addItem addItem() method}.
     * @chainable
     */
    Form.prototype.setItems = function (items) {
        if (this.panel) {
            this.panel.setItems(items);
            this.updateDependencies();
        }
        return this;
    };
    /**
     * Returns all the form's direct items.
     * @param {String|Number} id If the parameter is a string then
     * it will be take as the id for the element to find and return,
     * but if the element is a Number it will return the object with that
     * index position.
     * @returns {Object}
     */
    Form.prototype.getItem = function (i) {
        return Form.superclass.prototype.getItem.call(this.panel, i);
    };
    /**
     * Sets the title text for the form.
     * @param {String} title
     * @chainable
     */
    Form.prototype.setTitle = function (title) {
        if (typeof title === 'string') {
            this.title = title;
            if (this.header) {
                $(this.header).empty();
                if (title) {
                    $(this.header).append('<h2 class="pmui-form-title"></h2>').find('h2').text(title);
                } else {
                    $(this.header).append('<h2 class="pmui-form-title"></h2>').find('h2').html("&nbsp;");
                }
                this.updateDimensions();
            }
        }

        return this;
    };
    /**
     * Sets the name for the form
     * @param {String} name
     * @chainable
     */
    Form.prototype.setName = function (name) {
        this.name = name;
        if (this.html) {
            this.html.name = name;
        }

        return this;
    };
    /**
     * Sets the enctype property for the form
     * @param {String} encType
     * @chainable
     */
    Form.prototype.setEncType = function (encType) {
        this.encType = encType;
        if (this.html) {
            this.html.setAttribute("encType", encType);
        }

        return this;
    };
    /**
     * Shows the form header
     * @chainable
     */
    Form.prototype.showHeader = function () {
        this.visibleHeader = true;
        this.header.style.display = "";
        this.updateDimensions();

        return this;
    };
    /**
     * Hides the form's header
     * @chainable
     */
    Form.prototype.hideHeader = function () {
        this.visibleHeader = false;
        this.header.style.display = "none";
        this.updateDimensions();

        return this;
    };
    /**
     * Returns true if all the form's fields assert the their validations otherwise returns false.
     * @return {Boolean}
     */
    Form.prototype.isValid = function () {
        var res = this.panel.isValid();

        if (res) {
            this.hideRequiredFieldNotification();
        } else {
            this.showRequiredFieldNotification();
        }

        return res;
    };
    /**
     * Submits the form.
     * @chainable
     */
    Form.prototype.submit = function () {
        if (this.isValid()) {
            if (typeof this.onSubmit === 'function') {
                this.onSubmit(this.getData());
            }
            //TODO: call send() method from proxy
        }

        return this;
    };
    /**
     * Resets the form to their initial values.
     * @chainable
     */
    Form.prototype.reset = function () {
        var fields,
            i;

        fields = this.getFields();
        for (i = 0; i < fields.length; i += 1) {
            fields[i].reset();
        }
        if (this.dirty) {
            this.dirty = false;
        }
        return this;
    };
    /**
     * Returns the form's data.
     * @param {String} [format='object'] The format in which the data will be returned, it can take three values:
     *
     * - 'xml', the data will be returned in xml format.
     * - 'json', the data will be returned in a json-formated string.
     * - 'object', the data will be returned in a object literal format.
     * - otherwise or nothing, the data will be returned in a object literal format but only containing the name and values from
     * fields.
     * @return {Object|String} The data type of the returned item will depend on the input parameter, please read the
     * method description for more info.
     */
    Form.prototype.getData = function (format) {
        var fields,
            i,
            res,
            data = new PMUI.data.DataSet();

        fields = this.getFields();
        if (format !== 'xml' && format !== 'json' && format !== 'object') {
            res = {};
            for (i = 0; i < fields.length; i += 1) {
                if (!fields[i].disabled) {
                    res[fields[i].getName()] = fields[i].getValue();
                }
            }
        } else {
            for (i = 0; i < fields.length; i += 1) {
                if (!fields[i].disabled) {
                    data.addItem(fields[i].data);
                }
            }
            if (format === 'xml') {
                return data.getXML();
            } else if (format === 'json') {
                return data.getJSON();
            } else if (format === 'object') {
                return data.getData();
            }
        }
        return res;
    };
    Form.prototype.hideRequiredFieldNotification = function () {
        if (this._requiredFieldNotification) {
            this._requiredFieldNotification.style.display = 'none';
        }
        return this;
    };

    Form.prototype.showRequiredFieldNotification = function () {
        if (this._requiredFieldNotification) {
            this._requiredFieldNotification.style.display = '';
        }
        return this;
    };
    /**
     * Define the event listeners for the form.
     * @chainable
     */
    Form.prototype.defineEvents = function () {
        var that = this;

        if (this.html && !this.eventsDefined) {
            this.panel.defineEvents();
            this.footer.defineEvents();
            this.addEvent('submit').listen(this.html, function (e) {
                e.preventDefault();
                e.stopPropagation();
            });
            this.panel.setOnAvailabilityChange(function () {
                that.isValid();
            });
            this.eventsDefined = true;
        }
        return this;
    };
    /**
     * Creates the HTML for the form.
     * @return {HTMLElement}
     */
    Form.prototype.createHTML = function () {
        var html,
            header,
            notificationArea,
            notification,
            notificationText = "Fields marked with an asterisk (%%ASTERISK%%) are required.".translate()
                .replace(/%%ASTERISK%%/g, '<span style="color: #e84c3d">*</span>');

        if (this.html) {
            return this.html;
        }
        html = PMUI.createHTMLElement('form');
        html.id = this.id;
        html.className = 'pmui-form';

        header = PMUI.createHTMLElement('div');
        header.className = 'pmui-form-header';

        notification = new PMUI.field.TextAnnotationField({
            id: "requiredMessage",
            name: "Message",
            textType: PMUI.field.TextAnnotationField.TEXT_TYPES.HTML,
            text: notificationText,
            text_Align: "center"
        });
        notificationArea = PMUI.createHTMLElement('div');
        notificationArea.style.display = 'none';
        notificationArea.appendChild(notification.getHTML());
        this._requiredFieldNotification = notificationArea;

        this.header = header;

        html.appendChild(header);
        html.appendChild(this.panel.getHTML());
        html.appendChild(notificationArea);
        html.appendChild(this.footer.getHTML());
        this.html = html;

        this.setTitle(this.title)
            .setName(this.name)
            .setEncType(this.encType);

        if (this.visibleHeader) {
            this.showHeader();
        } else {
            this.hideHeader();
        }

        this.panel.setPadding('20px 10px 20px 10px');

        this.style.applyStyle();
        if (this.layout) {
            this.layout.applyLayout();
        }
        if (this.getFields().length && this.setFocus) {
            this.setFocus(0);
        }
        this.setButtonPanelPosition(this.buttonPanelPosition);

        this.defineEvents();

        return this.html;
    };
    /**
     * clean all form fields
     * @chainable
     */
    Form.prototype.clearItems = function () {
        this.panel.clearItems();
        return this;
    };
    /**
     * clear all form fields
     * @chainable
     */
    Form.prototype.removeItem = function (item) {
        var i = 0;
        Form.superclass.prototype.removeItem.call(this, item);
        this.panel.removeItem(item);
        return this;
    };
    /**
     * hides the footer where the buttons are located
     * @chainable
     */
    Form.prototype.hideFooter = function () {
        this.footer.setVisible(false);
        return this;
    };
    /**
     * shows the footer where the buttons are located
     * @chainable
     */
    Form.prototype.showFooter = function () {
        this.footer.setVisible(true);
        return this;
    };
    /**
     * @method setFocus
     * set the focus on a form field
     * @chainable
     */
    Form.prototype.setFocus = function () {
        var j,
            fields = this.getFields();

        for (j = 0; j < fields.length; j += 1) {
            if ((!fields[j].isReadOnly || !fields[j].isReadOnly()) && !fields[j].disabled && fields[j].isVisible() && fields[j].setFocus) {
                fields[j].setFocus();
                break;
            }
        }
        return this;
    }
    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = Form;
    }
    Form.prototype.setAlignmentButtons = function (value) {
        if (typeof value == "string") {
            this.alignmentButtons = value;
            this.footer.setAlignment(value);
            return this;
        }
        throw new Error('setAlignmentButtons(), the value is no type valid')
    };

    PMUI.extendNamespace('PMUI.form.Form', Form);
}());
(function () {
    /**
     * @class PMUI.form.Validator
     * Handles the validations of the fields
     * @extend PMUI.core.Base
     *
     * @constructor
     * Create a new instance of the class
     * @param {Object} settings
     * @param {PMUI.form.Field} parent
     *
     * @cfg {*} [criteria=null] This is the criteria info to execute the validation,
     * each validator needs a different data type criteria,
     * so please read about the criteria config option for every validator object.
     * @cfg {String} [errorMessage='the validation has failed'] This property defines
     * the error message to display if validation fails when filled fields
     * @cfg {PMUI.form.Field} [parent=null] Defines the field the validator belongs to.
     */
    var Validator = function (settings) {

        Validator.superclass.call(this, settings);
        /**
         * Defines the Field parent
         * @type {PMUI.form.Field}
         */
        this.parent = null;
        /**
         * Defines the criteria object
         * @type {Object}
         */
        this.criteria = null;
        /**
         * Defines if the object is validated
         * @type {Boolean}
         */
        this.validated = false;
        /**
         * Defines the validation state
         * @type {null/Boolean}
         */
        this.valid = null;
        /**
         * Defines the error message to show in case of the validation fails
         * @type {null/Boolean}
         */
        this.errorMessage = null;

        Validator.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Base', Validator);
    /**
     * Defines the object's type
     * @type {String}
     */
    Validator.prototype.type = 'Validator';
    /**
     * Defines the object's family
     * @type {String}
     */
    Validator.prototype.family = 'Validator';
    /**
     * Initializes the object with default values
     * @param {Object} settings
     * @param {PMUI.form.Field} parent
     */
    Validator.prototype.init = function (settings) {
        var defaults = {
            criteria: null,
            errorMessage: 'the validation has failed',
            parent: null
        };
        jQuery.extend(true, defaults, settings);
        this.setCriteria(defaults.criteria)
            .setParent(defaults.parent)
            .setErrorMessage(defaults.errorMessage);
    };
    /**
     * Sets the validation error message to show in case of the validation fails
     * @param {String} errorMessage
     * @chainable
     */
    Validator.prototype.setErrorMessage = function (errorMessage) {
        this.errorMessage = errorMessage;
        return this;
    };
    /**
     * Sets the validation error message to show in case of the validation fails
     * @param {String} errorMessage
     * @chainable
     */
    Validator.prototype.getErrorMessage = function () {
        return this.errorMessage;
    };
    /**
     * Sets the validation criteria
     * @param {Object} criteria
     * @chainable
     */
    Validator.prototype.setCriteria = function (criteria) {
        this.criteria = criteria;
        return this;
    };
    /**
     * Sets the parent field
     * @param {PMUI.form.Field|GridControlCell} parent
     * @chainable
     */
    Validator.prototype.setParent = function (parent) {
        if (parent) {
            if (parent instanceof PMUI.form.Field || parent instanceof PMUI.grid.GridControlCell) {
                this.parent = parent;
            } else {
                throw new Error("setParent() method only accepts a Field object as parameter.");
            }
        }
        return this;
    };

    /**
     * Returns the parent field
     * @return {PMUI.form.Field}
     */
    Validator.prototype.getParent = function () {
        return this.parent;
    };
    /**
     * Evaluates the validator
     */
    Validator.prototype.validate = function () {
        this.valid = true;
    };
    /**
     * Returns the validation response
     * @chainable
     */
    Validator.prototype.isValid = function () {
        this.validate();
        this.updateTooltip();
        return this.valid;
    };
    /**
     * Updates de the tooltip message
     */
    Validator.prototype.updateTooltip = function () {
        if (this.parent && this.parent.message) {
            if (this.valid) {
                this.parent.hideMessage();
            } else {
                this.parent.showMessage(this.errorMessage, "error");
            }
        }
    };
    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = Validator;
    }

    PMUI.extendNamespace('PMUI.form.Validator', Validator);

}());
(function () {
    /**
     * @class  PMUI.form.TextLengthValidator
     * @extend PMUI.form.Validator
     *
     * Create a new instace abstrac of the TextLengthValidator class
     * @param {Object} settings
     *
     * @cfg {String} [errorMessage='the validation has failed'] This property defines the error
     * message to display if the validation text size entered is not valid
     */
    var TextLengthValidator = function (settings) {

        TextLengthValidator.superclass.call(this, settings);
        TextLengthValidator.prototype.init.call(this, settings);

    };

    PMUI.inheritFrom('PMUI.form.Validator', TextLengthValidator);
    /**
     * Defines the object's type
     * @type {String}
     */
    TextLengthValidator.prototype.type = 'TextLengthValidator';

    TextLengthValidator.prototype.init = function (settings) {
        var maxMessage = settings.criteria.maxLength ? " at most " + settings.criteria.maxLength + " characters" : "",
            minMessage = settings.criteria.minLength ? "at least " + settings.criteria.minLength + " characters" : "",
            defaults = {
                errorMessage: "The text length must have " + minMessage + (maxMessage ? " and " : "") + maxMessage
            };

        jQuery.extend(true, defaults, settings);

        this.setErrorMessage(defaults.errorMessage);
    };
    /**
     * Evaluates the validator
     * this method is for validate the length of the string on control the value
     * input in text field, cutting spaces white in use trim of the jquery function
     * @chainable
     */
    TextLengthValidator.prototype.validate = function () {
        var res = false,
            value = this.criteria.trim ? jQuery.trim(this.parent.value) : this.parent.value;

        this.valid = true;

        if (this.criteria.maxLength) {
            this.valid = value.length <= parseInt(this.criteria.maxLength, 10);
        }
        if (this.criteria.minLength) {
            this.valid = (this.valid !== null ? this.valid : true) &&
                value.length >= parseInt(this.criteria.minLength, 10);
        }

        return this;
    };

    PMUI.extendNamespace('PMUI.form.TextLengthValidator', TextLengthValidator);

    if (typeof exports !== "undefined") {
        module.exports = TextLengthValidator;
    }

}());
(function () {
    /**
     * @class PMUI.form.RegExpValidator
     * @extends PMUI.form.Validator
     * A text validator based in regular expressions.
     *
     * Usually this object won't be instatiate directly, it will be instantiate through a
     * {@link PMUI.form.Field Field} object just like is shown in the
     * {@link PMUI.field.TextField TextField documentation}. We recommend to use it this way.
     *
     * The other way to instantiate an object from this class:
     *
     *      var myValidator,
     *          field,
     *          callback = function() {
     *              this.isValid();
     *          };
     *
     *      field = new PMUI.field.TextField({
     *          onChange: callback
     *      });
     *
     *      myValidator = new RegExpValidator({
     *          criteria: /^\d+$/,
     *          errorMessage: "You must introduce only number digits."
     *      });
     *
     *      field.addValidator(myValidator);
     *
     *      document.body.appendChild(field.getHTML());
     *
     * @cfg {Object|RegExp} criteria The criteria config option can be:
     *
     * - a JSON object: in this case it must have two porperties:
     *     - pattern (String): the pattern string to be use as the regular expression
     (don't forget to escape special characters)
     *     - modifiers (String): this is optional, this is the modifier for the regular expression.
     * - a RegExp object
     */
    var RegExpValidator = function (options) {
        RegExpValidator.superclass.call(this, options);
        RegExpValidator.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.form.Validator', RegExpValidator);

    RegExpValidator.prototype.type = "RegExpValidator";

    RegExpValidator.prototype.init = function (options) {
        var defaults = {
            errorMessage: "The text pattern doesn't match"
        };

        $.extend(true, defaults, options);

        this.setErrorMessage(defaults.errorMessage);
    };
    /**
     * Execute the validation.
     * @chainable
     * @private
     */
    RegExpValidator.prototype.validate = function () {
        var res = false,
            regExp;

        if (this.parent) {
            if (this.criteria instanceof RegExp) {
                if ((this.parent.valueType == 'integer' || this.parent.valueType == 'float' || this.parent.valueType == 'number') && !this.parent.value) {
                    this.valid = this.criteria.test(0);
                } else {
                    this.valid = this.criteria.test(this.parent.value);
                }
            } else if (typeof this.criteria === 'string') {
                regExp = new RegExp(this.criteria.pattern, this.criteria.modifiers);
                this.valid = regExp.text(this.parent.value);
            }
        } else {
            this.valid = false;
        }

        return this;
    };

    PMUI.extendNamespace('PMUI.form.RegExpValidator', RegExpValidator);

    if (typeof exports !== "undefined") {
        module.exports = RegExpValidator;
    }
}());
(function () {
    /**
     * @class  PMUI.form.ValidatorFactory
     * @extend PMUI.util.Factory
     * Extends the factory class to produce field validators.
     *
     * Its default products are:
     *
     * - {@link PMUI.form.TextLengthValidator TextLengthValidator} objects: using "textLength".
     * - {@link PMUI.form.RegExpValidator RegExpValidator} objects: using "regexp".
     *
     * @constructor
     * Creates a new instance od the class
     */
    var ValidatorFactory = function () {
        ValidatorFactory.superclass.call(this);
        ValidatorFactory.prototype.init.call(this);
    };

    PMUI.inheritFrom('PMUI.util.Factory', ValidatorFactory);

    ValidatorFactory.prototype.init = function () {
        var defaults = {
            products: {
                "textLength": PMUI.form.TextLengthValidator,
                "regexp": PMUI.form.RegExpValidator
            },
            defaultProduct: "textLength"
        };
        this.setProducts(defaults.products)
            .setDefaultProduct(defaults.defaultProduct);
    };

    PMUI.extendNamespace('PMUI.form.ValidatorFactory', ValidatorFactory);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = ValidatorFactory;
    }

}());
(function () {
    /**
     * @class  PMUI.panel.LayoutPanel
     * @extends PMUI.core.Panel
     * Handles layouts to be inserted into panels.
     * It is composed by five main elements: north, south, west, east and center
     *
     * Example:
     *
     *           @example
     *
     *           layout = new PMUI.panel.LayoutPanel({
     *               id: "myLayout",
     *               center: {
     *                   cssProperties: {
     *                       "background-color": "#91b3e0"    
     *                   },
     *                   cssClasses: ['my-class']
     *               },
     *               west: {
     *                   resizable: false,
     *                   closed: true
     *               },
     *               south: new PMUI.panel.LayoutPanel({
     *                   north:{
     *                       size: 50
     *                   }
     *               }),
     *               southConfig:{
     *                   size: 200
     *               },
     *               north: {
     *                   size: 50,
     *                   togglerOpen: 80,
     *                   overflow: false
     *               },
     *               east: {
     *                   closable: false
     *               }
     *           });
     *           document.body.appendChild(layout.getHTML());
     *           layout.render(true);
     *
     * @constructor
     * Create a new instance of the LayoutPanel class
     * @param {Object} options Contructor object
     */
    var LayoutPanel = function (options) {
        LayoutPanel.superclass.call(this, options);

        /**
         * @property {String} prefixPlugin
         * The prefix for the classes in jquery.layout plugin
         * @private
         */
        var prefixPlugin = 'ui-layout-',
            /**
             * @property {Array} positions
             * The array with the position accepted in the layout
             * @private
             */
            positions = ['north', 'center', 'south', 'east', 'west'],
            /**
             * @property {Object} settings
             * Defines the settings acceptted by the layout panel
             * @private
             */
            settings = {
                size: "size",
                closed: "initClosed",
                resizable: "resizable",
                closable: "closable",
                togglerOpen: "togglerLength_open",
                togglerClosed: "togglerLength_closed",
                overflow: "allowOverflow",
                hidden: "initHidden",
                cssProperties: "cssProperties",
                cssClasses: "cssClasses"
            };

        /**
         * @property {Object} panels
         * The settings for the plugin organized by section
         * @private
         */
        this.panels = {};

        /**
         * North panel pointer
         * @type {HTMLElement}
         */
        this.north = null;

        /**
         * Center panel pointer
         * @type {HTMLElement}
         */
        this.center = null;

        /**
         * South panel pointer
         * @type {HTMLElement}
         */
        this.south = null;

        /**
         * East panel pointer
         * @type {HTMLElement}
         */
        this.east = null;

        /**
         * West panel pointer
         * @type {HTMLElement}
         */
        this.west = null;

        /**
         * Javascript pointer to the jquery layout plugin instance
         * @type {Object}
         */
        this.instance = null;

        this.onClose = null;
        this.onOpen = null;
        /**
         * Returns the config object
         * @return {Object}
         */
        LayoutPanel.prototype.getConfig = function () {
            return {
                panels: this.panels,
                onclose_end: this.onClose,
                onopen_start: this.onOpen,
                prefix: prefixPlugin,
                positions: positions,
                isSetting: function (value) {
                    var valid = settings[value];
                    return (typeof valid !== 'undefined');
                },
                transformSetting: function (value) {
                    return settings[value];
                }
            };
        };

        /**
         * Sets the panel settings
         * @param {String} pos      Position (north, center, south, east or west)
         * @param {Object} settings Object settings
         */
        LayoutPanel.prototype.setConfig = function (pos, settings) {
            this.panels[pos] = settings;
            return this;
        };

        LayoutPanel.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.core.Panel', LayoutPanel);

    /**
     * Defines the object's type
     * @type {String}
     */
    LayoutPanel.prototype.type = 'LayoutPanel';

    /**
     * @private
     * Initializes the object with default values
     * @param  {Object} options
     */
    LayoutPanel.prototype.init = function (options) {
        var defaults = {
            center: {
                cssProperties: {},
                cssClasses: []
            },
            factory: {
                products: {
                    "layout": PMUI.panel.LayoutPanel,
                    "treePanel": PMUI.panel.TreePanel,
                    "panel": PMUI.core.Panel
                },
                defaultProduct: "panel"
            },
            onClose: null,
            onOpen: null
        };
        jQuery.extend(true, defaults, options);
        this.setFactory(defaults.factory)
            .setPluginSettings(defaults)
            .setPanels(defaults);
        this.setOnCloseHandler(defaults.onClose);
        this.setOnOpenHandler(defaults.onOpen);
    };

    LayoutPanel.prototype.setOnCloseHandler = function (hanlder) {
        if (!(typeof hanlder === "function" || hanlder === null)) {
            throw new Error("setOnCloseHandler(): hanlder should be a funtion");
        }
        this.onClose = hanlder;
        return this;
    };
    LayoutPanel.prototype.setOnOpenHandler = function (hanlder) {
        if (!(typeof hanlder === "function" || hanlder === null)) {
            throw new Error("setOnOpenHandler(): hanlder should be a funtion");
        }
        this.onOpen = hanlder;
        return this;
    };
    /**
     * Calculate and sets the settings for each panel
     * @param {Object} obj Object constructor
     */
    LayoutPanel.prototype.setPluginSettings = function (obj) {
        var config = this.getConfig(),
            that = this;

        jQuery.each(config.positions, function (index, pos) {
            var panel = obj[pos],
                panelSettings = {};

            if (panel) {
                if (that.factory && that.factory.isValidClass(panel)) {
                    panelSettings = obj[pos + "Config"] || {};
                } else {
                    jQuery.each(panel, function (key, value) {
                        if (config.isSetting(key)) {
                            panelSettings[config.transformSetting(key)] = value;
                        }
                    });
                }
                if (!(panelSettings.cssClasses && jQuery.isArray(panelSettings.cssClasses))) {
                    panelSettings.cssClasses = [];
                }
                panelSettings.cssClasses.push(config.prefix + pos);
                that.setConfig(pos, panelSettings);
            }
        });
        return this;
    };

    /**
     * Set panels into north, south, center, west and east pointers
     * @param {Object} obj Constructor object
     */
    LayoutPanel.prototype.setPanels = function (obj) {
        var config = this.getConfig(),
            that = this,
            treeOptions;
        jQuery.each(config.positions, function (index, pos) {
            if (obj[pos]) {

                that[pos] = that.factory.make(obj[pos]);

                if (that[pos] instanceof PMUI.panel.LayoutPanel) {
                    that.setConfig(pos, jQuery.extend(true, config.panels[pos], {instance: that[pos]}));
                }
                if (that[pos] instanceof PMUI.panel.TreePanel) {
                    treeOptions = {
                        cssProperties: that[pos].style.cssProperties,
                        cssClasses: that[pos].style.cssClasses
                    };
                    that.setConfig(pos, jQuery.extend(true, config.panels[pos], treeOptions));
                }

                that[pos].style.cssProperties = config.panels[pos].cssProperties || {};
                that[pos].style.cssClasses = config.panels[pos].cssClasses || [config.prefix + pos];

                that.addItem(that[pos]);
            }
        });
        return this;
    };


    /**
     * Create html for each item to container
     * @return {Object} the current object html
     */
    LayoutPanel.prototype.createHTML = function () {
        var i,
            items;

        items = this.items.asArray();
        LayoutPanel.superclass.prototype.createHTML.call(this);
        for (i = 0; i < items.length; i += 1) {
            $(this.html).append(items[i].getHTML());
        }
        return this.html;
    };
    /**
     * Render all the html object
     * @param  {Boolean} [recursive] Defines if the render must be recursive
     */
    LayoutPanel.prototype.render = function (recursive) {
        var that = this,
            options,
            renderRecursive = recursive || false;
        options = that.getConfig().panels;
        options.onclose_end = that.getConfig().onclose_end;
        options.onopen_start = that.getConfig().onopen_start;
        if (jQuery(that.html).height() === 0) {
            jQuery(that.html).height(jQuery(document).height() - 20);
        }
        this.instance = jQuery(that.html).layout(options);

        jQuery.each(options, function (position, val) {
            if (options[position] !== null) {
                if (options[position].instance && renderRecursive) {
                    options[position].instance.render(renderRecursive);
                }
                if (options[position].allowOverflow === true) {
                    that.instance.allowOverflow(position);
                }
            }
        });
    };

    /**
     * Remove each html related to the layout from the DOM and destroy the layout instance
     */
    LayoutPanel.prototype.destroy = function () {
        if (this.instance.destroy !== undefined) {
            this.instance.destroy();
        }
        if (this.html !== undefined) {
            jQuery(this.html).remove();
            this.html = null;
        }
        this.instance = null;
    };

    PMUI.extendNamespace('PMUI.panel.LayoutPanel', LayoutPanel);

    if (typeof exports !== 'undefined') {
        module.exports = LayoutPanel;
    }

}());
(function () {
    /**
     * @class PMUI.layout.Layout
     * Defines the properties for the different layouts in the panels
     * @abstract
     **/
    var Layout = function (options) {
        /**
         * Defines the container to apply the layout
         * @type {Object}
         */
        this.belongsTo = null;
        Layout.prototype.init.call(this, options);
    };

    /**
     * Defines the object's type
     * @type {String}
     */
    Layout.prototype.type = "Layout";

    /**
     * Defines the object's family
     * @type {String}
     */
    Layout.prototype.family = "Layout";

    Layout.prototype.init = function (options) {
        var defaults = {
            belongsTo: null
        };

        jQuery.extend(true, defaults, options);
        this.setContainer(defaults.belongsTo);
    };

    /**
     * Applies the layout to the container
     * @abstract
     */
    Layout.prototype.applyLayout = function () {
        return this;
    };

    /**
     * Sets the parent container
     * @param {Object} parent Container Object
     */
    Layout.prototype.setContainer = function (parent) {
        this.belongsTo = parent;
        return this;
    };

    PMUI.extendNamespace('PMUI.layout.Layout', Layout);

    if (typeof exports !== 'undefined') {
        module.exports = Layout;
    }

}());
(function () {
    /**
     * @class  PMUI.layout.HBox
     * @extends PMUI.layout.Layout
     * Class created to handle Horizontal Box layout changes
     *
     * @constructor
     * Creates a new isntance of the object
     * @param {Object} options Constructor object
     */
    var HBox = function (options) {
        HBox.superclass.call(this, options);
        HBox.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.layout.Layout', HBox);

    /**
     * Defines the object's type
     * @type {String}
     */
    HBox.prototype.type = "HBox";

    /**
     * @private
     * Initializes the object with default values
     * @param  {Object} options
     */
    HBox.prototype.init = function (options) {
        var defaults = {};
        jQuery.extend(true, defaults, options);
    };

    /**
     * Applies the layout to the current element
     */
    HBox.prototype.applyLayout = function () {
        // get the object of this layout
        var owner = this.belongsTo,
            items = owner.items,
            totalProportion = 0,
            usableWidth = owner.getUsableWidth(),
            usableHeight = owner.getUsableHeight(),
            i,
            width,
            item;

        var ownerParent = owner;
        // search the width in anysome parent
        if (owner != null) {
            if (owner.getWidth() != "auto" && owner.parent != null) {
                ownerParent = owner.parent;
            }
            if (owner.html) {
                owner.html.style.width = ownerParent.getUsableWidth();
            }
        }
        // compute totalProportion
        for (i = 0; i < items.getSize(); i += 1) {
            item = items.get(i);
            totalProportion += item.proportion;
        }

        xPosition = 0;
        for (i = 0; i < items.getSize(); i += 1) {
            item = items.get(i);
            if (item.html) {
                item.setDisplay("inline-block");
            }
            width = usableWidth * (item.proportion / totalProportion);
            item.setWidth(width);
            item.setHeight(usableHeight);
        }
        return this;
    };

    PMUI.extendNamespace('PMUI.layout.HBox', HBox);

    if (typeof exports !== 'undefined') {
        module.exports = HBox;
    }

}());
(function () {
    /**
     * @class  PMUI.layout.VBox
     * @extends PMUI.layout.Layout
     * Class created to handle Vertical Box layout changes
     *
     * @constructor
     * Creates a new isntance of the object
     * @param {Object} options Constructor object
     */
    var VBox = function (options) {
        VBox.superclass.call(this, options);
        VBox.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.layout.Layout', VBox);

    /**
     * Defines the object's type
     * @type {String}
     */
    VBox.prototype.type = "VBox";

    /**
     * @private
     * Initializes the object with default values
     * @param  {Object} options
     */
    VBox.prototype.init = function (options) {
        var defaults = {};

        jQuery.extend(true, defaults, options);
    };

    /**
     * Applies the layout to the current element
     */
    VBox.prototype.applyLayout = function () {
        // get the object of this layout
        var owner = this.belongsTo,
            items = owner.items,
            totalProportion = 0,
            usableWidth = owner.getUsableWidth(),
            usableHeight = owner.getUsableHeight(),
            i,
            height,
            item;

        // compute totalProportion
        for (i = 0; i < items.getSize(); i += 1) {
            item = items.get(i);
            totalProportion += item.proportion;
        }

        // set the width of each object based on the width of its parent
        for (i = 0; i < items.getSize(); i += 1) {
            item = items.get(i);
            if (item.html) {
                item.setDisplay("block");
            }
            height = usableHeight * (item.proportion / totalProportion);
            item.setHeight(height);
            item.setWidth(usableWidth);
        }
        return this;
    };

    PMUI.extendNamespace('PMUI.layout.VBox', VBox);

    if (typeof exports !== 'undefined') {
        module.exports = VBox;
    }

}());
(function () {
    /**
     * @class  PMUI.layout.Box
     * @extends PMUI.layout.Layout
     * Class created to no layout changes
     *
     * @constructor
     * Creates a new isntance of the object
     * @param {Object} options Constructor object
     */
    var Box = function (options) {
        Box.superclass.call(this, options);
        Box.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.layout.Layout', Box);

    /**
     * Defines the object's type
     * @type {String}
     */
    Box.prototype.type = "Box";

    /**
     * @private
     * Initializes the object with default values
     * @param  {Object} options
     */
    Box.prototype.init = function (options) {
        var defaults = {};

        jQuery.extend(true, defaults, options);
    };

    /**
     * Applies the layout to the current element
     */
    Box.prototype.applyLayout = function () {
    };

    PMUI.extendNamespace('PMUI.layout.Box', Box);

    if (typeof exports !== 'undefined') {
        module.exports = Box;
    }

}());
(function () {
    /**
     * @class PMUI.layout.LayoutFactory
     * @extends {PMUI.util.Factory}
     * Extends the functionality of Factory to set the constructor for layouts
     *
     * @constructor
     * Makes a new instance of the class
     * @param {Object} options
     */
    var LayoutFactory = function (options) {
        LayoutFactory.superclass.call(this, options);
        LayoutFactory.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.util.Factory', LayoutFactory);

    /**
     * Defines the object's type
     * @type {String}
     */
    LayoutFactory.prototype.type = 'LayoutFactory';

    /**
     * @private
     * Initializes the object with default values
     * @param  {Object} options
     */
    LayoutFactory.prototype.init = function (options) {
        var defaults = {
            products: {
                "hbox": PMUI.layout.HBox,
                "vbox": PMUI.layout.VBox,
                "box": PMUI.layout.Box
            },
            defaultProduct: "box"
        };
        jQuery.extend(true, defaults, options);
        this.setProducts(defaults.products)
            .setDefaultProduct(defaults.defaultProduct);
    };

    LayoutFactory.prototype.make = function (obj) {
        var product,
            productType = obj.pmType || '';

        if (this.isValidClass(obj)) {
            product = obj;
        } else if (this.isValidName(productType)) {
            product = this.build.call(this, productType, obj);
        } else if (this.isValidName(obj)) {
            product = this.build.call(this, obj, {});
        } else {
            product = this.build.call(this, this.defaultProduct, obj);
        }
        return product;
    };

    PMUI.extendNamespace('PMUI.layout.LayoutFactory', LayoutFactory);

    if (typeof exports !== 'undefined') {
        module.exports = LayoutFactory;
    }

}());
(function () {
    /**
     * @class PMUI.panel.TreePanel
     * A panel that contains {@link PMUI.item.TreeNode TreeNode items}.
     * @extends {PMUI.core.Panel}
     *
     * An example of a TreePanel with descendant nodes (using the items {@link #cfg-items config option}):
     *
     *      @example
     *      var t = new PMUI.panel.TreePanel({
     *          style: {
     *              cssClasses: ['xxxxxxxx']
     *          },
     *          items: [
     *              {
     *                  label: "America",
     *                  items: [
     *                      {
     *                          label: "North America",
     *                          items: [
     *                              {
     *                                  label: "Canada"
     *                              },
     *                              {
     *                                  label: "USA"
     *                              },
     *                              {
     *                                  label: "Mexico"
     *                              }
     *                          ]
     *                      },
     *                      {
     *                          label: "Central America and Caribbean",
     *                          items: [
     *                              {
     *                                  label: "Guatemala"
     *                              },
     *                              {
     *                                  label: "Cuba"
     *                              },
     *                              {
     *                                  label: "Costa Rica"
     *                              }
     *                          ]
     *                      },
     *                      {
     *                          label: "South America",
     *                          items: [
     *                              {
     *                                  label: "Argentina"
     *                              },
     *                              {
     *                                  label: "Bolivia"
     *                              },
     *                              {
     *                                  label: "Brasil"
     *                              }
     *                          ]
     *                      }
     *                  ]
     *              }
     *          ]
     *      });
     *      document.body.appendChild(t.getHTML());
     *      t.defineEvents();
     *
     * An example of a TreePanel with descendant nodes (using the {@link #cfg-dataItems dataItems} config option):
     *
     *      @example
     *      t2 = new PMUI.panel.TreePanel({
     *          nodeDefaultSettings: {
     *              labelDataBind: 'name',
     *              itemsDataBind: 'regions',
     *              recursiveChildrenDefaultSettings: true,
     *              childrenDefaultSettings: {
     *                  labelDataBind: 'name',
     *                  itemsDataBind: 'countries',
     *                  autoBind: true
     *              },
     *              autoBind: true
     *          },
     *          dataItems: [
     *              {
     *                  name: 'America',
     *                  regions: [
     *                      {
     *                          name: 'North America',
     *                          countries: [
     *                              {
     *                                  name: 'Canada'
     *                              },
     *                              {
     *                                  name: 'USA'
     *                              },
     *                              {
     *                                  name: 'Mexico'
     *                              }
     *                          ]
     *                      },
     *                      {
     *                          name: 'Central America and Caribbean',
     *                          countries: [
     *                              {
     *                                  name: 'Guatemala'
     *                              },
     *                              {
     *                                  name: 'Cuba'
     *                              }, 
     *                              {
     *                                  name: 'Costa Rica'
     *                              }
     *                          ]
     *                      },
     *                      {
     *                          name: 'South America',
     *                          countries: [
     *                              {
     *                                  name: 'Argentina'
     *                              },
     *                              {
     *                                  name: 'Bolivia'
     *                              }, 
     *                              {
     *                                  name: 'Brasil'
     *                              }
     *                          ]
     *                      }
     *                  ]
     *              }
     *          ]
     *      });
     *      document.body.appendChild(t2.getHTML());
     *      t2.defineEvents();
     *
     * @constructor
     * Creates a new instance of the class.
     * @param {Object} settings The config options.
     *
     * @cfg {Object|null} [nodeDefaultSettings=null] The default settings for all the direct child nodes of the tree panel.
     * It is an object with the config options for {@link PMUI.item.TreeNode TreeNode}.
     * @cfg {Object|null} [dataItems=null] An object with the data for the items in the tree panel.
     * @cfg {Array} [data=[]] An array in which each element is an object with the config options for
     * {@link PMUI.item.TreeNode TreeNode} an instance of it. If you use both dataItems and this config option, this
     * config option will be ignored and the dataItems will be used instead.
     * @cfg {String} [filterPlaceholder="search"] The text to show as placeholder for the text control for filtering.
     * @cfg {Function|String|null} [emptyMessage=null] The message to show when the grid is showing no items. It
     * can be:
     *
     * - A String, in this case the string will be displayed in both cases, when the treepanel has no items and when no
     * treepanel's item meet the filter.
     * - A Function, in this case the function must return a string or an HTML Element to be displayed. The function
     * will receive two parameters: the {@link PMUI.panel.TreePanel TreePanel}, a boolean If it is true it means the returned
     * value will be used when a filter is applied, otherwise it means that the returned value will be used when
     * there are no items in the treepanel.
     * - null, in this case a default message will be used for each situation.
     * @cfg {Boolean} [visibleTitle=false] If the title will be displayed.
     * @cfg {String} [title=""] The text for the title.
     * @cfg {Function} [onNodeClick=null] The callback function to be executed everytime the
     * {@link #event-onNodeClick onNodeClick} event fires. For info about the parameters sent to the callback please
     * read the {@link #event-onNodeClick onNodeClick} event documentation.
     * @cfg {Function|null} [onBeforeAppend=null] A callback function to be called everytime the
     * {@link #event-onBeforeAppend onBeforeAppend} event is fired. For info about the parameters used for this
     * callback please read the documentation for this event.
     * @cfg {Function|null} [onAppend=null] A callback function to be called everytime the
     * {@link #event-onAppend onAppend} event is fired. For info about the parameters used for this callback please
     * read the documentation for this event.
     */
    var TreePanel = function (settings) {
        TreePanel.superclass.call(this, jQuery.extend(settings, {
            factory: {
                products: {
                    'node': PMUI.item.TreeNode
                },
                defaultProduct: 'node'
            }
        }));
        /**
         * The default settings for all the direct child nodes of the tree panel.
         * @type {Object}
         */
        this.nodeDefaultSettings = null;
        /**
         * The filter criteria being applied currently.
         * @type {String}
         * @readonly
         */
        this.filterCriteria = "";
        /**
         * If the tree panel is filterable or not.
         * @type {Boolean}
         * @readonly
         */
        this.filterable = null;
        /**
         * The text control in which the user can introduce a text to filter.
         * @type {PMUI.control.TextControl}
         * @private
         */
        this.filterControl = null;
        /**
         * A string or a function or null. Determines the text/HTML Element to show when there are no items to show.
         * @type {Function|String|null}
         * @readonly
         */
        this.emptyMessage = null;
        /**
         * The title for the TreePanel
         * @type {String}
         */
        this.title = null;
        /**
         * If the title is visible or not.
         * @type {Boolean}
         * @readonly
         */
        this.visibleTitle = null;
        /**
         * @event onItemClick
         * Fired everytime an tree item is clicked.
         * @param {PMUI.panel.TreePanel} treePanel The TreePanel in which the event was fired.
         * @param {PMUI.item.TreeNode} node The node that was clicked.
         */
        this.onNodeClick = null;
        /**
         * An private object to store the DOM elements that compose the object's HTML.
         * @type {Object}
         * @private
         */
        this.dom = {};
        /**
         * @event onBeforeAppend
         * Fired before a child node is appended to the current node or to any of its children nodes.
         * @param {PMUI.item.TreeNode} node The node in which the event is being fired.
         * @param {PMUI.item.TreeNode} newNode The new node to be appended.
         */
        this.onBeforeAppend = null;
        /**
         * @event onAppend
         * Fired everytime a child node is appended to the current node or to any of its children nodes.
         * @param {PMUI.item.TreeNode} node The node in which the event is being fired.
         * @param {PMUI.item.TreeNode} newNode The appended node.
         */
        this.onAppend = null;
        TreePanel.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Panel', TreePanel);
    /**
     * The object's type.
     * @type {String}
     */
    TreePanel.prototype.type = 'TreePanel';
    /**
     * The class family.
     * @type {String}
     */
    TreePanel.prototype.family = 'Panel';
    /**
     * Initializes the object.
     * @param  {Object} settings The config options for the object.
     * @private
     */
    TreePanel.prototype.init = function (settings) {
        var defaults = {
            nodeDefaultSettings: null,
            dataItems: null,
            data: [],
            filterPlaceholder: 'search',
            filterable: false,
            emptyMessage: null,
            visibleTitle: false,
            title: "",
            onNodeClick: null,
            onAppend: null,
            onBeforeAppend: null,
            collapse: false,
            onStop : function(){console.log("trepanel")}
        };

        jQuery.extend(true, defaults, settings);

        this.setNodeDefaultSettings(defaults.nodeDefaultSettings)
            .setEmptyMessage(defaults.emptyMessage)
            .setTitle(defaults.title)
            .setOnNodeClickHandler(defaults.onNodeClick)
            .setOnAppendHandler(defaults.onAppend)
            .setOnBeforeAppendHandler(defaults.onBeforeAppend);
        this.filterControl = new PMUI.control.TextControl({
            onKeyUp: this.onFilterControlChangeHandler(),
            placeholder: defaults.filterPlaceholder
        });

        if (jQuery.isArray(defaults.dataItems)) {
            this.setDataItems(defaults.dataItems);
        } else {
            this.setItems(defaults.items);
        }
        if (defaults.filterable) {
            this.enableFiltering();
        } else {
            this.disableFiltering();
        }
        if (defaults.visibleTitle) {
            this.showTitle();
        } else {
            this.hideTitle();
        }
        if (defaults.collapse) {
            this.collapse(true);
        } else {
            this.expand(true);
        }
    };
    /**
     * Sets the callback function to be called everytime the {@link #event-onBeforeAppend onBeforeAppend} event is
     * fired. For info about the parameters used for this callback please read the documentation for this event.
     * @param {Function|null} handler
     */
    TreePanel.prototype.setOnBeforeAppendHandler = function (handler) {
        if (!(handler === null || typeof handler === 'function')) {
            throw new Error("setOnBeforeAppendHandler(): The parameter must be a function or null.");
        }
        this.onBeforeAppend = handler;
        return this;
    };
    /**
     * Sets the callback function to be called everytime the
     * {@link #event-onAppend onAppend} event is fired. For info about the parameters used for this callback please
     * read the documentation for this event.
     * @param {Function|null} handler
     */
    TreePanel.prototype.setOnAppendHandler = function (handler) {
        if (!(handler === null || typeof handler === 'function')) {
            throw new Error("setOnAppendHandler(): The parameter must be a function or null.");
        }
        this.onAppend = handler;
        return this;
    };
    /**
     * Sets the callback function to be executed everytime the {@link #event-onNodeclick onNodeClick} is fired. For
     * info about the parameters sent to the callback read the document for that event.
     * @param {Function} handler It can be a function or null.
     */
    TreePanel.prototype.setOnNodeClickHandler = function (handler) {
        if (!(handler === null || typeof handler === 'function')) {
            throw new Error("setOnNodeClickHandler(): The parameter must be a function.");
        }
        this.onNodeClick = handler;
        return this;
    };
    /**
     * @inheritdoc
     */
    TreePanel.prototype.clearItems = function () {
        TreePanel.superclass.prototype.clearItems.call(this);
        return this.showEmptyMessage();
    };
    /**
     * Hides the title bar.
     * @chainable
     */
    TreePanel.prototype.hideTitle = function () {
        this.visibleTitle = false;
        if (this.dom.title) {
            this.dom.title.style.display = 'none';
        }
        return this;
    };
    /**
     * Shows the title.
     * @chainable
     */
    TreePanel.prototype.showTitle = function () {
        this.visibleTitle = true;
        if (this.dom.title) {
            this.dom.title.style.display = '';
        }
        return this;
    };
    /**
     * Sets the title for the tree panel.
     * @param {String} title
     * @chainable
     */
    TreePanel.prototype.setTitle = function (title) {
        if (typeof title !== 'string') {
            throw new Error('showTitle(): The parameter must be a string.');
        }
        this.title = jQuery.trim(title);
        if (this.dom.title) {
            this.dom.title.textContent = this.title;
        }
        return this;
    };
    /**
     * Sets the message to display when there are no items to display in the tree panel.
     * @param {String|Function|null} emptyMessage It can be:
     *
     * - A String, in this case the string will be displayed in both cases, when the tree has no items and when no
     * tree's items meet the filter.
     * - A Function, in this case the function must return a string or an HTML Element to be displayed. The function
     * will receive two parameters: the {@link PMUI.panel.TreePanel tree panel}, a boolean If it is true it means the returned
     * value will be used when a filter is applied, otherwise it means that the returned value will be used when
     * there are no items in the tree panel.
     * - null, in this case a default message will be used for each situation.
     * @chainable
     */
    TreePanel.prototype.setEmptyMessage = function (emptyMessage) {
        if (!(emptyMessage === null || typeof emptyMessage === 'string' || typeof emptyMessage === 'function')) {
            throw new Error("setEmptyMessage(): the parameter must be a string, a function or null.");
        }
        this.emptyMessage = emptyMessage;
        return this;
    };
    /**
     * Sets the filter for the text control's placeholder.
     * @param {String} placeholder The text for the placeholder.
     * @chainable
     */
    TreePanel.prototype.setFilterPlaceholder = function (placeholder) {
        this.filterControl.setPlaceholder(placeholder);
        return this;
    };
    /**
     * Returns the placeholder for the text control for filtering.
     * @return {String}
     */
    TreePanel.prototype.getFilterPlaceholder = function () {
        return this.filterControl.placeholder;
    };
    /**
     * Returns the handler to be executed when the filter changes.
     * @return {Function}
     * @private
     */
    TreePanel.prototype.onFilterControlChangeHandler = function () {
        var that = this;
        return function () {
            var nextFilter = this.getHTML().value;
            if (that.filterCriteria !== nextFilter) {
                that.filter(nextFilter);
            }
        };
    };
    /**
     * Enables the filtering functionality.
     * @chainable
     */
    TreePanel.prototype.enableFiltering = function () {
        this.filterable = true;
        this.clearFilter().filterControl.setValue("");
        if (this.dom.toolbar) {
            this.dom.toolbar.style.display = '';
        }
        return this;
    };
    /**
     * Disables the filtering functionality.
     * @chainable
     */
    TreePanel.prototype.disableFiltering = function () {
        this.filterable = false;
        this.clearFilter().filterControl.setValue("");
        if (this.dom.toolbar) {
            this.dom.toolbar.style.display = 'none';
        }
        return this;
    };
    /**
     * Sets the default settings for the direct child nodes of the tree panel.
     * @param {Object} settings The config options to be applied to new child nodes to be added in case they are
     * missing any config option.
     * @chainable
     */
    TreePanel.prototype.setNodeDefaultSettings = function (settings) {
        if (typeof settings !== 'object') {
            throw new Error('setNodeDefaultSettings(): The parameter must be an object.');
        }
        this.nodeDefaultSettings = settings;

        return this;
    };
    /**
     * @inheritdoc
     */
    TreePanel.prototype.setFactory = function () {
        this.factory = new PMUI.util.Factory({
            products: {
                'node': PMUI.item.TreeNode
            },
            defaultProduct: 'node'
        });

        return this;
    };
    /**
     * The private handler fot the {@link #event-onBeforeAppend onBeforeAppend} event.
     * @private
     * @chainable
     */
    TreePanel.prototype.onBeforeAppendHandler = function (treeNode) {
        if (typeof this.onBeforeAppend === 'function') {
            this.onBeforeAppend(this, treeNode);
        }
        return this;
    };
    /**
     * The private handler for the {@link #event-onAppend} event.
     * @private
     * @chainable
     */
    TreePanel.prototype.onAppendHandler = function (treeNode) {
        if (typeof this.onAppend === 'function') {
            this.onAppend(this, treeNode);
        }
        return this;
    };
    /**
     * Adds a new node to the tree panel.
     * @param {Object|PMUI.item.TreeNode} item An instance of {@link PMUI.item.TreeNode TreeNode}, or an object with
     * the config options to create a new one. In the latter case, if any config option is missing then will be used
     * the respective one in the set by the {@link #method-setNodeDefaultSettings setNodeDefaultSettings()} method.
     * @param {Number} [index] The position index in which will be added the new item. If not specified the will be
     * added to the end.
     * @chainable
     */
    TreePanel.prototype.addItem = function (item, index) {
        var settings = {},
            itemToAdd;

        if (item instanceof PMUI.item.TreeNode) {
            item.setParent(this);
        } else if (typeof item === 'object') {
            jQuery.extend(true, settings, this.nodeDefaultSettings || {}, item);
            settings.parent = this;
            item = settings;
        } else {
            throw new Error('addItem(): The parameter must be an instance of PMUI.item.TreeNode or an object.');
        }
        itemToAdd = this.factory.make(item);
        if (itemToAdd) {
            this.hideEmptyMessage();
            this.onBeforeAppendHandler(itemToAdd);
            TreePanel.superclass.prototype.addItem.call(this, itemToAdd, index);
            this.onAppendHandler(itemToAdd);
        }
        return this;
    };
    /**
     * @inheritdoc
     */
    TreePanel.prototype.removeItem = function (item) {
        TreePanel.superclass.prototype.removeItem.call(this, item);
        if (!this.items.getSize()) {
            this.showEmptyMessage();
        }
        return this;
    };
    /**
     * @inheritdoc
     */
    TreePanel.prototype.setItems = function (items) {
        if (this.onAppend !== undefined) {
            TreePanel.superclass.prototype.setItems.call(this, items);
        }
        return this;
    };
    /**
     * Expands the child nodes.
     * @param  {Boolean} [expandAll=false] If the expand function will be applied to all the sub child items or not.
     * @chainable
     */
    TreePanel.prototype.expand = function (expandAll) {
        var i,
            items = this.items.asArray();

        for (i = 0; i < items.length; i += 1) {
            items[i].expand(expandAll);
        }
        return this;
    };
    /**
     * Collapse the child nodes.
     * @param  {Boolean} [collapseAll=false] If the collapse function will be applied to all the sub child items or
     * not.
     * @chainable
     */
    TreePanel.prototype.collapse = function (collapseAll) {
        var i,
            items = this.items.asArray();

        for (i = 0; i < items.length; i += 1) {
            items[i].collapse(collapseAll);
        }
        return this;
    };
    /**
     * Hides the message to show when there are no items to display.
     * @chainable
     * @private
     */
    TreePanel.prototype.hideEmptyMessage = function () {
        jQuery(this.html).find('.pmui-treepanel-emptymessage').hide();
        return this;
    };
    /**
     * Shows an empty message when there are no items to display.
     * @chainable
     * @private
     */
    TreePanel.prototype.showEmptyMessage = function () {
        var node,
            message;

        if (!this.html) {
            return this;
        }
        node = jQuery(this.html).find('.pmui-treepanel-emptymessage').empty().get(0);
        if (!node) {
            node = PMUI.createHTMLElement('div');
            node.className = 'pmui-treepanel-emptymessage';
        }
        if (typeof this.emptyMessage === 'function') {
            message = this.emptyMessage(this, !!this.filterCriteria);
        } else if (typeof this.emptyMessage === 'string') {
            message = this.emptyMessage;
        } else {
            if (this.filterCriteria) {
                message = 'No matches found for \"' + this.filterCriteria + '\"';
            } else {
                message = 'No items found.';
            }
        }
        if (typeof message === 'string') {
            node.appendChild(document.createTextNode(message));
        } else if (PMUI.isHTMLElement(message)) {
            node.appendChild(message);
        }
        node.style.display = '';
        this.html.appendChild(node);
        return this;
    };
    /**
     * Clears the current filter.
     * @chainable
     */
    TreePanel.prototype.clearFilter = function () {
        return this.filter("");
    };
    /**
     * Filters the tree using a criteria.
     * @param  {String} filterCriteria The criteria.
     * @chainable
     */
    TreePanel.prototype.filter = function (filterCriteria) {
        var i,
            childNodes = this.items.asArray(),
            partialRes,
            res = false;

        filterCriteria = filterCriteria.toString(10);
        this.filterCriteria = filterCriteria;
        jQuery(this.html).find('.pmui-treepanel-emptymessage').remove();
        for (i = 0; i < childNodes.length; i += 1) {
            childNodes[i].setVisible(partialRes = childNodes[i].filter(filterCriteria));
            res = res || partialRes;
        }
        if (!res) {
            this.showEmptyMessage();
        }
        return this;
    };
    /**
     * @inheritdoc
     */
    TreePanel.prototype.defineEvents = function () {
        TreePanel.superclass.prototype.defineEvents.call(this);
        this.filterControl.defineEvents();
        return this;
    };
    /**
     * @inheritdoc
     */
    TreePanel.prototype.createHTML = function () {
        var list,
            toolbar,
            title;

        if (this.html) {
            return this.html;
        }

        html = PMUI.createHTMLElement(this.elementTag || "div");
        html.id = this.id;
        PMUI.linkToPMUIObject(html, this);
        this.html = html;
        this.actionHTML = html;
        this.applyStyle();
        this.containmentArea = this.html;

        title = PMUI.createHTMLElement('h2');
        title.className = 'pmui-treepanel-title';
        this.html.appendChild(title);
        this.dom.title = title;
        list = PMUI.createHTMLElement('ul');
        list.className = 'pmui-treepanel-list';
        this.dom.list = list;
        this.containmentArea = list;
        toolbar = PMUI.createHTMLElement('div');
        toolbar.className = 'pmui-treepanel-toolbar';
        toolbar.style.display = 'none';
        toolbar.appendChild(this.filterControl.getHTML());
        this.dom.toolbar = toolbar;
        this.html.appendChild(toolbar);
        this.html.appendChild(list);
        this.setTitle(this.title);
        if(this.items.getSize() > 0){
            this.paintItems();
        }
        if (this.filterable) {
            this.enableFiltering();
        } else {
            this.disableFiltering();
        }
        if (this.visibleTitle) {
            this.showTitle();
        } else {
            this.hideTitle();
        }
        return this.html;
    };

    PMUI.extendNamespace('PMUI.panel.TreePanel', TreePanel);

    if (typeof exports !== 'undefined') {
        module.exports === TreePanel;
    }
}());
(function () {
    /**
     * @class PMUI.panel.TabPanel
     * @extends PMUI.core.Panel
     * Is the container for every {@link PMUI.item.TabItem TabItem} class
     *
     * Example:
     *
     *     @example
     var a,p,f,tree,w;

     //Form Panel*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
     p = new PMUI.form.FormPanel({
                width: 604, 
                height: 130,
                fieldset: true,
                legend: "my fieldset panel",
                items: [
                    {
                        pmType: "text",
                        label: "Name",
                        id: "123",
                        value: "",
                        placeholder: "insert your name",
                        name: "name"
                    },{
                        pmType: "text",
                        label: "Last name",
                        value: "",
                        placeholder: "your lastname here asshole!",
                        name: "lastname"
                    }
                ],
                layout: "vbox"
            });
     //treePanel *-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
     tree = new PMUI.panel.TreePanel({
                items: [
                    {
                        label: "America",
                        items: [
                            {
                                label: "North America",
                                items: [
                                    {
                                        label: "Canada"
                                    },
                                    {
                                        label: "USA"
                                    },
                                    {
                                        label: "Mexico"
                                    }
                                ]
                            }
                        ]
                    }
                ]
            });
     //Layout Panel *-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
     f = new PMUI.form.Form({
                onSubmit: function() {
                    console.log("submitting...");
                },
                onChange: function(field, newValue, previousValue) {
                    console.log("The field " + field.getName() + " has changed from \"" + previousValue + "\" to \"" + newValue + "\"");
                },
                items: [
                    {
                        pmType: "dropdown",
                        label: "age",
                        options: [
                            {
                                label: "from 0 to 7 years old",
                                value: "0-7"
                            },
                            {
                                label: "from 8 to 13 years old",
                                value: "8-13"
                            }
                        ],
                        name: "age",
                        helper: "Select one of the options",
                        required: true
                    },
                    {
                        pmType: "radio",
                        label: "Gender",
                        value: "m",
                        name: "gender",
                        required: true,
                        options: [
                            {
                                label: "Male",
                                value: "m"
                            },
                            {
                                label: "Female",
                                value: "f"
                            }
                        ]
                    }
                ],
                buttons: [
                    {
                        text: "Submit",
                        handler: function() {
                            f.submit();
                            console.log("submitting form...");
                        }
                    }
                ]
            });
     //Tab Item*--**-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
     a = new PMUI.panel.TabPanel({
            height : 400,
            width : 600,
            items:[
                {
                    icon :'pmui-gridpanel-engranaje',
                    title:'Tree Panel',
                    panel:tree
                },
                {
                    icon :'pmui-gridpanel-engranaje',
                    title:'Form Panel',
                    panel:p
                },
                {   icon :'pmui-gridpanel-engranaje',
                    title:'Form',
                    panel:f
                }
            ],
            itemsPosition : {position:"left",percentageWidth : 22}
            //itemsPosition : 'top'
    });
     //document.body.appendChild(a.getHTML());
     //a.defineEvents();
     w = new PMUI.ui.Window({
            title: "TabPanel",
            width: 650,
            height: 460,
            footerHeight: 'auto',
            bodyHeight: 'auto',
            modal:true,
            buttons: [
                {
                    text: 'Saves',
                    handler: function() {
                        alert('save');
                    }
                },
                {
                    text: 'Close',
                    handler: function() {
                        alert('close');
                    }
                },
                {
                    text: "third button"
                }
            ],
            buttonsPosition : 'center',
            spaceButtons : 30
        });
     w.open();
     w.addItem(a);
     w.defineEvents();

     * In the example, we use as a treePanel panels, FormPanel and layoutPanel, each TabItem can also use a className
     * to put an icon, see {@link PMUI.item.TabItem}, {@link PMUI.item.TabItem#cfg-icon icon}. The pocision of items [TabItem]
     * can be "top", "left", "right" and "bottom", see the configuration options.
     *
     * Note: used a window for a best visualisation.
     *
     * @cfg {Array} [items = []]
     * The items should be a Array, that Array should have Object JSON for config of the TabItems
     * for more detail see {@link PMUI.item.TabItem}
     *
     * @cfg {String|Object} [itemsPosition = 'top']
     * This option config can be of type:
     *
     *      Object, when position is "left" or "right", example:
     *          itemsPosition: {position: "left", percentageWidth: 20}
     *          itemsPosition: {position: "right", percentageWidth: 20}
     *              JSON options:
     *              position: must be a string (can only be "left" or "right")
     *              percentageWidth: must be a number between 0 and 100
     *
     *      String,  when the position is "top" or "bottom", example:
     *      itemsPosition: "top"
     *      itemsPosition: "bottom"
     *      percentageWidth is not require
     */
    var TabPanel = function (settings) {
        TabPanel.superclass.call(this, settings);
        /**
         * Defines the tabpanel panelContainer HTML Element where is fix the content of the family Panel
         * @type {HTMLElement}
         */
        this.dom = {};
        /**
         * a property to assign events to see if the tabs is selected or not
         * @type {PMUI.ite.TabItem}
         */
        this.selectedTab = null;
        /**
         * a property that can be true or false values​​, when set to true indicates
         * that the items will be collapsible
         * @type {boolean}
         */
        this.collapsible = null;
        /**
         * this posision items should be 'top', 'left', 'right' or 'bottom'
         * @type {String}
         */
        this.itemsPosition = null;
        /**
         * represents the space busy for the  containerItems HTMLElement,  default value
         * this property is 0, when itemsPosition is "right" or "left " this value is different 0
         * @type {Number}
         */
        this.percentageWidth = null;
        this.onTabClick = null;
        TabPanel.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom("PMUI.core.Panel", TabPanel);

    TabPanel.prototype.type = "tabpanel";
    TabPanel.prototype.family = "tabpanel";

    TabPanel.prototype.init = function (settings) {
        var defaults = {
            items: [],
            itemsPosition: 'top',
            onTabClick: null,
            collapsible: true
        };
        jQuery.extend(true, defaults, settings);
        this.setItems(defaults.items);
        this.setItemsPosition(defaults.itemsPosition);
        this.setOnTabClick(defaults.onTabClick);
        this.setCollapsible(defaults.collapsible);
    };

    TabPanel.prototype.setCollapsible = function (collapsible) {
        if (typeof collapsible !== 'boolean') {
            throw new Error('setCollapsible(): the parameter collapsible should be type "boolean"');
        }
        this.collapsible = collapsible;
        return this;
    };

    TabPanel.prototype.setOnTabClick = function (tabClick) {
        if (typeof tabClick !== 'function' && tabClick !== null) {
            throw new Error("setOnTabClick(): the parameter is not valid, should be a function.");
        }
        this.onTabClick = tabClick;
        return this;
    };

    /**
     * set in this method and call addItem to create the TabItem
     * @param {Array} The JSON array with objects with parameters for creating TabItem
     * @chainable
     */
    TabPanel.prototype.setItems = function (items) {
        var i;

        if (jQuery.isArray(items)) {
            this.clearItems();
            for (i = 0; i < items.length; i += 1) {
                this.addItem(items[i]);
            }
        } else {
            throw new Error("setTabs(): the parameter is not a valid, should to be as Array");
        }
        for (i = 0; i < this.items.getSize(); i += 1) {
            if (i == 0) {
                this.items.get(0).select();
            } else {
                this.items.get(i).deselect();
            }
        }
        return this;
    };
    /**
     * [onTabClickHandler description]
     * @return {[type]} [description]
     * @private
     */
    TabPanel.prototype.onTabClickHandler = function () {
        var that = this;

        return function (tabItem) {
            if (typeof that.onTabClick === 'function') {
                that.onTabClick(tabItem);
            }
        };
    };
    /**
     * Add an item to the panel.
     * @param {PMUI.item.TabItem} item
     * It can be a valid JSON object or an object that inherits from {@link PMUI.item.TabItem}.
     * @chainable
     */
    TabPanel.prototype.addItem = function (item) {
        var tabItem;

        item.onSelect = this.onTabSelectHandler();
        tabItem = new PMUI.item.TabItem(item);
        tabItem.deselect();
        tabItem.setOnClick(this.onTabClickHandler());
        this.items.insert(tabItem);
        if (this.html) {
            this.dom.listContainer.appendChild(tabItem.getHTML());
            this.updatePosition();
            if (this.eventsDefined) {
                tabItem.defineEvents();
            }
        }
        return this;
    };
    /**
     * @event
     * defines the function to be determined in each tabItem when it is selected, to
     * call the event set
     * @chainable
     */
    TabPanel.prototype.onTabSelectHandler = function () {
        var that = this,
            i;

        return function () {
            if (that.selectedTab) {
                jQuery(that.selectedTab.panel.getHTML()).detach();
            }
            that.selectedTab = this;
            for (i = 0; i < that.items.getSize(); i += 1) {
                if (that.items.get(i) !== that.selectedTab) {
                    that.items.get(i).deselect();
                }
            }
            jQuery(that.dom.panelContainer).append(this.getPanel().getHTML());
            if (!this.getPanel().eventsDefined) {
                this.getPanel().defineEvents();
            }
        };
    };
    /**
     * @method  setItemsPosition
     * changes the position of the elements [PM UI.item.TabItem] within the sizes assigned to TabPanel.
     * @param {String|Object}
     * Position where items are positioned, the more info about input value see {@link PMUI.panel.TabPanel}
     * config option: {@link PMUI.panel.TabPanel#cfg-itemsPosition itemsPosition}
     * @chainable
     */
    TabPanel.prototype.setItemsPosition = function (itemsPosition) {
        var validPosition,
            percentageWidth;

        if (typeof itemsPosition == "string" || typeof itemsPosition == "object") {
            if (typeof itemsPosition == "object") {
                if (itemsPosition.position == "left" || itemsPosition.position == "right") {
                    this.itemsPosition = itemsPosition.position;
                    this.percentageWidth = itemsPosition.percentageWidth || 20;
                } else {
                    throw new Error("setItemsPosition(): JSON no valid, send position : 'left' or 'right' with percentage width");
                }
            } else {
                if (itemsPosition == "top" || itemsPosition == "bottom") {
                    this.itemsPosition = itemsPosition;
                    this.percentageWidth = 0;
                } else {
                    throw new Error("setItemsPosition(): no valid, send position : 'top' or 'bottom' ");
                }
            }

            this.style.removeClasses(['pmui-tabpanel-left', 'pmui-tabpanel-right', 'pmui-tabpanel-top', 'pmui-tabpanel-bottom']);
            this.style.addClasses(['pmui-tabpanel-' + this.itemsPosition]);

            if (this.html) {
                this.updatePosition();
            }
        } else {
            throw new Error("setItemsPosition(): the parameter is not a valid, should to be as string or Object JSON");
        }
        return this;
    };
    /**
     * @private
     * update the position for the HTMLElement.
     * exists the next elements HTMLElements:
     *
     *     - containerItems
     *     - listContainer
     *     - barCollaspible
     *     - panelContainer
     *
     * this elements should update your position in the DOM, also update dimensions respect to the property itemsPosition
     * @chainable
     */
    TabPanel.prototype.updatePosition = function () {
        var i,
            itemsDisplay;

        switch (this.itemsPosition) {
            case 'top' :
            case 'left':
                $(this.dom.containerItems).prepend(this.dom.listContainer);
                $(this.html).prepend(this.dom.containerItems);


                this.dom.containerItems.style.display = this.itemsPosition === 'left' ? "inline-block" : "block";
                this.dom.listContainer.style.display = this.itemsPosition === 'left' ? "inline-block" : "block";
                this.dom.collapsibleBar.style.display = this.itemsPosition === 'left' ? "inline-block" : "none";
                this.dom.panelContainer.style.display = this.itemsPosition === 'left' ? "inline-block" : "block";
                $(this.dom.containerItems).css("float", this.itemsPosition === 'left' ? "left" : "");

                itemsDisplay = this.itemsPosition === 'left' ? "block" : "inline-block";

                for (i = 0; i < this.items.getSize(); i += 1) {
                    this.items.get(i).setDisplay(itemsDisplay);
                }

                if (this.itemsPosition == 'left') {
                    this.dom.containerItems.style.width = this.getWidth() * (this.percentageWidth / 100) + 'px';
                    this.dom.containerItems.style.height = this.getHeight() + "px";
                    this.dom.panelContainer.style.width = this.getWidth() - (this.getWidth() * (this.percentageWidth / 100)) + 'px';
                    this.dom.panelContainer.style.height = this.getHeight() + 'px';
                    this.dom.collapsibleBar.style.height = this.getHeight() + 'px';
                    jQuery(this.dom.listContainer).css("float", 'left');
                    for (i = 0; i < this.items.getSize(); i += 1) {
                        this.items.get(i).setWidth(this.getWidth() * (this.percentageWidth / 100) - 12);
                    }
                } else {
                    this.dom.containerItems.style.width = this.getWidth() + 'px';
                    this.dom.containerItems.style.height = '40px';
                    this.dom.panelContainer.style.width = this.getWidth() + 'px';
                    this.dom.panelContainer.style.height = this.getHeight() - 40 + 'px';
                    this.dom.collapsibleBar.style.height = this.getHeight() + 'px';
                    $(this.dom.listContainer).css("float", 'left');
                    for (i = 0; i < this.items.getSize(); i += 1) {
                        this.items.get(i).setWidth('auto');
                    }
                }
                break;
            case 'right' :
            case 'bottom' :
                $(this.dom.containerItems).prepend(this.dom.collapsibleBar);
                $(this.html).prepend(this.dom.panelContainer);
                this.dom.containerItems.style.display = this.itemsPosition === 'right' ? "inline-block" : "block";
                this.dom.listContainer.style.display = this.itemsPosition === 'right' ? "inline-block" : "block";
                this.dom.collapsibleBar.style.display = this.itemsPosition === 'right' ? "inline-block" : "none";
                this.dom.panelContainer.style.display = this.itemsPosition === 'right' ? "inline-block" : "block";
                $(this.dom.containerItems).css("float", this.itemsPosition === 'right' ? "right" : "");

                itemsDisplay = this.itemsPosition === 'right' ? "block" : "inline-block";

                for (i = 0; i < this.items.getSize(); i += 1) {
                    this.items.get(i).setDisplay(itemsDisplay);
                }

                if (this.itemsPosition == 'right') {
                    this.dom.containerItems.style.width = this.getWidth() * (this.percentageWidth / 100) + 'px';
                    this.dom.containerItems.style.height = this.getHeight() + "px";
                    this.dom.panelContainer.style.width = this.getWidth() - (this.getWidth() * (this.percentageWidth / 100)) + 'px';
                    this.dom.panelContainer.style.height = this.getHeight() + 'px';
                    this.dom.collapsibleBar.style.height = this.getHeight() + 'px';
                    $(this.dom.listContainer).css("float", 'right');
                    for (i = 0; i < this.items.getSize(); i += 1) {
                        this.items.get(i).setWidth(this.getWidth() * (this.percentageWidth / 100));
                    }
                } else {
                    this.dom.containerItems.style.width = this.getWidth() + 'px';
                    this.dom.containerItems.style.height = '40px';
                    this.dom.panelContainer.style.width = this.getWidth() + 'px';
                    this.dom.panelContainer.style.height = this.getHeight() + 'px';
                    this.dom.collapsibleBar.style.height = this.getHeight() + 'px';
                    $(this.dom.listContainer).css("float", 'none');
                    for (i = 0; i < this.items.getSize(); i += 1) {
                        this.items.get(i).setWidth('auto');
                    }
                }
                break;
        }
        return this;
    };
    /**
     * Create html for each item to container
     * @return {Object} the current object html
     */
    TabPanel.prototype.createHTML = function () {
        var containerItems,
            panelContainer,
            collapsibleBar,
            listContainer,
            i;

        if (this.html) {
            return html;
        }
        this.html = PMUI.createHTMLElement('div');
        this.html.className = 'pmui-tabpanel';
        this.html.id = this.id;
        containerItems = PMUI.createHTMLElement('div');
        listContainer = PMUI.createHTMLElement('ul');
        collapsibleBar = PMUI.createHTMLElement('div');
        panelContainer = PMUI.createHTMLElement('div');

        containerItems.className = "pmui-tabpanel-tabs_container";
        listContainer.className = "pmui-tabpanel-tabs";
        collapsibleBar.className = "pmui-collapsibleBar";
        panelContainer.className = "pmui-tabpanel-container";

        this.dom.containerItems = containerItems;
        this.dom.listContainer = listContainer;
        this.dom.collapsibleBar = collapsibleBar;
        this.dom.panelContainer = panelContainer;
        this.dom.panelContainer.style.overflow = 'auto';
        this.setItems(this.items.asArray().slice(0));

        this.dom.containerItems.appendChild(this.dom.listContainer);
        this.dom.containerItems.appendChild(this.dom.collapsibleBar);
        this.html.appendChild(this.dom.containerItems);
        this.html.appendChild(this.dom.panelContainer);

        if (this.itemsPosition == 'left' || this.itemsPosition == 'right') {
            this.setItemsPosition({position: this.itemsPosition, percentageWidth: this.percentageWidth});
        } else {
            this.setItemsPosition(this.itemsPosition);
        }
        this.applyStyle();
        if (this.eventsDefined) {
            this.defineEvents();
        }

        for (i = 0; i < this.items.getSize(); i += 1) {
            if (!this.getItems()[i].visible) {
                this.hideTab(i)
            }
        }
        return this.html;
    };
    /**
     * Executes children events defined
     * @chainable
     */
    TabPanel.prototype.defineEvents = function () {
        var that = this,
            i,
            auxPercentageWidth;

        this.removeEvents().eventsDefined = true;
        if (this.dom.collapsibleBar && this.collapsible) {
            this.addEvent('click').listen(this.dom.collapsibleBar, function (e) {
                e.preventDefault();
                e.stopPropagation();
                auxPercentageWidth = that.percentageWidth;
                if (that.collapsible) {
                    that.setItemsPosition({position: that.itemsPosition, percentageWidth: 9});
                    that.collapsible = false;
                    that.style.addClasses(['pmui-collipsable']);
                } else {
                    that.setItemsPosition({position: that.itemsPosition, percentageWidth: auxPercentageWidth});
                    that.style.removeClasses(['pmui-collipsable']);
                    that.collapsible = true;

                }
                that.percentageWidth = auxPercentageWidth;

                if (typeof that.onTabClick == 'function') {

                }
            });
            for (i = 0; i < that.items.getSize(); i += 1) {
                that.items.get(i).defineEvents();
            }
        }
        return this;
    };

    TabPanel.prototype.setWidth = function (width) {
        TabPanel.superclass.prototype.setWidth.call(this, width);
        this.updatePosition();
        return this;
    };

    TabPanel.prototype.setHeight = function (height) {
        TabPanel.superclass.prototype.setHeight.call(this, height);
        this.updatePosition();
        return this;
    };

    /**
     * hides a TabItem, if hidden the last TabItem passes focus to the previous,
     * otherwise the selection will move to the next always
     * @param  {Number} index  Is the selection of the TabItem that is concealed,
     * must be within the range of the length of existing items
     * @param  {[type]} current [when hidden a tabItem, can fix focus in other tabItem]
     * @chainable
     */
    TabPanel.prototype.hideTab = function (index, current) {
        var item,
            currentTab,
            i,
            len;

        for (i = 0; i < this.getItems().length; i += 1) {
            if (i === index) {
                item = this.getItems()[i];
            }
        }
        if (!current) {
            if (item.selected) {
                len = index - 1;
                while (len > -1) {
                    if (this.getItems()[len].visible === true) {
                        currentTab = this.getItems()[len];
                        len = -1;
                    } else {
                        len--;
                    }
                }
                if (!currentTab) {
                    i = index + 1;
                    while (i < this.items.getSize()) {
                        if (this.getItems()[i].visible === true) {
                            currentTab = this.getItems()[i];
                            i = this.items.getSize();
                        } else {
                            i++;
                        }
                    }
                }
            }
        } else {
            currentTab = this.getItems()[current];
        }

        item.setVisible(false);
        item.panel.setVisible(false);
        if (currentTab) {
            currentTab.select();
        }
        return this;
    }
    /**
     * tab shows a hidden item
     * @param  {Number} index Este valor es necesario para mostrar TabItem, debe estar dentro de un rango de la longitud de los elementos
     * @chainable
     */
    TabPanel.prototype.showTab = function (index) {

        if (typeof index == 'number') {
            if (!this.getItems()[index].visible) {
                this.getItems()[index].setVisible(true);
                this.getItems()[index].panel.setVisible(true);
            }
        }
        return this;
    };


    TabPanel.prototype.itemClick = function (index) {
        $($(this.dom.listContainer).find('a')[index]).trigger('click');
        return this;
    };

    if (typeof exports != 'undefined') {
        module.exports = TabPanel;
    }
    PMUI.extendNamespace("PMUI.panel.TabPanel", TabPanel);
}());
(function () {
    /**
     * @class PMUI.panel.AccordionPanel
     * @extends PMUI.core.Panel
     *
     * Class to handle an Accordion component, this is a container for {@link PMUI.item.AccordionItem AccordionItem}.
     *
     * The way of usage the class is showed on the example above
     *
     *              @example
     *               accordion   = new PMUI.panel.AccordionPanel({
     *                   width: 400,
     *                   height: 500,
     *                   hiddenTitle: true,
     *                   //multipleSelection : true,
     *                   title: "My accordion",
     *                   items: [
     *                       {
     *                           iconClass: "",
     *                           title: "First item",
     *                           selected: true
     *                       },
     *                       {
     *                           iconClass: "pmui-icon pmui-icon-warning",
     *                           title: "Second item",
     *                           body: "<a href=\"http://www.google.com\">Google Link</a>",
     *                           style: {
     *                               cssProperties: {
     *                                   "background-color": "#f2eaea"
     *                               }
     *                           }
     *                       },
     *                       {
     *                           title: "Third item <span class=\"classname-accordion\"></span>"
     *                       },
     *                       {
     *                           title: "Fourth"
     *                        }, new PMUI.item.AccordionItem({
     *                           title: "Five"
     *                        })
     *                   ],
     *                   listeners: {
     *                       select: function (obj, event) {
     *                           console.log("Selected...", obj);
     *                       }
     *                   }
     *               });
     *               document.body.appendChild(accordion.getHTML());
     *               accordion.defineEvents();
     *
     *
     * @constructor
     * Creates a new component
     * @param {Object} [settings] settings The configuration options may be specified as the follow sentence
     *      {
     *          items: [
     *              {
     *                  title: "First element",
     *                  body: item // This item can be any element that inherit from {@link PMUI.core.Panel Panel}
     *              },
     *              {
     *                  title: "Second element"
     *              }
     *          ]
     *      }
     * @cfg {String} name The name for the field.
     *
     * @cfg {Boolean} [hiddenTitle=true] Represents wether the title of the component will be showed, the
     * otherwise the panel will rendered without title.
     * @cfg {Array} [items=[]] The array contains the basic elements for {@PMUI.panel.AccordionPanel AccordionPanel class}
     * multipleSelection.
     * @cfg {String} [title=""] The title for the panel.
     * @cfg {Number} [heightItem = 'current height * 0.3'] Represents the height for every item
     * @cfg {Object} factory If it necessary is possible replace the current elements by default.
     *      factory: {
     *          products: {
     *              'accordionitem': PMUI.item.AccordionItem
     *          },
     *          defaultProduct: "accordionitem"
     *      }
     *
     */

    var AccordionPanel = function (settings) {
        AccordionPanel.superclass.call(this, settings);
        /**
         * @property {String} [title= ""] The text represents the title of the panel,
         * Whether the {PMUI.panel.AccordionPanel this.hiddenTitle} is false.
         */
        this.title = null;
        /**
         * @property {Boolean} [hiddenTitle=false] The property represents whether the title
         * will be showed.
         */
        this.hiddenTitle = null;
        /**
         * @property {Boolean} [multipleSelection=false] If the property is enabled, the {@link PMUI.item.AccordionItem items} can
         * be selected more than one time
         */
        this.multipleSelection = null;
        /**
         * @property {Object} [listeners] A config object containing one or more handlers to be added
         * to this object during initialization. by default the 'select' handler is enabled
         */
        this.listeners = {
            /**
             * @event select
             * Fires when a {@link PMUI.item.AccordionItem component} has been selected.
             * @param {Object} accordionItem {@link PMUI.item.AccordionItem AccordionItem}
             * @param {Object} event event
             */
            select: function () {
            }
        };
        /**
         * @property {HTMLElement} [header=null]
         * The property encapsulate the HTMLElement header for the panel.
         */
        this.header = null;
        /**
         * @property {HTMLElement} [body=null]
         * The property is related to HTMLElement body that will be used by
         * {@link PMUI.item.AccordionItem items}
         */
        this.body = null;
        /**
         * @property {HTMLElement} [footer=null]
         * The property encapsulate the HTMLElement footer for the panel.
         */
        this.footer = null;
        /**
         * @property {String} [iconClass='pmui-accordion-panel-icon']
         * Represents the class name for the icon.
         * It can be replaced on the config JSON if it is necessary
         * @private
         */
        this.iconClass = null;
        /**
         * @property {String} [headerClass='pmui-accordion-panel-header']
         * Represents the class name for the header of the panel.
         * It can be replaced on the config JSON if it is necessary
         * @private
         */
        this.headerClass = null;
        /**
         * @property {String} [bodyClass='pmui-accordion-panel-body']
         * Represents the class name for the body of the panel.
         * It can be replaced on the config JSON if it is necessary
         * @private
         */
        this.bodyClass = null;
        /**
         * @property {String} [footerClass='pmui-accordion-panel-footer']
         * Represents the class name for footer of the panel.
         * It can be replaced on the config JSON if it is necessary
         * @private
         */
        this.footerClass = null;
        /**
         * @property {String} [containerClass='pmui-accordion-panel-container']
         * Represents the class name for the father container.
         * It can be replaced on the config JSON if it is necessary
         * @private
         */
        this.containerClass = null;
        /**
         * @property {Number} [heightItem = 'current height * 0.3']
         * Represents the size height for every {@link PMUI.item.AccordionItem item}
         */
        this.heightItem = null;
        /**
         * determines whether the items of the accordion panel is automatically set when adding a new item
         * @type {Boolean}
         */
        this.selfAdjusting = null;
        AccordionPanel.prototype.init.call(this, settings);

    };

    PMUI.inheritFrom('PMUI.core.Panel', AccordionPanel);
    /**
     * Defines the object type of the element
     */
    AccordionPanel.prototype.type = 'Accordion';
    /**
     * Defines the object family of the element
     */
    AccordionPanel.prototype.family = 'Panel';

    AccordionPanel.prototype.init = function (settings) {
        var defaults = {
            multipleSelection: false,
            hiddenTitle: false,
            title: '',
            iconClass: 'pmui-accordion-panel-icon',
            headerClass: 'pmui-accordion-panel-header',
            bodyClass: 'pmui-accordion-panel-body',
            footerClass: 'pmui-accordion-panel-footer',
            containerClass: 'pmui-accordion-panel-container',
            heightItem: this.height * 0.3,
            items: [],
            factory: {
                products: {
                    'accordionitem': PMUI.item.AccordionItem
                },
                defaultProduct: "accordionitem"
            },
            listeners: {
                select: function () {
                }
            },
            selfAdjusting: false
        };

        jQuery.extend(true, defaults, settings);

        this.setFactory(defaults.factory)
            .setMultipleSelection(defaults.multipleSelection)
            .setHiddenTitle(defaults.hiddenTitle)
            .setTitle(defaults.title)
            .setIconClass(defaults.iconClass)
            .setHeaderClass(defaults.headerClass)
            .setBodyClass(defaults.bodyClass)
            .setFooterClass(defaults.footerClass)
            .setContainerClass(defaults.containerClass)
            .setHeightItem(defaults.heightItem)
            .setChildren(defaults.items)
            .setListeners(defaults.listeners)
            .setSelfAdjusting(defaults.selfAdjusting);
    };

    AccordionPanel.prototype.adjustHeightItems = function () {
        var maxHeight = this.height,
            headPanel = jQuery(this.header).outerHeight(),
            footerPanel = jQuery(this.footer).outerHeight(),
            headItems = 0,
            footerItems = 0,
            bodyHeight = 0,
            items,
            i,
            j;

        items = this.getItems();

        for (i = 0; i < items.length; i += 1) {
            headItems += jQuery(items[i].header.html).outerHeight() + 2;
            if (items[i].footer) {
                footerItems += jQuery(items[i].footer.html).outerHeight();
            }
        }

        bodyHeight = maxHeight - (headPanel + footerPanel + headItems + footerItems);
        for (j = 0; j < items.length; j += 1) {
            jQuery(items[j].body.html).height(bodyHeight);
        }
        return this;
    };
    /**
     * Clear all the object's current child items and add new ones
     * @param {Array} items An array where each element can be a {Element} object or a JSON object
     * where is specified the setting for the child item to be added
     * @chainable
     */
    AccordionPanel.prototype.setChildren = function (items) {
        var i;

        if (jQuery.isArray(items)) {
            this.clearItems();
            for (i = 0; i < items.length; i += 1) {
                this.addItem(items[i]);
            }
        }
        return this;
    };
    /**
     * Defines and sets whether the {@link PMUI.item.AccordionItem items} from {@link PMUI.panel.AccordionPanel Accordion}
     * will be selectable
     * @param {Boolean} parameter
     * @return {PMUI.panel.AccordionPanel} this
     */
    AccordionPanel.prototype.setMultipleSelection = function (parameter) {
        if (typeof parameter === 'boolean') {
            this.multipleSelection = parameter;
        }
        return this;
    };
    /**
     * Sets whether the title will be rendered or not inside the header
     * @param {Boolean} enabled
     */
    AccordionPanel.prototype.setHiddenTitle = function (enabled) {
        if (typeof enabled === 'boolean') {
            this.hiddenTitle = enabled;

            if (this.html) {
                if (this.hiddenTitle === true) {
                    this.header.setAttribute("style", "display:none")
                } else {
                    this.header.removeAttribute("style");
                }
                this.adjustHeightItems();
            }
        }
        return this;
    };
    /**
     * Sets the label for the Accordion title
     * @param {String} title
     */
    AccordionPanel.prototype.setTitle = function (title) {
        if (typeof title === "string") {
            this.title = title;

            if (this.html) {
                this.header.children[1].innerHTML = title;
            }
        }

        return this;
    };
    /**
     * Sets the class name for the header
     * @param {String} iconCls
     * @private
     */
    AccordionPanel.prototype.setIconClass = function (iconCls) {
        this.iconClass = iconCls;
        return this;
    };
    /**
     * Sets the class name for the Accordion's header
     * @param {String} class name
     * @private
     */
    AccordionPanel.prototype.setHeaderClass = function (className) {
        if (typeof className === 'string') {
            this.headerClass = className;
        }
        return this;
    };
    /**
     * Sets the class name for the Accordion's body
     * @param {String} class name
     * @private
     */
    AccordionPanel.prototype.setBodyClass = function (className) {
        if (typeof className === 'string') {
            this.bodyClass = className;
        }
        return this;
    };
    /**
     * Sets the class name for the Accordion's footer
     * @param {String} class name
     * @private
     */
    AccordionPanel.prototype.setFooterClass = function (className) {
        if (typeof className === 'string') {
            this.footerClass = className;
        }
        return this;
    };
    /**
     * Sets the class name for the object's container
     * @param {String} class name
     * @private
     */
    AccordionPanel.prototype.setContainerClass = function (className) {
        if (typeof className === 'string') {
            this.containerClass = className;
        }
        return this;
    };
    /**
     * Sets the height for every {@link PMUI.item.AccordionItem item}
     * @param {String} height
     */
    AccordionPanel.prototype.setHeightItem = function (height) {
        this.heightItem = height;
        return this;
    };
    /**
     * Sets the listeners for the object
     * @param {Object} events
     */
    AccordionPanel.prototype.setListeners = function (events) {
        if (typeof events === 'object') {
            this.listeners = events;
        }
        return this;
    };
    /**
     * Creates all the elements with their corresponding attributes
     * for make a container that encapsulate {@link PMUI.item.AccordionItem items}
     * @return  {HTMLElement} HTMLElement
     */
    AccordionPanel.prototype.createHTML = function () {
        var container,
            header,
            body,
            footer,
            title,
            icon,
            itemsLen,
            i;

        if (this.html) {
            return this.html;
        }
        container = AccordionPanel.superclass.prototype.createHTML.call(this);
        header = PMUI.createHTMLElement("div");
        header.setAttribute('class', this.headerClass);
        body = PMUI.createHTMLElement("div");
        body.setAttribute('class', this.bodyClass);
        footer = PMUI.createHTMLElement("div");
        footer.setAttribute('class', this.footerClass);

        title = PMUI.createHTMLElement("span");
        title.innerHTML = this.title;
        icon = PMUI.createHTMLElement("span");
        icon.setAttribute('class', this.iconClass);
        header.appendChild(icon);
        header.appendChild(title);
        if (this.hiddenTitle) {
            header.setAttribute("style", "display:none;");
        }
        itemsLen = this.items.getSize();
        if (typeof itemsLen === 'number') {
            for (i = 0; i < itemsLen; i += 1) {
                body.appendChild(this.getItems()[i].getHTML());
            }
        }

        this.header = header;
        this.body = body;
        this.footer = footer;

        container.appendChild(header);
        container.appendChild(body);
        container.appendChild(footer);
        container.className = this.containerClass;
        this.containmentArea = body;
        this.html = container;
        return this.html;
    };

    /**
     * Defines events related to the component and call to every DefineEvents method from
     * {@link PMUI.item.AccordionItem items}
     * @return {Object} {@link PMUI.panel.AccordionPanel Accordion}
     */
    AccordionPanel.prototype.defineEvents = function () {
        var j,
            children,
            that = this;

        if (that.items.getSize() > 0 && this.eventsDefined !== true) {
            children = that.getItems();
            for (j = 0; j < children.length; j += 1) {
                children[j].defineEvents();
            }
            if (this.selfAdjusting) {
                this.adjustHeightItems();
            }
            this.eventsDefined = true;
        }

        return this;
    };

    AccordionPanel.prototype.setSelfAdjusting = function (value) {
        if (typeof value === "boolean") {
            this.selfAdjusting = value;
        }
        return this;
    };

    PMUI.extendNamespace('PMUI.panel.AccordionPanel', AccordionPanel);
    if (typeof exports !== "undefined") {
        module.exports = AccordionPanel;
    }
}());
(function () {
    /**
     * @class PMUI.panel.ButtonPanel
     * A panel that contains instances of {@link PMUI.ui.Button Button} and instances of {@link PMUI.ui.TextLabel Label}.
     * @extends {PMUI.core.Panel}
     *
     * Usage example:
     *
     *      @example
     *      var a = new PMUI.panel.ButtonPanel({
     *          items: [
     *              {
     *                  pmType: 'button',
     *                  text: 'Click Me'
     *              },
     *              {
     *                  pmType: 'label',
     *                  text: 'and then'
     *              },
     *              {
     *                  pmType: 'button',
     *                  text: 'Click Me'
     *              }
     *          ]
     *      });
     *      document.body.appendChild(a.getHTML());
     *      a.defineEvents();
     *
     * @constructor
     * Creates a new instance of the class.
     * @param {Object} [settings={}] An object literal with the config options for the class.
     *
     * @cfg {Array} [items=[]] An array in which each element can be:
     *
     * - An object literal, in this case it can have the config options for create a {@link PMUI.ui.Button Button} or a
     * {@link PMUI.ui.TextLabel Label}, additionally it must include the respective pmType ('button' for Button and 'label'
     * for Label).
     *
     * -A PMUI object, in this case it must be an instance of {@link PMUI.ui.Button Button} or an instance of {@link
        * PMUI.ui.TextLabel Label}.
     */
    var ButtonPanel = function (settings) {
        ButtonPanel.superclass.call(this, settings);
        this.alignment = null;
        ButtonPanel.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Panel', ButtonPanel);
    /**
     * The object's type.
     * @type {String}
     */
    ButtonPanel.prototype.type = 'ButtonPanel';
    /**
     * Initializes the object.
     * @param  {Object} [settings={}] An object literal with the config options the new object will be initialized
     * with.
     * @chainable
     * @private
     */
    ButtonPanel.prototype.init = function (settings) {
        var defaults = {
            items: [],
            factory: {
                products: {
                    'button': PMUI.ui.Button,
                    'label': PMUI.ui.TextLabel
                },
                defaultProduct: 'button'
            },
            layout: 'box',
            alignment: 'center'
        };

        jQuery.extend(true, defaults, settings);

        this.setFactory(defaults.factory)
            .setLayout(defaults.layout)
            .setItems(defaults.items)
            .setAlignment(defaults.alignment);

        return this;
    };
    /**
     * Sets the alignment for the items in the panel.
     * @param {String} alignment A string which can take the values of 'center', 'left', 'right'.
     */
    ButtonPanel.prototype.setAlignment = function (alignment) {
        if (alignment === 'center' || alignment === 'left' || alignment === 'right') {
            this.alignment = alignment;
            this.style.addProperties({'text-align': alignment});
            return this;
        }
        throw new Error('setAlignment(): The parameter must be "center" or "left" or "right".');
    };

    PMUI.extendNamespace('PMUI.panel.ButtonPanel', ButtonPanel);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = ButtonPanel;
    }
}());
(function () {
    /**
     * @class PMUI.panel.List
     */
    var ListPanel = function (settings) {
        this.dom = {};
        ListPanel.superclass.call(this, settings);
        this.orientation = null;
        this.actions = null;
        this.dataItems = null;
        this.filterable = null;
        this.filterCriteria = null;
        this.filterControl = null;
        this.filterPlaceholder = null;
        this.statusBarMessage = null;
        this.visibleHeader = null;
        this.visibleStatusBar = null;
        this.listHeight = null;
        this.onItemClick = null;
        this.filteredItems = null;
        ListPanel.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Container', ListPanel);

    ListPanel.prototype.type = "ListPanel";

    ListPanel.prototype.init = function (settings) {
        var defaults = {
            orientation: 'vertical',
            actions: [],
            dataItems: null,
            itemTextDataBind: null,
            statusBarMessage: null,
            title: "[untitled list]",
            filterPlaceholder: "Search",
            visibleHeader: true,
            visibleStatusBar: true,
            filterable: true,
            listHeight: "auto",
            onItemClick: null
        };
        this.filteredItems = [];

        jQuery.extend(true, defaults, settings);

        this.filterControl = new PMUI.control.TextControl({
            onKeyUp: this.onFilterControlChangeHandler(),
            style: {
                cssClasses: [
                    "pmui-listpanel-search"
                ]
            }
        });

        this.setItemTextDataBind(defaults.itemTextDataBind)
            .setOrientation(defaults.orientation)
            .setElementTag(defaults.elementTag)
            .setActions(defaults.actions)
            .setStatusBarMessage(defaults.statusBarMessage)
            .setFilterPlaceholder(defaults.filterPlaceholder)
            .setTitle(defaults.title)
            .setListHeight(defaults.listHeight)
            .setOnItemClickHandler(defaults.onItemClick);

        if (defaults.filterable) {
            this.enableFiltering();
        } else {
            this.disableFiltering();
        }

        if (defaults.visibleHeader) {
            this.showHeader();
        } else {
            this.hideHeader();
        }

        if (defaults.visibleStatusBar) {
            this.showStatusBar();
        } else {
            this.hideStatusBar();
        }
    };

    ListPanel.prototype.onItemClickHandler = function (listItem) {
        if (typeof this.onItemClick === 'function') {
            this.onItemClick(this, listItem);
        }
        return this;
    };

    ListPanel.prototype.setOnItemClickHandler = function (handler) {
        if (!(handler === null || typeof handler === 'function')) {
            throw new Error("setOnItemClickHandler(): The parameter must be a function or a handler.");
        }
        this.onItemClick = handler;
        return this;
    };

    ListPanel.prototype.setListHeight = function (height) {
        if (typeof height === 'number') {
            this.listHeight = height;
        } else if (/^\d+(\.\d+)?px$/.test(height)) {
            this.listHeight = parseInt(height, 10);
        } else if (/^\d+(\.\d+)?%$/.test(height)) {
            this.listHeight = height;
        } else if (/^\d+(\.\d+)?em$/.test(height)) {
            this.listHeight = height;
        } else if (height === 'auto' || height === 'inherit') {
            this.listHeight = height;
        } else {
            throw new Error('setHeight: height param is not a number');
        }
        if (this.dom.list) {
            this.dom.list.style.height = height + (typeof height === 'number' ? 'px' : '');
        }
        return this;
    };

    ListPanel.prototype.hideStatusBar = function () {
        this.visibleStatusBar = false;
        if (this.dom.statusBar) {
            this.dom.statusBar.style.display = 'none';
        }
        return this;
    };

    ListPanel.prototype.showStatusBar = function () {
        this.visibleStatusBar = true;
        if (this.dom.statusBar) {
            this.dom.statusBar.style.display = '';
        }
        return this;
    };

    ListPanel.prototype.showHeader = function () {
        this.visibleHeader = true;
        if (this.dom.header) {
            this.dom.header.style.display = '';
        }
        return this;
    };

    ListPanel.prototype.hideHeader = function () {
        this.visibleHeader = false;
        if (this.dom.header) {
            this.dom.header.style.display = 'none';
        }
        return this;
    };

    ListPanel.prototype.enableFiltering = function () {
        this.filterable = true;
        if (this.dom.toolbar) {
            this.dom.toolbar.style.display = '';
        }
        return this;
    };

    ListPanel.prototype.disableFiltering = function () {
        this.filterable = false;
        if (this.dom.toolbar) {
            this.dom.toolbar.style.display = 'none';
        }
        this.clearFilter();
        return this;
    };

    ListPanel.prototype.setTitle = function (title) {
        if (typeof title !== 'string') {
            throw new Error("setTitle(): The parameter must be a string.");
        }
        this.title = title;
        if (this.html) {
            this.dom.header.textContent = title;
        }
        return this;
    };

    ListPanel.prototype.setFilterPlaceholder = function (filterPlaceholder) {
        this.filterControl.setPlaceholder(filterPlaceholder);
        return this;
    };

    ListPanel.prototype.onFilterControlChangeHandler = function () {
        var that = this;
        return function () {
            var nextFilter = this.getHTML().value;
            if (that.filterCriteria !== nextFilter) {
                that.filter(nextFilter);
            }
        };
    };

    ListPanel.prototype.setStatusBarMessage = function (statusBarMessage) {
        if (!(statusBarMessage === null || typeof statusBarMessage === 'function')) {
            throw new Error("setStatusBarMessage(): The parameter must be a function or null.")
        }
        this.statusBarMessage = statusBarMessage;
        return this;
    };

    ListPanel.prototype.setItemTextDataBind = function (itemTextDataBind) {
        var i,
            size;

        if (!(itemTextDataBind === null || typeof itemTextDataBind === 'string')) {
            throw new Error("setItemTextDataBind(): the parameter must be a string or null.");
        }
        this.itemTextDataBind = itemTextDataBind;
        size = this.items.getSize();
        for (i = 0; i < size; i += 1) {
            this.items.get(i).setTextDataBind(itemTextDataBind);
        }
        return this;
    };

    ListPanel.prototype.setActions = function (actions) {
        if (!jQuery.isArray(actions)) {
            throw new Error("setActions(): The parameter must be an array.");
        }
        this.actions = actions;
        return this;
    };

    ListPanel.prototype.addItem = function (item) {
        if (item instanceof PMUI.item.ListItem) {
            item.setActions(this.actions).setTextDataBind(this.itemTextDataBind);
        } else if (!item.actions) {
            item.actions = this.actions;
            item.setTextDataBind = this.itemTextDataBind;
        }

        ListPanel.superclass.prototype.addItem.call(this, item);

        return this;
    };

    ListPanel.prototype.setItems = function (items) {
        if (this.actions !== null) {
            ListPanel.superclass.prototype.setItems.call(this, items);
            this.updateStatusBar();
        }
        return this;
    };

    ListPanel.prototype.updateStatusBar = function () {
        var msg,
            size;

        if (this.dom.statusBar) {
            size = this.filterCriteria ? jQuery(this.containmentArea).find('>li').length : this.items.getSize();
            if (typeof this.statusBarMessage === 'function') {
                msg = this.statusBarMessage(this, size, !!this.filterCriteria, this.filterCriteria);
            } else {
                msg = size + (this.filterCriteria ? " result(s) matching \"" + this.filterCriteria + "\"" : " item(s).");
            }
            this.dom.statusBar.textContent = msg;
        }
        return this;
    };

    ListPanel.prototype.setOrientation = function (orientation) {
        var items,
            displayBlock,
            i;

        if (orientation !== 'horizontal' && orientation !== 'vertical') {
            throw new Error("setOrientation(): The parameter must be \"horizontal\" or \"vertical\"");
        }
        this.orientation = orientation;
        displayBlock = orientation === 'vertical' ? 'block' : 'inline-block';
        items = this.items.asArray();
        for (i = 0; i < items.length; i += 1) {
            items[i].setDisplay(displayBlock);
        }
        return this;
    };

    ListPanel.prototype.setFactory = function () {
        this.factory = new PMUI.util.Factory({
            products: {
                'listitem': PMUI.item.ListItem
            },
            defaultProduct: 'listitem'
        });
        return this;
    };

    ListPanel.prototype.getData = function () {
        var i,
            items = this.items.asArray(),
            data = [];

        for (i = 0; i < items.length; i += 1) {
            data.push(items[i].getData());
        }

        return data;
    };

    ListPanel.prototype.clearFilter = function () {
        var i,
            items = this.items.asArray();

        this.filterCriteria = "";
        if (this.html) {
            for (i = 0; i < items.length; i += 1) {
                jQuery(items[i].html).detach();
            }
            for (i = 0; i < items.length; i += 1) {
                this.containmentArea.appendChild(items[i].getHTML());
            }
        }
        this.updateStatusBar();
        return this;
    };

    ListPanel.prototype.getFilteredItems = function () {
        return this.filteredItems;
    };
    ListPanel.prototype.filter = function (criteria) {
        var i,
            regExp,
            itemsCopy;

        this.filteredItems = [];
        if (typeof criteria === 'string') {
            if (criteria === "") {
                this.clearFilter();
                return this;
            }
            this.filterCriteria = criteria;
        } else if (typeof criteria === 'number') {
            this.filterCriteria = criteria.toString(10);
        } else {
            throw new Error("filter(): The parameter must be a string or number");
        }

        if (!this.containmentArea) {
            return this;
        }

        regExp = new RegExp(this.filterCriteria.replace(/([\\\.\[\]\^\$\(\)\?\*\+\|\{\}])/g, "\\\$1"), "i");
        itemsCopy = this.items.asArray();

        for (i = 0; i < itemsCopy.length; i += 1) {
            jQuery(itemsCopy[i].html).detach();
        }

        for (i = 0; i < itemsCopy.length; i += 1) {
            if (regExp.test(itemsCopy[i].text)) {
                this.containmentArea.appendChild(itemsCopy[i].getHTML());
                this.filteredItems.push(itemsCopy[i]);
            }
        }

        this.updateStatusBar();
        return this;
    };

    ListPanel.prototype.defineEvents = function () {
        this.removeEvents().eventsDefined = true;
        this.filterControl.defineEvents();
        return this;
    };

    ListPanel.prototype.createHTML = function () {
        var list,
            toolbar,
            statusBar,
            header;

        if (this.html) {
            return this.html;
        }
        ListPanel.superclass.prototype.createHTML.call(this);

        list = PMUI.createHTMLElement('ul');
        list.className = 'pmui-listpanel-list';
        toolbar = PMUI.createHTMLElement('div');
        toolbar.className = 'pmui-listpanel-toolbar';
        statusBar = PMUI.createHTMLElement('div');
        statusBar.className = 'pmui-listpanel-statusbar';
        header = PMUI.createHTMLElement('div');
        header.className = 'pmui-listpanel-title';
        this.containmentArea = list;

        this.dom.list = list;
        this.dom.toolbar = toolbar;
        this.dom.statusBar = statusBar;
        this.dom.header = header;

        toolbar.appendChild(this.filterControl.getHTML());
        this.html.appendChild(header);
        this.html.appendChild(toolbar);
        this.html.appendChild(list);
        this.html.appendChild(statusBar);

        this.setTitle(this.title)
            .setListHeight(this.listHeight);

        if (this.filterable) {
            this.enableFiltering();
            this.filter(this.filterCriteria || "");
        } else {
            this.disableFiltering();
        }

        if (this.visibleHeader) {
            this.showHeader();
        } else {
            this.hideHeader();
        }

        if (this.visibleStatusBar) {
            this.showStatusBar();
        } else {
            this.hideStatusBar();
        }

        this.defineEvents();
        return this.html;
    };

    PMUI.extendNamespace('PMUI.panel.ListPanel', ListPanel);
}());
(function () {
    /**
     * @class PMUI.grid.GridPanel
     * Class to display data in a grid format.
     * @extends PMUI.core.Container
     *

     *
     * Single grid example:

     *
     *     @example
     *     var g;

     *
     *     $(function(){
     *         g = new PMUI.grid.GridPanel({
     *             pageSize: 6,
     *             columns:[
     *                 {
     *                     title:'columna1',
     *                     dataType:'string',
     *                     columnData: "name"
     *                 },
     *                 {
     *                     title:'columna2',
     *                     dataType:'number',
     *                     width : 150,
     *                     columnData: "lastName",
     *                     sortable: true
     *                 },
     *                 {
     *                     title:'columna3',
     *                     dataType:'button',
     *                     width : "200px",
     *                     onButtonClick: function(row, grid) {
     *                         console.log(row, grid);
     *                     }
     *                 }
     *             ],
     *             dataItems: [
     *                 {
     *                     name: "Daniel",
     *                     lastName: "Canedo"
     *                 }, {
     *                     name: "John",
     *                     lastName: "McAllister"
     *                 }, {
     *                     name: "Ximena",
     *                     lastName: "Jimenez"
     *                 }, {
     *                     name: "Fernando",
     *                     lastName: "Fernandez"
     *                 }, {
     *                     name: "Rodrigo",
     *                     lastName: "Rodriguez"
     *                 }, {
     *                     name: "Andrea",
     *                     lastName: "Postigo"
     *                 }, {
     *                     name: "Hernando",
     *                     lastName: "Hernandez"
     *                 }, {
     *                     name: "Ramiro",
     *                     lastName: "Ramirez"
     *                 }, {
     *                     name: "Domingo",
     *                     lastName: "Dominguez"
     *                 }, {
     *                     name: "Gonzalo",
     *                     lastName: "Gonzales"
     *                 }

     *             ]
     *         });
     *         document.body.appendChild(g.getHTML());
     *         g.defineEvents();
     *     });
     *
     * Example with two grids with interchangeable rows.
     *
     *      @example
     *      var g, g2;

     *
     *      $(function(){
     *          g2 = new PMUI.grid.GridPanel({
     *              behavior: 'dragdropsort',
     *              pageSize: 0,
     *              columns: [
     *                  {
     *                      title: 'lastName',
     *                      columnData: 'lastName',
     *                      sortable: true
     *                  }
     *              ],
     *              dataItems: [
     *                  {
     *                      name: "Ramiro",
     *                      lastName: "Ramirez"
     *                  }, {
     *                      name: "Pedro",
     *                      lastName: "Pedraza"
     *                  }, {
     *                      name: "Domingo",
     *                      lastName: "Dominguez"
     *                  }, {
     *                      name: "Oliva",
     *                      lastName: "Olivares"
     *                  }, {
     *                      name: "Gonzalo",
     *                      lastName: "Gonzales"
     *                  }, {
     *                      name: "Enrique",
     *                      lastName: "Enriquez"
     *                  }
     *              ],
     *             onDrop: function(container, draggableItem) {
     *                 var subject = draggableItem.getData();
     *                 subject = subject.name + " " + subject.lastName;
     *                 console.log(subject + " was dropped on grid 1");
     *             },
     *             onSort: function(container, item, index) {
     *                 var subject = item.getData();
     *                 subject = subject.name + " " + subject.lastName;
     *                 console.log(subject + "'s index has changed to " + index);

     *             }
     *          });
     *          g = new PMUI.grid.GridPanel({
     *              behavior: 'dragdropsort',
     *              pageSize:6,
     *              columns:[
     *                  {
     *                      title: 'Initials',
     *                      columnData: function(data) {
     *                          return data.name.substr(0, 1) + " " + data.lastName.substr(0, 1);
     *                      }
     *                  },
     *                  {
     *                      title:'Name',
     *                      dataType:'string',
     *                      columnData: "name"
     *                  },
     *                  {
     *                      title:'LastName',
     *                      dataType:'number',
     *                      width : 150,
     *                      columnData: "lastName",
     *                      sortable: true
     *                  },
     *                  {
     *                      title:'columna3',
     *                      dataType:'button',
     *                      width : "200px",
     *                      onButtonClick: function(row, grid) {
     *                          console.log(row, grid);
     *                      }
     *                  }
     *              ],
     *              dataItems: [
     *                  {
     *                      name: "Daniel",
     *                      lastName: "Canedo"
     *                  }, {
     *                      name: "John",
     *                      lastName: "McAllister"
     *                  }, {
     *                      name: "Ximena",
     *                      lastName: "Jimenez"
     *                  }, {
     *                      name: "Fernando",
     *                      lastName: "Fernandez"
     *                  }, {
     *                      name: "Rodrigo",
     *                      lastName: "Rodriguez"
     *                  }, {
     *                      name: "Andrea",
     *                      lastName: "Postigo"
     *                  }, {
     *                      name: "Hernando",
     *                      lastName: "Hernandez"
     *                  }
     *              ],
     *             onDrop: function(container, draggableItem) {
     *                 var subject = draggableItem.getData();
     *                 subject = subject.name + " " + subject.lastName;
     *                 console.log(subject + " was dropped on grid 2");
     *             },
     *             onSort: function(container, item, index) {
     *                 var subject = item.getData();
     *                 subject = subject.name + " " + subject.lastName;
     *                 console.log(subject + "'s index has changed to " + index);

     *             }
     *          });
     *          document.body.appendChild(g.getHTML());
     *          document.body.appendChild(g2.getHTML());
     *          g.defineEvents();
     *          g2.defineEvents();
     *      });
     *
     * @constructor
     * Creates a new instance of GridPanel class.
     * @param {Object} [settings=null] A JSON object with the config options.
     *
     * @cfg {Array} [columns=[]] An Array where each element is an object literal or an instance of
     * {@link PMUI.grid.GridPanelColumn}. In case to be an object literal it must have {@link PMUI.grid.GridPanelColumn
     * the config options for GridPanelColumn creation}.
     * @cfg {Array} [items=[]] An Array where each of its elements can be an instance of PMUI.grid.GridPanelRow or a
     * JSON object with the config options to build a new one.
     * @cfg {Array} [dataItems=[]] An Array where each of its elements can be a JSON object with the data available for
     * the item (row) that will be created.
     * @cfg {Number} [pageSize=0] The size for every page in the table, if it is set to 0 (default) then there will no
     * exists pagination.
     * @cfg {Boolean} [draggable=false] If the grid will apply the row dragging behavior.
     * @cfg {Boolean} [filterable=true] If the grid will have the filtering function enabled or not.
     * @cfg {Number} [initialPage=0] The page number to show initially.
     * @cfg {Function} [customStatusBar=null] The function to generate a custom message in the grid's status bar. This
     * function must return a string with the text to be used as the message to be displayed in thegrid's status bar.
     * When the function is called 5 parameters will be sent:
     *
     * - The current page (Number).
     * - The page size (Number).
     * - The number of items (Number).
     * - If a criteria is being applied to the grid (Boolean).
     * - The filter criteria applied to the grid (String).
     *
     * If you need access more grid's properties you can use 'this' to refer the grid since the function is called in
     * the grid's context.
     * @cfg {Object|PMUI.util.Style|null} [rowStyle=null] The style to be applied to the grid's rows. It can be:
     *
     * - An object literal: in this case the object can have the properties:
     *     - cssClasses: an array of strings, each string is a css class that will be applied to each row's html.
     *     - cssProperties: an object literal in which each property is a valid css property and its value is a valid
     *     css value for the respective css property.
     *
     * - An instance of the {@link PMUI.util.Style Style} class.
     * - null, in this case no custom style will be applied to the html of the grid's rows.
     *
     * @cfg {String} [filterPlaceholder] Sets the placeholder for the filter textbox.
     * @cfg {Boolean} [visibleHeaders=true] Turns on/off the visibility of the column headers.
     * @cfg {String} [sortableItems='> tr'] Specifies which items inside the element should be sortable in case a
     * sort behavior is applied. It must be a jQuery selector.
     * @cfg {Function|null} [onEmpty=null] A callback function to be called everytime the grid's last item is removed.
     * The value for this config option can be a function or the null constant for no callback execution. For more
     * info about the paramerters sent to the function please read the {@link #event-onEmpty onEmpty} event
     * documentation.
     * @cfg {Function|null} [onRowClick=null] The callback function to be executed everytime the
     * {@link #event-onRowClick onRowClick} event is fired. Read about this event to know about the parameters received
     * by the callback function.
     * @cfg {String|Function|null} [emptyMessage=null] The message to show when the grid is showing no items. It
     * can be:
     *
     * - A String, in this case the string will be displayed in both cases, when the grid has no items and when no
     * grid's item meet the filter.
     * - A Function, in this case the function must return a string or an HTML Element to be displayed. The function
     * will receive two parameters: the {@link PMUI.grid.Grid grid}, a boolean If it is true it means the returned
     * value will be used when a filter is applied, otherwise it means that the returned value will be used when
     * there are no items in the grid.
     * - null, in this case a default message will be used for each situation.
     */
    var GridPanel = function (settings) {
        /**
         * A JSON object that contains the object's DOM components.
         * @type {Object}
         * @private
         */
        this.dom = {};
        /**
         * The grid's list of {@link PMUI.grid.GridPanelColumn column objects}.
         * @type {PMUI.ui.ArrayList}
         * @private
         */
        this.columns = new PMUI.util.ArrayList();
        GridPanel.superclass.call(this, jQuery.extend({
            factory: {
                products: {
                    "GridPanelRow": PMUI.grid.GridPanelRow
                },
                defaultProduct: "GridPanelRow"
            }
        }, settings, {
            elementTag: "div"
        }));
        this.pages = null;
        this.displayedPages = null;
        this.prevText = null;
        this.nextText = null;
        this.invertPageOrder = null;
        this.labelMap = null;
        this.hrefTextPrefix = null;
        this.hrefTextSuffix = null;
        this.nextAtFront = null;
        this.edges = null;
        this.useStartEdge = null;
        this.ellipseText = null;
        this.selectOnClick = null;
        this.useEndEdge = null;
        /**
         * The number of rows to show in every page, 0 means no limit.
         * @type {Number}
         * @readonly
         */
        this.pageSize = null;
        /**
         * If the grid has the filtering functiionality enabled or not.
         * @type {Boolean}
         * @readonly
         */
        this.filterable = null;
        /**
         * The current filter criteria.
         * @type {String}
         * @readonly
         */
        this.filterCriteria = null;
        /**
         * The current page being displayed.
         * @type {Boolean}
         * @readonly
         */
        this.currentPage = null;
        /**
         * If the column sorting functionloty is enabled or not.
         * @type {Boolean}
         * @readonly
         */
        this.sortable = null;
        /**
         * The {@link PMUI.control.TextControl TextControl} object that is playing the filter text input control role.
         * @type {PMUI.control.TextControl}
         * @private
         */
        this.filterControl = null;
        /**
         * The {@link PMUI.ui.Button Button} to executes the filtering.
         * @type {[type]}
         */
        this.filterButton = null;
        /**
         * An {@link PMUI.util.ArrayList ArrayList} that contains the rows ordered by the
         * {@link #property-filterCriteria filterCriteria}.
         * @type {PMUI.util.ArrayList}
         * @private
         */
        this.usableItemsList = null;
        /**
         * An {@link PMUI.util.ArrayList ArrayList} that contains only the rows that meet the filter criteria.
         * @type {PMUI.util.ArrayList}
         * @private
         */
        this.filteredItems = null;
        /**
         * @property {Function} statusBarHandler The function to generate a custom message in the grid's status bar.
         */
        this.customStatusBar = null;
        /**
         * The style to be applied to all the rows in the grid.
         * @type {PMUI.util.Style}
         * @private
         */
        this.rowStyle = null;
        /**
         * The placeholder to be used in the text control for filtering. It is set by the
         * {@link #cfg-filterPlaceholder filterPlaceholder} config option and the
         * {@link #method-setFilterPlaceholer setFilterPlaceholer()} method.
         * @type {String}
         */
        this.filterPlaceholder = null;
        /**
         * The data about the sorting that is currently applied to the grid.
         * @type {Object}
         */
        this.sortingData = null;
        /**
         * @event onRowClick
         * Fired everytime a grid's row is clicked.
         * @param {PMUI.grid.GridPanelRow} row The row object the click was performed on.
         * @param {Object} data The row's data
         */
        this.onRowClick = null;
        /**
         * @event onEmpty
         * Fired everytime the Grid removes its last item.
         * @param {PMUI.grid.GridPanel} grid The GridPanel object.
         */
        this.onEmpty = null;
        /**
         * If the column headers are visible or not, it is set by the {@link #cfg-visibleHeaders visibleHeaders} config
         * option and the {@link #method-hideHeaders hideHeaders()} and {@link #method-showHeaders showHeaders()}.
         * @type {Boolean}
         */
        this.visibleHeaders = null;
        /**
         * [selectedRow description]
         * @type {[type]}
         */
        this.selectedRow = null;
        /**
         * [selectable description]
         * @type {[type]}
         */
        this.selectable = null;
        /**
         * If the footer is visible or not.
         * @type {Boolean}
         */
        this.visibleFooter = null;
        /**
         * The message to be displayed in the grid when there are no items to display. Set by the
         * {@link #cfg-emptyMessage emptyMessage config option} and the
         * {@link #method-setEmptyMessage setEmptyMessage()} method.
         * @type {String|Function}
         */
        this.emptyMessage = null;
        this._dynamicLoad = null;
        this.keys = null;
        this.customDataRest;
        /**
         * Height for the HTML element tableContainer, it can be a number or a string with the following format:
         ##px when ## is a number.
         * @type {Number|String}
         * @readonly
         */
        this.tableContainerHeight = "";
        GridPanel.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom("PMUI.core.Container", GridPanel);
    /**
     * The object type.
     * @type {String}
     */
    GridPanel.prototype.type = "GridPanel";
    /**
     * The object's family.
     * @type {String}
     */
    GridPanel.prototype.family = "GridPanel";
    /**
     * Initialize the object.
     * @param  {Object} settings A JSON object with the setting options.
     * @private
     */
    GridPanel.prototype.init = function (settings) {
        var defaults = {
            columns: [],
            items: [],
            dataItems: null,
            pages: 0,
            displayedPages: 3,
            prevText: 'Prev'.translate(),
            nextText: 'Next'.translate(),
            invertPageOrder: false,
            labelMap: [],
            hrefTextPrefix: '#page-',
            hrefTextSuffix: '',
            nextAtFront: false,
            edges: 2,
            useStartEdge: true,
            ellipseText: '&hellip;',
            selectOnClick: true,
            useEndEdge: true,
            pageSize: 0,
            filterable: true,
            inititalPage: 0,
            customStatusBar: null,
            rowStyle: null,
            visibleHeaders: true,
            filterPlaceholder: "",
            onRowClick: null,
            sortableItems: '> tr:not(.pmui-nodrag)',
            onEmpty: null,
            emptyMessage: null,
            selectable: false,
            visibleFooter: true,
            dynamicLoad: false,
            keys: {},
            tableContainerHeight: "auto"
        };

        jQuery.extend(true, defaults, settings);
        this.totalRows = 0;

        this.usableItemsList = new PMUI.util.ArrayList();
        this.sortingData = {
            criteria: null,
            type: null
        };
        this.rowStyle = new PMUI.util.Style();
        this.filterControl = new PMUI.control.TextControl({
            onKeyUp: this.onFilterControlChangeHandler(),
            style: {
                cssClasses: [
                    "pmui-gridpanel-search"
                ]
            }
        });
        this.filterButton = new PMUI.ui.Button({
            text: 'Search',
            style: {
                cssClasses: [
                    "pmui-gridpanel-buttonsearch"
                ]
            }
        });
        this.filteredItems = new PMUI.util.ArrayList();

        this.setPageSize(defaults.pageSize)
            .setCurrentPage(defaults.inititalPage);

        this.setSortableItems(defaults.sortableItems)
            .setSelectable(defaults.selectable)
            .setEmptyMessage(defaults.emptyMessage)
            .setOnEmptyHandler(defaults.onEmpty)
            .setRowStyle(defaults.rowStyle)
            .setColumns(defaults.columns)
            .setCustomStatusBar(defaults.customStatusBar)
            .setFilterPlaceholder(defaults.filterPlaceholder)
            .setOnRowClick(defaults.onRowClick)
            .setVisibleFooter(defaults.visibleFooter)
            .setDisplayedPages(defaults.displayedPages)
            .setPages(defaults.pages)
            .setPrevText(defaults.prevText)
            .setNextText(defaults.nextText)
            .setInvertPageOrder(defaults.invertPageOrder)
            .setLabelMap(defaults.labelMap)
            .setHrefTextPrefix(defaults.hrefTextPrefix)
            .setHrefTextSuffix(defaults.hrefTextSuffix)
            .setNextAtFront(defaults.nextAtFront)
            .setEdgesPagination(defaults.edges)
            .setUseStartEdge(defaults.useStartEdge)
            .setEllipseText(defaults.ellipseText)
            .setSelectOnClick(defaults.selectOnClick)
            .setUseEndEdge(defaults.useEndEdge)
            .setDynamicLoad(defaults.dynamicLoad)
            .setCustomDataRest(defaults.customDataRest)
            .setTableContainerHeight(defaults.tableContainerHeight);

        if (jQuery.isArray(defaults.dataItems)) {
            this.setDataItems(defaults.dataItems);
        } else {
            this.setItems(defaults.items);
        }


        if (defaults.filterable) {
            this.enableFiltering();
        } else {
            this.disableFiltering();
        }
        if (defaults.visibleHeaders) {
            this.showHeaders();
        } else {
            this.hideHeaders();
        }
        this.updateUsableItemsList();
    };
    /**
     * Set the height for the HTML element tableContainer
     * @param {Number|String} height it can be a number or a string.
     * In case of using a String you only can use 'auto' or 'inherit' or ##px or ##% or ##em when ## is a number.
     * @chainable
     */
    GridPanel.prototype.setTableContainerHeight = function(height){
        if (typeof height === 'number') {
            height = height+"px";
        } else if (/^\d+(\.\d+)?px$/.test(height)) {
            height = height;
        } else if (/^\d+(\.\d+)?%$/.test(height)) {
            height = height;
        } else if (/^\d+(\.\d+)?em$/.test(height)) {
            height = height;
        } else if (height === 'auto' || height === 'inherit') {
            height = height;
        } else {
            throw new Error('setHeight: height param is not a number');
        }
        this.tableContainerHeight = height;
        return this;
    };

    GridPanel.prototype.setCustomDataRest = function (handlder) {
        if (typeof handlder === "function") {
            this.customDataRest = handlder;
        }
        return this;
    };

    GridPanel.prototype.setKeys = function (keys) {
        if (!(keys.server && keys.projectID && keys.workspace && keys.accessToken && keys.endPoint)) {
            throw new Error('setKeys(): The parameter, in case of being an object, must define server, projectId, authBearer, URL, pageParam, totalPages;');
        }
        this.keys = keys || {};
        this.url = keys.server;
        this.url = this.url + "/api/1.0/" + keys.workspace + "/";
        this.url = this.url + keys.endPoint;
        return this;
    };
    GridPanel.prototype.setDynamicLoad = function (dynamicLoad) {
        this._dynamicLoad = !!dynamicLoad ? dynamicLoad : false;
        if (this._dynamicLoad.hasOwnProperty("keys")) {
            this.setKeys(this._dynamicLoad["keys"]);
        }
        return this;
    };
    /**
     * @inherirdoc
     */
    GridPanel.prototype.setDataItems = function (dataItems) {
        if (this.usableItemsList) {
            GridPanel.superclass.prototype.setDataItems.call(this, dataItems);
        }
        return this;
    };
    /**
     * @inheritdoc
     */
    GridPanel.prototype.setItems = function (items) {
        if (this.usableItemsList) {
            GridPanel.superclass.prototype.setItems.call(this, items);
        }
        return this;
    };
    /**
     * Sets the message to display when there are no items to display in the grid.
     * @param {String|Function|null} emptyMessage It can be:
     *
     * - A String, in this case the string will be displayed in both cases, when the grid has no items and when no
     * grid's item meet the filter.
     * - A Function, in this case the function must return a string or an HTML Element to be displayed. The function
     * will receive two parameters: the {@link PMUI.grid.Grid grid}, a boolean If it is true it means the returned
     * value will be used when a filter is applied, otherwise it means that the returned value will be used when
     * there are no items in the grid.
     * - null, in this case a default message will be used for each situation.
     * @chainable
     */
    GridPanel.prototype.setEmptyMessage = function (emptyMessage) {
        if (!(emptyMessage === null || typeof emptyMessage === 'string' || typeof emptyMessage === 'function')) {
            throw new Error("setEmptyMessage(): the parameter must be a string, a function or null.");
        }
        this.emptyMessage = emptyMessage;
        return this;
    };
    /**
     * Disables the behaviors and column's buttons.
     * @chainable
     */
    GridPanel.prototype.disable = function () {
        this.disableColumn();
        return GridPanel.superclass.prototype.disable.call(this);
    };
    /**
     * Enables the behaviors and column's buttons.
     * @chainable
     */
    GridPanel.prototype.enable = function () {
        this.enableColumn();
        return GridPanel.superclass.prototype.enable.call(this);
    };
    /**
     * @inheritdoc
     */
    GridPanel.prototype.getBehavioralItems = function () {
        var items = [],
            initialIndex = this.currentPage * this.pageSize,
            finalIndex = initialIndex + this.pageSize,
            i;

        for (i = initialIndex; i < finalIndex && this.getItem(i); i += 1) {
            items.push(this.getItem(i));
        }
        return items;
    };
    /**
     * Enables one or all the columns.
     * @param {String|Number|PMUI.grid.GridPanelColumn} [column] A criteria to select the column that will be
     * enabled. It supports the following data types:
     *
     * - String, in this case the parameter is used as the column's id.
     * - Number, in this case the parameter is used as the column's index.
     * - An instance of {@link PMUI.grid.GridPanelColumn GridPanelColumn}, in this case the supplied object must be
     * a child of the grid.
     * - [no value], since this parameter is optional, you can skip it. In this case all the columns in the grid will
     * be enabled.
     * @chainable
     */
    GridPanel.prototype.enableColumn = function (column) {
        var i,
            targetColumn = this.getColumns(column);

        for (i = 0; i < targetColumn.length; i += 1) {
            if (targetColumn[i].dataType === 'button') {
                targetColumn[i].enable();
            }
        }
        this.disabled = false;
        this.style.removeClasses(['pmui-disabled']);
        return this;
    };
    /**
     * Disables one or all the columns.
     * @param {String|Number|PMUI.grid.GridPanelColumn} [column] A criteria to select the column that will be
     * disabled. It supports the following data types:
     *
     * - String, in this case the parameter is used as the column's id.
     * - Number, in this case the parameter is used as the column's index.
     * - An instance of {@link PMUI.grid.GridPanelColumn GridPanelColumn}, in this case the supplied object must be
     * a child of the grid.
     * - [no value], since this parameter is optional, you can skip it. In this case all the columns in the grid will
     * be disabled.
     * @chainable
     */
    GridPanel.prototype.disableColumn = function (column) {
        var i,
            targetColumn = this.getColumns(column);

        for (i = 0; i < targetColumn.length; i += 1) {
            if (targetColumn[i].dataType === 'button') {
                targetColumn[i].disable();
            }
        }
        return this;
    };
    /**
     * Sets the width to one or all the columns.
     * @param {Number|String} width  The width to be applied to the column(s).
     * @param {String|Number|PMUI.grid.GridPanelColumn} [column] A criteria to select the column the width will be
     * applied. It supports the following data types:
     *
     * - String, in this case the parameter is used as the column's id.
     * - Number, in this case the parameter is used as the column's index.
     * - An instance of {@link PMUI.grid.GridPanelColumn GridPanelColumn}, in this case the supplied object must be
     * a child of the grid.
     * - [no value], since this parameter is optional, you can skip it. In this case the width will be applied to all
     * the columns in the grid.
     * @chainable
     */
    GridPanel.prototype.setColumnWidth = function (width, column) {
        var targetColumn = [],
            aux,
            i;

        if (typeof column === 'string') {
            aux = this.columns.find("id", column);
        } else if (typeof column === 'number') {
            aux = this.columns.get(column);
        } else if (column instanceof PMUI.grid.GridPanelColumn && this.isDirectParentOf(column)) {
            targetColumn.push(column);
        } else if (column === undefined) {
            targetColumn = this.column.asArray();
        }
        if (aux) {
            targetColumn.push(aux);
        }
        for (i = 0; i < targetColumn.length; i += 1) {
            targetColumn[i].setWidth(width);
        }
        if (i > 0) {
            this.goToPage(this.currentPage);
        }
        return this;
    };
    /**
     * Sets the function callback to be called everytime the grid's last item is removed.
     * @param {Function|null} handler The callback function or the null constant for no callback execution. For more
     * info about the paramerters sent to the function please read the {@link #event-onEmpty onEmpty} event
     * documentation.
     * @chainable
     */
    GridPanel.prototype.setOnEmptyHandler = function (handler) {
        if (!(typeof handler === 'function' || handler === null)) {
            throw new Error('setOnEmptyHandler(): The parameter must be a function or null.');
        }
        this.onEmpty = handler;
        return this;
    };
    /**
     * Shows the column headers.
     * @chainable
     */
    GridPanel.prototype.showHeaders = function () {
        this.visibleHeaders = true;
        if (this.html) {
            this.dom.thead.style.display = '';
        }
        return this;
    };
    /**
     * Hide the column headers.
     * @chainable
     */
    GridPanel.prototype.hideHeaders = function () {
        this.visibleHeaders = false;
        if (this.html) {
            this.dom.thead.style.display = 'none';
        }
        return this;
    };
    /**
     * Updates the usable items list.
     * @chainable
     * @private
     */
    GridPanel.prototype.updateUsableItemsList = function () {
        if (this.usableItemsList) {
            this.usableItemsList.set(this.items.asArray());
        }
        return this;
    };
    /**
     * Sets the placeholder for the filter textbox.
     * @param {String} filterPlaceholder
     * @chainable
     */
    GridPanel.prototype.setFilterPlaceholder = function (filterPlaceholder) {
        if (typeof filterPlaceholder !== 'string') {
            throw new Error('setFilterPlaceholder(): The parameter must be a string.');
        }
        this.filterControl.setPlaceholder(this.filterPlaceholder = filterPlaceholder);
        return this;
    };
    /**
     * Sets the custom style to be applied to the grid's rows.
     * @param {Object|PMUI.util.Style|null} rowStyle It can be an object literal (with the properties
     * cssProperties and/or cssClasses), an instance of PMUI.util.Style or null (in this case no custom style will be
     * applied to the rows).
     */
    GridPanel.prototype.setRowStyle = function (rowStyle) {
        var i,
            rows,
            rowsLength;

        if (rowStyle !== null && typeof rowStyle !== 'object' && !(rowStyle instanceof PMUI.util.Style)) {
            throw new Error("setRowStyle(): The parameter must be null or an object literal or an instance of "
                + "PMUI.grid.GridPanelRow.");
        }
        this.rowStyle.clear();
        if (rowStyle instanceof PMUI.util.Style) {
            this.rowStyle = rowStyle;
        } else if (rowStyle) {
            this.rowStyle.addProperties(rowStyle.cssProperties || {})
                .addClasses(rowStyle.cssClasses || []);
        }
        rows = this.items.asArray();
        rowsLength = rows.length;
        for (i = 0; i < rowsLength; i += 1) {
            rows[i].setStyle({
                cssProperties: this.rowStyle.cssProperties,
                cssClasses: this.rowStyle.cssClasses
            });
        }
        return this;
    };
    /**
     * Sets a function to generate a custom message in the grid's status bar.
     * @param {Function|NULL} handler The function for generate the message to be shown in the grid's status bar. It
     * also can be NULL, in this case the message shown in the grid's status bar wiil be the default one.
     * For more info about the sent parameters to the function read the {@link #cfg-customStatusBar customStatusBar}
     * config option.
     */
    GridPanel.prototype.setCustomStatusBar = function (handler) {
        if (!(typeof handler === 'function' || handler === null)) {
            throw new Error('setCustomStatusBar(): this method only accepts as parameter a function or null.');
        }
        this.customStatusBar = handler;
        return this;
    };
    /**
     * Enables the filter functionality.
     * @chainable
     */
    GridPanel.prototype.enableFiltering = function () {
        this.filterable = true;
        if (this.dom.toolbar) {
            this.dom.toolbar.style.display = '';
            if (!this._dynamicLoad) {
                this.goToPage(this.currentPage);
            }

        }
        return this;
    };
    /**
     * Disables the filter functionality.
     * @chainable
     */
    GridPanel.prototype.disableFiltering = function () {
        this.filterable = false;
        if (this.dom.toolbar) {
            this.dom.toolbar.style.display = 'none';
            this.filterCriteria = "";
            this.filterControl.setValue("");
            if (!this._dynamicLoad) {
                this.goToPage(this.currentPage);
            }
        }
        return this;
    };
    /**
     * Returns the index in page for the specified row.
     * @param  {PMUI.grid.GridPanelRow|Number|String} row It can be:
     *
     * - {@link PMUI.grid.GridPanelRow}.
     * - Number, in this case it is interpreted as the global index for the row.
     * - String, it will be interpreted as the row's id.
     * @return {Number}
     */
    GridPanel.prototype.indexInPage = function (row) {
        var globalIndex = this.getItemIndex(row);

        if (this.pageSize !== 0 && globalIndex >= 0) {
            globalIndex = globalIndex % this.pageSize;
        }

        return globalIndex;
    };
    /**
     * Returns the handler to be executed when the filter changes.
     * @return {Function}
     * @private
     */
    GridPanel.prototype.onFilterControlChangeHandler = function () {
        var that = this;

        return function () {
            var nextFilter = this.getHTML().value;

            if (that.filterCriteria !== nextFilter) {
                that.filter(nextFilter);
            }
        };
    };
    /**
     * Sets the columns.
     * @param {Array} columns An Array where each element is an object literal with the
     * {@link PMUI.grid.GridPanelColumn the config options for GridPanelColumn creation} or a
     * {@link PMUI.grid.GridPanelColumn GridPanelColumn object}.
     * @chainable
     */
    GridPanel.prototype.setColumns = function (columns) {
        var i;

        this.clearColumns();
        if (!jQuery.isArray(columns)) {
            throw new Error("setColumns(): The parameter must be an Array");
        }

        for (i = 0; i < columns.length; i += 1) {
            this.addColumn(columns[i]);
        }
        return this;
    };
    /**
     * Returns one or all the grid's columns.
     * @param {String|Number|PMUI.grid.GridPanelColumn} [column] A criteria to select the column that will be
     * returned. It supports the following data types:
     *
     * - String, in this case the parameter is used as the column's id.
     * - Number, in this case the parameter is used as the column's index.
     * - An instance of {@link PMUI.grid.GridPanelColumn GridPanelColumn}, in this case the supplied object must be
     * a child of the grid.
     * - [no value], since this parameter is optional, you can skip it. In this case all the columns in the grid will
     * be returned.
     * @return {Array}
     */
    GridPanel.prototype.getColumns = function (column) {
        var targetColumn = [],
            aux;

        if (typeof column === 'string') {
            aux = this.columns.find("id", column);
        } else if (typeof column === 'number') {
            aux = this.columns.get(column);
        } else if (column instanceof PMUI.grid.GridPanelColumn && this.isDirectParentOf(column)) {
            targetColumn.push(column);
        } else if (column === undefined) {
            targetColumn = this.columns.asArray().slice(0);
        }
        if (aux) {
            targetColumn.push(aux);
        }
        return targetColumn;
    };
    /**
     * Paints a column into the grid's head.
     * @param  {PMUI.grid.GridPanelColumn} column
     * @chainable
     * @private
     */
    GridPanel.prototype.paintColumn = function (column) {
        if (this.dom.thead) {
            this.dom.thead.appendChild(column.getHTML());
        }
        return this;
    };
    GridPanel.prototype.paintColumns = function () {
        var i,
            columns = this.columns.asArray();

        for (i = 0; i < columns.length; i += 1) {
            this.paintColumn(columns[i]);
        }
        return this;
    };
    /**
     * Adds a column.
     * @param {PMUI.grid.GridPanelColumn|JSON} column A {@link PMUI.grid.GridPanelColumn} or a JSON Object with the
     * {@link PMUI.grid.GridPanelColumn#cfg config options} for the new column.
     */
    GridPanel.prototype.addColumn = function (column) {
        var that = this,
            columnToAdd,
            rows,
            i,
            defaults = {
                grid: this,
                title: '[untitled]',
                type: 'string',
                sortable: false,
                searchable: true,
                onSort: function (order) {
                    var criteria = this.columnData;
                    that.sort(criteria, order);
                }
            };

        if (column instanceof PMUI.grid.GridPanelColumn) {
            columnToAdd = column;
            columnToAdd.clearCells();
            columnToAdd.setGrid(this);
        } else if (typeof column === 'object') {
            jQuery.extend(true, defaults, column);
            columnToAdd = new PMUI.grid.GridPanelColumn(defaults);
        } else {
            throw new Error('addColumn(): The method only accepts an object or an instace of PMUI.grid.GridPanelColumn'
                + ' as parameter.');
        }

        //TODO control align and width
        //console.log("\tCOLUMN insert " + columnToAdd.title/*isd*/);
        this.columns.insert(columnToAdd);
        this.paintColumn(columnToAdd);
        rows = this.items.asArray();
        for (i = 0; i < rows.length; i += 1) {
            rows[i].addCell(columnToAdd);
        }

        return this;
    };
    /**
     * Clears all the columns for the grid.
     * @chainable
     */
    GridPanel.prototype.clearColumns = function () {
        while (this.columns.getSize()) {
            this.removeColumn(0);
        }
        return this;
    };
    /**
     * Removes a column and all its cells from the grid.
     * @param  {Number|String|PMUI.grid.GridPanelColumn} column This value is used to determine the row to remove:
     *
     * - String, in this case the string must be the id of a column in the grid.
     * - Number, in this case the number is the index of the column to remove.
     * - a {@link PMUI.grid.GridPanelColumn GridPanelColumn object}, if it's a column that belongs to the grid then
     * this column will be the one to remove.
     * @chainable
     */
    GridPanel.prototype.removeColumn = function (column) {
        var columnToRemove,
            i,
            row,
            size,
            index;

        if (column instanceof PMUI.grid.GridPanelColumn) {
            columnToRemove = column;
        } else if (typeof column === 'number') {
            columnToRemove = this.columns.get(column);
        } else if (typeof column === 'string') {
            columnToRemove = this.columns.find('id', column);
        }

        if (columnToRemove) {
            index = this.columns.indexOf(columnToRemove);
            size = this.items.getSize();
            for (i = 0; i < size; i += 1) {
                row = this.items.get(i);
                row.removeCell(index);
            }
            columnToRemove.grid = null;
            columnToRemove.clearCells();
            this.columns.remove(columnToRemove);
            jQuery(columnToRemove.getHTML()).remove();
        }
        return this;
    };
    /**
     * Adds a new Item
     * @param {Object|PMUI.grid.GridPanelRow} item An instance of {@link PMUi.grid.GridPanelRow GridPanelRow} or an
     * object with the properties to be used to create a new one.
     * @param {Number} [index] The insertion index position.
     */
    GridPanel.prototype.addItem = function (item, index) {
        var itemToBeAdded;

        if (typeof item !== 'object') {
            throw new Error('addItem(): The first parameter must be an object or an instance of PMUI.grid.GridPanel.');
        }
        item.onSelect = this.onRowSelectHandler();
        if (this.factory) {
            itemToBeAdded = this.factory.make(item);
        }
        if (itemToBeAdded && !this.isDirectParentOf(itemToBeAdded)) {
            itemToBeAdded.parent = this;
            itemToBeAdded.style.addClasses(['pmui-' + (this.items.getSize() % 2 ? 'odd' : 'even')]);
            if (itemToBeAdded.html || this.columns.asArray()) {
                itemToBeAdded.setCells();
            }

            if (typeof index === 'number') {
                this.items.insertAt(itemToBeAdded, index);
                if (this.usableItemsList) {
                    this.usableItemsList.insertAt(itemToBeAdded, index);
                }
                if (this.filterCriteria) {
                    this.filter(this.filterCriteria);
                } else {
                    this.goToPage(this.currentPage);
                }
            } else {
                this.items.insert(itemToBeAdded);
                this.usableItemsList.insert(itemToBeAdded);
                if (this.dom.tbody && !this.massiveAction) {
                    this.goToPage(this.currentPage);

                }
            }
            if (this.eventsDefined) {
                itemToBeAdded.defineEvents();
            }
        }
        return this;
    };
    /**
     * [onRowSelectHandler description]
     * @return {[type]} [description]
     */
    GridPanel.prototype.onRowSelectHandler = function () {
        var that = this;
        return function () {
            if (that.selectedRow) {
                that.selectedRow.deselectRow();
            }
            that.selectedRow = this;
        };
    };
    /**
     * Sets the callback function to be executed everytime the {@link #event-onRowClick onRowClick} event is fired.
     * Read about this event to know about the parameters received by the callback function.
     * @param {Function|null} handler
     * @chainable
     */
    GridPanel.prototype.setOnRowClick = function (handler) {
        if (typeof handler !== 'function' && handler !== null) {
            throw new Error("sdfsdfgsd");
        }
        this.onRowClick = handler;
        return this;
    };
    /**
     * Shows an empty row in grid.
     * @param sizeItems
     * @chainable
     * @private
     */
    GridPanel.prototype.showEmptyCell = function (sizeItems) {
        var tr,
            td,
            message;

        if (this.dom.tbody) {
            tr = PMUI.createHTMLElement('tr');
            td = PMUI.createHTMLElement('td');
            tr.className = 'pmui-gridpanel-emptyrow pmui-nodrag';
            td.colSpan = this.columns.getSize();
            tr.appendChild(td);
            $(this.dom.tbody).find('.pmui-gridpanel-emptyrow').remove();
            if (!sizeItems) {
                // the empty row will be added only if there is not items in the container 
                $(this.dom.tbody).append(tr);
                if (typeof this.emptyMessage === 'function') {
                    message = this.emptyMessage(this, !!this.filterCriteria);
                } else if (typeof this.emptyMessage === 'string') {
                    message = this.emptyMessage;
                } else {
                    if (this.filterCriteria) {
                        message = 'No matches found for \"' + this.filterCriteria + '\"';
                    } else {
                        message = 'No records found.';
                    }
                }
                if (typeof message === 'string') {
                    td.appendChild(document.createTextNode(message));
                } else if (PMUI.isHTMLElement(message)) {
                    td.appendChild(message);
                }
                if (this.items.getSize() === 0 && typeof this.onEmpty === 'function') {
                    this.onEmpty(this);
                }
            }
        }
        return this;
    };
    /**
     * Removes a row from the object.
     * @param  {PMUI.core.Element|String|Number} item It can be a string (id of the child to remove),
     a number (index of the child to remove) or a {Element} object
     * @chainable
     */
    GridPanel.prototype.removeItem = function (item) {
        var currentPage = this.currentPage;
        if (item.selected) {
            item.deselectRow();
        }

        GridPanel.superclass.prototype.removeItem.call(this, item);

        if (!this.massiveAction) {
            this.updateUsableItemsList();
            this.filteredItems.remove(item);
            this.showEmptyCell(this.items.getSize());
            if (currentPage >= this.getTotalPages()) {
                currentPage -= 1;
                this.currentPage = currentPage < 0 ? 0 : currentPage;
            }
            if (this.dom.tbody) {
                this.goToPage(this.currentPage, true);
            }
        }
        return this;
    };
    /**
     * @inheritdoc
     */
    GridPanel.prototype.clearItems = function () {
        var currentPage = this.currentPage;
        GridPanel.superclass.prototype.clearItems.call(this);
        this.updateUsableItemsList();
        this.showEmptyCell(this.items.getSize());
        if (currentPage >= this.getTotalPages()) {
            currentPage -= 1;
            this.currentPage = currentPage < 0 ? 0 : currentPage;
        }
        if (this.dom.tbody) {
            this.goToPage(this.currentPage);
        }
        return this;
    };
    /**
     * Remove all columns´s cells
     * @returns {PMUI.grid.GridPanel}
     */
    GridPanel.prototype.clearItemsColumns = function () {
        var i,
            max = this.getColumns().length;
        for (i = 0; i < max; i += 1) {
            this.getColumns()[i].clearCells();
        }
        return this;
    };
    /**
     * Moves a row from its current position to another.
     * @param  {PMUI.grid.GridPanelRow|Number|String} item  It can be:

     *
     * - An instance of PMUI.grid.GridPanelRow, in this case this object must be a item of the object.
     * - A Number, in this case the number is interpereted as the index of the item to move.
     * - A String, in this case the string is interpreted as the id of the item to move.
     * @param  {Number} index The index in which the item will be moved to.
     * @chainable
     */
    GridPanel.prototype.moveItem = function (item, index) {
        var items = this.items,
            currentIndex,
            referenceObject;

        item = this.getItem(item);

        if (item instanceof PMUI.core.Element && this.isDirectParentOf(item)) {
            currentIndex = items.indexOf(item);
            items = items.asArray();
            referenceObject = this.items.get(index + (currentIndex < index ? 1 : 0));
            item = items.splice(currentIndex, 1)[0];
            if (this.items.indexOf(item) > -1) {
                this.items.remove(item);
            }
            this.items.insertAt(item, index);
            this.updateUsableItemsList();
            if (this.html) {
                if (!referenceObject) {
                    this.goToPage(this.currentPage);
                } else if (index === (this.currentPage * this.pageSize + this.pageSize - 1)
                    || (this.getTotalPages() === (this.currentPage + 1)
                    && index === (this.items.getSize() % this.pageSize) - 1)) {
                    this.containmentArea.appendChild(item.getHTML());
                } else {
                    this.containmentArea.insertBefore(item.getHTML(), referenceObject.getHTML());
                }
            }
        }
        return this;
    };
    /**
     * Returns the data for every rowe in the grid.
     * @return {Array}
     */
    GridPanel.prototype.getData = function () {
        var i,
            items = this.items.asArray(),
            data = [];

        for (i = 0; i < items.length; i += 1) {
            data.push(items[i].getData());
        }

        return data;
    };
    /**
     * @inheritdoc
     */
    GridPanel.prototype.paintItem = function (item, index) {
        var intitalItem = this.currentPage * this.pageSize,
            finalIndex = this.pageSize ? this.currentPage + this.pageSize - 1 : null;

        if (index === undefined) {
            index = this.items.getSize();
        }
        if (index >= initialIndex || (index <= finalIndex || finalIndex === null)) {
            this.goToPage(this.currentPage);
        }
        return this;
    };
    /**
     * @inheritdoc
     */
    GridPanel.prototype.paintItems = function () {
        return this.filterCriteria ? this.filter(this.filterCriteria) : this.goToPage(this.currentPage);
    };
    /**
     * Creates the HTML for the Grid.
     * @return {HTMLElement}
     */
    GridPanel.prototype.createHTML = function () {
        var table,
            thead,
            tbody,
            toolbarDiv,
            footer,
            pager,
            statusBar,
            tableContainer,
            pager2;

        if (this.html) {
            return this.html;
        }

        GridPanel.superclass.superclass.prototype.createHTML.call(this);
        table = PMUI.createHTMLElement("table");
        table.className = 'pmui-gridpanel-table';
        table.cellSpacing = 0;
        table.cellPadding = 0;
        thead = PMUI.createHTMLElement("thead");
        tbody = PMUI.createHTMLElement("tbody");
        tableContainer = PMUI.createHTMLElement("div");
        tableContainer.className = "pmui-gridpanel-tableContainer";
        tbody.className = 'pmui-gridpanel-tbody';
        this.containmentArea = tbody;
        tableContainer.style.overflow = 'auto';
        toolbarDiv = PMUI.createHTMLElement("div");
        toolbarDiv.className = 'pmui-gridpanel-toolbar';
        toolbarDiv.appendChild(this.filterControl.getHTML());
        if (this._dynamicLoad) {
            var searchLoad = PMUI.createHTMLElement("span");
            searchLoad.className = "pmui-gridpanel-searchload";
            toolbarDiv.appendChild(searchLoad);
        }
        this.html.appendChild(toolbarDiv);

        this.dom.tableContainer = tableContainer;
        this.dom.thead = thead;
        this.dom.tbody = tbody;
        this.dom.table = table;
        this.dom.toolbar = toolbarDiv;


        table.appendChild(thead);
        table.appendChild(tbody);
        tableContainer.appendChild(table);
        this.html.appendChild(tableContainer);
        pager = PMUI.createHTMLElement('ul');
        pager.className = 'pmui-gridpanel-pager';

        statusBar = PMUI.createHTMLElement('div');
        statusBar.className = 'pmui-gridpanel-statusbar';

        footer = PMUI.createHTMLElement("div");
        footer.className = 'pmui-gridpanel-footer';
        pager2 = PMUI.createHTMLElement("ul");
        pager2.className = 'pmui-gridpanel-pager';
        if (this._dynamicLoad) {
            footer.appendChild(pager);
        } else {
            footer.appendChild(pager2);
        }

        this.html.appendChild(footer);

        this.dom.pager2 = pager2;

        this.dom.pager = pager;
        this.dom.statusBar = statusBar;
        this.dom.footer = footer;

        this.setVisibleFooter(this.visibleFooter);
        this.paintColumns()
            .setHeight(this.height);

        tableContainer.style.height = this.tableContainerHeight;

        this.style.applyStyle();

        if (this.filterable) {
            this.enableFiltering();
        } else {
            this.disableFiltering();
        }
        if (this.visibleHeaders) {
            this.showHeaders();
        } else {
            this.hideHeaders();
        }
        this.showEmptyCell(this.items.getSize());
        if (this.eventsDefined) {
            this.defineEvents();
        }
        if (this.sortingData.sortingCriteria) {
            this.sort(this.sortingData.criteria, this.sortingData.type);
        }

        return this.html;
    };
    /**
     * [updateStatusBar description]
     * @return {[type]} [description]
     */
    GridPanel.prototype.updateStatusBar = function () {
        var itemsLength,
            filterCriteria,
            b,
            currentPage,
            filtered,
            totalPages;

        filterCriteria = this.filterCriteria;
        currentPage = this.currentPage;
        filtered = !!(filterCriteria && this.filterable);
        itemsLength = filtered ? this.filteredItems.getSize() : this.items.getSize();
        jQuery(this.dom.statusBar).empty();
        totalPages = this.getTotalPages(filtered);
        if (typeof this.customStatusBar === 'function') {
            this.dom.statusBar.textContent =
                this.customStatusBar(currentPage, this.pageSize, itemsLength, filtered, filterCriteria || "");
        } else if (totalPages === 0) {
            b = PMUI.createHTMLElement('b');
            b.appendChild(document.createTextNode(""));
            this.dom.statusBar.appendChild(b);
        } else {
            b = PMUI.createHTMLElement('b');
            b.appendChild(document.createTextNode("PAGE " + (currentPage + 1)));
            this.dom.statusBar.appendChild(b);
            this.dom.statusBar.appendChild(document.createTextNode(" of " + totalPages
                + (filtered ? " filtered pages" : "")));
        }
        return this;
    };

    /**
     * [param] this-GridPanel
     * @return Object
     */
    GridPanel.prototype._getInterval = function (that) {
        return {
            start: Math.ceil(that.currentPage > that.halfDisplayed ? Math.max(Math.min(that.currentPage - that.halfDisplayed, (that.pages - that.displayedPages)), 0) : 0),
            end: Math.ceil(that.currentPage > that.halfDisplayed ? Math.min(that.currentPage + that.halfDisplayed, that.pages) : Math.min(that.displayedPages, that.pages))
        };
    };

    /**
     * [number] pageIndex
     * [object] opts
     */
    GridPanel.prototype._appendItem = function (pageIndex, opts) {
        var self = this.dom.footer,
            options,
            $link,
            $linkWrapper = $('<li></li>'),
            $ul = $(this.dom.footer).find('ul');

        pageIndex = pageIndex < 0 ? 0 : (pageIndex < this.pages ? pageIndex : this.pages - 1);

        options = {
            text: pageIndex + 1,
            classes: ''
        };

        if (this.labelMap.length && this.labelMap[pageIndex]) {
            options.text = this.labelMap[pageIndex];
        }

        options = $.extend(options, opts || {});

        if (pageIndex == this.currentPage || this.disabled) {
            if (this.disabled) {
                $linkWrapper.addClass('disabled');
            } else {
                $linkWrapper.addClass('active');
            }
            $link = (options.text == 'Prev') ? $('<span class="pmui-gridpanel-pager-current"><span style="font-size: 18px; line-height: normal;"> &lsaquo; </span>' + (options.text) + '</span>') : (options.text == 'Next') ? $('<span class="pmui-gridpanel-pager-current">' + (options.text) + '<span style="font-size: 18px; line-height: normal;"> &rsaquo; </span></span>') : $('<span class="pmui-gridpanel-pager-current">' + (options.text) + '</span>');

        } else {
            $link = (options.text == 'Prev') ? $('<a href="' + this.hrefTextPrefix + (pageIndex + 1) + this.hrefTextSuffix + '" class="page-link"><span style="font-size: 18px; line-height: normal;"> &lsaquo; </span>' + (options.text) + '</a>') : (options.text == 'Next') ? $('<a href="' + this.hrefTextPrefix + (pageIndex + 1) + this.hrefTextSuffix + '" class="page-link">' + (options.text) + '<span style="font-size: 18px; line-height: normal;"> &rsaquo; </span></a>') : $('<a href="' + this.hrefTextPrefix + (pageIndex + 1) + this.hrefTextSuffix + '" class="page-link">' + (options.text) + '</a>');
            var that = this;
            $link.click(function (event) {
                return that._selectPage.call(that, pageIndex, event);
            });
        }

        if (options.classes) {
            $link.addClass(options.classes);
        }

        $linkWrapper.append($link);

        if ($ul.length) {
            $(this.dom.pager2).append($linkWrapper);
        } else {
            $(this.dom.pager2).append($linkWrapper); //... review
        }
    };

    /**
     * Select number in a page.
     * @param  {Number} pageIndex The index of the page to go.
     * @param  {event} click event.
     * @chainable
     */
    GridPanel.prototype._selectPage = function (pageIndex, event) {
        this.currentPage = pageIndex;
        if (this.selectOnClick) {
            this.goToPage(pageIndex, true);
        }
    }

    /**
     * Change the grid page.
     * @param  {Number} index The index of the page to go.
     * @param  {Boolean} [force] If its true you go to the specified page even if there's no data in it.
     * @chainable
     */
    GridPanel.prototype.goToPage = function (index, force) {
        var modal;

        if (this._dynamicLoad) {
            var i, numDisplayedItems = 0, that = this;

            if (!((index < 0) && !force)) {
                this.currentPage = index;

                if (!this.html) {
                    return this;
                }
            }
            if (!this.html) {
                return this;
            }

            this.showEmptyCell(numDisplayedItems);
            if (this.html) {
                $(this.dom.toolbar).find(".pmui-gridpanel-searchload").addClass("load");
                modal = document.createElement("div");
                modal.id = "dynamic-modal-load";
                $(modal).css({
                    height: $(this.dom.tableContainer).outerHeight(),
                    width: $(this.dom.tableContainer).outerWidth(),
                    background: "rgba(204, 197, 197, 0.4)",
                    position: "absolute",
                    zIndex: "200"
                });
                $(this.dom.tableContainer).prepend(modal);
            }
            $.ajax({
                url: this.url + "?filter=" + (this.filterCriteria ? this.filterCriteria : "") + "&start=" + (this.pageSize * index) + "&limit=" + this.pageSize + "&type=" + (this.typeList ? this.typeList : ""),
                type: "GET",
                async: true,
                contentType: "application/json",
                beforeSend: function (xhr) {
                    xhr.setRequestHeader("Authorization", "Bearer " + that.keys.accessToken);
                },
                success: function (data) {
                    var i,
                        newItem,
                        numDisplayedItems = 0,
                        dataReceived;
                    if (typeof that.customDataRest === "function") {
                        dataReceived = that.customDataRest(data["data"]);
                    } else {
                        dataReceived = data["data"];
                    }
                    that._dynamicLoad.totalPages = Math.ceil(data["total"] / that.pageSize);
                    $(that.dom.tbody).empty();
                    if (dataReceived.length) {
                        for (i = 0; i < dataReceived.length; i += 1) {
                            newItem = that.factory.make({
                                data: dataReceived[i]
                            });
                            newItem.parent = that;
                            newItem.style.addClasses(['pmui-' + (i % 2 ? 'odd' : 'even')]);
                            if (newItem.html || that.columns.asArray()) {
                                newItem.setCells();
                            }
                            that.dom.tbody.appendChild(newItem.updateCellsWidth().getHTML());
                            $(newItem.html).find(".pmui-gridpanelcell-content").each(function () {
                                if (this.children.length === 0 && this.textContent.length != 0) {
                                    this.title = this.textContent;
                                }
                            });
                            numDisplayedItems += 1;
                        }
                    }
                    that.showEmptyCell(numDisplayedItems);
                    that.setBehavior(that.behavior);
                    if (that.html) {
                        $(that.dom.toolbar).find(".pmui-gridpanel-searchload").removeClass("load");
                        $(that.dom.tableContainer).find("#dynamic-modal-load").remove();
                    }
                    return that.updatePager().updateStatusBar();
                    that.dom.footer.appendChild(this.dom.pager);
                },
                error: function (xhr, textStatus, errorThrown) {

                }
            });
            this.setBehavior(this.behavior);
        } else {
            this.halfDisplayed = this.displayedPages / 2;
            this.currentPage = index;
            if (force && this.filterCriteria && this.filterable) {
                this.pages = Math.ceil(this.filteredItems.getSize() / this.pageSize) ? Math.ceil(this.filteredItems.getSize() / this.pageSize) : 1;
            } else {
                this.pages = Math.ceil(this.getItems().length / this.pageSize) ? Math.ceil(this.getItems().length / this.pageSize) : 1;
            }
            var interval = this._getInterval(this);
            //Updates the pager
            if (this.dom.pager2 && this.getItems().length != 0) {
                jQuery(this.dom.pager2).empty();

                //Generate Prev link
                if (this.prevText) {
                    this._appendItem.call(this, !this.invertPageOrder ? this.currentPage - 1 : this.currentPage + 1, {
                        text: this.prevText,
                        classes: 'prev'
                    });
                }


                //Generate Next link
                if (this.nextText && this.nextAtFront) {
                    this._appendItem.call(this, !this.invertPageOrder ? this.currentPage + 1 : this.currentPage - 1, {
                        text: this.nextText,
                        classes: 'next'
                    });
                }


                //Generate start edges
                if (!this.invertPageOrder) {
                    if (interval.start > 0 && this.edges > 0) {
                        if (this.useStartEdge) {
                            var end = Math.min(this.edges, interval.start);
                            for (i = 0; i < end; i += 1) {
                                this._appendItem.call(this, i);
                            }
                        }
                        if (this.edges < interval.start && (interval.start - this.edges != 1)) {
                            $(this.dom.pager2).append('<li class="disabled"><span class="pmui-gridpanel-pager-ellipse">' + this.ellipseText + '</span></li>');
                        } else if (interval.start - this.edges == 1) {
                            this._appendItem.call(this, this.edges);
                        }
                    }
                } else {
                    if (interval.end < this.pages && this.edges > 0) {
                        if (this.useStartEdge) {
                            var begin = Math.max(this.pages - this.edges, interval.end);
                            for (i = this.pages - 1; i >= begin; i -= 1) {
                                this._appendItem.call(this, i);
                            }
                        }
                        if (this.pages - this.edges > interval.end && (this.pages - this.edges - interval.end != 1)) {
                            $(this.dom.pager2).append('<li class="disabled"><span class="pmui-gridpanel-pager-ellipse">' + this.ellipseText + '</span></li>');
                        } else if (this.pages - this.edges - interval.end == 1) {
                            this._appendItem.call(this, interval.end);
                        }
                    }
                }


                //Generate interval links
                if (!this.invertPageOrder) {
                    for (i = interval.start; i < interval.end; i += 1) {
                        this._appendItem.call(this, i);
                    }
                } else {
                    for (i = interval.end - 1; i >= interval.start; i -= 1) {
                        this._appendItem.call(this, i);
                    }
                }

                //Generate end edges
                if (!this.invertPageOrder) {
                    if (interval.end < this.pages && this.edges > 0) {
                        if (this.pages - this.edges > interval.end && (this.pages - this.edges - interval.end != 1)) {
                            $(this.dom.pager2).append('<li class="disabled"><span class="pmui-gridpanel-pager-ellipse">' + this.ellipseText + '</span></li>');
                        } else if (this.pages - this.edges - interval.end == 1) {
                            this._appendItem.call(this, interval.end);
                        }
                        if (this.useEndEdge) {
                            var begin = Math.max(this.pages - this.edges, interval.end);
                            for (i = begin; i < this.pages; i += 1) {
                                this._appendItem.call(this, i);
                            }
                        }
                    }
                } else {
                    if (interval.start > 0 && this.edges > 0) {
                        if (this.edges < interval.start && (interval.start - this.edges != 1)) {
                            $(this.dom.pager2).append('<li class="disabled"><span class="pmui-gridpanel-pager-ellipse">' + this.ellipseText + '</span></li>');
                        } else if (interval.start - this.edges == 1) {
                            this._appendItem.call(this, this.edges);
                        }

                        if (this.useEndEdge) {
                            var end = Math.min(this.edges, interval.start);
                            for (i = end - 1; i >= 0; i -= 1) {
                                this._appendItem.call(this, i);
                            }
                        }
                    }
                }

                //Generate Next link
                if (this.nextText && !this.nextAtFront) {
                    this._appendItem.call(this, !this.invertPageOrder ? this.currentPage + 1 : this.currentPage - 1, {
                        text: this.nextText,
                        classes: 'next'
                    });
                }
            } else {
                if (this.getItems().length == 0) {
                    jQuery(this.dom.pager2).empty();
                }
            }
            //Show rows in gridPanel
            var i, initRow, lastRow, listTarget, size, numDisplayedItems = 0, totalPages;
            if (!((index < 0 || index >= this.getTotalPages(this.filterCriteria)) && !force)) {
                if (!this.html) {
                    return this;
                }
                if (this.filterCriteria && this.filterable) {
                    listTarget = this.filteredItems.asArray();
                } else {
                    listTarget = this.usableItemsList.asArray();
                }
                size = listTarget.length;

                totalPages = this.getTotalPages(!!this.filterCriteria);
                jQuery(this.dom.tbody).find('>tr').detach();
                if (force) {
                    initRow = this.currentPage * this.pageSize;
                    lastRow = this.pageSize !== 0 ? (initRow + this.pageSize) : size;
                } else {
                    initRow = (this.filterCriteria && this.filterable) ? 0 : this.pageSize !== 0 ? ((this.currentPage + 1) * this.pageSize) - this.pageSize : 0;
                    lastRow = this.pageSize !== 0 ? (initRow + this.pageSize) : size;
                }
                for (i = initRow; i < lastRow && i < size; i += 1) {
                    this.dom.tbody.appendChild(listTarget[i].updateCellsWidth().getHTML());
                    numDisplayedItems += 1;
                }
            }

            if (!this.html) {
                return this;
            }

            this.showEmptyCell(numDisplayedItems);
            this.setBehavior(this.behavior);
            return this;
        }
    };

    /**
     * Deprecated...
     * Updates the pager.
     * @chainable
     * @private
     */
    GridPanel.prototype.updatePager = function () {
        var totalPages,
            page,
            i,
            inputPage,
            link,
            textContent,
            additionalClass,
            currentPage = this.currentPage,
            dataIndex,
            infoLink;

        if (this.dom.pager) {
            jQuery(this.dom.pager).empty();
            if (this._dynamicLoad) {

                totalPages = this._dynamicLoad.totalPages;
                var first, next, first, last, total;
                first = PMUI.createHTMLElement('li');
                first.className = "list-item-ajax";
                link = PMUI.createHTMLElement('a');
                link.className = 'pmui-gridpanel-pagelink pmui-gridpanel-previousbutton';
                textContent = "«";
                link.textContent = textContent;
                link.href = '#';
                first.appendChild(link);
                jQuery(link).css({
                    height: "auto"
                }).data({"index": 0});
                this.dom.pager.appendChild(first);
                if (this.currentPage !== 0) {
                    jQuery(link).removeClass("disable");
                } else {
                    jQuery(link).addClass("disable");
                }
                prev = PMUI.createHTMLElement('li');
                prev.className = "list-item-ajax";
                link = PMUI.createHTMLElement('a');
                link.className = 'pmui-gridpanel-pagelink';
                link.innerHTML = "&lt;";
                jQuery(link).addClass("pmui-gridpanel-previousbutton");
                link.href = '#';
                prev.appendChild(link);
                jQuery(link).css({
                    height: "auto"
                }).data({"index": this.currentPage - 1});
                this.dom.pager.appendChild(prev);
                if (this.currentPage !== 0) {
                    jQuery(link).removeClass("disable");
                } else {
                    jQuery(link).addClass("disable");
                }
                infoLink = PMUI.createHTMLElement('li');
                infoLink.className = "list-item-ajax";
                total = PMUI.createHTMLElement("a");
                total.className = "pmui-gridpanel-total";
                total.textContent = " Page ";
                infoLink.appendChild(total);
                this.dom.pager.appendChild(infoLink);

                infoLink = PMUI.createHTMLElement('li');
                infoLink.className = "list-item-ajax";
                inputPage = PMUI.createHTMLElement("input");
                jQuery(inputPage).addClass("pmui-gridpanel-gotoPage");
                infoLink.appendChild(inputPage);
                this.dom.pager.appendChild(infoLink);
                jQuery(inputPage).val(this.currentPage + 1);
                jQuery(infoLink).css({
                    "vertical-align": "top"
                });
                infoLink = PMUI.createHTMLElement('li');
                infoLink.className = "list-item-ajax";
                total = PMUI.createHTMLElement("a");
                total.className = "pmui-gridpanel-total";
                total.textContent = " of " + (totalPages ? totalPages : 1);
                infoLink.appendChild(total);
                this.dom.pager.appendChild(infoLink);

                next = PMUI.createHTMLElement('li');
                next.className = "list-item-ajax";
                link = PMUI.createHTMLElement('a');
                link.className = 'pmui-gridpanel-pagelink';
                link.innerHTML = "&gt;";
                jQuery(link).addClass("pmui-gridpanel-nextbutton");
                link.href = '#';
                next.appendChild(link);
                jQuery(link).css({
                    height: "auto"
                }).data({"index": this.currentPage + 1});
                this.dom.pager.appendChild(next);
                if (currentPage + 1 < totalPages) {
                    jQuery(link).removeClass("disable");
                } else {
                    jQuery(link).addClass("disable");
                }
                last = PMUI.createHTMLElement('li');
                last.className = "list-item-ajax";
                link = PMUI.createHTMLElement('a');
                link.className = 'pmui-gridpanel-pagelink pmui-gridpanel-nextbutton';
                textContent = "»";
                link.textContent = textContent;
                link.href = '#';
                last.appendChild(link);
                jQuery(link).css({
                    height: "auto"
                }).data({"index": this._dynamicLoad.totalPages - 1});
                this.dom.pager.appendChild(last);
                if (currentPage + 1 < totalPages) {
                    jQuery(link).removeClass("disable");
                } else {
                    jQuery(link).addClass("disable");
                }
            } else {
                totalPages = this.getTotalPages(!!this.filterCriteria);
                if (!totalPages) {
                    return this;
                }
                for (i = -1; i <= totalPages; i += 1) {
                    additionalClass = textContent = dataIndex = "";
                    if (i < 0) {
                        if (currentPage > 0) {
                            dataIndex = 'p';
                            additionalClass = 'pmui-gridpanel-previousbutton';
                        }
                    } else if (i === totalPages) {
                        if (currentPage < totalPages - 1) {
                            additionalClass = 'pmui-gridpanel-nextbutton';
                            dataIndex = 'n';
                        }
                    } else {
                        textContent = i + 1;
                        if (currentPage === i) {
                            additionalClass = 'pmui-active';
                        }
                        dataIndex = i;
                    }
                    if (dataIndex !== "") {
                        page = PMUI.createHTMLElement('li');
                        link = PMUI.createHTMLElement('a');
                        if (this._dynamicLoad) {
                            $(link).css({
                                height: 21
                            });
                        }
                        link.className = 'pmui-gridpanel-pagelink';
                        link.setAttribute("data-index", dataIndex);
                        if (textContent) {
                            link.textContent = textContent;
                        } else {
                            textContent = PMUI.createHTMLElement('div');
                            textContent.className = 'pmui-icon';
                            link.appendChild(textContent);
                        }
                        jQuery(link).addClass(additionalClass);
                        link.href = '#';
                        page.appendChild(link);
                        this.dom.pager.appendChild(page);
                    }
                }
            }
            return this;
        }
    };

    /**
     * Sets the size for the grid's pages.
     * @param {Number} pageSize
     * @chainable
     */
    GridPanel.prototype.setPageSize = function (pageSize) {
        var previousSize = this.pageSize;
        if (typeof pageSize !== 'number') {
            throw new Error("setPageSize(): The method only accepts a number as parameter.");
        }
        if (pageSize < 0) {
            throw new Error("setPageSize(): The method only accepts a number major or equal to zero (0).");
        }
        this.pageSize = pageSize;
        if (this.html && previousSize !== pageSize) {
            if (pageSize === 0) {
                jQuery(this.dom.pager).hide();
                this.currentPage = 0;
            } else {
                jQuery(this.dom.pager).show();
            }
            this.goToPage(this.currentPage);
        }
        return this;
    };

    /**
     * Sets the size for the grid's displayedPages.
     * @param {Number} pageSize
     * @chainable
     */
    GridPanel.prototype.setDisplayedPages = function (displayedPages) {
        this.displayedPages = displayedPages;
        return this;
    };

    /**
     * Sets the size for the grid's pages.
     * @param {Number} pageSize
     * @chainable
     */
    GridPanel.prototype.setPages = function (pages) {
        this.pages = pages;
        return this;
    };

    /**
     * Sets Prev text in paginator.
     * @param {Number} pageSize
     * @chainable
     */
    GridPanel.prototype.setPrevText = function (prevText) {
        this.prevText = prevText;
        return this;
    };

    /**
     * Sets Next text in paginator.
     * @param {Number} pageSize
     * @chainable
     */
    GridPanel.prototype.setNextText = function (nextText) {
        this.nextText = nextText
        return this;
    };

    /**
     * Sets the size for the grid's pages.
     * @param {Number} pageSize
     * @chainable
     */
    GridPanel.prototype.setInvertPageOrder = function (invertPageOrder) {
        this.invertPageOrder = invertPageOrder
        return this;
    };

    /**
     * Sets numbers in a paginator.
     * @param {Number} pageSize
     * @chainable
     */
    GridPanel.prototype.setLabelMap = function (labelMap) {
        this.labelMap = labelMap
        return this;
    };

    /**
     * Sets the size for the grid's pages.
     * @param {Number} pageSize
     * @chainable
     */
    GridPanel.prototype.setHrefTextPrefix = function (hrefTextPrefix) {
        this.hrefTextPrefix = hrefTextPrefix
        return this;
    };


    /**
     * Sets the size for the grid's pages.
     * @param {Number} pageSize
     * @chainable
     */
    GridPanel.prototype.setHrefTextSuffix = function (hrefTextSuffix) {
        this.hrefTextSuffix = hrefTextSuffix
        return this;
    };


    /**
     * Sets the size for the grid's pages.
     * @param {Number} pageSize
     * @chainable
     */
    GridPanel.prototype.setNextAtFront = function (nextAtFront) {
        this.nextAtFront = nextAtFront
        return this;
    };


    /**
     * Sets the size for the grid's pages.
     * @param {Number} pageSize
     * @chainable
     */
    GridPanel.prototype.setEdgesPagination = function (edges) {
        this.edges = edges
        return this;
    };


    /**
     * Sets the size for the grid's pages.
     * @param {Number} pageSize
     * @chainable
     */
    GridPanel.prototype.setUseStartEdge = function (useStartEdge) {
        this.useStartEdge = useStartEdge
        return this;
    };


    /**
     * Sets the size for the grid's pages.
     * @param {Number} pageSize
     * @chainable
     */
    GridPanel.prototype.setEllipseText = function (ellipseText) {
        this.ellipseText = ellipseText
        return this;
    };


    /**
     * Sets the size for the grid's pages.
     * @param {Number} pageSize
     * @chainable
     */
    GridPanel.prototype.setSelectOnClick = function (selectOnClick) {
        this.selectOnClick = selectOnClick
        return this;
    };

    /**
     * Sets the size for the grid's pages.
     * @param {Number} pageSize
     * @chainable
     */
    GridPanel.prototype.setUseEndEdge = function (useEndEdge) {
        this.useEndEdge = useEndEdge
        return this;
    };

    /**
     * Returns the total number of pages on the grid.
     * @param  {Boolean} [filter=false] If the calculation will be done taking only the filtered results.
     * @return {Number}
     */
    GridPanel.prototype.getTotalPages = function (filter) {
        var targetListSize = filter ? this.filteredItems.getSize() : this.items.getSize();

        return !this.items.getSize() ? 0 : (this.pageSize === 0 ? 1 : Math.ceil(targetListSize / this.pageSize));
    };

    /**
     * It defines the events for the Grid.
     * @chainable
     */
    GridPanel.prototype.defineEvents = function () {
        var that = this,
            i,
            columnsNum = this.columns.getSize();

        this.removeEvents().eventsDefined = true;
        if (this.dom.pager) {
            this.filterControl.defineEvents();

            for (i = 0; i < columnsNum; i += 1) {
                this.columns.get(i).defineEvents();
            }

            /*for(i = 0; i < this.items.getSize(); i += 1) {
             this.items.get(i).defineEvents();
             }*/
            this.addEvent('click').listenWithDelegation(this.dom.pager, 'a', function (e) {
                var where;
                e.preventDefault();
                e.stopPropagation();
                where = jQuery(this).data("index");
                if (that._dynamicLoad) {
                    if ((where > -1 && where < that._dynamicLoad.totalPages) && (that.currentPage !== where)) {
                        that.goToPage(where === 'n' ? that.currentPage + 1 : (where === 'p' ? that.currentPage - 1 : where));
                    }
                } else {
                    that.goToPage(where === 'n' ? that.currentPage + 1 : (where === 'p' ? that.currentPage - 1 : where));
                }
            });
            this.addEvent('keypress').listenWithDelegation(this.dom.pager, 'input', function (e) {
                if (e.keyCode == 13 || e.keyCode == 32) {
                    e.preventDefault();
                    e.stopPropagation();
                    if (/^\d*$/.test(this.value) && (this.value <= that._dynamicLoad.totalPages)) {
                        if (this.value > 0) {
                            that.goToPage(parseInt(this.value) - 1);
                        }

                    }
                }
            });
            this.addEvent('change').listenWithDelegation(this.dom.pager, 'input', function (e) {
                e.preventDefault();
                e.stopPropagation();
                if (this.value !== that.currentPage) {
                    this.value = that.currentPage + 1;
                }
            });
        }
        return this;
    };
    /**
     * Sets the current page to be displayed in the grid.
     * @param {Number} currentPage
     * @chainable
     */
    GridPanel.prototype.setCurrentPage = function (currentPage) {
        this.currentPage = currentPage;
        if (this.html) {
            this.goToPage(this.currentPage);
        }
        return this;
    };
    /**
     * Clears the filter.
     * @chainable
     */
    GridPanel.prototype.clearFilter = function () {
        this.filterCriteria = null;
        this.goToPage(this.currentPage);
        return this;
    };
    /**
     * Executes the sorting.
     * @param  {String} criteria The sorting criteria.
     * @param  {String} [sortType="asc"] The sorting order: "asc" or "desc".
     * @chainable
     */
    GridPanel.prototype.sort = function (criteria, sortType) {
        var items,
            theColumn,
            i,
            columns,
            evaluate;

        this.sortingData.criteria = criteria;
        this.sortingData.type = sortType;
        evaluate = function (a, b) {
            var dataA,
                dataB,
                res;

            if (typeof criteria === 'function') {
                dataA = criteria.call(a, a.getData()).toString();
                dataB = criteria.call(b, b.getData()).toString();
            } else {
                dataA = a.getData();
                dataB = b.getData();
                dataA = (dataA[criteria] && dataA[criteria].toString()) || "";
                dataB = (dataB[criteria] && dataB[criteria].toString()) || "";
            }
            if (dataA < dataB) {
                res = 1;
            } else if (dataA > dataB) {
                res = -1;
            } else {
                res = 0;
            }
            if (sortType === 'asc') {
                res *= -1;
            }
            return res;
        };
        if (!this.html) {
            return this;
        }
        this.updateUsableItemsList();
        items = this.usableItemsList.asArray();
        items.sort(evaluate);
        this.usableItemsList.set(items);
        columns = this.getColumns();
        for (i = 0; i < columns.length; i += 1) {
            columns[i].style.removeClasses(['pmui-sort-asc', 'pmui-sort-desc']);
            if (columns[i].columnData === criteria) {
                theColumn = columns[i];
            }
        }

        if (theColumn) {
            theColumn.style.addClasses(['pmui-sort-' + sortType]);
        }

        if (this.filterCriteria) {
            this.filter(this.filterCriteria);
        } else {
            this.goToPage(this.currentPage);
        }
        this.goToPage(this.currentPage);
        return this;
    };
    /**
     * Returns the function to be executed when an accepted draggedgable is dragged over the droppable area.
     * @return {Function}
     */
    GridPanel.prototype.onDragOver = function () {
        return function () {
        };
    };
    /**
     * Filters the rows using a criteria. The filter will be applied only in the columns in which its property
     * "searchable" is true.
     * @param  {String} criteria The criteria.
     * @chainable
     */
    GridPanel.prototype.filter = function (criteria) {
        var i,
            columns,
            j,
            cell,
            regExp,
            rowsCopy,
            visibleRows,
            pmuiObject,
            sortingCriteria = this.sortingData.criteria;

        if (typeof criteria === 'string') {
            if (criteria === "") {
                this.clearFilter();
                return this;
            }
            this.filterCriteria = criteria;
        } else if (typeof criteria === 'number') {
            this.filterCriteria = criteria.toString(10);
        }

        if (!this.dom.tbody) {
            return this;
        }

        columns = this.columns.asArray();
        regExp = new RegExp(this.filterCriteria.replace(/([\\\.\[\]\^\$\(\)\?\*\+\|\{\}])/g, "\\\$1"), "i");
        rowsCopy = this.usableItemsList.asArray().slice(0);

        visibleRows = jQuery(this.dom.tbody).find('tr');
        for (i = 0; i < visibleRows.length; i += 1) {
            pmuiObject = PMUI.getPMUIObject(visibleRows[i]);
            if (pmuiObject) {
                jQuery(pmuiObject.html).detach();
            }
        }
        this.filteredItems.clear();
        //if there is a sorting criteria set at the moment, first when use it to filter the rows
        //this is necessary to keep the current sorting order.
        if (sortingCriteria) {
            for (j = 0; j < rowsCopy.length; j += 1) {
                for (i = 0; i < columns.length; i += 1) {
                    if (columns[i].getDataType() !== 'button' && columns[i].isSearchable()) {
                        if (!rowsCopy[j].html) {
                            rowsCopy[j].getHTML();
                            rowsCopy[j].setCells();
                        }
                        cell = rowsCopy[j].getCells()[i];
                        if (regExp.test(cell.getContent()) && cell.visible) {
                            this.filteredItems.insert(rowsCopy.splice(j, 1)[0]);
                            j -= 1;
                            break;
                        }
                    }
                }
            }
        }
        if (rowsCopy.length) {
            for (i = 0; i < columns.length; i += 1) {
                if (columns[i].getDataType() !== 'button' && columns[i].isSearchable()) {
                    for (j = 0; j < rowsCopy.length; j += 1) {
                        if (!rowsCopy[j].html) {
                            rowsCopy[j].getHTML();
                            rowsCopy[j].setCells();
                        }
                        cell = rowsCopy[j].getCells()[i];
                        if (regExp.test(cell.getContent()) && cell.visible) {
                            this.filteredItems.insert(rowsCopy.splice(j, 1)[0]);
                            j -= 1;
                        }
                    }
                }
            }
        }
        this.goToPage(0, true);
        return this;
    };
    /**
     * [setSelectable description]
     * @param {[type]} value [description]
     */
    GridPanel.prototype.setSelectable = function (value) {
        if (typeof value !== 'boolean') {
            throw new Error('setSelectable(): the property is not valid, should be a boolean');
        }
        this.selectable = value;
        if (!value && this.selectedRow) {
            this.selectedRow.deselectRow();
            this.selectedRow = null;
        }
        return this;
    };
    /**
     * fixed the property visibleFooter, to hide or not the foot of GridPanel
     * @param {Boolean} value the property should be type 'boolean'
     */
    GridPanel.prototype.setVisibleFooter = function (value) {
        if (typeof value !== 'boolean') {
            throw new Error("setFooterHidden(): the value is no valid, should be a value type 'boolean'");

        }
        this.visibleFooter = value;
        if (this.html) {
            if (this.visibleFooter) {
                this.showFooter();
            } else {
                this.hiddenFooter();
            }
        }
        return this;
    };
    /**
     * hidden the footer of the GridPanel if is visible
     * @chainable
     */
    GridPanel.prototype.hiddenFooter = function () {
        this.dom.footer.style.display = 'none';
        return this;
    };
    /**
     * show the footer of the GridPanel if it has been hidden
     * @chainable
     */
    GridPanel.prototype.showFooter = function () {
        this.dom.footer.style.display = 'block';
        return this;
    };
    /**
     * Set the height for the HTML element
     * @param {Number|String} height it can be a number or a string.
     * In case of using a String you only can use 'auto' or 'inherit' or ##px or ##% or ##em when ## is a number.
     * also sets the height of the table being rendered, according to the high set in order gridPanel
     * @chainable
     */
    GridPanel.prototype.setHeight = function (height) {
        GridPanel.superclass.prototype.setHeight.call(this, height);


        if (this.dom.tableContainer) {
            if (typeof this.height !== "string") {
                this.dom.tableContainer.style.height = this.height - 74 + 'px';
            } else {
                this.dom.tableContainer.style.height = 'auto';
            }
        }
        return this;
    };

    PMUI.extendNamespace("PMUI.grid.GridPanel", GridPanel);

    if (typeof exports !== 'undefined') {
        module.exports === GridPanel;
    }
}());

(function () {
    /**
     * @class PMUI.grid.GridPanelColumn
     * Class that represents a column to be used in a {@link PMUI.grid.GridPanel GridPanel}.
     * Usually you don't need to instantiate this class since the {@link PMUI.grid.GridPanel GridPanel} do it on its
     * own, in fact, this class can't be instantiate if a grid parent
     * ({@link PMUI.grid.GridPanel#cfg-grid grid config option}) is not specified.
     * @extends {PMUI.core.Element}
     *
     * We don't include any example for this class 'cause we don't recommend you to do it, let the GridPanelColumn do
     * it. Anyway, if you need add new columns to a GridPanel use its
     * {@link PMUI.grid.GridPanel#method-addColumn addColumn() method}.
     *
     * @constructor
     * Creates a new instance of the GridPanelColumn object.
     * @param {Object} settings A JSON object with the config options.
     *
     * @cfg {String} [title='[untitled]'] The text to display in the column header.
     * @cfg {String} [dataType='string'] The column data type. The accepted values are: "string", "number", "index" or
     * "date".
     * @cfg {Boolean} [sortable=false] If the column will have the sorting function enabled.
     * @cfg {Boolean} [searchable=true] If the column must be filter when a filter criteria is set.
     * @cfg {Function} [onButtonClick=null] A callback function to be called when a column's button is clicked. For
     * info about the callback parameters please read the
     * {@link PMUI.grid.GridPanelColumn#event-onButtonClick onButtonClick event doc}.
     * @cfg {String} [buttonLabel='[button]'] The label for the button on a column for buttons.
     * @cfg {Array} [cells=[]] The cells that belongs to the column.
     * @cfg {PMUI.grid.GridPanel} grid The grid the column belongs to.
     * @cfg {String|Function} [columnData=null]
     * - In case of a String: The data key to be used for the cells in this column.
     * - In case of a Function: A function that will return the content for the cells in column, the parameters
     * received by the function are:
     *    - data {Object}: a JSON object with the data for the current row.
     *    The function will be called in the context of the current {@link PMUI.grid.GridPanelRow row object}.
     * @cfg {Function} [onSort=null] A callback function to be called every time the sorting is executed.
     * @cfg {String} [alignmentCell="center"] A string that specifies the alignmentCell for the content of cells that
     * appertains to this column. The accepted values are: "center", "left", "right".
     * @cfg {Object|PMUI.util.Style|null} [cellStyle=null] The style to be applied to the cells that belongs to the
     * column, if it is set to null (the default value) no custom styles will be applied to the cells.
     * @cfg {Object|PMUI.util.Style|Function|null} [buttonStyle=null] The style for the buttons in the column (only applicable
     * if the column has its {@link #property-dataType dataType property} set to "button"). It can be:
     *
     *  - null then the buttons won't apply any custom style.
     *  - An object literal in that case it can have the properties: cssClasses (an array in which each element is
     *  an string that is a class name) and cssProperties (another object literal that can contain css properties and
     *  its respective values).
     *  - An instance of {@link PMUI.util.Style}.
     *  - A function that must return any of the previous value types. The parameters received by the functions are:
     *    - the {@link PMUI.grid.GridPanelCell GridPanelCell} object in which the style is being applied.
     *    - data {Object}: a JSON object with the data for the current row.
     *    The function will be called in the context of the current {@link PMUI.grid.GridPanelRow row object}.
     * @cfg {Boolean} [disabledButtons=false] If the buttons will be disabled.
     * @cfg {String|Function|null} [buttonTooltip=null] Sets the tooltip message for the buttons (only applicable if
     * the column has its {@link #property-dataType dataType property} set to "button"). It can be a string
     * or null or a function. In the latter case the function must return a string, it will receive two parameters:
     *
     * - The cell, a {@link PMUI.grid.GridPanelCell GridPanelCell} object.
     * - The data that belongs to the current row.
     *
     * The function will be called in the context of the current
     * {@link PMUI.grid.GridPanelCell GridPanelCell cell object}.
     */
    var GridPanelColumn = function (settings) {
        /**
         * The cells that belongs to the column.
         * @type {PMUI.util.ArrayList}
         */
        this.cells = new PMUI.util.ArrayList();
        GridPanelColumn.superclass.call(this, jQuery.extend(settings, {elementTag: "th"}));
        /**
         * The text to be shown in the column header.
         * @type {String}
         */
        this.title = null;
        /**
         * The data type for the cells in this column. It is set by the {@link #cfg-dataType dataType config option} or
         * the {@link #method-setDataType setDataType()} method.
         * @type {String}
         */
        this.dataType = null;
        /**
         * If the column has the sorting functionality enabled.
         * @type {Boolean}
         */
        this.sortable = null;
        /**
         * If the column has the searching functionality enabled.
         * @type {Boolean}
         */
        this.searchable = null;
        /**
         * @event onButtonClick
         * Fired when a button in this column is clicked. The event is called in the column context.
         * @param {PMUI.grid.GridPanelRow} row The row the button belongs to.
         * @param {PMUI.grid.GridPanel} grid The grid the button belongs to.
         */
        this.onButtonClick = null;
        /**
         * The label for the buttons in the column (only applicable when the datatType is set to "button").
         * @type {String|Function}
         */
        this.buttonLabel = null;
        /**
         * If the buttons are disabled or not.
         * @type {Boolean}
         */
        this.disabledButtons = null;
        /**
         * The grid the column belongs to.
         * @type {PMUI.grid.GridPanel}
         */
        this.grid = null;
        /**
         * The data key to be used for the cells in this column.
         * @type {String}
         */
        this.columnData = null;
        /**
         * The column sort order ("ASC" or "DESC").
         * @type {String}
         */
        this.sortOrder = null;
        /**
         * The alignmentCell to be applied to the content of the cells that appertain to this column.
         * @type {String}
         */
        this.alignmentCell = null;
        /**
         * The style object for the cells that belongs to the column.
         * @type {PMUI.util.Style}
         * @private
         */
        this.cellStyle = null;
        /**
         * @property {PMUI.util.Style} buttonStyle The style object to be applied in the buttons that belong to the
         * column. Only applicable when the column's type is 'button'.
         */
        this.buttonStyle = null;
        /**
         * The HTML element in which the cell content will be appended.
         * @type {HTMLElement}
         * @private
         */
        this.contentTitle = null;
        /**
         * The tooltip to be shown for the buttons in the column. Only applicable when the column's type is 'button'.
         * @type {String|Function}
         */
        this.buttonTooltip = null;
        /**
         * The DOM element in which the sorting arrow is shown.
         * @type {HTMLElement}
         * @private
         */
        this.sortIcon = null;
        /**
         * Determines the alignment for the title in the column's header,
         * @type {String}
         */
        this.alignmentTitle = null;
        GridPanelColumn.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom("PMUI.core.Element", GridPanelColumn);

    GridPanelColumn.prototype.type = "GridPanelColumn";
    /**
     * Initialize the object
     * @param  {Object} [settings=null] A JSON object with the config options.
     * @private
     */
    GridPanelColumn.prototype.init = function (settings) {
        var defaults = {
            title: '[untitled]',
            dataType: 'string',
            sortable: false,
            searchable: true,
            onButtonClick: function () {
            },
            buttonLabel: '[button]',
            cells: [],
            grid: null,
            columnData: null,
            onSort: null,
            alignmentCell: "center",
            cellStyle: null,
            buttonStyle: null,
            visible: true,
            disabledButtons: false,
            buttonTooltip: null,
            alignmentTitle: 'center',
            positionMode: 'inherit'
        };

        jQuery.extend(true, defaults, settings);

        this.buttonStyle = new PMUI.util.Style();
        this.cellStyle = new PMUI.util.Style();
        this.onSort = defaults.onSort;
        this.setPositionMode(defaults.positionMode);
        this.setTitle(defaults.title)
            .setButtonTooltip(defaults.buttonTooltip)
            .setCellStyle(defaults.cellStyle)
            .setButtonStyle(defaults.buttonStyle)
            .setAlignmentCell(defaults.alignmentCell)
            .setColumnData(defaults.columnData)
            .setDataType(defaults.dataType)
            .setOnButtonClick(defaults.onButtonClick)
            .setButtonLabel(defaults.buttonLabel)
            .setGrid(defaults.grid)
            .setSortable(defaults.sortable)
            .setCells(defaults.cells)
            .setVisible(defaults.visible)
            .setAlignmentTitle(defaults.alignmentTitle);

        if (defaults.searchable) {
            this.enableSearch();
        } else {
            this.disableSearch();
        }

        if (defaults.disabled) {
            this.disable();
        } else {
            this.enable();
        }
    };
    /**
     * Sets the button tooltip. Only applicable
     * @param {String|Function|null} tooltip The tooltip to be used in the buttons in the column. It can be a string
     * or null or a function. In the latter case the function must return a string, it will receive two parameters:
     *
     * - The cell, a {@link PMUI.grid.GridPanelCell GridPanelCell} object.
     * - The data that belongs to the current row.
     *
     * The function will be called in the context of the current
     * {@link PMUI.grid.GridPanelCell GridPanelCell cell object}.
     */
    GridPanelColumn.prototype.setButtonTooltip = function (tooltip) {
        if (!(typeof tooltip === 'function' || typeof tooltip === 'string' || tooltip === null)) {
            throw new Error('setButtonTooltip(): the parameter must be a function or a string or null.');
        }
        this.buttonTooltip = tooltip;
        return this;
    };
    /**
     * Disables all the buttons in the column, this is applied only if the column's property
     * {@link #property-dataType dataType} is set to "button".
     * @chainable
     */
    GridPanelColumn.prototype.disable = function () {
        var i,
            cells,
            cellsLength;

        this.disabledButtons = true;
        cellsLength = (cells = this.cells.asArray()).length;
        for (i = 0; i < cellsLength; i += 1) {
            cells[i].disable();
        }
        return this;
    };
    /**
     * Disable one item cell
     * @param index
     * @returns {PMUI.grid.GridPanelColumn.options.GridPanelColumn}
     */
    GridPanelColumn.prototype.disableCell = function (index) {
        var cells = this.cells.asArray(),
            cellsLength = cells.length;
        if (index < cellsLength && index >= 0) {
            cells[index].disable();
        }
        return this;
    };
    /**
     * Enable one item cell
     * @param index
     * @returns {PMUI.grid.GridPanelColumn.options.GridPanelColumn}
     */
    GridPanelColumn.prototype.enableCell = function (index) {
        var cells = this.cells.asArray(),
            cellsLength = cells.length;
        if (index < cellsLength && index >= 0) {
            cells[index].enable();
        }
        return this;
    };
    /**
     * Enables all the buttons in the column, this is applied only if the column's property
     * {@link #property-dataType dataType} is set to "button".
     * @chainable
     */
    GridPanelColumn.prototype.enable = function () {
        var i,
            cells,
            cellsLength;

        if (this.dataType === 'button') {
            this.disabledButtons = false;
            cellsLength = (cells = this.cells.asArray()).length;
            for (i = 0; i < cellsLength; i += 1) {
                cells[i].enable();
            }
        }
        return this;
    };
    /**
     * [setVisible description]
     * @param {[type]} visible [description]
     */
    GridPanelColumn.prototype.setVisible = function (visible) {
        var cells,
            cellsLength,
            i;

        GridPanelColumn.superclass.prototype.setVisible.call(this, visible);
        if (this.cells) {
            cells = this.cells.asArray();
            cellsLength = cells.length;
            for (i = 0; i < cellsLength; i += 1) {
                cells[i].setVisible(this.visible);
            }
        }
        return this;
    };
    /**
     * [getUsableButtonStyle description]
     * @param  {[type]} cell [description]
     * @return {[type]}      [description]
     */
    GridPanelColumn.prototype.getUsableButtonStyle = function (cell) {
        var theStyle,
            buttonStyle = this.buttonStyle,
            theRow;

        if (typeof buttonStyle !== 'function') {
            return {
                cssClasses: buttonStyle.cssClasses,
                cssProperties: buttonStyle.cssProperties
            };
        } else {
            theRow = cell.row;
            theStyle = buttonStyle.call(theRow, cell, theRow.getData());
            return {
                cssClasses: (theStyle && theStyle.cssClasses) || [],
                cssProperties: (theStyle && theStyle.cssProperties) || {}
            };
        }
    };
    /**
     * Sets the style for the buttons in the column (only applicable if the column has its
     * {@link #property-dataType dataType property} set to "button").
     * @param {PMUI.util.Style|Object|null|Function} cellStyle The style to be applied. It can be:
     *
     *  - null then the buttons won't apply any custom style.
     *  - An object literal in that case it can have the properties: cssClasses (an array in which each element is
     *  an string that is a class name) and cssProperties (another object literal that can contain css properties and
     *  its respective values).
     *  - An instance of {@link PMUI.util.Style}.
     *  - A function that must return any of the previous value types.
     *  @chainable
     */
    GridPanelColumn.prototype.setButtonStyle = function (buttonStyle) {
        var i,
            cells,
            cellsNum,
            theStyle,
            theRow;

        if (buttonStyle !== null && typeof buttonStyle !== 'object' && typeof buttonStyle !== 'function' && !(buttonStyle instanceof PMUI.util.Style)) {
            throw new Error("setbuttonStyle(): The parameter only can be an object, an instance of PMUI.util.Style or null.");
        }
        this.buttonStyle = this.buttonStyle.clear();
        if (buttonStyle instanceof PMUI.util.Style) {
            this.buttonStyle = buttonStyle;
        } else if (typeof buttonStyle === 'function') {
            this.buttonStyle = buttonStyle;
        } else if (buttonStyle) {
            this.buttonStyle.addProperties((buttonStyle && buttonStyle.cssProperties) || {})
                .addClasses((buttonStyle && buttonStyle.cssClasses) || []);
        }
        if (this.dataType === 'button') {
            cells = this.cells.asArray();
            cellsNum = cells.length;
            for (i = 0; i < cellsNum; i += 1) {
                cells[i].getContent().setStyle(this.getUsableButtonStyle(cells[i]));
            }
        }
        return this;
    };
    /**
     * Returns the style for the buttons that belongs to the column.
     * @return {Object} An object literal with the properties: cssProperties and cssClasses
     */
    GridPanelColumn.prototype.getButtonStyle = function () {
        return {
            cssClasses: this.buttonStyle.cssClasses,
            cssProperties: this.buttonStyle.cssProperties
        };
    };
    /**
     * Sets the style for the cells in the column.
     * @param {PMUI.util.Style|Object|null} cellStyle The style to be applied. If it is null then the cells won't apply
     * any custom style.
     */
    GridPanelColumn.prototype.setCellStyle = function (cellStyle) {
        var i,
            cells,
            cellsNum;

        if (cellStyle !== null && typeof cellStyle !== 'object' && !(cellStyle instanceof PMUI.util.Style)) {
            throw new Error("setCellStyle(): The parameter only can be an object, an instance of PMUI.util.Style or null.");
        }
        this.cellStyle = this.cellStyle.clear();
        if (cellStyle instanceof PMUI.util.Style) {
            this.cellStyle = cellStyle;
        } else if (cellStyle) {
            this.cellStyle.addProperties(cellStyle.cssProperties || {})
                .addClasses(cellStyle.cssClasses || []);
        }
        cells = this.cells.asArray();
        cellsNum = cells.length;
        for (i = 0; i < cellsNum; i += 1) {
            cells[i].setStyle({
                cssClasses: this.cellStyle.cssClasses,
                cssProperties: this.cellStyle.cssProperties
            });
        }
        return this;
    };
    /**
     * Returns the style for the cells that belongs to the column.
     * @return {Object} An object literal with the properties: cssProperties and cssClasses
     */
    GridPanelColumn.prototype.getCellStyle = function () {
        return {
            cssProperties: this.cellStyle.cssProperties,
            cssClasses: this.cellStyle.cssClasses
        };
    };
    /**
     * Sets the alignmentCell for the content in the cells that appertain to the column.
     * @param {String} alignmentCell The accepted values are: "center", "left", "right".
     * @chainable
     */
    GridPanelColumn.prototype.setAlignmentCell = function (alignmentCell) {
        var i,
            cells;

        if (alignmentCell === 'center' || alignmentCell === 'left' || alignmentCell === 'right') {
            this.alignmentCell = alignmentCell;
            cells = this.getCells();
            for (i = 0; i < cells.length; i += 1) {
                cells[i].setAlignment(this.alignmentCell);
            }
        } else {
            throw new Error('setAlignment(): This method only accepts one of the following values: '
                + '"center", "left", "right"');
        }
        return this;
    };

    GridPanelColumn.prototype.setAlignmentTitle = function (alignmentTitle) {
        if (alignmentTitle === 'center' || alignmentTitle === 'left' || alignmentTitle === 'right') {
            this.alignmentTitle = alignmentTitle;
            if (this.contentTitle) {
                this.contentTitle.style.textAlign = alignmentTitle;
            }
        } else {
            throw new Error('setAlignment(): This method only accepts one of the following values: '
                + '"center", "left", "right"');
        }
        return this;
    };
    /**
     * Returns the alignment for the content in the cells that appertain to the column.
     * @return {String}
     */
    GridPanelColumn.prototype.getAlignment = function () {
        return this.alignment;
    };
    /**
     * Returns true is the filtering function is enabled, otherwise returns false.
     * @return {Boolean}
     */
    GridPanelColumn.prototype.isSearchable = function () {
        return this.searchable;
    };
    /**
     * Enables the filtering functionality.
     * @chainable
     */
    GridPanelColumn.prototype.enableSearch = function () {
        this.searchable = true;
        return this;
    };
    /**
     * Disables the filtering functionality.
     * @chainable
     */
    GridPanelColumn.prototype.disableSearch = function () {
        this.searchable = false;
        return this;
    };
    /**
     * Returns the data's field name that is used by the row to fill its cell for this column.
     * @return {String}
     */
    GridPanelColumn.prototype.getColumnData = function () {
        return this.columnData;
    };
    /**
     * Sets a function or a string that will represent the data's field name that is used by the row to fill its cell
     * for this column.
     * @param {String|Function} columnData It can be a String or a Function
     * - In case of a String: The data key to be used for the cells in this column.
     * - In case of a Function: A function that will return the content for the cells in column, the parameters receive
     * by the function are:
     *    - data {Object}: a JSON object with the data for the current row.
     *    The function will be called in the context of the current {@link PMUI.grid.GridPanelRow row object}.
     * @chainable
     */
    GridPanelColumn.prototype.setColumnData = function (columnData) {
        if (columnData === null || typeof columnData === 'string' || typeof columnData === 'function') {
            this.columnData = columnData;
        } else {
            throw new Error('setColumnData(): The method only accepts a string or a function or NULL as parameter.');
        }

        return this;
    };
    /**
     * Sets the title to be shown in the column header.
     * @param {String} title
     * @chainable
     */
    GridPanelColumn.prototype.setTitle = function (title) {
        this.title = title;
        if (this.html) {
            jQuery(this.contentTitle).empty();
            this.contentTitle.appendChild(document.createTextNode(title));
            this.contentTitle.appendChild(this.sortIcon);
        }
        return this;
    };
    /**
     * Returns the column's data type.
     * @return {String}
     */
    GridPanelColumn.prototype.getDataType = function () {
        return this.dataType;
    };
    /**
     * Sets the data type for the column.
     * @param {String} dataType The accepted values are:
     *
     * - "number" for cells with numeric values.
     * - "string" for cells with string values.
     * - "index" for cells with the row index.
     * - "date" for cells with date values.
     * - "button" for cells that contain a button.
     * @chainable
     */
    GridPanelColumn.prototype.setDataType = function (dataType) {
        if (dataType === 'string' || dataType === 'number' || dataType === 'index' || dataType === 'date' || dataType === 'button') {
            this.dataType = dataType;
        } else {
            throw new Error('setDataType(): It only accepts "string", "number", "index" or "date".');
        }
        return this;
    };
    /**
     * Enables or disables the sorting functionality.
     * @param {Boolean} sortable
     * @chainable
     */
    GridPanelColumn.prototype.setSortable = function (sortable) {
        if (!(typeof sortable === 'boolean')) {
            throw new Error("setSortable(): property 'sortable' should be a boolean");
        }
        //TODO enable event to listen the clicks on column for sorting
        this.sortable = sortable;
        if (this.html) {
            if (sortable) {
                this.style.addClasses(['pmui-sortable']);
            } else {
                this.style.removeClasses(['pmui-sortable']);
            }
        }
        return this;
    };
    /**
     * Returns the callback function to be executed when a column's button is clicked.
     * @return {Function}
     */
    GridPanelColumn.prototype.getOnButtonClick = function () {
        return this.onButtonClick;
    };
    /**
     * Sets the callback function to be executed when a column's button is clicked.
     * @param {Function} onButtonClick
     * @chainable
     */
    GridPanelColumn.prototype.setOnButtonClick = function (onButtonClick) {
        if (!(typeof onButtonClick == 'function')) {
            throw new Error("setOnButtonClick(): property 'onButtonClick' should be a function");
        }
        this.onButtonClick = onButtonClick;
        //TODO update the listeners for all the buttons in the column
        return this;
    };
    /**
     * Sets the text for the button labels in the column.
     * @param {String|Function} buttonLabel A string or a function that returns the string for the button. Three
     * parameters function will be sent to the function:
     *
     * - The row the button belongs to ({@link PMUI.grid.GridPanelRow GridPanelRow}).
     * - The data from the row (object literal).
     *
     * It is important to mention that the function is called in the context of the column object.
     * @chainable
     */
    GridPanelColumn.prototype.setButtonLabel = function (buttonLabel) {
        if (typeof buttonLabel !== 'string' && typeof buttonLabel !== 'function') {
            throw new Error('setButtonLabel(): the parameter must be a string or a function.');
        }
        this.buttonLabel = buttonLabel;
        //TODO update the label for all the buttons in the column
        return this;
    };
    /**
     * Set the grid the column belongs to.
     * @param {PMUI.grid.Grid} grid
     * @chainable
     */
    GridPanelColumn.prototype.setGrid = function (grid) {
        if (!(grid instanceof PMUI.grid.GridPanel)) {
            throw new Error('setGrid(): the grid property should be instance of [PMUI.grid.GridPanel]');
        }
        this.grid = grid;
        return this;
    };
    /**
     * Adds a cell to the column.
     * @param {PMUI.grid.GridPanelCell} cell
     * @chainable
     */
    GridPanelColumn.prototype.addCell = function (cell) {
        if (!(cell instanceof PMUI.grid.GridPanelCell)) {
            throw new Error("addCell(): this method only accepts instances of PMUI.grid.GridPanelCell.");
        }
        cell.setAlignment(this.alignmentCell);
        cell.setStyle({
            cssProperties: this.cellStyle.getProperties(),
            cssClasses: this.cellStyle.getClasses()
        });
        this.cells.insert(cell);

        return this;
    };
    /**
     * Set the cells for the column.
     * @param {PMUI.util.ArrayList|Array} cells An array in which every element is an instance of the
     * {@link PMUI.grid.GridPanelCell GridPanelCell class}.
     * @chainable
     */
    GridPanelColumn.prototype.setCells = function (cells) {
        var cellsAux = [],
            i;

        this.clearCells();

        if (cells instanceof PMUI.util.ArrayList) {
            cellsAux = cells.items.asArray();
        } else if (jQuery.isArray(cells)) {
            cellsAux = cells;
        } else {
            throw new Error('setCells(): the cells property should be instanceof of PMUI.util.ArrayList or an Array');
        }
        for (i = 0; i < cellsAux.length; i += 1) {
            this.addCell(cellsAux[i]);
        }
        return this;
    };
    /**
     * Return the column's cells.
     * @return {Array} An array in which each element is an instance of the
     * {@link PMUI.grid.GridPanelCell GridPanelCell class}.
     */
    GridPanelColumn.prototype.getCells = function () {
        return this.cells.asArray().slice(0);
    };
    /**
     * Removes a cell from the column.
     * @param  {PMUI.grid.GridPanelCell|Number|String} cell The cell to remove, it can be:
     *
     * - An instance of {@link PMUI.grid.GridPanelCell GridPanelCell}, in this case this cell must belong to the
     * column.
     * - A Number, in this case it is interpreted as the index of the cell to remove.
     * - A String, in this case it is interpreted as the id of the cell to remove.
     * @chainable
     */
    GridPanelColumn.prototype.removeCell = function (cell) {
        var cellToRemove;

        if (cell instanceof PMUI.grid.GridPanelCell) {
            cellToRemove = cell;
        } else if (typeof cell === 'number') {
            cellToRemove = this.cells.get(cell);
        } else if (typeof cell === 'string') {
            cellToRemove = this.cells.find("id", cell);
        }

        if (cellToRemove) {
            cellToRemove.column = null;
            this.cells.remove(cellToRemove);
        }

        return this;
    };
    /**
     * Clears all the cells of the column.
     * @chainable
     */
    GridPanelColumn.prototype.clearCells = function () {
        this.cells.clear();
        while (this.cells.getSize() > 0) {
            this.removeCell(0);
        }
        return this;
    };
    /**
     * Defines the event for the object.
     * @chainable
     */
    GridPanelColumn.prototype.defineEvents = function () {
        var that = this;

        this.removeEvents().eventsDefined = true;
        if (this.html) {
            this.addEvent('click').listen(this.html, function () {
                if (that.sortable) {
                    that.sortOrder = that.sortOrder === 'asc' ? 'desc' : 'asc';
                    if (typeof that.onSort === 'function') {
                        that.onSort(that.sortOrder);
                    }
                }
            });
        }
        return this;
    };
    /**
     * Creates the html for the object.
     * @return {HTMLElement}
     */
    GridPanelColumn.prototype.createHTML = function () {
        var contentTitle,
            sortIcon;

        if (this.html) {
            return this.html;
        }
        GridPanelColumn.superclass.prototype.createHTML.call(this);
        sortIcon = PMUI.createHTMLElement('span');
        sortIcon.className = 'pmui-grid-sort-icon';
        contentTitle = PMUI.createHTMLElement('span');
        contentTitle.className = 'pmui-gridpanelColumn-title';
        this.contentTitle = contentTitle;
        this.sortIcon = sortIcon;
        this.html.appendChild(contentTitle);
        this.setTitle(this.title)
            .setSortable(this.sortable)
            .setWidth(this.width)
            .setAlignmentTitle(this.alignmentTitle);
        return this.html;
    };

    GridPanelColumn.prototype.setWidth = function (width) {
        var finalWidth,
            i,
            cellsLength,
            cells;

        if (typeof width === 'number') {
            this.width = width;
        } else if (/^\d+(\.\d+)?px$/.test(width)) {
            this.width = parseInt(width, 10);
        } else if (/^\d+(\.\d+)?%$/.test(width)) {
            this.width = width;
        } else if (/^\d+(\.\d+)?em$/.test(width)) {
            this.width = width;
        } else if (width === 'auto') {
            this.width = width;
        } else {
            throw new Error('setWidth: width param is not a number');
        }

        if (typeof this.width === 'string') {
            finalWidth = 'auto';
        } else {
            finalWidth = this.width + 'px';
        }

        if (this.html) {
            this.contentTitle.style.minWidth = finalWidth;
        }
        cellsLength = (cells = this.cells.asArray()).length;
        for (i = 0; i < cellsLength; i += 1) {
            cells[i].setWidth(finalWidth);
        }
        return this;
    };

    GridPanelColumn.prototype.applyStyle = function () {
        if (this.html) {
            this.style.applyStyle();

            this.style.addProperties({
                display: this.visible ? this.display : "none",
                position: this.positionMode,
                left: this.x,
                top: this.y,
                width: (typeof this.width === 'string') ? this.width : 'auto',
                height: this.height,
                zIndex: this.zOrder
            });
        }
        return this;
    };

    PMUI.extendNamespace("PMUI.grid.GridPanelColumn", GridPanelColumn);
    if (typeof exports !== 'undefined') {
        module.exports = GridPanelColumn;
    }
}());
(function () {
    /**
     * @class PMUI.grid.GridPanelRow
     * Class that defines a row to be used in a {@link PMUI.grid.GridPanel GridPanel}.
     * The single class is not very useful alone, it should be used with a {@link PMUI.grid.GridPanel GridPanel}.
     * @extends {PMUI.core.Element}
     *
     * Usage example:
     *
     *      @example
     *      var row = new PMUI.grid.GridPanelRow({
     *              data: {
     *                  name: "John",
     *                  lastname: "McAllister"
     *              }
     *          }),
     *          grid = new PMUI.grid.GridPanel({
     *             pageSize: 6,
     *             columns:[
     *                 {
     *                     title:'columna1',
     *                     dataType:'string',
     *                     columnData: "name"
     *                 },
     *                 {
     *                     title:'columna2',
     *                     dataType:'number',
     *                     width : 150,
     *                     columnData: "lastName",
     *                     sortable: true
     *                 }
     *             ],
     *             items: [
     *                 row
     *             ]
     *          });
     *
     *      document.body.appendChild(grid.getHTML());
     *
     * @constructor
     * Creates a new instance of the GridPanelRow class.
     * @param {Object} settings A JSON object with the config options.
     *
     * @cfg {PMUI.data.DataField|Object} [data={}] A {@link PMUI.data.DataField DataField} or a JSON object with the
     * data for the GridPanelRow.
     * @cfg {PMUI.grid.GridPanel} [parent=null] The {@link PMUI.grid.GridPanel GridPanel} the GridPanelRow belongs to.
     */
    var GridPanelRow = function (settings) {
        GridPanelRow.superclass.call(this, jQuery.extend(settings, {elementTag: "tr"}));
        /**
         * An ArrayList that contains the row's cells ({@link PMUI.grid.GridPanelCell GridPanelCell}).
         * @type {PMUI.util.ArrayList}
         * @private
         */
        this.cells = null;
        /**
         * The parent GridPanel of the GridPanelRow.
         * @type {PMUI.grid.GridPanel GridPanel}
         * @readonly
         */
        this.parent = null;
        /**
         * The data that belongs to the GridPanelRow.
         * @type {PMUI.data.DataField}
         * @private
         */
        this.data = null;

        this.onClick = null;

        this.selected = null;
        this.onSelect = null;
        GridPanelRow.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Element', GridPanelRow);
    /**
     * The class family.
     * @type {String}
     */
    GridPanelRow.prototype.family = 'GridPanelRow';
    /**
     * The object's family.
     * @type {String}
     */
    GridPanelRow.prototype.type = 'GridPanelRow';
    /**
     * Initialize the object.
     * @param  {Object} settings A JSON object with the config options.
     * @private
     */
    GridPanelRow.prototype.init = function (settings) {
        var defaults = {
            data: {},
            parent: null,
            onClick: null,
            onSelect: function () {
            }
        };

        jQuery.extend(true, defaults, settings);

        this.cells = new PMUI.util.ArrayList();
        this.data = new PMUI.data.DataField();

        this.setData(defaults.data)
            .setParent(defaults.parent)
            .setOnClick(defaults.onClick)
            .setOnSelectCallback(defaults.onSelect);
    };
    /**
     * Set the data for the object.
     * @param {PMUI.data.DataField|Object} dataç
     * @chainable
     */
    GridPanelRow.prototype.setData = function (data) {
        var key;

        if (data instanceof PMUI.data.DataField) {
            this.data = data;
        } else if (typeof data === 'object') {
            this.data.clear();
            for (key in data) {
                this.data.addAttribute(key, data[key]);
            }
        } else {
            throw new Error("setData(): it only accepts a JSON object o an instance of PMUI.data.DataField as parameter.");
        }

        if (this.html) {
            this.setCells();
        }

        return this;
    };
    /**
     * Set the parent grid for the object.
     * @param {PMUI.grid.GridPanel} parent
     * @chainable
     */
    GridPanelRow.prototype.setParent = function (parent) {
        if (parent) {
            if (parent instanceof PMUI.grid.GridPanel) {
                this.parent = parent;
            } else {
                throw new Error("setParent() method only accepts a object [PMUI.panel.GridPanel] as parameter.");
            }
        }
        return this;
    };
    /**
     * Returns the object's parent grid.
     * @return {PMUI.grid.GridPanel}
     */
    GridPanelRow.prototype.getParent = function () {
        return this.parent;
    };
    /**
     * Updates the width for each row's cell using the respective column's width.
     * @chainable
     */
    GridPanelRow.prototype.updateCellsWidth = function () {
        var i,
            cells,
            cellsLength;

        cellsLength = (cells = this.cells.asArray()).length;
        for (i = 0; i < cellsLength; i += 1) {
            cells[i].setWidth(cells[i].column.width);
        }
        return this;
    };
    /**
     * Paints a cell in the row.
     * @param  {PMUI.grid.GridPanelCell} cell
     * @chainable
     * @private
     */
    GridPanelRow.prototype.paintCell = function (cell) {
        if (this.html) {
            this.html.appendChild(cell.getHTML());
        }
        return this;
    };
    /**
     * Paints all cells in the row.
     * @chainable
     * @private
     */
    GridPanelRow.prototype.paintCells = function () {
        var i,
            cells = this.cells.asArray();

        for (i = 0; i < cells.length; i += 1) {
            this.paintCell(cells[i]);
        }
        return this;
    };
    /**
     * Add a single cell into the row.
     * @param {PMUI.grid.GridPanelColumn|null} [column=null] It can be:
     *
     * - null, in this case an empty cell will be added at the end of the row.
     * - A {@link PMUI.grid.GridPanelColumn GridPanelColumn}, in this case the cell will be created using the settings
     * the parameter has for its cells. Also the cell will be associated to the column.
     */
    GridPanelRow.prototype.addCell = function (column) {
        var data,
            columnData,
            cell,
            cellIndex,
            buttonLabel,
            buttonTooltip,
            content,
            that = this;

        if (!(column === null || column instanceof PMUI.grid.GridPanelColumn)) {
            throw new Error("addCell(): The parameter must be an instance of PMUI.grid.GridPanel o null.");
        }
        data = this.data.getRecord();
        cellIndex = this.parent.getColumns().length;
        cell = new PMUI.grid.GridPanelCell({
            columnIndex: cellIndex,
            row: this,
            column: column || null,
            visible: (column && column.visible) && true,
            width: (column && column.width) || "auto"
        });
        if (column) {
            columnData = column.getColumnData();
            if (column.dataType === 'button') {
                buttonLabel = column.buttonLabel;
                buttonTooltip = column.buttonTooltip;
                content = new PMUI.ui.Button({
                    style: column.getUsableButtonStyle(),
                    text: typeof buttonLabel === 'function' ? buttonLabel(this, data) : (buttonLabel || ""),
                    messageTooltip: (typeof buttonTooltip === 'function' ? buttonTooltip.call(cell, cell, data)
                        : (buttonTooltip || "")),
                    handler: (function (handler) {
                        return function () {
                            handler(that, that.parent);
                        };
                    }(column.getOnButtonClick()))
                });
            } else if (column.dataType === 'index') {
                content = this.parent.getItemIndex(this) + 1;
            } else if (columnData) {
                if (typeof columnData === 'function') {
                    content = columnData.call(this, data);
                } else {
                    content = this.data.getAttribute(columnData);
                }
            }

            cell.style.addClasses(["pmui-gridpanelcell-" + column.dataType]);
        } else {
            content = null;
        }
        cell.setContent(content);
        this.cells.insert(cell);
        column.addCell(cell);
        this.paintCell(cell);
        return this;
    };
    /**
     * @method setCells
     * Build the cells for the row using its parent {@link PMUI.grid.GridPanel#property-columns GridPanel's columns} to
     * retrieve the data from the odject's {@link PMUI.grid.GridPanelRow#property-data data}.
     * @chainable
     */
    GridPanelRow.prototype.setCells = function () {
        var i,
            columns;

        this.clearCells();
        if (this.parent) {
            columns = this.parent.getColumns();
            for (i = 0; i < columns.length; i += 1) {
                this.addCell(columns[i]);
            }
        }

        return this;
    };
    /**
     * Returns the data that belongs to the object.
     * @return {Object} A JSON object with the data.
     */
    GridPanelRow.prototype.getData = function () {
        return this.data.getRecord();
    };
    /**
     * Returns the object's cells.
     * @return {Array} An array in which every item is a object's {@link PMUI.grid.GridPanelCell cell}.
     */
    GridPanelRow.prototype.getCells = function () {
        return this.cells.asArray().slice(0);
    };
    /**
     * Clear the cells from the object.
     * @chainable
     */
    GridPanelRow.prototype.clearCells = function () {
        var i;
        while (this.cells.getSize()) {
            this.removeCell(0);
        }
        return this;
    };
    /**
     * Remove a cell from the object.
     * @param  {PMUI.grid.GridPanelCell|Number|String} cell It can be:
     *
     * - A instance of {@link PMUI.grid.GridPanelCell GridPanelCell}, in this case it must be a cell that belongs to
     * the object.
     * - A Number, in this case the cell with that index will be removed.
     * - A String, in this case the cell with that id will be removed.
     * @chainable
     */
    GridPanelRow.prototype.removeCell = function (cell) {
        var cellToRemove,
            column;

        if (cell instanceof PMUI.grid.GridPanelCell) {
            cellToRemove = cell;
        } else if (typeof cell === 'number') {
            cellToRemove = this.cells.get(cell);
        } else if (typeof cell === 'string') {
            cellToRemove = this.cells.find("id", cell)
        } else {
            throw new Error("removeCell(): This method only accepts a number or a string or a instance of " +
                "PMUI.grid.GridPanelCell as parameter");
        }

        if (cellToRemove) {
            cellToRemove.setColumnIndex(null);
            cellToRemove.row = null;
            jQuery(cellToRemove.html).remove();
            column = cellToRemove.getColumn();
            if (column) {
                column.removeCell(cellToRemove);
            }
            cellToRemove.column = null;
            this.cells.remove(cellToRemove);
        }

        return this;
    };
    /**
     * Attach the listeners for the object and every of its children.
     * @chainable
     */
    GridPanelRow.prototype.defineEvents = function () {
        var that = this;

        this.removeEvents().eventsDefined = true;
        if (this.html) {
            this.addEvent('click').listen(this.html, function (e) {
                e.preventDefault();
                if (typeof that.onClick === 'function') {
                    that.onClick(that, that.getData());
                }
                if (that.parent.selectable) {
                    if (!that.selected) {
                        that.selectRow();
                    }
                }
                if (that.parent && typeof that.parent.onRowClick === 'function') {
                    that.parent.onRowClick(that, that.getData());
                }
            });
        }
        return this;
    };
    /**
     * Creates the object's HTML.
     * @return {HTMLElement}
     */
    GridPanelRow.prototype.createHTML = function () {
        if (this.html) {
            return this.html;
        }
        GridPanelRow.superclass.prototype.createHTML.call(this);
        this.paintCells()
            .defineEvents();
        return this.html;
    };
    /**
     * [setOnClick description]
     * @param {[type]} onClick [description]
     */
    GridPanelRow.prototype.setOnClick = function (onClick) {
        if (typeof  onClick !== 'function' && onClick !== null) {
            throw new Error('setOnClick(): the parameter is no valid, should be a function');
        }
        this.onClick = onClick;
        return this;
    };
    /**
     * [selectRow description]
     * @return {[type]} [description]
     */
    GridPanelRow.prototype.selectRow = function () {
        this.selected = true;
        this.style.addClasses(['pmui-active-row']);
        if (typeof this.onSelect === 'function') {
            this.onSelect();
        }
        return this;
    };
    /**
     * [isSelectedRow description]
     * @return {Boolean} [description]
     */
    GridPanelRow.prototype.isSelectedRow = function () {
        return this.selected;
    };
    /**
     * [deselectRow description]
     * @return {[type]} [description]
     */
    GridPanelRow.prototype.deselectRow = function () {
        if (this.onSelect) {
            this.selected = false;
            this.style.removeClasses(['pmui-active-row']);
        }

        return this;
    };
    /**
     * [setOnSelectRowCallback description]
     * @param {Function} callback [description]
     */
    GridPanelRow.prototype.setOnSelectCallback = function (callback) {
        if (typeof callback === 'function' || callback == null) {
            this.onSelect = callback;
        } else {
            throw new Error("setOnSelectCallback(): The parameter is not a function.");
        }
        return this;
    };

    PMUI.extendNamespace('PMUI.grid.GridPanelRow', GridPanelRow);

    if (typeof exports !== 'undefined') {
        module.exports = GridPanelRow;
    }
}());
(function () {
    /**
     * @class PMUI.grid.GridPanelCell
     * Class that represent a cell to be used in instances of {@link PMUI.grid.GridPanelRow GridPanelRow}.
     * It's not very useful to instantiate this class to use it alone. Most of cases you won't need to instantiate it,
     * the {@link PMUI.grid.GridPanelRow GridPanelRow} creates its own cells objects automatically.
     * {@link PMUI.grid.GridPanelRow GridPanelRow}.
     * @extends {PMUI.core.Element}
     *
     *    Usage example:
     *
     *        @example
     *        var cell = new PMUI.grid.GridPanelCell({
     *        content: "This is the cell content"
     *        });
     *
     *        document.body.appendChild(cell.getHTML());
     *
     * @constructor
     * Creates a new instance of the GridPanelCell class.
     * @param {Object} settings A JSON object with the config options.
     *
     * @cfg {String|Number|PMUI.core.Element|null} [content=null] The content to be displayed in the cell, in case to
     * be null the cells has no content.
     * @cfg {Number} [columnIndex=null] The column index the cell belongs to.
     * @cfg {PMUI.grid.GridPanelRow} [row=null] The row the cell belongs to.
     * @cfg {PMUI.grid.GridPanelColumn} [column=null] The column the cell belongs to.
     * @cfg {String} [alignment="center"] A string that specifies the alignment for the cell content. The accepted
     * values are: "center", "left", "right".
     * @cfg {Boolean} [disabled=false] If the cell will be disabled or not.
     */
    var GridPanelCell = function (settings) {
        GridPanelCell.superclass.call(this, jQuery.extend(settings, {elementTag: "td"}));
        /**
         * The content for the cell.
         * @type {PMUI.core.Element|String|Number}
         */
        this.content = null;
        /**
         * The position index for the cell, relative to its parent row.
         * @type {Number}
         */
        this.columnIndex = null;
        /**
         * The row the cell belongs to.
         * @type {PMUI.grid.GridPanelRow}
         */
        this.row = null;
        /**
         * The column the cell appertains to.
         * @type {PMUI.grid.GridPanelColumn}
         */
        this.column = null;
        /**
         * The alignment to be applied to the cell content.
         * @type {String}
         */
        this.alignment = null;
        this.contentSpan = null;
        /**
         * If the cell content is disabled or not.
         * @type {Boolean}
         */
        this.disabled = null;
        GridPanelCell.prototype.init.call(this, settings);
    };
    PMUI.inheritFrom("PMUI.core.Element", GridPanelCell);
    /**
     * The object's type.
     * @type {String}
     */
    GridPanelCell.prototype.type = "GridPanelCell";
    /**
     * The class family.
     * @type {String}
     */
    GridPanelCell.prototype.family = "grid";
    /**
     * Initializes the object.
     * @param  {Object} settings A JSON object with the config options.
     * @private
     */
    GridPanelCell.prototype.init = function (settings) {
        var defaults = {
            content: null,
            columnIndex: null,
            row: null,
            column: null,
            alignment: "center",
            disabled: false
        };
        jQuery.extend(true, defaults, settings);

        this.row = defaults.row;
        this.column = defaults.column;
        this.eventsDefined = false;

        if (defaults.disabled) {
            this.disable();
        } else {
            this.enable();
        }

        this.setAlignment(defaults.alignment)
            .setContent(defaults.content)
            .setColumnIndex(defaults.columnIndex);
    };
    /**
     * Sets the alignment for the content in the cell.
     * @param {String} alignment The accepted values are: "center", "left", "right".
     * @chainable
     */
    GridPanelCell.prototype.setAlignment = function (alignment) {
        if (alignment === 'center' || alignment === 'left' || alignment === 'right') {
            this.alignment = alignment;
            if (this.html && this.contentSpan) {
                this.contentSpan.style.textAlign = this.alignment;
                this.style.addProperties({'text-align': this.alignment});
            }
        } else {
            throw new Error('setAlignment(): This method only accepts one of the following values: '
                + '"canter", "left", "right"');
        }
        return this;
    };
    /**
     * Returns the cell's content alignment.
     * @return {String}
     */
    GridPanelCell.prototype.getAlignment = function () {
        return this.alignment;
    };
    /**
     * Sets the content for the cell.
     * @param {String|Number|PMUI.core.Element|null} content The content to be displayed in the cell, in case to
     * be null the cells has no content.
     * @chainable
     */
    GridPanelCell.prototype.setContent = function (content) {
        var type,
            visibleContent;

        this.content = content;

        if (content instanceof PMUI.core.Element) {
            visibleContent = content.getHTML();
        } else if (typeof content === 'number' || typeof content === 'string') {
            visibleContent = document.createTextNode(content);
        } else if (content === null || content === undefined) {
            visibleContent = undefined;
        } else {
            throw new Error("setContent(): invalid parameter, it should be a Number, String or an instance of PMUI.core.Element.");
        }

        if (this.html) {
            jQuery(this.contentSpan).empty();
            if (visibleContent) {
                this.contentSpan.appendChild(visibleContent);
            } else {
                this.contentSpan.innerHTML = '&nbsp;';
            }
            if (this.disabled) {
                this.disable();
            } else {
                this.enable();
            }
        }

        return this;
    };
    /**
     * Returns the column the cell belongs to.
     * @return {PMUI.grid.GridPanelColumn}
     */
    GridPanelCell.prototype.getColumn = function () {
        return this.column;
    };
    /**
     * Returns the row the cell belongs to.
     * @return {PMUI.grid.GridPanelRow}
     */
    GridPanelCell.prototype.getRow = function () {
        return this.row;
    };
    /**
     * Returns the cell's content.
     * @return {String|Number|PMUI.core.Element|null}
     */
    GridPanelCell.prototype.getContent = function () {
        return this.content;
    };
    /**
     * Sets the index of the column the cell belongs to.
     * @param {Number} columnIndex
     */
    GridPanelCell.prototype.setColumnIndex = function (columnIndex) {
        this.columnIndex = columnIndex
        return this;
    };
    /**
     * Returns the index of the column the cell belongs to.
     * @return {Number}
     */
    GridPanelCell.prototype.getColumnIndex = function () {
        return this.columnIndex;
    };
    /**
     * Defines the events for the object.
     * @chainable
     */
    GridPanelCell.prototype.defineEvents = function () {
        this.eventsDefined = true;
        if (this.content instanceof PMUI.core.Element) {
            this.content.defineEvents();
        }
        return this;
    };
    /**
     * Creates the object's html.
     * @return {HTMLElement}
     */
    GridPanelCell.prototype.createHTML = function () {
        var type,
            contentSpan;

        if (this.html) {
            return this.html;
        }

        GridPanelCell.superclass.prototype.createHTML.call(this);
        contentSpan = PMUI.createHTMLElement('span');
        contentSpan.className = 'pmui-gridpanelcell-content';
        this.contentSpan = contentSpan;
        this.html.appendChild(this.contentSpan);
        this.setAlignment(this.alignment)
            .setContent(this.content)
            .setWidth(this.width)
            .defineEvents();
        return this.html;
    };

    GridPanelCell.prototype.setWidth = function (width) {
        if (typeof width === 'number') {
            this.width = width;
        } else if (/^\d+(\.\d+)?px$/.test(width)) {
            this.width = parseInt(width, 10);
        } else if (/^\d+(\.\d+)?%$/.test(width)) {
            this.width = width;
        } else if (/^\d+(\.\d+)?em$/.test(width)) {
            this.width = width;
        } else if (width === 'auto') {
            this.width = width;
        } else {
            throw new Error('setWidth: width param is not a number');
        }

        if (this.html) {
            if (typeof this.width === 'string') {
                this.contentSpan.style.width = 'auto';
            } else {
                this.contentSpan.style.width = this.width + 'px';
            }
        }
        return this;
    };

    GridPanelCell.prototype.applyStyle = function () {
        if (this.html) {
            this.style.applyStyle();

            this.style.addProperties({
                display: this.visible ? this.display : "none",
                position: this.positionMode,
                left: this.x,
                top: this.y,
                width: (typeof this.width === 'string') ? this.width : 'auto',
                height: this.height,
                zIndex: this.zOrder
            });
        }
        return this;
    };
    /**
     * Enables the cell's content.
     * @chainable
     */
    GridPanelCell.prototype.enable = function () {
        this.disabled = false;
        if (this.content && this.content.enable) {
            this.content.enable();
        }
        return this;
    };
    /**
     * Disables the cell's content.
     * @chainable
     */
    GridPanelCell.prototype.disable = function () {
        this.disabled = true;
        if (this.content && this.content.disable) {
            this.content.disable();
        }
        return this;
    };
    PMUI.extendNamespace("PMUI.grid.GridPanelCell", GridPanelCell);

    if (typeof exports !== 'undefined') {
        module.exports = GridPanelCell;
    }

}());
(function () {
    var ControlGrid = function (settings) {
        ControlGrid.superclass.call(this, jQuery.extend(true, settings, {
            factory: {
                products: {
                    "GridControlRow": PMUI.grid.GridControlRow
                },
                defaultProduct: "GridControlRow"
            }
        }));
        this.columnsFactory = new PMUI.util.Factory();
        this.dependencies = {};
        ControlGrid.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.grid.GridPanel', ControlGrid);

    ControlGrid.prototype.init = function (settings) {
        var defaults = {
            allowedCells: {
                'text': PMUI.grid.TextControlColumn,
                'dropdownlist': PMUI.grid.DropDownListControlColumn,
                'checkboxgroup': PMUI.grid.CheckBoxGroupColumn,
                'number': PMUI.grid.NumberColumn
            }
        };

        jQuery.extend(true, defaults, settings);

        this.setAllowedCells(defaults.allowedCells)
            .setColumns(defaults.columns);
    };

    ControlGrid.prototype.setAllowedCells = function (allowedCells) {
        this.columnsFactory.clearProducts()
            .setProducts(allowedCells);
        return this;
    };

    ControlGrid.prototype.getColumn = function (column) {
        var the_column = null;
        if (typeof column === 'string') {
            the_column = this.columns.find('name', column);
        } else if (typeof column === 'number') {
            the_column = this.columns.get(column);
        }
        return the_column;
    };

    ControlGrid.prototype.setColumns = function (columns) {
        if (this.columnsFactory) {
            ControlGrid.superclass.prototype.setColumns.call(this, columns);
        }
        return this;
    };

    ControlGrid.prototype.removeColumn = function (column) {
        ControlGrid.superclass.prototype.removeColumn.call(this, column);
        return this.updateDependencies();
    };

    ControlGrid.prototype.updateDependencies = function () {
        var dependencies = {},
            i,
            j,
            columns = this.columns.asArray(),
            dependent,
            dependents;

        for (i = 0; i < columns.length; i += 1) {
            dependents = columns[i].dependentCols;
            for (j = 0; j < dependents.length; j += 1) {
                if (this.getColumn(dependents[j])) {
                    if (!dependencies[dependents[j]]) {
                        dependencies[dependents[j]] = [];
                    }
                    dependencies[dependents[j]].push(columns[i]);
                }
            }
        }
        this.dependencies = dependencies;
        return this;
    };

    ControlGrid.prototype.addColumn = function (column) {
        var newColumn;

        if (typeof column === 'object') {
            column.grid = this;
            newColumn = this.columnsFactory.make(column);
        } else {
            throw new Error('addColumn(): invalid column to add');
        }

        if (newColumn) {
            this.columns.insert(newColumn);
            if (this.dom.thead) {
                this.dom.thead.appendChild(newColumn.getHTML());
            }
        }
        this.updateDependencies();
        return this;
    };

    /**
     * Disables one or all the columns.
     * @param {String|Number|PMUI.grid.GridPanelColumn} [column] A criteria to select the column that will be
     * disabled. It supports the following data types:
     *
     * - String, in this case the parameter is used as the column's id.
     * - Number, in this case the parameter is used as the column's index.
     * - An instance of {@link PMUI.grid.GridPanelColumn GridPanelColumn}, in this case the supplied object must be
     * a child of the grid.
     * - [no value], since this parameter is optional, you can skip it. In this case all the columns in the grid will
     * be disabled.
     * @chainable
     */
    ControlGrid.prototype.disableColumn = function (column) {
        var i,
            targetColumn = this.getColumns(column);

        for (i = 0; i < targetColumn.length; i += 1) {
            targetColumn[i].disable();
        }
        return this;
    };
    /**
     * [disableColumn description]
     * @param  {[type]} column [description]
     * @return {[type]}        [description]
     */
    ControlGrid.prototype.enableColumn = function (column) {
        var i,
            targetColumn = this.getColumns(column);

        for (i = 0; i < targetColumn.length; i += 1) {
            targetColumn[i].enable();
        }
        return this;
    };

    ControlGrid.prototype.isValid = function () {
        var i,
            rows,
            valid = true;

        rows = this.getItems();
        for (i = 0; i < rows.length; i += 1) {
            valid = valid && rows[i].isValid();
            if (!valid) {
                return valid;
            }
        }
        return valid;
    };

    ControlGrid.prototype.fireDependency = function () {
    };

    PMUI.extendNamespace('PMUI.grid.ControlGrid', ControlGrid);
}());
(function () {
    var GridControlCell = function (settings) {
        this.controls = [];
        GridControlCell.superclass.call(this, settings);
        /**
         * @property {Boolean} [validAtChange=true]
         * If the validation must be executed every time the field's value changes.
         */
        this.validAtChange = null;
        this.value = null;
        this.onChange = null;
        this.validAtChange = null;
        this.disable = null;
        GridControlCell.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.grid.GridPanelCell', GridControlCell);

    GridControlCell.prototype.init = function (settings) {
        var defaults = {
            onChange: null,
            validAtChange: true,
            disabled: false,
            value: ""
        };

        jQuery.extend(true, defaults, settings);
        this.initControls();

        this.setOnChangeHandler(defaults.onChange)
            .setValidAtChange(defaults.validAtChange)
            .setValue(defaults.value);

        if (defaults.disabled) {
            this.disable();
        } else {
            this.enable();
        }
    };
    /**
     * [setValueToControls description]
     * @param {[type]} value [description]
     */
    GridControlCell.prototype.setValueToControls = function (value) {
        var i;

        for (i = 0; i < this.controls.length; i += 1) {
            this.controls[i].setValue(value);
        }
        return this;
    };
    /**
     * [setValue description]
     * @param {[type]} value [description]
     */
    GridControlCell.prototype.setValue = function (value) {
        if (typeof value === 'number') {
            value = value.toString();
        }
        if (typeof value === 'string') {
            this.value = value;
        } else {
            throw new Error("The setValue() method only accepts string values!");
        }
        this.setValueToControls(value);
        return this;
    };
    /**
     * Turns on/off the validation when the field's value changes.
     * @param {Boolean} validAtChange
     * @chainable
     */
    GridControlCell.prototype.setValidAtChange = function (validAtChange) {
        this.validAtChange = !!validAtChange;
        return this;
    };
    /**
     * Sets the callback function to be called when the field's value changes.
     * @param {Function} handler
     * @chainable
     */
    GridControlCell.prototype.setOnChangeHandler = function (handler) {
        if (typeof handler === 'function' || handler === null) {
            this.onChange = handler;
        }

        return this;
    };

    GridControlCell.prototype.initControls = function () {
        return this;
    };

    GridControlCell.prototype.setValuesToControls = function (values) {
        return this;
    };

    GridControlCell.prototype.setContent = function (content) {
        return this.setValuesToControls(content);
    };

    /**
     * Update the field's value property from the controls
     * @protected
     * @chainable
     */
    GridControlCell.prototype.updateContentFromControls = function () {
        var value = '',
            i;

        for (i = 0; i < this.controls.length; i += 1) {
            value += ' ' + this.controls[i].getValue();
        }

        this.value = this.content = value.substr(1);

        return this;
    };
    /**
     * The onChange event handler constructor.
     * @private
     * @return {Function} The handler.
     */
    GridControlCell.prototype.onChangeHandler = function () {
        var that = this;

        return function () {
            var previousValue = that.content;
            that.updateContentFromControls();
            if (that.validAtChange) {
                that.isValid();
            }

            if (typeof that.onChange === 'function') {
                that.onChange(that.getContent(), previousValue);
            }

            if (that.column) {
                that.column.onChangeHandler(that, that.content, previousValue);
            }
        };
    };
    GridControlCell.prototype.hideMessage = function () {
        this.style.removeClasses(['pmui-error']);
        this.html.removeAttribute("title");
        try {
            jQuery(this.html).tooltip("destroy");
        } catch (e) {
        }
        return this;
    };
    GridControlCell.prototype.showMessage = function (message) {
        this.style.addClasses(['pmui-error']);
        this.html.setAttribute("title", "error");
        jQuery(this.html).tooltip({
            content: message,
            tooltipClass: 'pmui-cell-tooltip-error',
            position: {
                my: "left",
                at: "left bottom"
            }
        });
        jQuery(this.html).tooltip('open');
        return this;
    };
    /**
     * [evalRequired description]
     * @return {[type]} [description]
     */
    GridControlCell.prototype.evalRequired = function () {
        var valid = true, column = this.column;
        if (column) {
            if (column.required && this.value === "") {
                valid = false;
            }
        }
        if (valid) {
            this.hideMessage();
        } else {
            this.showMessage(column.requiredMessage);
        }
        return valid;
    };
    /**
     * [isValid description]
     * @return {Boolean} [description]
     */
    GridControlCell.prototype.isValid = function () {
        var valid = true,
            validator,
            validators;

        valid = valid && this.evalRequired();
        if (!valid) {
            return valid;
        }
        if (this.column) {
            validators = this.column.validators;
            for (validator in validators) {
                if (validators.hasOwnProperty(validator)) {
                    validators[validator].setParent(this);
                    valid = validators[validator].isValid();
                    if (!valid) {
                        return valid;
                    }
                }
            }
        }
        return valid;
    };
    /**
     * Attach the event listeners to the HTML element
     * @private
     * @chainable
     */
    GridControlCell.prototype.defineEvents = function () {
        var i;

        if (this.eventsDefined) {
            return this;
        }
        ;
        for (i = 0; i < this.controls.length; i += 1) {
            this.controls[i].setOnChangeHandler(this.onChangeHandler())
                .defineEvents();
        }
        this.eventsDefined = true;

        return this;
    };
    /**
     * @inheritdoc
     */
    GridControlCell.prototype.enable = function () {
        var i;

        for (i = 0; i < this.controls.length; i += 1) {
            this.controls[i].disable(false);
        }
        this.disabled = false;
        return this;
    };
    /**
     * @inheritdoc
     */
    GridControlCell.prototype.disable = function () {
        var i;

        for (i = 0; i < this.controls.length; i += 1) {
            this.controls[i].disable(true);
        }
        this.disabled = true;
        return this;
    };
    /**
     * [createHTML description]
     * @return {[type]} [description]
     */
    GridControlCell.prototype.createHTML = function () {
        var i;

        if (this.html) {
            return this.html;
        }
        GridControlCell.superclass.prototype.createHTML.call(this);
        for (i = 0; i < this.controls.length; i += 1) {
            this.contentSpan.appendChild(this.controls[i].getHTML());
        }

        this.defineEvents();
        return this.html;
    };

    PMUI.extendNamespace('PMUI.grid.GridControlCell', GridControlCell);
}());
(function () {
    var NumberCell = function (settings) {
        NumberCell.superclass.call(this, settings);

        NumberCell.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.grid.GridControlCell', NumberCell);

    NumberCell.prototype.init = function (settings) {
        var defaults = {
            placeholder: "",
            maxLength: 524288,
            mask: ["###,###.###"],
            onKeyUp: null,
            content: ''
        };

        jQuery.extend(true, defaults, settings);
        this.setPlaceholder(defaults.placeholder)
            .setMaxLength(defaults.maxLength)
            .setOnKeyUpHandler(defaults.onKeyUp)
            .setContent(defaults.content)
            .setMask(defaults.mask);
    };

    NumberCell.prototype.setOnKeyUpHandler = function (handler) {
        this.controls[0].setOnKeyUpHandler(handler);
        return this;
    };

    NumberCell.prototype.setPlaceholder = function (placeholder) {
        this.controls[0].setPlaceholder(placeholder);
        return this;
    };

    NumberCell.prototype.setMask = function (mask) {
        this.controls[0].setMask(mask);
        return this;
    };

    NumberCell.prototype.setMaxLength = function (maxLength) {
        this.controls[0].setMaxLength(maxLength);
        return this;
    };

    NumberCell.prototype.setReadOnly = function (value) {
        this.controls[0].setReadOnly(value);
        return this;
    };

    NumberCell.prototype.initControls = function () {
        this.controls[0] = new PMUI.control.NumberControl();
        return this;
    };

    NumberCell.prototype.setContent = function (content) {
        return this.setValuesToControls(content);
    };

    NumberCell.prototype.setValuesToControls = function (values) {
        if (this.controls.length) {
            this.controls[0].setValue(values);
            this.content = this.controls[0].getValue();
        }
        return this;
    };

    NumberCell.prototype.getPlaceholder = function () {
        return this.controls[0].getPlaceholder();
    };
    /**
     * [isReadOnly description]
     * @return {Boolean} [description]
     */
    NumberCell.prototype.isReadOnly = function () {
        return this.controls[0].isReadOnly();
    };
    /**
     * Switches on/off the value trimming for the field's value when it loses focus.
     * @param {Boolean} trimOnBlur Send true for switch on or false to switch off.
     * @chainable
     */
    PMUI.extendNamespace('PMUI.grid.NumberCell', NumberCell);

    if (typeof exports !== 'undefined') {
        module.exports = NumberCell;
    }
}());
(function () {
    var GridTextControlCell = function (settings) {
        GridTextControlCell.superclass.call(this, settings);
        GridTextControlCell.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.grid.GridControlCell', GridTextControlCell);

    GridTextControlCell.prototype.init = function (settings) {
        var defaults = {
            placeholder: "",
            maxLength: 0,
            trimOnBlur: true,
            readOnly: false
        };

        jQuery.extend(true, defaults, settings);

        this.setPlaceholder(defaults.placeholder)
            .setMaxLength(defaults.maxLength)
            .setTrimOnBlur(defaults.trimOnBlur)
            .setReadOnly(defaults.readOnly);
    };

    GridTextControlCell.prototype.initControls = function () {
        this.controls[0] = new PMUI.control.TextControl();
        return this;
    };

    GridTextControlCell.prototype.setValuesToControls = function (values) {
        return this;
    };

    GridTextControlCell.prototype.setContent = function (content) {
        return this.setValuesToControls(content);
    };
    /**
     * Sets the placeholder for the control. Note that this feature is only supported
     * by browsers which support the "placeholder" for input elements.
     * @param {String} placeholder
     * @chainable
     */
    GridTextControlCell.prototype.setReadOnly = function (readonly) {
        this.controls[0].setReadOnly(readonly);
        return this;
    };
    /**
     * [setPlaceholder description]
     * @param {[type]} placeholder [description]
     */
    GridTextControlCell.prototype.setPlaceholder = function (placeholder) {
        this.controls[0].setPlaceholder(placeholder);
        return this;
    };
    /**
     * [getPlaceholder description]
     * @return {[type]} [description]
     */
    GridTextControlCell.prototype.getPlaceholder = function () {
        return this.controls[0].getPlaceholder();
    };
    /**
     * [setMaxLength description]
     * @param {[type]} maxLength [description]
     */
    GridTextControlCell.prototype.setMaxLength = function (maxLength) {
        this.controls[0].setMaxLength(maxLength);
        return this;
    };
    /**
     * [getMaxLength description]
     * @return {[type]} [description]
     */
    GridTextControlCell.prototype.getMaxLength = function () {
        return this.controls[0].getMaxLength();
    };
    /**
     * [isReadOnly description]
     * @return {Boolean} [description]
     */
    GridTextControlCell.prototype.isReadOnly = function () {
        return this.controls[0].isReadOnly();
    };
    /**
     * Switches on/off the value trimming for the field's value when it loses focus.
     * @param {Boolean} trimOnBlur Send true for switch on or false to switch off.
     * @chainable
     */
    GridTextControlCell.prototype.setTrimOnBlur = function (trimOnBlur) {
        this.trimOnBlur = !!trimOnBlur;
        return this;
    };

    PMUI.extendNamespace('PMUI.grid.GridTextControlCell', GridTextControlCell);

    if (typeof exports !== 'undefined') {
        module.exports = GridTextControlCell;
    }
}());
(function () {
    var DropDownListControlCell = function (settings) {
        DropDownListControlCell.superclass.call(this, settings);
        DropDownListControlCell.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.grid.GridControlCell', DropDownListControlCell);

    DropDownListControlCell.prototype.init = function (settings) {
        var defaults = {
            options: [],
            value: null
        };

        jQuery.extend(true, defaults, settings);

        this.setOptions(defaults.options);

        if (defaults.value !== null) {
            this.setValue(defaults.value);
        }
    };

    DropDownListControlCell.prototype.initControls = function () {
        this.controls[0] = new PMUI.control.DropDownListControl();
        return this;
    };

    DropDownListControlCell.prototype.clearOptions = function () {
        this.controls[0].clearOptions();
        this.content = this.controls[0].getValue();
        return this;
    };

    DropDownListControlCell.prototype.enableDisableOption = function (disabled, option, group) {
        this.controls[0].enableDisableOption(disabled, option, group);
        return this;
    };

    DropDownListControlCell.prototype.disableOption = function (option, group) {
        return this.controls[0].enableDisableOption(true, option, group);
    };

    DropDownListControlCell.prototype.enableOption = function (option, group) {
        return this.controls[0].enableDisableOption(false, option, group);
    };

    DropDownListControlCell.prototype.removeOption = function (option, group) {
        this.controls[0].removeOption(option, group);
        return this;
    };

    DropDownListControlCell.prototype.addOptionGroup = function (optionGroup) {
        this.controls[0].addOptionGroup(optiongroup);
        return this;
    };

    DropDownListControlCell.prototype.addOption = function (option, group) {
        this.controls[0].addOption(option, group);
        return this;
    };

    DropDownListControlCell.prototype.getSelectedLabel = function () {
        return this.controls[0].getSelectedLabel();
    };

    DropDownListControlCell.prototype.valueExistsInOptions = function (value) {
        return this.controls[0].valueExistsInOptions(value);
    };

    DropDownListControlCell.prototype.getFirstAvailableOption = function () {
        return this.controls[0].getFirstAvailableOption();
    }

    DropDownListControlCell.prototype.setOptions = function (options) {
        this.controls[0].setOptions(options);
        return this;
    };

    DropDownListControlCell.prototype.getOptions = function (includeGroups) {
        return this.controls[0].getOptions(includeGroups);
    };

    DropDownListControlCell.prototype.setValuesToControls = function (values) {
        if (this.controls.length) {
            this.controls[0].setValue(values);
            this.content = this.controls[0].getValue();
        }
        return this;
    };

    DropDownListControlCell.prototype.getContent = function () {
        return this.controls[0].getValue();
    };

    PMUI.extendNamespace('PMUI.grid.DropDownListControlCell', DropDownListControlCell);
}());
(function () {
    var CheckBoxGroupControlCell = function (settings) {
        CheckBoxGroupControlCell.superclass.call(this, settings);
        /**
         * @property {String} controlPositioning The direction for the options to be added in the field.
         * @readonly
         */
        this.controlPositioning = null;
        /**
         * @property {Number} maxDirectionOptions The max number of options that can be in the current options
         * direction.
         * @readonly
         */
        this.maxDirectionOptions = null;
        /**
         * The status if the controls to be saved when the field is switch between enabled/disabled.
         * @type {Object}
         * @private
         */
        this.auxControlStates = {};
        CheckBoxGroupControlCell.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.grid.GridControlCell', CheckBoxGroupControlCell);

    CheckBoxGroupControlCell.prototype.init = function (settings) {
        var defaults = {
            options: [],
            controlPositioning: "vertical",
            maxDirectionOptions: 1,
            value: '[]'
        };

        jQuery.extend(true, defaults, settings);
        this.setOptions(defaults.options);
        this.setMaxDirectionOptions(defaults.maxDirectionOptions);
        this.setControlPositioning(defaults.controlPositioning);
    };


    CheckBoxGroupControlCell.prototype.enable = function () {
        var key,
            i,
            controlsLength = this.controls.length,
            controls = this.controls;

        CheckBoxGroupControlCell.superclass.prototype.enable.call(this);
        this.disabled = false;
        for (i = 0; i < controlsLength; i += 1) {
            controls[i].disable(this.auxControlStates[controls[i].id]);
        }
        return this;
    };

    CheckBoxGroupControlCell.prototype.setMaxDirectionOptions = function (max) {
        if (typeof max === 'number') {
            this.maxDirectionOptions = Math.floor(max);
            if (this.html) {
                this.setControlPositioning(this.controlPositioning);
            }
        } else {
            throw new Error("setMaxDirectionOptions(): it only accepts number values.");
        }

        return this;
    };

    CheckBoxGroupControlCell.prototype.setControlPositioning = function (positioning) {
        var errorMessage = "The setControlPositioning() method only accepts \"horizontal\" or \"vertical\" as value.",
            table,
            tbody,
            cell,
            row,
            i,
            column,
            rowIndex;

        if (typeof positioning === 'string') {
            if (!(positioning === 'horizontal' || positioning === 'vertical')) {
                return this;
            }
            this.controlPositioning = positioning;
            if (this.html && this.controls) {
                for (i = 0; i < this.controls.length; i += 1) {
                    jQuery(this.controls[i].getHTML()).detach();
                }
                $(this.contentSpan).empty();
                table = PMUI.createHTMLElement("table");
                table.className = 'pmui-field-control-table';
                tbody = PMUI.createHTMLElement("tbody");
                if (positioning === 'horizontal') {
                    row = PMUI.createHTMLElement("tr");
                    for (i = 0; i < this.controls.length; i += 1) {
                        cell = PMUI.createHTMLElement('td');
                        this.controls[i].getHTML();
                        this.controls[i].control.tabIndex = i;
                        cell.appendChild(this.controls[i].getHTML());
                        row.appendChild(cell);
                        if (this.maxDirectionOptions > 0 && (i + 1) % this.maxDirectionOptions === 0) {
                            tbody.appendChild(row);
                            row = PMUI.createHTMLElement("tr");
                        }
                    }
                    cell.style.textAlign = "start";
                    tbody.appendChild(row);
                } else {
                    column = 0;
                    for (i = 0; i < this.controls.length; i += 1) {
                        cell = PMUI.createHTMLElement('td');
                        this.controls[i].getHTML();
                        this.controls[i].control.tabIndex = i;
                        cell.appendChild(this.controls[i].getHTML());
                        rowIndex = this.maxDirectionOptions === 0 ? i : i % this.maxDirectionOptions;

                        row = jQuery(tbody).find('tr').eq(rowIndex).get(0);
                        if (!row) {
                            row = PMUI.createHTMLElement('tr');
                            tbody.appendChild(row);
                        }
                        cell.style.textAlign = "start";
                        row.appendChild(cell);
                    }
                }
                table.appendChild(tbody);
                this.contentSpan.appendChild(table);
            }
        }

        return this;
    };

    CheckBoxGroupControlCell.prototype.setOptions = function (options) {
        var i = 0;

        this.content = [];
        this.content = JSON.stringify(this.content);

        if (jQuery.isArray(options)) {
            for (i = 0; i < options.length; i += 1) {
                this.addOption(options[i]);
            }
        } else {
            throw new Error("setOptions(): the supplied argument must be an array.");
        }

        return this;
    };

    CheckBoxGroupControlCell.prototype.addOption = function (option) {
        var newOption;

        newOption = new PMUI.control.SelectableControl(jQuery.extend(true, option, {
            mode: 'checkbox',
            name: this.controls.length + 1,
            selected: option.selected
        }));

        if (newOption.isSelected()) {
            this.content = JSON.parse(this.content);
            this.content.push(newOption.getValue());
            this.content = JSON.stringify(this.content);
        }
        if (this.eventsDefined) {
            newOption.setOnChangeHandler(this.onChangeHandler()).getHTML();
            newOption.defineEvents();
        }
        this.auxControlStates[newOption.id] = newOption.disabled;
        this.controls.push(newOption);
        this.setControlPositioning(this.controlPositioning);

        return this;
    };

    CheckBoxGroupControlCell.prototype.removeOption = function (item) {
        var itemToRemove,
            i;

        if (item instanceof PMUI.control.SelectableControl) {
            for (i = 0; i < this.controls.length; i += 1) {
                if (this.controls[i] === item) {
                    itemToRemove = i;
                    break;
                }
            }
        } else if (typeof item === 'string') {
            for (i = 0; i < this.controls.length; i += 1) {
                if (this.controls[i].id === item) {
                    itemToRemove = this.controls[i];
                    break;
                }
            }
        } else {
            itemToRemove = item;
        }
        if (typeof itemToRemove === 'number') {
            itemToRemove = this.controls[itemToRemove];
            delete this.auxControlStates[itemToRemove.id];
            jQuery(itemToRemove.html).detach();
            this.controls.splice(itemToRemove, 1);
            this.setControlPositioning(this.controlPositioning);
        }
        return this;
    };

    CheckBoxGroupControlCell.prototype.clearOptions = function () {
        while (this.controls.length) {
            this.removeOption(0);
        }

        return this;
    };

    CheckBoxGroupControlCell.prototype.updateContentFromControls = function () {
        var value = [],
            i;

        for (i = 0; i < this.controls.length; i += 1) {
            if (this.controls[i].isSelected()) {
                value.push(this.controls[i].getValue());
            }
        }

        this.content = JSON.stringify(value);
        return this;
    };

    CheckBoxGroupControlCell.prototype.evalRequired = function () {
        var valid = true,
            value;

        value = JSON.parse(this.getContent());
        if (this.required && value.length === 0) {
            this.showMessage(this.requiredMessage, "error");
            valid = false;
        } else {
            this.hideMessage();
        }

        return valid;
    };


    CheckBoxGroupControlCell.prototype.disableOption = function (value) {

        var index = -1,
            i;

        if (this.html) {
            if (typeof value === 'string') {

                for (i = 0; i < this.controls.length; i += 1) {
                    if (this.controls[i].label === value) {
                        index = i;
                    }
                }
            } else {
                index = value;
            }
            if (index === -1) {
                throw new Error('the value send is not part of group CheckBox');
            } else {
                this.controls[index].disable(true);
            }
        }
        return this;
    };

    CheckBoxGroupControlCell.prototype.enableOption = function (value) {
        var index = -1,
            i;

        if (this.html) {
            if (typeof value === 'string') {

                for (i = 0; i < this.controls.length; i += 1) {
                    if (this.controls[i].label === value) {
                        index = i;
                    }
                }
            } else {
                index = value;
            }
            if (index === -1) {
                throw new Error('the value send is not part from group CheckBox');
            } else {

                this.controls[index].disable(false);
            }
        }
        return this;
    };

    CheckBoxGroupControlCell.prototype.createHTML = function () {
        if (this.html) {
            return this.html;
        }

        CheckBoxGroupControlCell.superclass.prototype.createHTML.call(this)

        this.setControlPositioning(this.controlPositioning);

        return this.html;
    };

    CheckBoxGroupControlCell.prototype.setValuesToControls = function (value) {
        var i,
            j,
            values,
            controls,
            controlsLength;

        if (value) {
            try {
                values = JSON.parse(value);
            } catch (e) {
                values = '[]';
            }

            controlsLength = (controls = this.controls.slice(0)).length;
            for (i = 0; i < controlsLength; i += 1) {
                controls[i].deselect();
            }
            for (i = 0; i < values.length; i += 1) {
                for (j = 0; j < controlsLength; j += 1) {
                    if (controls[j].getValue() === values[i]) {
                        controls[j].select();
                        controls.splice(j, 1);
                        controlsLength -= 1;
                        j -= 1;
                    }
                }
            }
            this.updateContentFromControls();
        }

        return this;
    };

    PMUI.extendNamespace('PMUI.grid.CheckBoxGroupControlCell', CheckBoxGroupControlCell);
}());
(function () {
    var GridControlColumn = function (settings) {
        this.cells = new PMUI.util.ArrayList();
        GridControlColumn.superclass.call(this, jQuery.extend(settings, {elementTag: "th"}));
        /**
         * The name for the column.
         * @type {String}
         */
        this.name = null;
        /**
         * The text to be shown in the column header.
         * @type {String}
         */
        this.title = null;
        /**
         * The grid the column belongs to.
         * @type {PMUI.grid.GridPanel}
         */
        this.grid = null;
        /**
         * The HTML element in which the cell content will be appended.
         * @type {HTMLElement}
         * @private
         */
        this.contentTitle = null;
        /**
         * [dependentCols description]
         * @type {Array}
         */
        this.dependentCols = [];
        /**
         * [onChange description]
         * @type {[type]}
         */
        this.onChange = null;
        /**
         * [dependencyHandler description]
         * @type {[type]}
         */
        this.dependencyHandler = null;
        /**
         * [disabled description]
         * @type {[type]}
         */
        this.disabled = null;
        /**
         * [validatorFactory description]
         * @type {[type]}
         */
        this.validatorFactory = null;
        /**
         * [validators description]
         * @type {Object}
         */
        this.validators = {};
        /**
         * [required description]
         * @type {[type]}
         */
        this.required = null;
        /**
         * [requiredMessage description]
         * @type {[type]}
         */
        this.requiredMessage = null;
        GridControlColumn.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Element', GridControlColumn);

    GridControlColumn.prototype.CELL = null;

    GridControlColumn.prototype.init = function (settings) {
        var defaults = {
            name: this.id,
            title: '[untitled]',
            grid: null,
            cells: [],
            dependentCols: [],
            onChange: null,
            dependencyHandler: null,
            disabled: false,
            validators: [],
            required: false,
            requiredMessage: 'This cell is required.',
            validatorFactory: new PMUI.form.ValidatorFactory()
        };

        jQuery.extend(true, defaults, settings);

        this.setName(defaults.name)
            .setTitle(defaults.title)
            .setGrid(defaults.grid)
            .setRequired(defaults.required)
            .setDependentCols(defaults.dependentCols)
            .setOnChangeHandler(defaults.onChange)
            .setDependencyHandler(defaults.dependencyHandler)
            .setValidatorFactory(defaults.validatorFactory)
            .setValidators(defaults.validators)
            .setRequiredMessage(defaults.requiredMessage);

        if (defaults.disabled) {
            this.disable();
        } else {
            this.enable();
        }
    };

    GridControlColumn.prototype.setRequiredMessage = function (requiredMessage) {
        if (typeof requiredMessage !== 'string') {
            throw new Error('setRequiredMessage(): The parameter must be a strng.');
        }
        this.requiredMessage = requiredMessage;
        return this;
    };

    GridControlColumn.prototype.setRequired = function (required) {
        this.required = !!required;
        return this;
    };

    GridControlColumn.prototype.setValidatorFactory = function (factory) {
        if (factory instanceof PMUI.util.Factory) {
            this.validatorFactory = factory;
        } else {
            this.validatorFactory = new PMUI.form.ValidatorFactory(factory);
        }
        return this;
    };

    GridControlColumn.prototype.addValidator = function (validator) {
        var newValidator;
        if (this.validatorFactory) {
            if (this.validatorFactory.isValidClass(validator) || this.validatorFactory.isValidName(validator.pmType)) {
                newValidator = this.validatorFactory.make(validator);
            } else {
                throw new Error('Invalid validator to add.');
            }
        }

        if (newValidator && newValidator instanceof PMUI.form.Validator) {
            this.validators[newValidator.type] = newValidator;
        }

        return this;
    };

    GridControlColumn.prototype.clearValidators = function () {
        var key;

        for (key in this.validators) {
            if (this.validators.hasOwnProperty(key)) {
                this.validators[key] = null;
                delete this.validators[key];
            }
        }

        return this;
    };

    GridControlColumn.prototype.setValidators = function (validators) {
        var i = 0;

        if (jQuery.isArray(validators)) {
            this.clearValidators();
            for (i = 0; i < validators.length; i += 1) {
                this.addValidator(validators[i]);
            }
        }
        return this;
    };

    GridControlColumn.prototype.getCells = function () {
        return this.cells.asArray().slice(0);
    };

    GridControlColumn.prototype.setName = function (name) {
        if (typeof name !== 'string') {
            throw new Error("setName(): The parameter must be a string.");
        }
        this.name = name;
        return this;
    };

    GridControlColumn.prototype.setDependencyHandler = function (handler) {
        if (!(typeof handler === 'function' || handler === null)) {
            throw new Error("setDependencyHandler(): The parameter must be a function or null.");
        }
        this.dependencyHandler = handler;
        return this;
    };

    GridControlColumn.prototype.onChangeHandler = function (cell, newValue, oldValue) {
        var i,
            dependentCols = this.dependentCols,
            grid = this.grid,
            dependentCell,
            col,
            row;

        cell.row.updateData(cell);
        if (typeof this.onChange === 'function') {
            this.onChange(cell, newValue, oldValue);
        }
        if (grid) {
            for (i = 0; i < dependentCols.length; i += 1) {
                col = grid.getColumn(dependentCols[i]);
                if (!col) {
                    this.removeDependentCol(dependentCols[i]);
                    continue;
                }
                dependentCell = cell.row.getCell(col);
                col.fireDependencyHandler(dependentCell);
            }
        }
        return this;
    };

    GridControlColumn.prototype.fireDependencyHandler = function (cell) {
        var grid = this.grid,
            dependsOf,
            obj = {},
            i,
            targetRow,
            targetCells,
            j;

        if (grid) {
            targetCells = cell ? [cell] : this.cells.asArray();
            for (j = 0; j < targetCells.length; j += 1) {
                cell = targetCells[j];
                targetRow = cell.row;
                dependsOf = grid.dependencies[this.name];
                for (i = 0; i < dependsOf.length; i += 1) {
                    obj[dependsOf[i].name] = targetRow.getCell(dependsOf[i]);
                }
                if (typeof this.dependencyHandler === 'function') {
                    this.dependencyHandler.call(cell, cell, obj);
                }
            }
        }
        return this;
    };

    GridControlColumn.prototype.enable = function () {
        var i,
            cells = this.getCells();

        for (i = 0; i < cells.length; i += 1) {
            cells[i].enable();
        }
        this.disabled = false;
        return this;
    };

    GridControlColumn.prototype.disable = function () {
        var i,
            cells = this.getCells();

        for (i = 0; i < cells.length; i += 1) {
            cells[i].disable();
        }
        this.disabled = true;
        return this;
    };

    GridControlColumn.prototype.setOnChangeHandler = function (handler) {
        if (!(typeof handler === 'function' || handler === null)) {
            throw new Error("setOnChangeHandler(): the parameter must be a function or null.");
        }
        this.onChange = handler;
        return this;
    };

    GridControlColumn.prototype.setDependentCols = function (dependentCols) {
        if (!jQuery.isArray(dependentCols)) {
            throw new Error("setDependentCols(): The parameter must be an array.");
        }
        this.dependentCols = dependentCols;
        if (this.grid) {
            this.grid.updateDependencies();
        }
        return this;
    };

    GridControlColumn.prototype.removeDependentCol = function (columnName) {
        var i;

        for (i = 0; this.dependentCols.length; i += 1) {
            if (this.dependentCols[i] === columnName) {
                this.dependentCols.splice(i, 1);
                i--;
            }
        }
        return this;
    };

    GridControlColumn.prototype.removeCell = function (cell) {
        var cellToRemove;

        if (cell instanceof PMUI.grid.GridControlCell) {
            cellToRemove = cell;
        } else if (typeof cell === 'number') {
            cellToRemove = this.cells.get(cell);
        } else if (typeof cell === 'string') {
            cellToRemove = this.cells.find("id", cell);
        }

        if (cellToRemove) {
            cellToRemove.column = null;
            this.cells.remove(cellToRemove);
        }

        return this;
    };

    GridControlColumn.prototype.clearCells = function () {
        this.cells.clear();
        while (this.cells.getSize() > 0) {
            this.removeCell(0);
        }
        return this;
    };

    GridControlColumn.prototype.addCell = function (cell) {
        if (!(cell instanceof PMUI.grid.GridControlCell)) {
            throw new Error("addCell(): The parameter must be an instance of PMUI.grid.GridControlCell.");
        }
        this.cells.insert(cell);
        return this;
    };

    GridControlColumn.prototype.setTitle = function (title) {
        this.title = title;
        if (this.html) {
            jQuery(this.contentTitle).empty();
            this.contentTitle.appendChild(document.createTextNode(title));
        }
        return this;
    };

    GridControlColumn.prototype.setGrid = function (grid) {
        if (!(grid instanceof PMUI.grid.ControlGrid)) {
            throw new Error('setGrid(): the grid property should be instance of [PMUI.grid.GridControl]');
        }
        this.grid = grid;
        this.grid.updateDependencies();
        return this;
    };

    GridControlColumn.prototype.getCellDefaultCfg = function () {
        return {
            disabled: this.disabled
        };
    };

    GridControlColumn.prototype.createCell = function () {
        var cell;

        if (!this.CELL instanceof PMUI.grid.GridControlCell) {
            throw new Error("The cell associated to the " + this.type + " is not valid!");
        }
        cell = new this.CELL(this.getCellDefaultCfg());
        this.addCell(cell);

        return cell;
    };

    GridControlColumn.prototype.createHTML = function () {
        var contentTitle,
            sortIcon;

        if (this.html) {
            return this.html;
        }
        GridControlColumn.superclass.prototype.createHTML.call(this);
        contentTitle = PMUI.createHTMLElement('span');
        contentTitle.className = 'pmui-gridpanelColumn-title';
        this.contentTitle = contentTitle;
        this.html.appendChild(contentTitle);
        this.setTitle(this.title);

        return this.html;
    };

    PMUI.extendNamespace('PMUI.grid.GridControlColumn', GridControlColumn);
}());
(function () {
    var NumberColumn = function (settings) {
        NumberColumn.superclass.call(this, settings);
        this.placeholder = null;
        this.maxLength = null;
        this.mask = null;
        NumberColumn.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.grid.GridControlColumn', NumberColumn);

    NumberColumn.prototype.CELL = PMUI.grid.NumberCell;

    NumberColumn.prototype.init = function (settings) {
        var defaults = {
            placeholder: "",
            maxLength: 524288,
            mask: "##############.###############",
            content: '',
            readOnly: false
        };

        jQuery.extend(true, defaults, settings);

        this.setMaxLength(defaults.maxLength)
            .setPlaceHolder(defaults.placeholder)
            .setReadOnly(defaults.readOnly)
            .setMask(defaults.mask);
    };

    NumberColumn.prototype.setMaxLength = function (input) {
        this.maxLength = input
        return this;
    };

    NumberColumn.prototype.setPlaceHolder = function (input) {
        this.placeholder = input;
        return this;
    };

    NumberColumn.prototype.setReadOnly = function (input) {
        this.readOnly = input;
        return this;
    };

    NumberColumn.prototype.setMask = function (mask) {
        this.mask = mask;
        return this;
    };

    NumberColumn.prototype.getCellDefaultCfg = function () {
        return jQuery.extend(true, NumberColumn.superclass.prototype.getCellDefaultCfg.call(this), {
            maxLength: this.maxLength,
            mask: this.mask,
            placeholder: this.placeholder,
            readOnly: this.readOnly
        });
    };

    PMUI.extendNamespace('PMUI.grid.NumberColumn', NumberColumn);
}());
(function () {
    var TextControlColumn = function (settings) {
        TextControlColumn.superclass.call(this, settings);
        this.maxLength = null;
        this.placeholder = null;
        this.trimOnBlur = null;
        this.readOnly = null;
        TextControlColumn.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.grid.GridControlColumn', TextControlColumn);

    TextControlColumn.prototype.CELL = PMUI.grid.GridTextControlCell;

    TextControlColumn.prototype.init = function (settings) {
        var defaults = {
            maxLength: null,
            placeholder: null,
            trimOnBlur: false,
            readOnly: false
        };

        jQuery.extend(true, defaults, settings);

        this.setMaxLength(defaults.maxLength)
            .setPlaceHolder(defaults.placeholder)
            .setTrimOnBlur(defaults.trimOnBlur)
            .setReadOnly(defaults.readOnly);
    };

    TextControlColumn.prototype.setMaxLength = function (input) {
        var i,
            cells,
            cellsLength;

        if (typeof input !== 'number') {
            throw new Error("setMaxLength(): the parameter must be a number.");
        }
        this.maxLength = input;
        cells = this.cells.asArray();
        cellsLength = cells.length;
        for (i = 0; i < cellsLength; i += 1) {
            cells[i].setMaxLength(this.maxLength);
        }
        return this;
    };
    TextControlColumn.prototype.setPlaceHolder = function (input) {
        var i,
            cells,
            cellsLength;

        if (typeof input !== 'string') {
            throw new Error("setPlaceHolder(): the parameter must be a string.");
        }
        this.placeholder = input;
        cells = this.cells.asArray();
        cellsLength = cells.length;
        for (i = 0; i < cellsLength; i += 1) {
            cells[i].setPlaceHolder(this.placeholder);
        }
        return this;
    };
    TextControlColumn.prototype.setTrimOnBlur = function (input) {
        var i,
            cells,
            cellsLength;

        this.trimOnBlur = !!input;
        cells = this.cells.asArray();
        cellsLength = cells.length;
        for (i = 0; i < cellsLength; i += 1) {
            cells[i].setTrimOnBlur(this.trimOnBlur);
        }
        return this;
    };
    TextControlColumn.prototype.setReadOnly = function (input) {
        var i,
            cells,
            cellsLength;

        this.readOnly = !!input;
        cells = this.cells.asArray();
        cellsLength = cells.length;
        for (i = 0; i < cellsLength; i += 1) {
            cells[i].setReadOnly(this.readOnly);
        }
        return this;
    };
    TextControlColumn.prototype.getCellDefaultCfg = function () {
        return jQuery.extend(true, TextControlColumn.superclass.prototype.getCellDefaultCfg.call(this), {
            maxLength: this.maxLength,
            trimOnBlur: this.trimOnBlur,
            placeholder: this.placeholder,
            readOnly: this.readOnly
        });
    };

    PMUI.extendNamespace('PMUI.grid.TextControlColumn', TextControlColumn);
}());
(function () {
    var DropDownListControlColumn = function (settings) {
        DropDownListControlColumn.superclass.call(this, settings);
        this.options = [];
        DropDownListControlColumn.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.grid.GridControlColumn', DropDownListControlColumn);

    DropDownListControlColumn.prototype.CELL = PMUI.grid.DropDownListControlCell;

    DropDownListControlColumn.prototype.init = function (settings) {
        var defaults = {
            options: []
        };

        jQuery.extend(true, defaults, settings);

        this.setOptions(defaults.options);
    };

    DropDownListControlColumn.prototype.setOptions = function (options) {
        var i,
            cells;

        if (!jQuery.isArray(options)) {
            throw new Error("setOptions(): the parameter must be an array.");
        }
        this.options = options;
        cells = this.cells.asArray();
        for (i = 0; i < cells.length; i += 1) {
            cells[i].setOptions(this.options);
        }
        return this;
    };

    DropDownListControlColumn.prototype.getCellDefaultCfg = function () {
        return jQuery.extend(true, DropDownListControlColumn.superclass.prototype.getCellDefaultCfg.call(this), {
            options: this.options
        });
    };

    PMUI.extendNamespace('PMUI.grid.DropDownListControlColumn', DropDownListControlColumn);
}());
(function () {
    var CheckBoxGroupColumn = function (settings) {
        CheckBoxGroupColumn.superclass.call(this, settings);
        this.controlPositioning = null;
        this.maxDirectionOptions = null;
        this.options = [];
        CheckBoxGroupColumn.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.grid.GridControlColumn', CheckBoxGroupColumn);

    CheckBoxGroupColumn.prototype.CELL = PMUI.grid.CheckBoxGroupControlCell;

    CheckBoxGroupColumn.prototype.init = function (settings) {
        var defaults = {
            options: [],
            controlPositioning: "vertical",
            maxDirectionOptions: 1
        };
        jQuery.extend(true, defaults, settings);

        this.setOptions(defaults.options);
        this.setMaxDirectionOptions(defaults.maxDirectionOptions);
        this.setControlPositioning(defaults.controlPositioning);
    };

    CheckBoxGroupColumn.prototype.setOptions = function (options) {
        var i,
            cells;

        if (!jQuery.isArray(options)) {
            throw new Error("setOptions(): the parameter must be an array.");
        }
        this.options = options;
        cells = this.cells.asArray();
        for (i = 0; i < cells.length; i += 1) {
            cells[i].setOptions(this.options);
        }
        return this;
    };
    CheckBoxGroupColumn.prototype.setControlPositioning = function (positioning) {
        this.controlPositioning = positioning;
        return this;
    };

    CheckBoxGroupColumn.prototype.setMaxDirectionOptions = function (maxDirectionOptions) {
        this.maxDirectionOptions = maxDirectionOptions;
        return this;
    };

    CheckBoxGroupColumn.prototype.getCellDefaultCfg = function () {
        return jQuery.extend(true, CheckBoxGroupColumn.superclass.prototype.getCellDefaultCfg.call(this), {
            controlPositioning: this.controlPositioning,
            maxDirectionOptions: this.maxDirectionOptions,
            options: this.options
        });
    };

    PMUI.extendNamespace('PMUI.grid.CheckBoxGroupColumn', CheckBoxGroupColumn);
}());
(function () {
    var GridControlRow = function (settings) {
        GridControlRow.superclass.call(this, settings);
        this.removeButton = null;
    };

    PMUI.inheritFrom('PMUI.grid.GridPanelRow', GridControlRow);

    GridControlRow.prototype.getCell = function (column) {
        var cell = null;
        if (column instanceof PMUI.grid.GridControlColumn) {
            cell = this.cells.find("column", column);
        } else if (typeof column === 'number') {
            cell = this.cells.get(column);
        }
        return cell;
    };

    GridControlRow.prototype.setCells = function () {
        var i,
            columns,
            cellsFactory,
            cell;

        this.clearCells();
        if (this.parent) {
            columns = this.parent.columns.asArray();
            cellsFactory = this.parent.cellsFactory;
            for (i = 0; i < columns.length; i += 1) {
                cell = columns[i].createCell();
                cell.column = columns[i];
                cell.row = this;
                this.cells.insert(cell);
                if (this.html) {
                    this.html.appendChild(cell.getHTML());
                }
                this.data.addAttribute(cell.column.name, cell.getContent());
            }
        }
        return this;
    };

    GridControlRow.prototype.updateData = function (cell) {
        if (this.cells.contains(cell)) {
            this.data.addAttribute(cell.column.name, cell.getContent());
        }
        return this;
    };

    GridControlRow.prototype.defineEvents = function () {
        var that = this;

        if (!this.removeButton) {
            return this;
        }

        jQuery(this.html).on('mouseenter', function () {
            var cell = that.getCell(that.cells.getSize() - 1);
            cell.html.appendChild(that.removeButton);
        }).on('mouseleave', function () {
            jQuery(that.removeButton).detach();
        });
        jQuery(this.removeButton).on('click', '.pmui-gridpanelrow-deleteButton-link', function () {
            if (that.parent) {
                that.parent.removeItem(that);
            }
        });
        return this;
    };

    GridControlRow.prototype.createHTML = function () {
        var deleteButton,
            deleteAnchor;

        if (this.html) {
            return this.html;
        }
        GridControlRow.superclass.prototype.createHTML.call(this);
        deleteButton = PMUI.createHTMLElement('div');
        deleteAnchor = PMUI.createHTMLElement('a');
        deleteButton.className = 'pmui-gridpanelrow-deleteButton';
        deleteAnchor.className = 'pmui-gridpanelrow-deleteButton-link';
        deleteAnchor.href = '#';
        deleteButton.appendChild(deleteAnchor);
        this.removeButton = deleteButton;
        this.defineEvents();
        return this.html;
    };

    GridControlRow.prototype.isValid = function () {
        var i,
            cells = this.cells.asArray(),
            valid = true;

        for (i = 0; i < cells.length; i += 1) {
            valid = valid && cells[i].isValid();
            if (!valid) {
                return valid;
            }
        }
        return valid;
    };

    PMUI.extendNamespace('PMUI.grid.GridControlRow', GridControlRow);
}());
(function () {
    var ContainableItem = function (settings) {
        ContainableItem.superclass.call(this, settings);
        /**
         * The parent container for the object.
         * @type {PMUI.core.Container}
         * @internal
         */
        this.parent = null;
        /**
         * The data abstraction for the object.
         * @type {PMUI.data.DataField}
         */
        this.data = null;
        ContainableItem.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Element', ContainableItem);

    ContainableItem.prototype.init = function (settings) {
        var defaults = {
            parent: null,
            data: new PMUI.data.DataField()
        };

        $.extend(true, defaults, settings);

        this.setParent(defaults.parent)
            .setData(defaults.data);
    };

    ContainableItem.prototype.setData = function (data) {
        if (data instanceof PMUI.data.DataField) {
            this.data = data;
        }

        return this;
    };

    ContainableItem.prototype.getData = function () {
        return this.data;
    };

    ContainableItem.prototype.setParent = function (parent) {
        if (parent !== undefined) {
            if (parent instanceof PMUI.core.Container || parent === null) {
                this.parent = parent;
            } else {
                throw new Error("setParent(): The parameter must be instance of PMUI.core.Container or null.");
            }
        }

        return this;
    };

    PMUI.extendNamespace('PMUI.item.ContainableItem', ContainableItem);

    if (typeof exports !== 'undefined') {
        module.exports == ContainableItem;
    }
}());
(function () {
    /**
     * @class PMUI.item.TabItem
     * Creates an TabItem class, this is a basic element
     * @extends {PMUI.core.Element}
     *
     *  Usage example:
     *
     *      @example
     *          var ti,p;
     *          p = new PMUI.form.FormPanel({
     *                    width: 604,
     *                    height: 130,
     *                    fieldset: true,
     *                    legend: "my fieldset panel",
     *                    items: [
     *                        {
     *                            pmType: "text",
     *                            label: "Name",
     *                            id: "123",
     *                            value: "",
     *                            placeholder: "insert your name",
     *                            name: "name"
     *                        },{
     *                            pmType: "text",
     *                            label: "Last name",
     *                            value: "",
     *                            placeholder: "your lastname here asshole!",
     *                            name: "lastname"
     *                        }, {
     *                            pmType: "panel",
     *                            layout: 'hbox',
     *                            items: [
     *                                {
     *                                    pmType: "text",
     *                                    label: "E-mail",
     *                                    value: "",
     *                                    name: "email"
     *                                },{
     *                                    pmType: "text",
     *                                    label: "Phone",
     *                                    value: "555",
     *                                    name: "phone"
     *                                }
     *                            ]
     *                        }
     *                    ],
     *                    layout: "vbox"
     *                });
     *               ti = new PMUI.item.TabItem({
     *                       title : 'titulo1',
     *                       panel : p,
     *                       icon : 'pmui-gridpanel-engranaje'
     *                   });
     *               jQuery('body').append(ti.getHTML());
     *               ti.defineEvents();
     *
     * create a new instance of the TabItem, according to the example we set the following properties
     * @cfg {String} [title='[untitled]'], Title The title for the Window
     * @cfg {PMUI.core.Panel|Object} panel, this property is to set TabItems that are sent from the JSON
     * @cfg {String} [icon = ""], this property serves to define one TabItem icon with css with putting a className for this icon
     * parameter to family {panel}
     * @cfg {Function} [onSelect= function() {}], this property is a function handler to from execute the
     * function when is callback define events
     */
    var TabItem = function (settings) {
        TabItem.superclass.call(this, settings);
        /**
         * Defines the Title of the TabItem
         * @type {String}
         */
        this.title = null;
        /**
         * Element defines the panel that concentrates tab
         * @type {PMUI.core.Panel}
         */
        this.panel = null;
        /**
         * Defines the property is a function handler to from execute the
         * function when is callback define events
         * @type {function}
         */
        this.onSelect = null;
        /**
         * The property define the TabItem is selected for triggering the
         * @type {boolean}
         */
        this.selected = null;
        /**
         * The property define the TabItem is selected for triggering
         * @type {boolean}
         */
        this.onClose = null;
        /**
         * property is defined when a TabItem is selected or event triggers a reference
         * @type {HTMLElement}
         */
        this.link = null;
        /**
         * Defines the fatory  for childrend [PMUI.core.Panel]
         * @type {PMUI.util.factory}
         */
        this.factory = null;
        /**
         * property defined Tag for icon
         * @type {HTMLElement}
         */
        this.classNameIcon = null;
        /**
         * property defined Tag for spanTitle
         * @type {HTMLElement}
         */
        this.spanTitle = null;
        /**
         * TabItem title can be hidden or viewed by the user if you only want an icon mostart
         * @type {Boolean}
         */
        this.titleVisible = null;
        /**
         * [onClick description]
         * @type {[type]}
         * @private
         */
        this.onClick = null;
        this.icon = null;
        TabItem.prototype.init.call(this, settings);
    };
    PMUI.inheritFrom('PMUI.core.Element', TabItem);

    TabItem.prototype.family = 'TabItem';
    TabItem.prototype.type = 'TabItem';

    TabItem.prototype.init = function (settings) {
        var defaults = {
            title: '[untitled]',
            panel: null,
            onSelect: function () {
            },
            elementTag: 'li',
            onClick: null
        };

        jQuery.extend(true, defaults, settings);

        this.factory = new PMUI.util.Factory({
            products: {
                'treepanel': PMUI.panel.TreePanel,
                'panel': PMUI.core.Panel,
                'grid': PMUI.grid.GridPanel
            },
            defaultProduct: 'panel'
        });
        this.setElementTag(defaults.elementTag)
            .setTitle(defaults.title)
            .setPanel(defaults.panel)
            .setOnSelectCallback(defaults.onSelect)
            .setOnClick(defaults.onClick);
    };

    TabItem.prototype.setOnClick = function (onClick) {
        if (typeof onClick !== 'function' && onClick !== null) {
            throw new Error('setOnClick(): the parameter is no valid, should be a function');
        }
        this.onClick = onClick;

        return this;
    }

    /**
     * Add an item to the panel.
     * @param {PMUI.item.TabItem} item
     * It can be a valid JSON object or an object that inherits from {@link PMUI.item.TabItem}.
     * @chainable
     */
    TabItem.prototype.setOnSelectCallback = function (callback) {
        if (typeof callback === 'function') {
            this.onSelect = callback;
        } else {
            throw new Error("setOnSelectCallback(): The parameter is not a function.");
        }
        return this;
    };
    /**
     * @event
     * Define the event listeners for the TabPanel.
     * @chainable
     */
    TabItem.prototype.defineEvents = function () {
        var that = this;

        this.removeEvents().eventsDefined = true;
        if (this.link) {
            this.addEvent('click').listen(this.link, function (e) {
                if (typeof that.onSelect === 'function') {
                    e.preventDefault();
                    e.stopPropagation();
                    if (!that.selected) {
                        that.select();
                    }
                }
                if (typeof that.onClick === 'function') {
                    that.onClick(that);
                }
            });
            that.panel.defineEvents();
        }
        return this;
    };
    /**
     * Sets the tabItem title
     * Set the proportion of the html element
     * @param {string} title
     * @chainable
     */
    TabItem.prototype.setTitle = function (title) {
        this.title = title;
        if (this.html) {
            this.spanTitle.textContent = this.title;
        }
        return this;
    };
    /**
     * Returns title the tabItem.
     * @return {string}
     */
    TabItem.prototype.getTitle = function () {
        return this.title;
    };
    /**
     * Sets the tabPanel [PMUI.core.panel] and full children except Window
     * @chainable
     */
    TabItem.prototype.setPanel = function (panel) {
        var newPanel, errorMessage = "setPanel(): the object supplied as a parameter is not a valid type.";
        if (!panel) {
            throw new Error("setPanel(): you must specified a valid panel.");
        }
        if (panel instanceof PMUI.ui.Window) {
            throw new Error(errorMessage);
        }
        if (this.factory) {
            if (this.factory.isValidClass(panel) || this.factory.isValidName(panel.pmType) || (panel instanceof PMUI.grid.GridPanel)) {
                newPanel = this.factory.make(panel);
            } else {
                throw new Error(errorMessage);
            }
        }
        if (newPanel) {
            this.panel = newPanel;
        }
        return this;
    };
    /**
     * Returns panel the tabItem.
     * @return {PMUI.core.Panel}
     */
    TabItem.prototype.getPanel = function () {
        return this.panel;
    };
    /**
     * if the tab item is selected, the selected property changes
     * @return {boolean}
     */
    TabItem.prototype.isSelected = function () {
        return this.selected;
    };
    /**
     * if the tab item is selected, the selected property changes to true and calls
     * your function onSelect
     * @chainable
     */
    TabItem.prototype.select = function () {
        this.selected = true;
        this.style.addClasses(['pmui-active']);
        if (this.html) {
            this.onSelect();
        }
        return this;
    };
    /**
     * if the tab item is selected, the selected property changes to false
     * @chainable
     */
    TabItem.prototype.deselect = function () {
        this.selected = false;
        this.style.removeClasses(['pmui-active']);
        return this;
    };
    /**
     * @method hideTitle
     * hide the title of TabItem
     * @chainable
     */
    TabItem.prototype.hideTitle = function () {
        this.titleVisible = false;
        if (this.html) {
            this.spanTitle.style.display = "none";
        }
        return this;
    };
    /**
     * @method hideTitle
     * shows the title of the TabItem
     * @chainable
     */
    TabItem.prototype.showTitle = function () {
        this.titleVisible = true;
        if (this.html) {
            this.spanTitle.style.display = "inline-block";
        }
        return this;
    };

    TabItem.prototype.createHTML = function () {
        var tab,
            ref,
            spanTitle,
            title,
            icon;

        if (this.html) {
            return this.html;
        }
        TabItem.superclass.prototype.createHTML.call(this);
        ref = PMUI.createHTMLElement('a');
        spanTitle = PMUI.createHTMLElement('span');
        icon = PMUI.createHTMLElement('i');
        icon.className = 'pmui-tab-icon';
        ref.className = 'pmui-tab-ref';
        ref.href = '#';
        spanTitle.className = 'pmui-tab_title';
        spanTitle.textContent = this.title;
        ref.appendChild(icon);
        ref.appendChild(spanTitle);
        this.icon = icon;
        this.html.appendChild(ref);
        this.spanTitle = spanTitle;
        this.link = ref;
        this.setWidth(this.width);

        if (this.eventsDefined) {
            this.defineEvents();
        }
        return this.html;
    };

    TabItem.prototype.setWidth = function (width) {
        TabItem.superclass.prototype.setWidth.call(this, width);
        if (this.html) {
            if (width == "auto") {
                this.link.setAttribute("title", "");
                this.spanTitle.style.width = "auto";
            } else {
                this.link.setAttribute("title", this.title);
                if (this.width >= 65) {
                    this.spanTitle.style.width = this.width - 46 + 'px';
                } else {
                    this.spanTitle.style.width = '0px';
                }

            }

        }
        return this;
    };

    PMUI.extendNamespace('PMUI.item.TabItem', TabItem);

    if (typeof exports !== "undefined") {
        module.exports = TabItem;
    }

}());
(function () {
    /**
     * @class PMUI.item.AccordionItem
     * @extends PMUI.core.Item
     *
     * Class to handle Accordion items, this is the basic element for make an {@link PMUI.panel.AccordionPanel Accordion}
     *
     * @constructor
     * For creates a new component the JSON config can have the following sentences
     * @param {Object} settings The configuration options contain:
     *
     *          {
     *          title: "The title",
     *          body: panel // This item can be any element that inherit from {@link PMUI.core.Panel Panel}
     *          iconClass: "the-class",
     *          style: {
     *              cssProperties: {
     *                  "background-color": "#f2eaea"
     *              }
     *          }
     *      } ...
     * @cfg {String} [title=""] Shows the title for the container
     * @cfg {Object} [body=null] Defines the element that will be inside the element
     * @cfg {String} [iconClass='pmui-accordion-item-icon'] Defines the class name for the header of the item
     *
     */
    AccordionItem = function (settings) {
        AccordionItem.superclass.call(this, settings);
        /**
         * @property {String} [iconClass='pmui-accordion-item-icon']
         * Defines the class name for the header of the item
         */
        this.iconClass = null;
        /**
         * @property {String} [headerClass='pmui-accordion-item-header']
         * Defines the class name for the header
         * @private
         */
        this.headerClass = null,
        /**
         * @property {String} [bodyClass='pmui-accordion-item-body']
         * Defines the class name for the body
         * @private
         */
            this.bodyClass = null,
        /**
         * @property {String} [footerClass='pmui-accordion-item-footer']
         * Defines the class name for the footer
         * @private
         */
            this.footerClass = null;
        /**
         * @property {String} [containerClass='pmui-accordion-item-container']
         * Defines the class name for the global container
         * @private
         */
        this.containerClass = null;
        /**
         * @property {String} [iconExpanded='pmui-accordion-item-closed']
         * Defines the class name that represents the icon expanded for the AccordionItem
         * @private
         */
        this.iconExpanded = null;
        /**
         * @property {String} [iconClosed='pmui-accordion-item-expanded']
         * Defines the class name that represents the icon closed for the AccordionItem
         * @private
         */
        this.iconClosed = null;

        /**
         * @property {Boolean} [collapsed=true]
         * Defines whether the object is collapsed or not
         * @private
         */
        this.collapsed = null;
        /**
         * @property {Object} [container=null]
         * container Defines the container object for the class xxxxxxx
         * @private
         */
        this.container = null;
        /**
         * @property {Object} [header=null]
         * Defines the header object for the class
         * @private
         */
        this.header = null;
        /**
         * @property {Object} [body={}]
         * Defines the body object for the item
         * @private
         */
        this.body = {};
        /**
         * @property {Object} [footer=null]
         * Defines the footer object for the class
         * @private
         */
        this.footer = null;
        /**
         * @property {Boolean} [selected=false]
         * Defines whether the item is selected by default when the component is ready to render
         * on the browser
         */
        this.selected = null;
        /**
         * @property {Boolean} [propName]
         * Represents a flag for control the rendered of the item.
         * If the property is True the item was rendered the otherwise if it
         * False the item still was not rendered
         * @private
         */
        this.hiddenBody = null;
        /**
         * @property {Object} [iconNode=null]
         * Defines the iconNode object for the class
         * @private
         */
        this.iconNode = null;
        AccordionItem.prototype.init.call(this, settings);

    };

    PMUI.inheritFrom('PMUI.core.Item', AccordionItem);
    AccordionItem.prototype.type = 'Accordion';
    AccordionItem.prototype.family = 'Item';
    AccordionItem.prototype.init = function (settings) {
        var defaults = {
            title: '',
            body: '',
            collapsed: true,
            iconClass: 'pmui-accordion-item-icon',
            headerClass: 'pmui-accordion-item-header',
            bodyClass: 'pmui-accordion-item-body',
            footerClass: 'pmui-accordion-item-footer',
            containerClass: 'pmui-accordion-item-container',
            iconClosed: "pmui-accordion-item-closed",
            iconExpanded: "pmui-accordion-item-expanded",
            selected: false,
            factory: {
                products: {
                    'panel': PMUI.core.Item
                }
            }
        };

        jQuery.extend(true, defaults, settings);

        this.setFactory(defaults.products)
            .setTitle(defaults.title)
            .setBody(defaults.body)
            .setIconClass(defaults.iconClass)
            .setHeaderClass(defaults.headerClass)
            .setBodyClass(defaults.bodyClass)
            .setFooterClass(defaults.footerClass)
            .setContainerClass(defaults.containerClass)
            .setIconExpanded(defaults.iconExpanded)
            .setIconClosed(defaults.iconClosed)
            .setCollapsed(defaults.collapsed)
            .setSelected(defaults.selected);
    };

    /**
     * Sets the string that represent the title. It could be an simple string
     * or an string that contains html tags. If the string contains html code
     * it will be rendered
     * @param {String} title
     */
    AccordionItem.prototype.setTitle = function (title) {
        if (typeof title === "string") {
            this.title = title;
            if (this.header) {
                this.header.items[2].title.html.innerHTML = title;
            }
        }

        return this;
    };
    /**
     * Sets the class name for the AccordionItem's title
     * @param {String} class name
     */
    AccordionItem.prototype.setIconClass = function (className) {
        this.iconClass = (typeof className === 'string') ? className : '';
        if (this.header) {
            this.header.items[1].iconTitle.html.className = 'pmui-accordion-item-iconbase ' + className;
        }
        return this;
    };
    /**
     * Sets the class name for the AccordionItem's header
     * @param {String} class name
     * @private
     */
    AccordionItem.prototype.setHeaderClass = function (className) {
        this.headerClass = (typeof className === 'string') ? className : '';
        if (this.header) {
            this.header.html.className = className;
        }
        return this;
    };
    /**
     * Sets the class name for the AccordionItem's body
     * @param {String} class name
     * @private
     */
    AccordionItem.prototype.setBodyClass = function (className) {
        this.bodyClass = (typeof className === 'string') ? className : '';
        if (this.html) {
            this.container[1].className = className;
        }
        return this;
    };
    /**
     * Sets the class name for the AccordionItem's footer
     * @param {String} class name
     * @private
     */
    AccordionItem.prototype.setFooterClass = function (className) {
        this.footerClass = (typeof className === 'string') ? className : '';
        if (this.html) {
            this.container[2].className = className;
        }
        return this;
    };
    /**
     * Sets the class name for the AccordionItem's container
     * @param {String} class name
     * @private
     */
    AccordionItem.prototype.setContainerClass = function (className) {
        this.containerClass = (typeof className === 'string') ? className : '';
        if (this.html) {
            this.html.className = className;
        }
        return this;
    };
    /**
     * Sets the class name that represents the icon in its expanded status
     * @param {String} class name
     * @private
     */
    AccordionItem.prototype.setIconExpanded = function (className) {
        this.iconExpanded = (typeof className === 'string') ? className : '';
        if (this.header) {
            this.header.items[0].iconNode.html.className = 'pmui-accordion-item-iconbase ' + className;
        }
        return this;
    };
    /**
     * Sets the class name that represents the icon in its closed status
     * @param {String} class name
     * @private
     */
    AccordionItem.prototype.setIconClosed = function (className) {
        this.iconClosed = (typeof className === 'string') ? className : '';
        if (this.header) {
            this.header.items[0].iconNode.html.className = 'pmui-accordion-item-iconbase ' + className;
        }
        return this;
    };
    /**
     * Sets the status for the item, whether it collapsed or not
     * @param {Boolean} option
     * @private
     */
    AccordionItem.prototype.setCollapsed = function (option) {
        this.collapsed = option;
        return this;
    };
    /**
     * Sets the status collapsed. If it true the container will be opened
     * and if it false will be closed
     * @param {Boolean} status
     */
    AccordionItem.prototype.setSelected = function (status) {
        if (typeof status === 'boolean') {
            this.selected = status;
            this.setCollapsed(!status);
        }
        return this;
    };
    /**
     * Sets the body for the item as a new child for the container.
     * The parameter can be:
     *
     *  - {@link PMUI.core.Panel panel}: In this case the item can be any class that inherit from
     * {@link PMUI.core.Panel Panel}. For example {@link PMUI.form.Form Form}, {@link PMUI.panel.TreePanel TreePanel}, etc.
     *  - String: In this case the string must have contain html code o can be a simple string like to exaple below:
     *
     *      "<a href=\"http://www.colosa.com\">Colosa</a>"
     *
     * @param {PMUI.core.Panel|String} body
     */
    AccordionItem.prototype.setBody = function (body) {
        var currentPanel = this.body.html;
        if (body instanceof PMUI.core.Container) {
            body.getHTML();
            this.body.items = [
                {
                    panel: {
                        html: body.html
                    }
                }
            ];
        } else if (typeof body === 'string') {
            this.body.items = [
                {
                    panel: {
                        html: body
                    }
                }
            ];
        }
        try {
            this.body.html.removeChild(this.body.items[0].panel.html);
        } catch (e) {
        }

        this.addItem(body);
        return this;
    };
    /**
     * Adds an child item to the object.
     * @param {PMUI.core.Element|Object} item It can be one of the following data types:
     * - {PMUI.core.Element} the object to add.
     * - {Object} a JSON object with the settings for the Container to be added.
     * @param {Number} [index] An index in which the item will be added.
     * @chainable
     */
    AccordionItem.prototype.addItem = function (item, index) {
        var itemToBeAdded;
        if (this.factory) {
            itemToBeAdded = this.factory.make(item);
        }
        if (itemToBeAdded) {
            itemToBeAdded.parent = this;
            this.clearItems();
            this.items.insert(itemToBeAdded);
            if (this.body.html) {
                this.body.html.appendChild(itemToBeAdded.getHTML());

                if (this.eventsDefined) {
                    itemToBeAdded.defineEvents();
                }
            }
        }
        return this;
    };
    /**
     * Handler the collapse funtionality
     * @return {PMUI.item.AccordionItem}
     */
    AccordionItem.prototype.toggleCollapse = function () {
        if (this.collapsed) {
            this.expand();
        } else {
            this.collapse();
        }
        return this;
    };
    /**
     * Collapses the item and sets the collapsed property in false
     */
    AccordionItem.prototype.collapse = function () {
        var items;

        items = this.container;
        jQuery(items[1]).slideUp(300);
        jQuery(items[2]).slideUp(300);
        this.header.items[0].iconNode.html.className = 'pmui-accordion-item-iconbase ' + this.iconClosed;
        this.collapsed = true;

        return this;
    };
    /**
     * Expands the item selected. If the multipleSelection is enabled, It is posible select
     * one or more items the otherwise is only possible select one item.
     * @return {PMUI.item.AccordionItem}
     */
    AccordionItem.prototype.expand = function () {
        var i,
            items,
            otherElements;

        items = this.container;
        jQuery(items[1]).slideDown(300);
        jQuery(items[2]).slideDown(300);
        this.header.items[0].iconNode.html.className = 'pmui-accordion-item-iconbase ' + this.iconExpanded;
        this.collapsed = false;

        //For other items
        if (this.parent.multipleSelection === false) {
            otherElements = this.parent.getItems();
            for (i = 0; i < otherElements.length; i += 1) {
                if (this.id != otherElements[i].id) {
                    otherElements[i].collapsed = false;
                    otherElements[i].toggleCollapse();
                }
            }
        }
        return this;
    };

    AccordionItem.prototype.createHTML = function () {
        var container,
            header,
            iconNode,
            iconTitle,
            title,
            body,
            footer,
            containerIcon;

        if (this.html) {
            return this.html;
        }
        container = AccordionItem.superclass.prototype.createHTML.call(this);
        container.setAttribute('class', this.containerClass);
        /*=============================================
         Header accordion
         =============================================*/
        header = PMUI.createHTMLElement("div");
        header.setAttribute('class', this.headerClass);
        iconNode = PMUI.createHTMLElement('span');
        containerIcon = PMUI.createHTMLElement('span');
        containerIcon.className = "pmui-accordion-item-iconContainer";
        if (this.collapsed) {
            iconNode.setAttribute("class", 'pmui-accordion-item-iconbase ' + this.iconClosed);
        } else if (!this.collapsed) {
            iconNode.setAttribute("class", 'pmui-accordion-item-iconbase ' + this.iconExpanded);
        }
        iconTitle = PMUI.createHTMLElement('span');
        iconTitle.setAttribute('class', 'pmui-accordion-item-iconbase ' + this.iconClass);
        title = PMUI.createHTMLElement('span');
        title.className = 'pmui-accordion-item-title';
        title.innerHTML = this.title;
        containerIcon.appendChild(iconNode);
        header.appendChild(containerIcon);

        this.iconNode = containerIcon;
        header.appendChild(iconTitle);
        header.appendChild(title);
        this.header = {
            html: header,
            items: [
                {
                    iconNode: {
                        html: iconNode
                    }
                },
                {
                    iconTitle: {
                        html: iconTitle
                    }
                },
                {
                    title: {
                        html: title
                    }
                }
            ]
        };
        /*=============================================
         body accordion
         =============================================*/
        body = PMUI.createHTMLElement("div");
        body.setAttribute('class', this.bodyClass);

        if (typeof this.parent.heightItem === "string") {
            body.style.height = this.parent.heightItem;
        } else {
            body.style.height = this.parent.heightItem + 'px';
        }
        if (this.collapsed) {
            body.style.display = 'none';
        }

        this.body.html = body;
        jQuery(body).html(this.body.items[0].panel.html);

        /*=============================================
         footer accordion
         =============================================*/
        footer = PMUI.createHTMLElement("div");
        footer.setAttribute('class', this.footerClass);
        this.container = new Array();
        this.container.push(header, body, footer);
        /*=============================================
         Container accordion
         =============================================*/
        container.appendChild(header);
        container.appendChild(body);
        container.appendChild(footer);
        this.html = container;

        return this.html;
    };
    /**
     * Defines events related to the current panel and the calls to DefineEvents from
     * every {@link PMUI.core.Item item}
     * return {this}
     */
    AccordionItem.prototype.defineEvents = function () {
        var j,
            fnSelect,
            children,
            that = this;

        this.removeEvents().eventsDefined = true;
        if (that.container[0]) {
            that.addEvent('click').listen(this.iconNode, function (e) {
                e.preventDefault();

                that.toggleCollapse();
                fnSelect = that.parent.listeners.select;
                fnSelect(that, e);

                e.stopPropagation();
            });
        }

        if (that.items.getSize() > 0) {
            children = that.getItems();
            for (j = 0; j < children.length; j += 1) {
                children[j].defineEvents();
            }
        }
        return this;
    };

    PMUI.extendNamespace('PMUI.item.AccordionItem', AccordionItem);
    if (typeof exports !== "undefined") {
        module.exports = AccordionItem;
    }
}());
(function () {
    /**
     * @class PMUI.item.TreeNode
     * Each node in a TreePanel. It is able to apply data binding, that means that it can use its
     * {@link PMUI.item.TreeNode#property-data data property} for set another object's property.
     * @extends PMUI.core.Item
     *
     * The following example shows a TreeNode object whose child nodes are created using the
     * {@link PMUI.item.TreeNode#cfg-items items config option}:
     *
     *      @example
     *      var t;
     *      $(function() {
     *          t = new PMUI.item.TreeNode({
     *              label: 'America',
     *              childrenDefaultSettings: {
     *                  label: '[untitled node]'
     *              },
     *              items: [
     *                  {
     *                      label: "North America",
     *                      items: [
     *                          {
     *                              label: 'Canada',
     *                              data: {
     *                                  label: 'Canada',
     *                                  value: 'cnd'
     *                              }
     *                          },
     *                          {
     *                              label: 'Mexico'
     *                          }
     *                      ]
     *                  },
     *                  {
     *                      label: "Central America and Caribbean",
     *                      items: [
     *                          {
     *                              label: 'Cuba'
     *                          },
     *                          {
     *                              label: 'Guatemala'
     *                          } 
     *                      ]
     *                  },
     *                  {
     *                      label: "South America",
     *                      items: [
     *                          {
     *                              label: 'Argentina'
     *                          }, 
     *                          {
     *                              label: 'Bolivia'
     *                          }
     *                      ]
     *                  }
     *              ]
     *          });
     *          document.body.appendChild(t.getHTML());
     *          t.defineEvents();
     *      });
     *
     * The following example shows a TreeNode object whose child nodes are created using the
     * {@link PMUI.item.TreeNode#cfg-dataItems dataItems config option}:
     *
     *      @example
     *      var t2;
     *      $(function() {
     *          t2 = new PMUI.item.TreeNode({
     *              data: {
     *                  name: 'America'
     *              },
     *              labelDataBind: 'name',
     *              autoBind: true,
     *              childrenDefaultSettings: {
     *                  labelDataBind: 'name',
     *                  autoBind: true
     *              },
     *              dataItems: [
     *                  {
     *                      name: "Argentina"
     *                  },
     *                  {
     *                      name: "Bolivia"
     *                  },
     *                  {
     *                      name: "Brasil"
     *                  }
     *              ]
     *          });
     *          document.body.appendChild(t2.getHTML());
     *          t2.defineEvents();
     *      });
     *
     * @cfg {Boolean} [sterile=false] If the node will be able to have children nodes.
     * @cfg {String} [itemsDataBind=null] The key from the object's data to be used as the array for create the child
     * nodes of the current node when the data binding be applied. If the data binding is applied then the
     * {@link PMUI.item.TreeNode#cfg-items items config option} will be ignored.
     * @cfg {String} [labelDataBind=null] The key from the object's data to be used as the label for the current node
     * when the data binding be applied. If the data binding is applied then the
     * {@link PMUI.item.TreeNode#cfg-label label config option} will be ignored.
     * @cfg {String} [nodeClassDataBind=null] The key from the object's data to be used as the class for the node's
     * html when the data binding be applied. If the data binding is applied then the
     * {@link PMUI.item.TreeNode#cfg-nodeClass nodeClass config option} will be ignored.
     * @cfg {Object} [data={}] The data for the node.
     * @cfg {Boolean} [collapsed=true] If the node will be collapsed or not from the beginning.
     * @cfg {String} [label='[item]'] The label text for the node.
     * @cfg {String} [nodeClass=''] The css class for the node's html.
     * @cfg {Function} [onCollapse=null] The callback function to be executed everytime the current node is collpased.
     * Please read the {@link PMUI.item.TreeNode#event-onCollapse onCollapse event documentation}.
     * @cfg {Function} [onExpand=null] The callback function yto be executed everytime the current node is expanded.
     * Please read the {@link PMUI.item.TreeNode#event-onExpanded onExpanded event documentation}.
     * @cfg {Object} [childrenDefaultSettings={}] The default config options to be applied to the new child nodes for
     * the current node. This only will be applied when the appended item is an object literal.
     * @cfg {Boolean} [autobind] If the data binding will be applied automatically on instantiation and everytime the
     * data is set. This will cause that some others config options be ignored (like
     * {@link PMUI.item.TreeNode#cfg-label label}, {@link PMUI.item.TreeNode#cfg-nodeClass nodeClass} and/or
     * {@link PMUI.item.TreeNode#cfg-items items}).
     * @cfg {Boolean} [recursiveChildrenDefaultSettings=false] If the current
     * {@link PMUI.item.TreeNode#cfg-childrenDefaultSettings childrenDefaultSettings config option} will be inherited
     * to the same config option of its child nodes.
     * @cfg {Function|null} [onBeforeAppend=null] A callback function to be called everytime the
     * {@link #event-onBeforeAppend onBeforeAppend} event is fired. For info about the parameters used for this
     * callback please read the documentation for this event.
     * @cfg {Function|null} [onAppend=null] A callback function to be called everytime the
     * {@link #event-onAppend onAppend} event is fired. For info about the parameters used for this callback please
     * read the documentation for this event.
     */
    var TreeNode = function (settings) {
        TreeNode.superclass.call(this, settings);
        /**
         * If the node can have child nodes.
         * @type {Boolean}
         */
        this.sterile = null;
        /**
         * If the node is collapsed.
         * @type {Boolean}
         * @readonly
         */
        this.collapsed = null;
        /**
         * The current label being used by the node.
         * @type {String}
         * @readonly
         */
        this.label = null;
        /**
         * A literal object that will contain all the dom elements that compose the node's html structure.
         * @type {Object}
         * @readonly
         */
        this.dom = {};
        /**
         * The css class for the node's html.
         * @type {String}
         * @readonly
         */
        this.nodeClass = null;
        /**
         * @event onClick
         * Fired when the current node is clicked
         */
        this.onClick = null;
        /**
         * @event onCollapse
         * Fired when the current node is collapsed.
         */
        this.onCollapse = null;
        /**
         * @event onExpand
         * Fired when the current node is expanded.
         */
        this.onExpand = null;
        /**
         * The key of the object's data that will be used as the array to build the child nodes for the current one
         * when the data binding is applied.
         * @type {String}
         * @readonly
         */
        this.itemsDataBind = null;
        /**
         * The key of the object's data that will be used as the label for the current node when the data binding is
         * applied.
         * @type {String}
         * @readonly
         */
        this.labelDataBind = null;
        /**
         * The key of the object's data that will be used as the css class for the current node's html when the data
         * binding is applied.
         * @type {String}
         * @readonly
         */
        this.nodeClassDataBind = null;
        /**
         * If the data binding will be applied on initialization and everytime the data is set.
         * @type {Boolean}
         */
        this.autoBind = null;
        /**
         * The default config options to be applied to the child nodes to be add to the current node, but only when an
         * object literal is used.
         * @type {Object}
         */
        this.childrenDefaultSettings = {};
        /**
         * The data for the current node. This data can be used to modify another node's properties through data
         * binding.
         * @type {PMUI.data.DataField}
         * @private
         */
        this.data = null;
        /**
         * If the settings defined in the {@link #property-childrenDefaultSettings childrenDefaultSettings} property will
         * be inherited to all the child nodes of the current one.
         * @type {Boolean}
         */
        this.recursiveChildrenDefaultSettings = null;
        /**
         * @event onBeforeAppend
         * Fired before a child node is appended to the current node or to any of its children nodes.
         * @param {PMUI.item.TreeNode} node The node in which the event is being fired.
         * @param {PMUI.item.TreeNode} newNode The new node to be appended.
         */
        this.onBeforeAppend = null;
        /**
         * @event onAppend
         * Fired everytime a child node is appended to the current node or to any of its children nodes.
         * @param {PMUI.item.TreeNode} node The node in which the event is being fired.
         * @param {PMUI.item.TreeNode} newNode The appended node.
         */
        this.onAppend = null;
        TreeNode.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Item', TreeNode);
    /**
     * @property {String} type=TreeNode The type of the class.
     * @readonly
     */
    TreeNode.prototype.type = 'TreeNode';
    /**
     * @property {String} family=Tree The class family.
     * @readonly
     */
    TreeNode.prototype.family = 'Tree';
    /**
     * Initializes the object.
     * @param  {Object} settings The object literal with the config options.
     * @private
     */
    TreeNode.prototype.init = function (settings) {
        var defaults = {
            itemsDataBind: null,
            labelDataBind: null,
            nodeClassDataBind: null,
            data: {},
            sterile: false,
            collapsed: true,
            label: '[item]',
            nodeClass: "",
            onCollapse: null,
            onExpand: null,
            onClick: null,
            childrenDefaultSettings: {},
            autoBind: false,
            recursiveChildrenDefaultSettings: false,
            onAppend: null,
            onBeforeAppend: null
        };

        jQuery.extend(true, defaults, settings);

        this.data = new PMUI.data.DataField();

        this.setNodeClass(defaults.nodeClass)
        if (defaults.autoBind) {
            this.enableAutoBind();
        } else {
            this.disableAutoBind();
        }
        this.setRecursiveChildrenDefaultSettings(defaults.recursiveChildrenDefaultSettings)
            .setChildrenDefaultSettings(defaults.childrenDefaultSettings)
            .setSterility(defaults.sterile)
            .setLabel(defaults.label)
            .setParent(defaults.parent)
            .setOnCollapseHandler(defaults.onCollapse)
            .setOnExpandHandler(defaults.onExpand)
            .setOnClickHandler(defaults.onClick)
            .setItemsDataBind(defaults.itemsDataBind)
            .setLabelDataBind(defaults.labelDataBind)
            .setNodeClassDataBind(defaults.nodeClassDataBind)
            .setOnAppendHandler(defaults.onAppend)
            .setOnBeforeAppendHandler(defaults.onBeforeAppend);

        if (defaults.dataItems && jQuery.isArray(defaults.dataItems)) {
            this.setDataItems(defaults.dataItems);
        } else {
            this.setItems(defaults.items);
        }
        this.setData(defaults.data);

        if (defaults.collapsed) {
            this.collapse();
        } else {
            this.expand();
        }
        this.elementTag = 'li';
    };
    /**
     * Sets the callback function to be called everytime the {@link #event-onBeforeAppend onBeforeAppend} event is
     * fired. For info about the parameters used for this callback please read the documentation for this event.
     * @param {Function|null} handler
     */
    TreeNode.prototype.setOnBeforeAppendHandler = function (handler) {
        if (!(handler === null || typeof handler === 'function')) {
            throw new Error("setOnBeforeAppendHandler(): The parameter must be a function or null.");
        }
        this.onBeforeAppend = handler;
        return this;
    };
    /**
     * Sets the callback function to be called everytime the
     * {@link #event-onAppend onAppend} event is fired. For info about the parameters used for this callback please
     * read the documentation for this event.
     * @param {Function|null} handler
     */
    TreeNode.prototype.setOnAppendHandler = function (handler) {
        if (!(handler === null || typeof handler === 'function')) {
            throw new Error("setOnAppendHandler(): The parameter must be a function or null.");
        }
        this.onAppend = handler;
        return this;
    };
    /**
     * Returns the level of the node relative to its root node.
     * @return {Number}
     */
    TreeNode.prototype.getDepth = function () {
        if (!this.parent || !(this.parent instanceof PMUI.item.TreeNode)) {
            return 0;
        } else {
            return this.parent.getDepth() + 1;
        }
    };
    /**
     * Returns the root node of the current one.
     * @return {PMUI.item.TreeNode}
     */
    TreeNode.prototype.getRoot = function () {
        if (this.getDepth() === 0) {
            return this;
        } else {
            return this.parent.getRoot();
        }
    };
    /**
     * Turns on the auto binding functionality.
     * @chainable
     */
    TreeNode.prototype.enableAutoBind = function () {
        this.autoBind = true;
        return this;
    };
    /**
     * Turns off the auto binding functionality.
     * @chainable
     */
    TreeNode.prototype.disableAutoBind = function () {
        this.autoBind = false;
        return this;
    };
    /**
     * Returns true if the current {@link PMUI.item.TreeNode#property-childrenDfaultSettings childrenDfaultSettings}
     * will be inherited to the next child nodes that will be added.
     * @return {Boolean} [description]
     */
    TreeNode.prototype.isRecursiveChildrenDefaultSettings = function () {
        return this.recursiveChildrenDefaultSettings;
    };
    /**
     * Establish if the current object's childrenDefaultSettings property values will be set it to the same property
     * of its future child nodes.
     * @param {Boolean} recursive
     */
    TreeNode.prototype.setRecursiveChildrenDefaultSettings = function (recursive) {
        this.recursiveChildrenDefaultSettings = !!recursive;
        return this;
    };
    /**
     * Returns the default config options for the node's child nodes.
     * @return {Object}
     */
    TreeNode.prototype.getChildrenDefaultSettings = function () {
        return this.childrenDefaultSettings;
    };
    /**
     * Sets the default config options for the child nodes to be added. Note that if you set this, the effect will
     * take effect only in new nodes added from the moment of the new set. The default settings will have effect only
     * when you add an item using anything but an instance of {@link PMUI.core.Element} (or ant subclass of it).
     * @param {Object} settings A object literal with some or all of the config options for a TreeNode instance.
     */
    TreeNode.prototype.setChildrenDefaultSettings = function (settings) {
        if (typeof settings !== 'object') {
            throw new Error('setChildrenDefaultSettings(): The parameter must be an object.');
        }
        this.childrenDefaultSettings = settings;

        return this;
    };
    /**
     * Sets the key of the current node's data to be used as the css class for the node's html.
     * @param {String} nodeClassDataBind
     */
    TreeNode.prototype.setNodeClassDataBind = function (nodeClassDataBind) {
        if (nodeClassDataBind !== null && typeof nodeClassDataBind !== 'string') {
            throw new Error("setNodeClassDataBind(): the parameter must be a string or null.");
        }
        this.nodeClassDataBind = nodeClassDataBind;
        return this;
    };
    /**
     * Sets the key of the current node's data to be used as the array of items to be add to the current node.
     * Note that every item in the array will be added as a data item
     * (see the {@link PMUI.core.Container#method-addDataItem addDataItem()} method).
     * @param {String} itemsDataBind
     * @chainable
     */
    TreeNode.prototype.setItemsDataBind = function (itemsDataBind) {
        if (itemsDataBind !== null && typeof itemsDataBind !== 'string') {
            throw new Error("setItemsDataBind(): the parameter must be a string or null.");
        }
        this.itemsDataBind = itemsDataBind;
        return this;
    };
    /**
     * Sets the key of the current node's data to be used as the label for the current node.
     * @param {String} labelDataBind
     * @chainable
     */
    TreeNode.prototype.setLabelDataBind = function (labelDataBind) {
        if (labelDataBind !== null && typeof labelDataBind !== 'string') {
            throw new Error("setLabelDataBind(): the parameter must be a string or null.");
        }
        this.labelDataBind = labelDataBind;
        return this;
    };
    /**
     * Bind the node's data to its properties using:
     *
     * - The {@link PMUI.item.TreeNode#property-labelDataBind labelDataBind property} to set the
     * {@link PMUI.item.TreeNode#property-label label property}.
     * - The {@link PMUI.item.TreeNode#property-itemsDataBind labelDataBind property} for set the node's child items.
     * - The {@link PMUI.item.TreeNode#property-nodeClassDataBind nodeClassDataBind property} for set the css class for
     * the node's html.
     * @chainable
     */
    TreeNode.prototype.bindData = function () {
        var data = this.data.getRecord();

        this.setLabel(data[this.labelDataBind] || this.label);
        if (this.itemsDataBind && jQuery.isArray(data[this.itemsDataBind])) {
            this.setDataItems(data[this.itemsDataBind]);
        }
        this.setNodeClass(data[this.nodeClassDataBind] || this.nodeClass);

        return this;
    };
    /**
     * Sets the data for the node.
     * @param {Object|PMUI.data.DataField} data
     */
    TreeNode.prototype.setData = function (data) {
        var key;

        if (data instanceof PMUI.data.DataField) {
            this.data = data;
        } else if (typeof data === 'object') {
            this.data.clear();
            for (key in data) {
                this.data.addAttribute(key, data[key]);
            }
        } else {
            throw new Error("setData(): it only accepts a JSON object o an instance of PMUI.data.DataField as parameter.");
        }

        if (this.autoBind) {
            this.bindData();
        }

        return this;
    };
    /**
     * Return the node's data.
     * @return {Object} A object literal containing the node's data.
     */
    TreeNode.prototype.getData = function () {
        return this.data.getRecord();
    };
    /**
     * Sets the callback function for the {@link PMUI.item.TreeNode#event-onClickHandler onClickHandler event}.
     * @param {Function} handler
     */
    TreeNode.prototype.setOnClickHandler = function (handler) {
        if (handler === null || typeof handler === 'function') {
            this.onClick = handler;
        }
        return this;
    };
    /**
     * Sets the callback function for the {@link PMUI.item.TreeNode#event-onCollapse onCollapse event}.
     * @param {Function} handler
     */
    TreeNode.prototype.setOnCollapseHandler = function (handler) {
        if (handler === null || typeof handler === 'function') {
            this.onCollapse = handler;
        }

        return this;
    };
    /**
     * Sets the callback function for the {@link PMUI.item.TreeNode#event-onExpand onExpand event}.
     * @param {Function} handler
     */
    TreeNode.prototype.setOnExpandHandler = function (handler) {
        if (handler === null || typeof handler === 'function') {
            this.onExpand = handler;
        }

        return this;
    };
    /**
     * Sets the factory for the curren node.
     * @private
     * @chainable
     */
    TreeNode.prototype.setFactory = function () {
        this.factory = new PMUI.util.Factory({
            products: {
                'treeNode': PMUI.item.TreeNode
            },
            defaultProduct: 'treeNode'
        });

        return this;
    };
    /**
     * Sets the parent node for the current one.
     * @param {null|PMUI.item.TreeNode|PMUI.panel.TreePanel} parent.
     * @chainable
     */
    TreeNode.prototype.setParent = function (parent) {
        if (parent === null || parent instanceof PMUI.item.TreeNode || parent instanceof PMUI.panel.TreePanel) {
            TreeNode.superclass.prototype.setParent.call(this, parent);
        }
        return this;
    };
    /**
     * Returns true if the node is sterile (it can have more child nodes).
     * @return {Boolean}
     */
    TreeNode.prototype.isSterile = function () {
        return this.sterile;
    };
    /**
     * Sets a custom css class for the node's html.
     * @param {String} nodeClass
     */
    TreeNode.prototype.setNodeClass = function (nodeClass) {
        if (typeof nodeClass !== 'string') {
            throw new Error('setNodeClass(): the parameter must be a string.');
        }
        nodeClass = jQuery.trim(nodeClass);
        if (this.html) {
            jQuery(this.dom.nodeElement).removeClass(this.nodeClass).addClass('pmui-treepanel-node').addClass(nodeClass);
        }
        this.nodeClass = nodeClass;
        return this;
    };
    /**
     * Returns the custom css class of the node's html.
     * @return {String}
     */
    TreeNode.prototype.getNodeClass = function () {
        return this.nodeClass;
    };
    /**
     * Sets if the node is sterile or not (if can have more child nodes).
     * @param {Boolean} sterile
     * @chainable
     */
    TreeNode.prototype.setSterility = function (sterile) {
        this.sterile = !!sterile;
        return this;
    };
    /**
     * Sets the label for the node.
     * @param {String} label
     */
    TreeNode.prototype.setLabel = function (label) {
        if (typeof label !== 'string') {
            throw new Error('setLabel(): This method only accepts parameter of type String.');
        }
        this.label = label;
        if (this.html) {
            this.dom.label.textContent = label;
        }

        return this;
    };
    /**
     * Applies the collapsing to its child nodes list.
     * @param  {Boolean} applyToChildren If the child nodes will apply the collapsing too.
     * @chainable
     */
    TreeNode.prototype.collapse = function (applyToChildren) {
        var i, childNodes;
        this.collapsed = true;
        if (applyToChildren) {
            childNodes = this.items.asArray();
            for (i = 0; i < childNodes.length; i += 1) {
                childNodes[i].collapse(true);
            }
        }
        if (this.html) {
            jQuery(this.containmentArea).hide();
            jQuery(this.dom.nodeElement).addClass('pmui-treepanel-node-collapsed');
            if (typeof this.onCollapse === 'function') {
                this.onCollapse();
            }
        }

        return this;
    };
    /**
     * Applies the expanding to its child nodes list.
     * @param  {Boolean} applyToChildren If the child nodes will apply the expanding too.
     * @chainable
     */
    TreeNode.prototype.expand = function (applyToChildren) {
        var i,
            childNodes;

        this.collapsed = false;
        if (applyToChildren) {
            childNodes = this.items.asArray();
            for (i = 0; i < childNodes.length; i += 1) {
                childNodes[i].expand(true);
            }
        }
        if (this.html) {
            jQuery(this.containmentArea).show();
            jQuery(this.dom.nodeElement).removeClass('pmui-treepanel-node-collapsed');
            if (typeof this.onExpand === 'function') {
                this.onExpand();
            }
        }

        return this;
    };
    /**
     * Returns true if the node's child list is collapsed, otherwise it returns false.
     * @return {Boolean}
     */
    TreeNode.prototype.isCollapsed = function () {
        return this.collapsed;
    };
    /**
     * Returns true if the node has no child nodes, otherwise it returns false.
     * @return {Boolean}
     */
    TreeNode.prototype.isLeaf = function () {
        return !this.items.getSize();
    };
    /**
     * Returns true id the node is root, otherwise it returns false.
     * @return {Boolean}
     */
    TreeNode.prototype.isRoot = function () {
        return this.parent instanceof PMUI.panel.TreePanel || !this.parent;
    };
    /**
     * Updates the node's css class that sepcifies if the node is leaf or not.
     * @private
     * @chainable
     */
    TreeNode.prototype.updateNodeTypeClass = function () {
        if (this.items.getSize() === 0) {
            jQuery(this.dom.nodeElement).removeClass('pmui-treepanel-node-father').addClass('pmui-treepanel-node-leaf');
        } else {
            jQuery(this.dom.nodeElement).removeClass('pmui-treepanel-node-leaf').addClass('pmui-treepanel-node-father');
        }
        return this;
    };
    /**
     * The private handler fot the {@link #event-onBeforeAppend onBeforeAppend} event.
     * @private
     * @chainable
     */
    TreeNode.prototype.onBeforeAppendHandler = function (treeNode) {
        if (typeof this.onBeforeAppend === 'function') {
            this.onBeforeAppend(this, treeNode);
        }
        this.parent && this.parent.onBeforeAppendHandler(treeNode);
        return this;
    };
    /**
     * The private handler for the {@link #event-onAppend} event.
     * @private
     * @chainable
     */
    TreeNode.prototype.onAppendHandler = function (treeNode) {
        if (typeof this.onAppend === 'function') {
            this.onAppend(this, treeNode);
        }
        this.parent && this.parent.onAppendHandler(treeNode);
        return this;
    };
    /**
     * Adds a new child node.
     * @param {Object|PMUI.item.TreeNode} item  The item to add, it can be:
     * - A object literal with the config options to create the {@link PMUI.item.TreeNode TreeNode} to be added. If
     * you have set the {@link PMUI.item.TreeNode#property-childDefaultSettings childDefaultSettings property} the
     * contents of the object literal will be merge with (and into) the contens of this property. The resulting object
     * literal will be used to create the {@link PMUI.item.TreeNode TreeNode} to be added.
     * - An instance of {@link PMUI.item.TreeNode TreeNode}.
     * @param {Number} index The index position for the node insertion.
     */
    TreeNode.prototype.addItem = function (item, index) {
        var settings = {},
            toAdd;

        if (this.isSterile()) {
            throw new Error('addItem(): The tree node is sterile, it can\'t have children.');
        }
        if (typeof item !== 'object' && !(item instanceof PMUI.item.TreeNode)) {
            throw new Error('addItem(): The parameter must be an instance of PMUI.item.TreeNode or an object.');
        }

        if (item instanceof PMUI.item.TreeNode) {
            item.setParent(this);
            toAdd = item;
        } else if (typeof item === 'object') {
            if (this.recursiveChildrenDefaultSettings) {
                item.childrenDefaultSettings = this.childrenDefaultSettings;
                item.recursiveChildrenDefaultSettings = true;
            }
            jQuery.extend(true, settings, this.childrenDefaultSettings, item);
            settings.parent = this;
            toAdd = this.factory.make(settings);
        }

        if (toAdd) {
            if (typeof this.onBeforeAppend === 'function') {
                this.onBeforeAppend(this, toAdd);
            }
            this.onBeforeAppendHandler(toAdd);
            TreeNode.superclass.prototype.addItem.call(this, toAdd, index);
            this.onAppendHandler(toAdd);
        }

        if (this.html) {
            this.updateNodeTypeClass();
        }

        return this;
    };
    /**
     * @inheritdoc
     */
    TreeNode.prototype.setItems = function (items) {
        if (this.onAppend !== undefined) {
            TreeNode.superclass.prototype.setItems.call(this, items);
        }
        return this;
    };
    /**
     * Removes a direct child node.
     * @param  {Number|PMUI.item.TreeNode|String} item It can be a string (id of the child to remove),
     * a number (index of the child to remove) or a TreeNode object.
     * @chainable
     */
    TreeNode.prototype.removeItem = function (item) {
        TreeNode.superclass.prototype.removeItem.call(this, item);
        this.updateNodeTypeClass();
        return this;
    };
    /**
     * Toggles the collapsing/expanding actions.
     * @chainable
     */
    TreeNode.prototype.toggleCollapsing = function () {
        if (this.isCollapsed()) {
            this.expand();
        } else {
            this.collapse();
        }
        return this;
    };
    /**
     * Clears the current filter criteria.
     * @chainable
     */
    TreeNode.prototype.clearFilter = function () {
        this.filter("");
        return this;
    };
    /**
     * Filter the current node and all its children.
     * @param  {String} filterCriteria The criteria for filtering, the filtering will be perform in the node's data.
     * @return {Boolean} True if any of its nodes meets the filtering criteria.
     */
    TreeNode.prototype.filter = function (filterCriteria) {
        var res = false,
            childNodes = this.items.asArray(),
            i,
            regExp,
            partialRes;

        for (i = 0; i < childNodes.length; i += 1) {
            childNodes[i].setVisible(partialRes = childNodes[i].filter(filterCriteria));
            res = res || partialRes;
        }
        if (!res) {
            regExp = new RegExp(filterCriteria, "i");
            if (regExp.test(this.label)) {
                return true;
            }
        }
        return res;
    };
    /**
     * Define then events for the object.
     * @chainable
     */
    TreeNode.prototype.defineEvents = function () {
        var that;

        TreeNode.superclass.prototype.defineEvents.call(this);
        if (this.dom.title) {
            that = this;
            this.addEvent('click').listen(this.dom.title, function (e) {
                var root = that.getRoot(),
                    panel = root.parent;

                e.preventDefault();
                if (typeof that.onClick === 'function') {
                    that.onClick();
                }
                if (panel instanceof PMUI.panel.TreePanel && typeof panel.onNodeClick === 'function') {
                    panel.onNodeClick(panel, that);
                }
                that.toggleCollapsing();
            });
        }

        return this;
    };
    /**
     * Creates the node's html.
     * @return {HTMLELement}
     */
    TreeNode.prototype.createHTML = function () {
        var nodeElement,
            title,
            label,
            icon,
            childrenList,
            elbow;

        if (this.html) {
            return this.html;
        }

        html = PMUI.createHTMLElement(this.elementTag || "div");
        html.id = this.id;
        PMUI.linkToPMUIObject(html, this);
        this.html = html;
        this.actionHTML = html;
        this.applyStyle();

        nodeElement = this.html;
        nodeElement.className = 'pmui-treepanel-node';
        nodeElement.id = this.id;
        title = PMUI.createHTMLElement('a');
        title.className = 'pmui-treepanel-node-title';
        title.href = '#';
        elbow = PMUI.createHTMLElement('b');
        elbow.className = 'pmui-treepanel-node-elbow';
        elbow.innerHTML = '&nbsp;';
        label = PMUI.createHTMLElement('span');
        label.className = 'pmui-treepanel-node-label';
        icon = PMUI.createHTMLElement('i');
        icon.className = 'pmui-treepanel-node-icon';
        icon.innerHTML = '&nbsp;';
        childrenList = PMUI.createHTMLElement('ul');
        childrenList.className = 'pmui-treepanel-list';

        this.actionHTML = title;

        title.appendChild(elbow);
        title.appendChild(icon);
        title.appendChild(label);
        nodeElement.appendChild(title);
        nodeElement.appendChild(childrenList);

        this.dom.nodeElement = nodeElement;
        this.dom.title = title;
        this.dom.label = label;
        this.dom.icon = icon;
        this.containmentArea = childrenList;

        this.html = nodeElement;

        this.updateNodeTypeClass();

        this.setParent(this.parent)
            .setSterility(this.sterile)
            .setLabel(this.label)
            .setNodeClass(this.nodeClass);
        if (this.items.getSize() > 0) {
            this.paintItems(this.items.asArray().slice(0));
        }
        if (this.collapsed) {
            this.collapse();
        } else {
            this.expand();
        }

        this.style.applyStyle();
        return this.html;
    };

    PMUI.extendNamespace('PMUI.item.TreeNode', TreeNode);
}());
(function () {
    /**
     * @class PMUI.item.ListItem
     */
    var ListItem = function (settings) {
        ListItem.superclass.call(this, settings);
        this.text = null;
        this.iconClass = null;
        this.toolbar = null;
        this.dom = {};
        this.actions = [];
        this.visibleIcon = null;
        this.data = null;
        this.textDataBind = null;
        this.onClick = null;
        ListItem.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Item', ListItem);

    ListItem.prototype.type = "ListItem";

    ListItem.prototype.init = function (settings) {
        var defaults = {
            elementTag: 'li',
            text: '[list-item]',
            actions: [],
            iconClass: null,
            visibleIcon: false,
            data: {},
            textDataBind: null,
            onClick: null
        };

        this.toolbar = new PMUI.toolbar.Toolbar({
            style: {
                cssClasses: ['pmui-listitem-actions']
            }
        });

        this.data = new PMUI.data.DataField();

        jQuery.extend(true, defaults, settings);

        this.setTextDataBind(defaults.textDataBind)
            .setElementTag(defaults.elementTag)
            .setText(defaults.text)
            .setIconClass(defaults.iconClass)
            .setActions(defaults.actions)
            .setData(defaults.data)
            .setOnClickHandler(defaults.onClick);

        if (defaults.visibleIcon) {
            this.showIcon();
        } else {
            this.hideIcon();
        }
    };

    ListItem.prototype.setOnClickHandler = function (handler) {
        if (!(handler === null || typeof handler === 'function')) {
            throw new Error("setOnClickHandler(): The parameter must be or null.");
        }
        this.onClick = handler;
        return this;
    };

    ListItem.prototype.onClickHandler = function () {
        var that = this;

        return function () {
            if (typeof that.onClick === 'function') {
                that.onClick(that);
            }
        };
    };

    ListItem.prototype.setTextDataBind = function (textDataBind) {
        if (textDataBind !== null && typeof textDataBind !== 'string') {
            throw new Error("setTextDataBind(): the parameter must be a string or null.");
        }
        this.textDataBind = textDataBind;
        return this.bindData();
    };

    ListItem.prototype.bindData = function () {
        var data = this.data.getRecord();

        this.setText(data[this.textDataBind] || this.text || "");

        return this;
    };

    ListItem.prototype.setData = function (data) {
        var key;

        if (data instanceof PMUI.data.DataField) {
            this.data = data;
        } else if (typeof data === 'object') {
            this.data.clear();
            for (key in data) {
                this.data.addAttribute(key, data[key]);
            }
        } else {
            throw new Error("setData(): it only accepts a JSON object o an instance of PMUI.data.DataField as parameter.");
        }
        this.bindData();

        return this;
    };

    ListItem.prototype.clearActions = function () {
        this.toolbar.clearItems();
        return this;
    };

    ListItem.prototype.addAction = function (action) {
        var that = this,
            aux,
            handler;

        aux = action.handler || null;
        if (!(aux === null || typeof aux === 'function')) {
            throw new Error("addAction(): The parameter's \"'handler\" property must be a function or null or not be specified.");
        }

        handler = aux ? function () {
            if (aux) {
                aux.call(that, that, this);
            }
        } : null;

        action.action = handler;

        if (action instanceof PMUI.toolbar.ToolbarAction) {
            action.setAction(handler);
        } else if (typeof action === 'object') {
            action.action = handler;
        }

        this.toolbar.addItem(action);

        return this;
    };

    ListItem.prototype.setActions = function (actions) {
        var i;

        if (!jQuery.isArray(actions)) {
            throw new Error("setActions(): The parameter must be an array.");
        }
        this.clearActions();
        for (i = 0; i < actions.length; i += 1) {
            this.addAction(actions[i]);
        }
        return this;
    };

    ListItem.prototype.setIconClass = function (iconClass) {
        if (!(iconClass === null || typeof iconClass === 'string')) {
            throw new Error("setIconClass(): the parameter must be a string or null.");
        }
        this.iconClass = iconClass;
        if (this.dom.iconContainer) {
            this.dom.iconContainer.className = 'pmui-listitem-icon ' + (iconClass || "");
        }
        return this;
    };

    ListItem.prototype.hideIcon = function () {
        this.visibleIcon = false;
        if (this.dom.iconContainer) {
            this.dom.iconContainer.style.display = 'none';
        }
        return this;
    };

    ListItem.prototype.showIcon = function () {
        this.visibleIcon = true;
        if (this.dom.iconContainer) {
            this.dom.iconContainer.style.display = '';
        }
        return this;
    }

    ListItem.prototype.setText = function (text) {
        if (typeof text !== 'string') {
            throw new Error("setText(): the parameter must be a string.");
        }
        this.text = text;
        if (this.dom.textContainer) {
            this.dom.textContainer.textContent = text;
        }
        return this;
    };

    ListItem.prototype.getData = function () {
        return this.data.getRecord();
    };

    ListItem.prototype.defineEvents = function () {
        return this;
    };

    ListItem.prototype.createHTML = function () {
        var textContainer,
            iconContainer,
            that = this;

        if (this.html) {
            return this.html;
        }
        ListItem.superclass.prototype.createHTML.call(this);
        textContainer = PMUI.createHTMLElement('div');
        textContainer.className = 'pmui-listitem-text';
        iconContainer = PMUI.createHTMLElement('i');
        iconContainer.className = 'pmui-listitem-icon';

        this.dom.textContainer = textContainer;
        this.dom.iconContainer = iconContainer;

        this.html.appendChild(iconContainer);
        this.html.appendChild(textContainer);
        this.html.appendChild(this.toolbar.getHTML());

        this.setText(this.text);

        if (this.visibleIcon) {
            this.showIcon();
        } else {
            this.hideIcon();
        }

        this.addEvent('click').listen(this.html, function (e) {
            var parent = that.getParent();
            e.stopPropagation();
            if (typeof that.onClick === 'function') {
                that.onClick(that);
            }
            if (parent) {
                parent.onItemClickHandler(that);
            }
        });

        return this.html;
    };

    PMUI.extendNamespace('PMUI.item.ListItem', ListItem);
}());
(function () {
    /**
     * @class PMUI.proxy.Proxy
     * Defines the proxy functionality to persist data
     * @abstract
     *
     * @constructor
     * Creates a new instance of this class
     * @param {Object} options
     *
     * @cfg {Object} data Object that will be sent through the proxy
     */
    var Proxy = function (options) {
        /**
         * Data object used to send/receive through proxy
         * @type {Object}
         */
        this.data = null;
        Proxy.prototype.init.call(this, options);
    };

    /**
     * Defines the object's type
     * @type {String}
     */
    Proxy.prototype.type = "Proxy";

    /**
     * Defines the object's family
     * @type {String}
     */
    Proxy.prototype.family = "Proxy";

    /**
     * @private
     * Initilizes the object with the default values
     * @param {Object} options
     */
    Proxy.prototype.init = function (options) {
        var defaults = {
            data: null
        };

        jQuery.extend(true, defaults, options);
        this.setData(defaults.data);
    };

    /**
     * Sets the data
     * @param {Object} data Object to be sent
     */
    Proxy.prototype.setData = function (data) {
        this.data = data;
        return this;
    };

    /**
     * Returns the data related to this class
     * @return {Object}
     */
    Proxy.prototype.getData = function () {
        return this.data;
    };

    /**
     * @abstract
     * Sends the data to the target
     */
    Proxy.prototype.send = function () {
    };

    /**
     * @abstract
     * Receives the data from the target
     */
    Proxy.prototype.receive = function () {
    };

    PMUI.extendNamespace("PMUI.proxy.Proxy", Proxy);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = Proxy;
    }

}());
(function () {
    /**
     * @class PMUI.proxy.RestProxy
     * @extend PMUI.proxy.Proxy
     *
     * How used the class
     * The {@link PMUI.proxy.RestClient Restclient} class encapsulate all the funcionality about the communicate
     * with the server.
     *
     *          @example
     *          restclient = new PMUI.proxy.RestProxy({
     *              url: "url.php",
     *              method: "GET",
     *              data: {},
     *              dataType: "json",
     *              success: function () {
     *                  ...
     *              },
     *              failure: function () {
     *                  ...
     *              },
     *              complete: function() {
     *                  ...
     *              }
     *          });
     *
     *
     * @constructor
     * Creates a new component
     * @param {Object} [settings] settings The configuration options are specified on the config options:
     *
     * @cfg {String} [url=""] The url property defines the target for the RestClient.
     * @cfg {String} method
     * The available methods to consume are the follow:
     *
     *          'create', 'read', 'update', 'delete'
     *
     * These have a directly relation to:
     *
     *          'create': 'POST',
     *          'read'  : 'GET',
     *          'update': 'PUT',
     *          'delete': 'DELETE'
     *
     * Based in the {@link RestClient RestClient} library
     * @cfg {Object} data Object that will be sent through the proxy.
     * @cfg {String} [dataType='json'] The available options are the following sentences:
     *
     *      json: 'application/json',
     *      plain: 'text/plain',
     *      form: 'application/x-www-form-urlencoded',
     *      html: 'text/html'
     *
     * @cfg {String} [authorizationType= "none"] The options availables for the property:
     *
     *      "none"
     *      "basic"
     *      "oauth2"
     *
     * @cfg {Boolean} [authorizationOAuth=false] For enable the communicate between client-server
     * through Oauth protocol the property must to be true
     *
     */
    var RestProxy = function (options) {
        RestProxy.superclass.call(this, options);
        this.url = null;
        this.method = null;
        this.rc = null;
        this.data = null;
        RestProxy.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.proxy.Proxy', RestProxy);
    /**
     * @property {String}
     * @readonly
     */
    RestProxy.prototype.type = "RestProxy";
    RestProxy.prototype.init = function (options) {
        var defaults = {
            url: null,
            method: 'GET',
            data: {},
            dataType: 'json',
            authorizationType: 'none',
            authorizationOAuth: false,
            success: function () {
            },
            failure: function () {
            },
            complete: function () {
            }
        };
        jQuery.extend(true, defaults, options);
        this.setRestClient()
            .setUrl(defaults.url)
            .setAuthorizationOAuth(defaults.authorizationOAuth)
            .setMethod(defaults.method)
            .setData(defaults.data)
            .setDataType(defaults.dataType)
            .setSuccessAction(defaults.success)
            .setFailureAction(defaults.failure)
            .setCompleteAction(defaults.complete);
    };

    /**
     * Creates an instance of the {@link RestClient ResClient} and
     * stores the object inside of {@link RestProxy#rc rc property}
     */
    RestProxy.prototype.setRestClient = function () {
        if (this.rc instanceof RestClient === false) {
            this.rc = new RestClient();
        }
        return this;
    };
    /**
     * Sets the url that will be used for send or get the data
     * from server, It's a basic element for {@link RestClient RestClient}
     * @param {String} url A string that represents the connection between
     * Client and server
     *
     *
     */
    RestProxy.prototype.setUrl = function (url) {
        this.url = url;
        return this;
    };
    /**
     * Sets the OAuth protocol to 'false' or 'true' for enable or disable the property.
     *
     *      //Example
     *      obj = new PMUI.proxy.RestProxy();
     *      obj.setAuthorizationOAuth(true);
     *
     * @param {Boolean} option Defines the boolean option
     */
    RestProxy.prototype.setAuthorizationOAuth = function (option) {
        if (typeof option === 'boolean') {
            this.rc.setSendBearerAuthorization(option);
        }
        return this;
    };
    /**
     * This method is useful for update the current action.
     * The available methods to consume are the follow:
     *      'create', 'read', 'update', 'delete'
     * These have a directly relation to:
     *      'create': 'POST',
     *      'read'  : 'GET',
     *      'update': 'PUT',
     *      'delete': 'DELETE'
     * Based in the {@link RestClient RestClient} library
     * @param {String} A string that reporesents
     */
    RestProxy.prototype.setMethod = function (method) {
        this.method = method;
        return this;
    };
    RestProxy.prototype.setSuccessAction = function (action) {
        RestProxy.prototype.success = action;
        return this;
    };
    RestProxy.prototype.setFailureAction = function (action) {
        RestProxy.prototype.failure = action;
        return this;
    };
    RestProxy.prototype.setCompleteAction = function (action) {
        RestProxy.prototype.complete = action;
        return this;
    };
    /**
     *
     * @param {Object} data Data for send to server
     * @return {PMUI.proxy.RestProxy} this The current component
     *
     */
    RestProxy.prototype.setData = function (data) {
        this.data = data;
        return this;
    };
    /**
     * Sets the Data type used for request data.
     * the types availables for the methods are the following:
     *
     *      json: 'application/json',
     *      plain: 'text/plain',
     *      form: 'application/x-www-form-urlencoded',
     *      html: 'text/html'
     *
     *      //For example
     *      restclient = new PMUI,proxy.RestProxy();
     *      restclient.setDataType("json");
     *      ...
     *
     * @param {String} dataType Type
     */
    RestProxy.prototype.setDataType = function (dataType) {
        this.rc.setDataType(dataType);
        return this;
    };
    RestProxy.prototype.setCredentials = function (usr, pass) {
        this.rc.setBasicCredentials(usr, pass);
        return this;
    };
    RestProxy.prototype.setContentType = function () {
        this.rc.setContentType();
        return this;
    };
    /**
     * Sets the authorization type and credentials for the RestClient.
     *
     * The options availables about authorization types are:
     *
     *      'none'
     *      'basic'
     *      'oauth2'
     *
     *      The credentials must have two keys: The username and password
     *
     *      //For example
     *      restclient = new PMUI.proxy.RestProxy();
     *      restclient.setAuthorizationType('oauth2', credentials);
     *
     *      Is possible sets the property while instances the class like to sentence below:
     *
     *      restclient = new PMUI.proxy.RestProxy({
     *          ...
     *          authorizationOAuth: true
     *          ...
     *      });
     *
     *
     * @param {Object} type Authorization type
     * @param {Object} credentials These are the keys that represent the username and password
     */
    RestProxy.prototype.setAuthorizationType = function (type, credentials) {
        this.rc.setAuthorizationType(type);
        switch (type) {
            case 'none':
                break;
            case 'basic':
                this.rc.setBasicCredentials(credentials.client, credentials.secret);
                break;
            case 'oauth2':
                this.rc.setAccessToken(credentials);
                break;
        }

        return this;
    };
    /**
     * Send data to server using POST method from RestClient
     * @param {Object} settings Configuration parameters
     *
     *      //For example
     *      this.post({
     *          url: "url.php",
     *          data: {
     *              grant_type: "authorization_code",
     *              code: keys.authorizedCode
     *          },
     *          success: function (xhr, response) {
     *              console.log(response);
     *          },
     *          failure: function (xhr, response) {
     *              console.log(response);
     *          },
     *          complete: function (xhr, response) {
     *              console.error(response);
     *          }
     *      });
     *
     */
    RestProxy.prototype.post = function (settings) {
        var that = this;
        if (settings !== undefined) {
            that.init(settings);
        }
        if (this.rc) {
            that.rc.postCall({
                url: that.url,
                id: that.uid,
                data: that.data,
                success: function (xhr, response) {
                    that.success.call(that, xhr, response);
                },
                failure: function (xhr, response) {
                    that.failure.call(that, xhr, response);
                },
                complete: function (xhr, response) {
                    that.complete.call(that, xhr, response);
                }
            });
            that.rc.setSendBearerAuthorization(false);

        } else {
            throw new Error("the RestClient was not defined, please verify the property 'rc' for continue.");
        }
    };
    /**
     * Updates data from the server using UPDATE method from RestClient
     * @param  {Object} settings
     * If it necessary is possible overwrite all the data instantiated on the RestClient
     *
     *      //For example
     *      this.update({
     *          url: "url.php",
     *          authorizationOAuth: true,
     *          success: function (xhr, response) {
     *              console.log(response);
     *          },
     *          failure: function (xhr, response) {
     *              console.error(response);
     *          },
     *          complete: function (xhr, response) {
     *              console.error(response);
     *          }
     *      });
     *
     */
    RestProxy.prototype.update = function (settings) {
        var that = this;

        if (settings !== undefined) {
            that.init(settings);
        }
        if (this.rc) {
            this.rc.putCall({
                url: this.url,
                id: this.uid,
                data: this.data,
                success: function (xhr, response) {
                    that.success.call(this, xhr, response);
                },
                failure: function (xhr, response) {
                    that.failure.call(this, xhr, response);
                },
                complete: function (xhr, response) {
                    that.complete.call(that, xhr, response);
                }
            });
        } else {
            throw new Error("the RestClient was not defined, please verify the property 'rc' for continue.");
        }
    };
    /**
     * Gets data from the server using GET method from RestClient
     * @param  {Object} settings
     * If it necessary is possible overwrite all the data instantiated on the RestClient
     *
     *      //For example
     *      this.get({
     *          url: "url.php",
     *          authorizationOAuth: true,
     *          success: function (xhr, response) {
     *              console.log(response);
     *          },
     *          failure: function (xhr, response) {
     *              console.error(response);
     *          },
     *          complete: function (xhr, response) {
     *              console.error(response);
     *          }
     *      });
     *
     */
    RestProxy.prototype.get = function (settings) {
        var that = this;

        if (settings !== undefined) {
            that.init(settings);
        }
        if (this.rc) {
            that.rc.getCall({
                url: that.url,
                id: that.uid,
                data: that.data,
                success: function (xhr, response) {
                    that.success.call(that, xhr, response);
                },
                failure: function (xhr, response) {
                    that.failure.call(that, xhr, response);
                },
                complete: function (xhr, response) {
                    that.complete.call(that, xhr, response);
                }
            });
            that.rc.setSendBearerAuthorization(false);

        } else {
            throw new Error("the RestClient was not defined, please verify the property 'rc' for continue.");
        }
    };
    /**
     * Removes data using DELETE method from RestClient
     * @param  {Object} settings
     * If it necessary is possible overwrite all the data instantiated on the RestClient
     *
     *      //For example
     *      this.remove({
     *          url: "url.php",
     *          authorizationOAuth: true,
     *          success: function (xhr, response) {
     *              console.log(response);
     *          },
     *          failure: function (xhr, response) {
     *              console.error(response);
     *          },
     *          complete: function (xhr, response) {
     *              console.error(response);
     *          }
     *      });
     *
     */
    RestProxy.prototype.remove = function (settings) {
        var that = this;

        if (settings !== undefined) {
            that.init(settings);
        }
        if (this.rc) {
            this.rc.deleteCall({
                url: this.url,
                id: this.uid,
                data: this.data,
                success: function (xhr, response) {
                    that.success.call(this, xhr, response);
                },
                failure: function (xhr, response) {
                    that.failure.call(this, xhr, response);
                },
                complete: function (xhr, response) {
                    that.complete.call(that, xhr, response);
                }
            });
        } else {
            throw new Error("the RestClient was not defined, please verify the property for continue.");
        }
    };
    /**
     * This is the callback for {@link RestClient RestClient} and it will be actived
     * and executed when the {@link RestClient RestClient} petition was success.
     * @param {Object} xhr
     * @param {Object} response
     */
    RestProxy.prototype.success = function (xhr, response) {
    };
    /**
     * If something happend with the connection or maybe the server is
     * down, this callback will be executed
     * @param {Object} xhr
     * @param {Object} response
     */
    RestProxy.prototype.failure = function (xhr, response) {
    };
    /**
     * This is the callback for {@link RestClient RestClient} and it will be actived
     * and executed when the {@link RestClient RestClient} petition finishes.
     * @param {Object} xhr
     * @param {Object} response
     */
    RestProxy.prototype.complete = function (xhr, response) {
    };

    PMUI.extendNamespace('PMUI.proxy.RestProxy', RestProxy);

    if (typeof exports !== 'undefined') {
        module.exports = RestProxy;
    }

}());
(function () {
    /**
     * @class PMUI.event.Event
     * Handles the events generated in the PMUI library
     * @abstract
     *
     * @constructor
     * Creates a new instance of class
     * @param {Object} options
     *
     * @cfg {Object} element Defines the HTMLElement
     * @cfg {Function} handler Defines the callback Function to be executed
     * @cfg {PMUI.event.Action} action Defines an Action to be used to handle the callback
     * @cfg {String} name Event name
     */
    var Event = function (options) {
        /**
         * Stores the HTMLElement associated
         * @type {Object}
         */
        this.element = null;
        /**
         * Stores the callback function to be executed
         * @type {Function}
         */
        this.handler = null;
        /**
         * Stores the selector for delegated event listeners.
         * @type {String}
         */
        this.selector = null;
        /**
         * Event name
         * @type {String}
         */
        this.eventName = null;
        Event.prototype.init.call(this, options);
    };

    /**
     * Defines the object's type
     * @type {String}
     */
    Event.prototype.type = "Event";

    /**
     * Defines the object's family
     * @type {String}
     */
    Event.prototype.family = "Event";

    /**
     * @private
     * Initializes the object with default options
     * @param  {Object} options
     */
    Event.prototype.init = function (options) {
        var defaults = {
            handler: function (scope) {
            }
        };
        jQuery.extend(true, defaults, options);
        if (defaults.action && defaults.action instanceof PMUI.event.Action) {
            this.setHandler(defaults.action.handler);
        } else {
            this.setHandler(defaults.handler);
        }
        this.setElement(defaults.element)
            .setEventName(defaults.name);
        return this;
    };

    /**
     * Sets the HTML Element
     * @param {Object} element [description]
     */
    Event.prototype.setElement = function (element) {
        this.element = element;
        return this;
    };

    /**
     * Sets the callback function
     * @param {Function} fn
     */
    Event.prototype.setHandler = function (fn) {
        if (typeof fn === 'function') {
            this.handler = fn;
        }
        return this;
    };
    /**
     * Sets the selector to be used for delegated event handlers.
     * @param {String} selector
     * @chainable
     */
    Event.prototype.setSelector = function (selector) {
        if (typeof selector === 'string') {
            this.selector = selector;
        }
        return this;
    };
    /**
     * Sets the event name
     * @param {String} name
     */
    Event.prototype.setEventName = function (name) {
        this.eventName = name;
        return this;
    };
    /**
     * Add a handler to listen for a event with delegation.
     * @param  {HTMLElement} parentElement The parent element whose child elements the event will be delegate to.
     * @param  {String} selector      A valid jQuery selector to filter the descendants of the selected
     * elements that trigger the event.
     * @param  {Function} handler       A function to execute when the event is triggered.
     * @chainable
     */
    Event.prototype.listenWithDelegation = function (parentElement, selector, handler) {
        var handlerFunction;
        if (handler instanceof PMUI.event.Action) {
            handlerFunction = handler.handler;
        } else {
            handlerFunction = handler;
        }
        $(parentElement).on(this.eventName, selector, handler);
        this.setHandler(handler).setElement(parentElement).setSelector(selector);
        return this;
    };

    /**
     * @abstract
     * Defines the way to listen the event (jquery)
     * @param  {HTMLElement} element [description]
     * @param  {Function} handler [description]
     */
    Event.prototype.listen = function (element, handler) {
        var handlerFunction;
        if (handler instanceof PMUI.event.Action) {
            handlerFunction = handler.handler;
        } else {
            handlerFunction = handler;
        }
        jQuery(element).on(this.eventName, handlerFunction);
        this.setHandler(handlerFunction).setElement(element);
        return this;
    };
    /**
     * Removes the event listener.
     * @chainable
     */
    Event.prototype.unlisten = function () {
        if (this.selector) {
            jQuery(this.element).off(this.eventName, this.selector, this.handler);
        } else {
            jQuery(this.element).off(this.eventName, this.handler);
        }
        return this;
    };

    PMUI.extendNamespace('PMUI.event.Event', Event);

    if (typeof exports !== 'undefined') {
        module.exports = Event;
    }

}());
(function () {
    /**
     * @class  PMUI.event.EventFactory
     * @extend PMUI.util.Factory
     * Extends the factory class to produce Events instances
     *
     * @constructor
     * Creates a new instance od the class
     */
    var EventFactory = function () {
        EventFactory.superclass.call(this);
        EventFactory.prototype.init.call(this);
    };

    PMUI.inheritFrom('PMUI.util.Factory', EventFactory);

    /**
     * Defines the object's type
     * @type {String}
     */
    EventFactory.prototype.type = 'EventFactory';

    /**
     * @private
     * Define the event types supported
     * @type {Object}
     */
    EventFactory.prototype.eventTypes = {
        'click': 'mouse',
        'mousedown': 'mouse',
        'mouseup': 'mouse',
        'mousemove': 'mouse',
        'mouseover': 'mouse',
        'mouseout': "mouse",
        'mouseenter': "mouse",
        'mouseleave': "mouse",
        'dblclick': "mouse",
        'drag': 'mouse',
        'drop': 'mouse',
        'resize': 'mouse',
        'rightclick': 'mouse',
        'contextmenu': 'mouse',
        'blur': 'form',
        'change': 'form',
        'focus': 'form',
        'select': 'form',
        'submit': 'form',
        'keyup': 'keyboard',
        'keydown': 'keyboard',
        'keypress': 'keyboard'
    };

    /**
     * @private
     * Initializes the object with default values
     */
    EventFactory.prototype.init = function () {
        var defaults = {
            products: {
                "mouse": PMUI.event.MouseEvent,
                "form": PMUI.event.FormEvent,
                "keyboard": PMUI.event.KeyboardEvent,
                "event": PMUI.event.Event
            },
            defaultProduct: "event"
        };
        this.setProducts(defaults.products)
            .setDefaultProduct(defaults.defaultProduct);
    };

    /**
     * Overwrite the make function to accept strings
     * @param  {Object/String} type
     * @return {PMUI.event.Event}
     */
    EventFactory.prototype.make = function (type) {
        var eventInstance,
            eventType;
        if (this.isValidClass(type)) {
            eventInstance = type;
        } else {
            eventType = this.eventTypes[type] || 'event';
            eventInstance = this.build(eventType, {name: type});
        }
        return eventInstance;
    };

    PMUI.extendNamespace('PMUI.event.EventFactory', EventFactory);

    if (typeof exports !== 'undefined') {
        module.exports = EventFactory;
    }

}());
(function () {
    /**
     * @class PMUI.event.MouseEvent
     * @extend PMUI.event.Event
     * Handles the events generated by the mouse
     *
     * @constructor
     * Create a new instance
     * @param {Object} options Constructor options
     *
     * @cfg {Object} element Defines the HTMLElement
     * @cfg {Function} handler Defines the callback Function to be executed
     * @cfg {PMUI.event.Action} action Defines an Action to be used to handle the callback
     * @cfg {String} name Event name
     */
    var MouseEvent = function (options) {
        MouseEvent.superclass.call(this, options);
    };

    PMUI.inheritFrom('PMUI.event.Event', MouseEvent);

    /**
     * Defines the object's type
     * @type {String}
     */
    MouseEvent.prototype.type = "MouseEvent";

    PMUI.extendNamespace('PMUI.event.MouseEvent', MouseEvent);

    if (typeof exports !== 'undefined') {
        module.exports = MouseEvent;
    }

}());
(function () {
    /**
     * @class PMUI.event.KeyboardEvent
     * @extend PMUI.event.Event
     * Handles the events generated by the mouse
     *
     * @constructor
     * Create a new instance
     * @param {Object} options Constructor options
     *
     * @cfg {Object} element Defines the HTMLElement
     * @cfg {Function} handler Defines the callback Function to be executed
     * @cfg {PMUI.event.Action} action Defines an Action to be used to handle the callback
     * @cfg {String} name Event name
     */
    var KeyboardEvent = function (options) {
        KeyboardEvent.superclass.call(this, options);
    };

    PMUI.inheritFrom('PMUI.event.Event', KeyboardEvent);

    /**
     * Defines the object's type
     * @type {String}
     */
    KeyboardEvent.prototype.type = "KeyboardEvent";

    PMUI.extendNamespace('PMUI.event.KeyboardEvent', KeyboardEvent);

    if (typeof exports !== 'undefined') {
        module.exports = KeyboardEvent;
    }

}());
(function () {
    /**
     * @class PMUI.event.FormEvent
     * @extend PMUI.event.Event
     * Handles the events generated by the mouse
     *
     * @constructor
     * Create a new instance
     * @param {Object} options Constructor options
     *
     * @cfg {Object} element Defines the HTMLElement
     * @cfg {Function} handler Defines the callback Function to be executed
     * @cfg {PMUI.event.Action} action Defines an Action to be used to handle the callback
     * @cfg {String} name Event name
     */
    var FormEvent = function (options) {
        FormEvent.superclass.call(this, options);
    };

    PMUI.inheritFrom('PMUI.event.Event', FormEvent);

    /**
     * Defines the object's type
     * @type {String}
     */
    FormEvent.prototype.type = "FormEvent";

    PMUI.extendNamespace('PMUI.event.FormEvent', FormEvent);

    if (typeof exports !== 'undefined') {
        module.exports = FormEvent;
    }

}());
(function () {
    /**
     * @class PMUI.draw.Core
     * @extends PMUI.core.Element
     *
     * This class contains the common behavior of the main families of classes
     * in the library
     * This class should never be instantiated since its just an abstraction of
     * properties that classes in the library share
     *
     *      //e.g.
     *      //We will set the properties defined in this class, to a custom shape
     *      var customShape = new PMUI.draw.CustomShape({
     *          id : "someid",
     *          canvas : someCanvas //assuming there is a canvas instance
     *          style: { //style options regarding the objects
     *              cssProperties: {}, //These are the style properties we want the
     *              //object to have
     *              cssClasses: ["someclass"] //css classes that will be applied
     *              to the object
     *          },
     *          //now we set the width and height
     *          width : 30,
     *          height : 50,
     *          //and the coordinates we want the shape to be positioned
     *          x : 10,
     *          y : 5,
     *          //z-index of the element
     *          zOrder : 1,
     *          //set to true if we want to make it visible
     *          visible : true,
     *
     *      });
     *
     * @constructor
     * Creates an instance of the class
     * @param {Object} options options for initializing the object
     * @cfg {String} id id that will be assigned to the element
     * @cfg {Object} [style={
     *     cssProperties:  {},
     *     cssClasses:  []
     * }] style properties and classes that we want to assign to the element
     * @cfg {PMUI.draw.Canvas} [canvas=null] canvas associated to the element
     * @cfg {number} [width=0] width of the element
     * @cfg {number} [height=0] height of the element
     * @cfg {number} [x=0] x coordinate of the element
     * @cfg {number} [y=0] y coordinate of the element
     * @cfg {number} [zOrder=1] z-Index applied to the element
     * @cfg {boolean} [visible=true] Determines whether an element will be visible
     */

    var Core = function (options) {
        Core.superclass.call(this, options);

        /**
         * Defines the canvas object
         * @type {Object}
         */
        this.canvas = null;
        /**
         * previous width of the Core
         * @property {number}
         */
        this.oldWidth = 0;
        /**
         * previous height of the Core
         * @property {number}
         */
        this.oldHeight = 0;
        /**
         * previous x coordinate of the Core
         * @property {number}
         */
        this.oldX = 0;
        /**
         * previous y coordinate of the Core
         * @property {number}
         */
        this.oldY = 0;

        /**
         * The x coordinate relative to the canvas
         * @property {number}
         * @deprecated Use {@link PMUI.draw.Core#getAbsoluteY} instead.
         */
        this.absoluteX = 0;
        /**
         * The y coordinate relative to the canvas
         * @property {number}
         * @deprecated Use {@link PMUI.draw.Core#getAbsoluteX} instead.
         */
        this.absoluteY = 0;
        /**
         * Previous x coordinate relative to the canvas
         * @property {number}
         */
        this.oldAbsoluteX = 0;
        /**
         * Previous y coordinate relative to the canvas
         * @property {number}
         */
        this.oldAbsoluteY = 0;
        /**
         * Zoom in x
         * @property {number}
         */
        this.zoomX = 0;

        /**
         * Zoom in y
         * @property {number}
         */
        this.zoomY = 0;

        /**
         * Width after the zoom
         * @property {number}
         */
        this.zoomWidth = 0;

        /**
         * Height after the zoom
         * @property {number}
         */
        this.zoomHeight = 0;

        /**
         * Saved options is a copy of the default initializer extended
         * with the parameter 'options'
         * @property {Object}
         */
        this.savedOptions = {};

        /**
         * Defines the drag behavior object
         * @type {PMUI.behavior.DragBehavior}
         */
        this.drag = null;

        Core.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.core.Element', Core);

    /**
     * Denotes the type of the object
     * @property {String}
     */
    Core.prototype.type = "Core";
    /**
     * @abstract Method for applying the styles and preform tasks related to the
     * view of the object
     */
    Core.prototype.paint = function () {
    };


    /**
     * Initializes the element with the options given
     * @param {Object} options options for initializing the object
     */
    Core.prototype.init = function (options) {
        var defaults = {
            zOrder: 1,
            visible: true,
            drag: "nodrag",
            positionMode: "absolute"
        };
        $.extend(true, defaults, options);
        this.setZOrder(defaults.zOrder)
            .setVisible(defaults.visible)
            //            .setDragBehavior(defaults.drag)
            .setCanvas(defaults.canvas);
    };

    Core.prototype.applyStyle = function () {
        if (this.html) {
            this.style.applyStyle();

            this.style.addProperties({
                display: this.visible ? this.display : "none",
                position: "absolute", //TODO use this.positionMode instead,
                left: this.zoomX,
                top: this.zoomY,
                width: this.zoomWidth,
                height: this.zoomHeight,
                zIndex: this.zOrder
            });
        }
        return this;
    };

    Core.prototype.createHTML = function () {
        var properties;
        if (!this.html) {
            Core.superclass.prototype.createHTML.call(this);
            this.defineEvents();
        }
        return this.html;
    };
    /**
     * Sets the position of the Core to a given pair of coordinates
     * @param {Number} newX new x coordinate for the Core
     * @param {Number} newY new y coordinate for the Core
     * @chainable
     */
    Core.prototype.setPosition = function (newX, newY) {
        this.setX(newX);
        this.setY(newY);
        return this;
    };

    /**
     * Sets the dimension of the Core to a given width and height
     * @param {Number} newWidth new width of the Core
     * @param {Number} newHeight new height of the Core
     * @chainable
     */
    Core.prototype.setDimension = function (newWidth, newHeight) {
        this.setWidth(newWidth);
        this.setHeight(newHeight);
        return this;
    };

    /**
     * Sets the x coordinate of the Core, returns true if successful
     * @param {Number} newX
     * @chainable
     */
    Core.prototype.setX = function (newX) {
        if (typeof newX === "number") {
            newX = Math.round(newX);
            this.x = newX;
            if (this.canvas) {
                this.zoomX = this.x * this.canvas.zoomFactor;
            } else {
                this.zoomX = this.x;
            }
            this.setAbsoluteX();
            if (this.html) {
                this.style.addProperties({left: this.zoomX});
            }
        } else {
            throw new Error("setX :  parameter newX is not a number");
        }

        return this;
    };
    /**
     * Sets the y coordinate of the Core, returns true if successful
     * @param {Number} newY
     * @chainable
     */
    Core.prototype.setY = function (newY) {
        if (typeof newY === "number") {
            newY = Math.round(newY);
            this.y = newY;
            if (this.canvas) {
                this.zoomY = this.y * this.canvas.zoomFactor;
            } else {
                this.zoomY = this.y;
            }
            this.setAbsoluteY();
            if (this.html) {
                this.style.addProperties({top: this.zoomY});
            }
        }
        return this;
    };

    /**
     * Sets the x coordinate of the Core relative to the canvas
     * @chainable
     * @deprecated absoluteX will be an in-moment property retrieved by the {PMUI.draw.Core#getAbsoluteX} method.
     */
    Core.prototype.setAbsoluteX = function () {
        if (!this.parent) {
            this.absoluteX = this.zoomX;
        } else {
            this.absoluteX = this.zoomX + this.parent.absoluteX;
        }
        return this;
    };

    /**
     * Sets the value to an old X reference
     * @param {Number} newX
     * @chainable
     */
    Core.prototype.setOldX = function (newX) {
        if (typeof newX === "number") {
            this.oldX = newX;
        }
        return this;
    };
    /**
     * Sets the value to an old y reference
     * @param {Number} newY
     * @chainable
     */
    Core.prototype.setOldY = function (newY) {
        if (typeof newY === "number") {
            this.oldY = newY;
        }
        return this;
    };
    /**
     * Sets the y coordinate of the Core relative to the canvas
     * @chainable
     * @deprecated absoluteY will be an in-time generated property retrieved by the {PMUI.draw.Core#getAbsoluteY} getter.
     */
    Core.prototype.setAbsoluteY = function () {
        if (!this.parent) {
            this.absoluteY = this.zoomY;
        } else {
            this.absoluteY = this.zoomY + this.parent.absoluteY;
        }
        return this;
    };

    /**
     * Sets the width of the Core, returns true if successful
     * @param {Number} newWidth
     * @chainable
     */
    Core.prototype.setWidth = function (newWidth) {
        var intPart;
        if (typeof newWidth === "number" && newWidth >= 0) {
            this.width = newWidth;
            if (this.canvas) {
                this.zoomWidth = this.width * this.canvas.zoomFactor;
                intPart = Math.floor(this.zoomWidth);
                this.zoomWidth = (this.zoomWidth % 2 === 0) ? intPart + 1 : intPart;
            } else {
                this.zoomWidth = this.width;
            }
            if (this.html) {
                this.style.addProperties({width: this.zoomWidth});
            }
        }
        return this;
    };

    /**
     * Sets the height of the Core, returns true if successful
     * @param {Number} newHeight
     * @chainable
     */
    Core.prototype.setHeight = function (newHeight) {
        var intPart;
        if (typeof newHeight === "number" && newHeight >= 0) {
            this.height = newHeight;
            if (this.canvas) {
                this.zoomHeight = this.height * this.canvas.zoomFactor;
                intPart = Math.floor(this.zoomHeight);
                this.zoomHeight = (this.zoomHeight % 2 === 0) ? intPart + 1 : intPart;
            } else {
                this.zoomHeight = this.height;
            }
            if (this.html) {
                this.style.addProperties({height: this.zoomHeight});
            }
        }
        return this;
    };

    /**
     * Sets the zOrder of this element
     * @param {Number} newZOrder
     * @chainable
     */
    Core.prototype.setZOrder = function (newZOrder) {
        if (typeof newZOrder === "number" && newZOrder > 0) {
            this.zOrder = newZOrder;
            if (this.html) {
                this.style.addProperties({zIndex: this.zOrder});
            }
        }
        return this;
    };

    /**
     * Sets the canvas for the current object
     * @param {PMUI.draw.Canvas} newCanvas
     * @returns {PMUI.draw.Core}
     */
    Core.prototype.setCanvas = function (newCanvas) {
        if (newCanvas && newCanvas.family === "Canvas") {
            this.canvas = newCanvas;
        }
        return this;
    };

    /**
     * Sets this element to be visible or not if it has html, just sets the display
     * property to inline or none
     * @param {boolean} newVisible
     * @chainable
     */
    Core.prototype.setVisible = function (newVisible) {

        if (typeof newVisible === "boolean") {
            this.visible = newVisible;
            if (this.html) {
                if (newVisible) {
                    this.style.addProperties({display: "inline"});
                } else {
                    this.style.addProperties({display: "none"});
                }
            }
        }
        return this;
    };
    /**
     * Sets the id of this element and updates the html if there is such
     * @param {String} newID
     * @chainable
     */
    Core.prototype.setID = function (newID) {
        this.id = newID;
        if (this.html) {
            this.html.id = this.id;
        }
        return this;
    };

    /**
     * Returns the canvas related to this object
     * @returns {PMUI.draw.Canvas}
     */
    Core.prototype.getCanvas = function () {
        return this.canvas;
    };

    /**
     * Returns the x coordinate relative to the canvas of this object
     * @return {Number}
     */
    Core.prototype.getAbsoluteX = function () {
        var x, oldDisplay;
        if (this instanceof PMUI.draw.Canvas) {
            return 0;
        } else if (PMUI.isInDOM(this.html)) {
            oldDisplay = this.html.style.display;
            this.html.style.display = 'inline-block';
            x = $(this.html).offset().left;
            this.html.style.display = oldDisplay;
            return x - $(this.canvas.html).offset().left;
        } else {
            return this.zoomX + this.parent.getAbsoluteX();
        }
    };
    /**
     * Returns the y coordinate relative to the canvas of this object
     * @return {Number}
     */
    Core.prototype.getAbsoluteY = function () {
        var y, oldDisplay;
        if (this instanceof PMUI.draw.Canvas) {
            return 0;
        } else if (PMUI.isInDOM(this.html)) {
            oldDisplay = this.html.style.display;
            this.html.style.display = 'inline-block';
            y = $(this.html).offset().top;
            this.html.style.display = oldDisplay;
            return y - $(this.canvas.html).offset().top;
        } else {
            return this.zoomY + this.parent.getAbsoluteY();
        }
    };

    /**
     * Returns the style of this Core
     * @return {PMUI.util.Style}
     */
    Core.prototype.getStyle = function () {
        return this.style;
    };

    /**
     * Gets the x coordinate relative to the zoom scale
     * @return {Number}
     */
    Core.prototype.getZoomX = function () {
        return this.zoomX;
    };

    /**
     * Gets the y coordinate relative to the zoom scale
     * @return {Number}
     */
    Core.prototype.getZoomY = function () {
        return this.zoomY;
    };

    /**
     * Gets the width relative to the zoom scale
     * @return {Number}
     */
    Core.prototype.getZoomWidth = function () {
        return this.zoomWidth;
    };

    /**
     * Gets the height relative to the zoom scale
     * @return {Number}
     */
    Core.prototype.getZoomHeight = function () {
        return this.zoomHeight;
    };

    /**
     * Retrieves the previous value for coordinate x
     * @return {Number}
     */
    Core.prototype.getOldX = function () {
        return this.oldX;
    };

    /**
     * Retrieves the previous value for coordinate y
     * @return {Number}
     */
    Core.prototype.getOldY = function () {
        return this.oldY;
    };

    /**
     * Retrieves the previous value for width
     * @return {Number}
     */
    Core.prototype.getOldWidth = function () {
        return this.oldWidth;
    };

    /**
     * Retrieves the previous value for height
     * @return {Number}
     */
    Core.prototype.getOldHeight = function () {
        return this.oldHeight;
    };

    /**
     * Stringifies the basic data of this shape and the drag behavior of this shape
     * @return {Object}
     */
    Core.prototype.stringify = function () {
        return {
            id: this.getID(),
            x: this.getX(),
            y: this.getY(),
            width: this.getWidth(),
            height: this.getHeight(),
            type: this.type,
            style: this.getStyle().stringify(),
            drag: this.savedOptions.drag
        };
    };

    /**
     * Sets a valid drag behavior
     * @param {Object} obj
     */
    Core.prototype.setDragBehavior = function (obj) {
        var factory = new PMUI.behavior.BehaviorFactory({
            products: {
                "customshapedrag": PMUI.behavior.CustomShapeDragBehavior,
                "regulardrag": PMUI.behavior.RegularDragBehavior,
                "connectiondrag": PMUI.behavior.ConnectionDragBehavior,
                "connection": PMUI.behavior.ConnectionDragBehavior,
                "nodrag": PMUI.behavior.NoDragBehavior
            },
            defaultProduct: "nodrag"
        });
        this.drag = factory.make(obj);
        if (this.html && this.drag) {
            this.drag.attachDragBehavior(this);
        }
        return this;
    };

    /**
     * Functions that calculates the relative position over the canvas
     * @param  {Object} e jQuery Event
     * @return {Object}
     */
    Core.prototype.relativePoint = function (e) {
        var auxX, auxY;
        auxX = e.pageX - this.absoluteX;
        auxY = e.pageY - this.absoluteY;
        if (this.canvas) {
            auxX += this.canvas.getLeftScroll();
            auxY += this.canvas.getTopScroll();
            auxX = Math.floor(auxX / this.canvas.zoomFactor);
            auxY = Math.floor(auxY / this.canvas.zoomFactor);
        }
        return {
            x: auxX,
            y: auxY
        };
    };

    /**
     * JSON parser for creating PMDrawObjects
     * @param {Object} json
     */
    Core.prototype.parseJSON = function (json) {
        this.initObject(json);
    };

    /**
     * Number that represents the top direction
     * @property {number}
     */
    Core.prototype.TOP = 0;
    /**
     * Number that represents the right direction
     * @property {number}
     */
    Core.prototype.RIGHT = 1;
    /**
     * Number that represents the bottom direction
     * @property {Number}
     */
    Core.prototype.BOTTOM = 2;
    /**
     * Number that represents the left direction
     * @property {Number}
     */
    Core.prototype.LEFT = 3;
    /**
     * Number that represents horizontal direction
     * @property {Number}
     */
    Core.prototype.HORIZONTAL = 0;
    /**
     * Number that represents  vertical direction
     * @property {Number}
     */
    Core.prototype.VERTICAL = 1;
    /**
     * Number of zoom scales available
     * @property {Number}
     */
    Core.prototype.ZOOMSCALES = 5;

    PMUI.extendNamespace('PMUI.draw.Core', Core);

    if (typeof exports !== 'undefined') {
        module.exports = Core;
    }

}());
(function () {
    /**
     * @class PMUI.draw.BehavioralElement
     * Class that encapsulates the behavior of all elements that have container and
     * drop behaviors attached to them.
     * since this class inherits from {@link PMUI.draw.Core}, then the common behaviors
     * and properties for all elements in the designer are also part of this class
     * The purpose of this class is to encapsulate behaviors related to drop and
     * containment of elements, so it shouldn't be instantiated, we should
     * instantiate the elements that inherit from this class instead.
     *
     *          //i.e
     *          //we will set the behaviors that are related only to this class
     *          var shape = new PMUI.draw.CustomShape({
     *          //we can set different types of containers here and the factory
     *          //will do all the work
     *              container : "regular",
     *              drop : {
     *              //type specifies the drop behavior we want, again we just need
     *              // to pass a string
     *               type : "container",
     *                //selectors are the css selectors that this element will
     *                //accept to be dropped
     *               selectors : [".firstselector",".secondselector"],
     *              //overwrite is an option to override previous and default
     *              //selectors
     *               overwrite : false
     *              }
     *          });
     *
     *
     * @extends PMUI.draw.Core
     *
     * @constructor Creates a new instance of this class
     * @param {Object} options
     * @cfg {String} [container="nocontainer"] the type of container behavior
     * we want for an object, it can be regular,or nocontainer, or any other class
     * that extends the {@link PMUI.behavior.ContainerBehavior} class, but also note that we would
     * need to override the factory for container behaviors located in this class.
     * @cfg {Object} [drop={
     *     drop : "nodrop",
     *     selectors : [],
     *     overwrite : false
     * }] Object that contains the options for the drop behavior we want an object
     * to have, we can, assign type which can be container, connection,
     * connectioncontainer, or no drop. As with the container behavior we can extend
     * the behaviors and factory for this functionality.
     * We also have selectors that specify the selectors the drop behavior will
     * accept and the overwrite feature
     */
    var BehavioralElement = function (options) {
        BehavioralElement.superclass.call(this, options);
        /**
         * Determines the Behavior object that this object has
         * @property {PMUI.behavior.ContainerBehavior}
         */
        this.containerBehavior = null;
        /**
         * Determines the Drop Behavior of the element
         * @type {PMUI.behavior.DropBehavior}
         */
        this.dropBehavior = null

        /**
         * List of the children
         * @property {*}
         */
        this.children = null;
        BehavioralElement.prototype.init.call(this, options);
    };
    PMUI.inheritFrom('PMUI.draw.Core', BehavioralElement);
    /**
     * Type of the all instances of this class
     * @property {String}
     */
    BehavioralElement.prototype.type = "BehavioralElement";
    /**
     * Instance initializer which uses options to extend the default config options.
     * The default options are container: nocontainer, and drop: nodrop
     * @param {Object} options
     */
    BehavioralElement.prototype.init = function (options) {
        var defaults = {
            drop: "nodrop",
            container: "nocontainer"
        };
        $.extend(true, defaults, options);
        var defaults = {
            drop: {
                type: "nodrop",
                selectors: [],
                overwrite: false
            },
            container: "nocontainer"
        };
        $.extend(true, defaults, options);
        this.setDropBehavior(defaults.drop.type, defaults.drop.selectors,
            defaults.drop.overwrite);
        this.setContainerBehavior(defaults.container);
        this.children = new PMUI.util.ArrayList();
    };
    BehavioralElement.prototype.setDropBehavior = function (behavior, selectors,
                                                            overwrite) {
        this.dropBehavior = this.dropBehaviorFactory(behavior, selectors);
        this.dropBehavior.setSelectors(selectors, overwrite);
        if (this.html && this.dropBehavior) {
            this.dropBehavior.attachDropBehavior(this);

            // update the saved object
            // added by mauricio to save the drop behavior of this shape
            $.extend(true, this.savedOptions.drop, {
                type: behavior,
                overwrite: overwrite
            });

            if (selectors && selectors.hasOwnProperty('length')) {
                this.dropBehavior.updateSelectors(this, selectors, overwrite);
                // update the saved object
                // added by mauricio to save the drop behavior of this shape
                $.extend(true, this.savedOptions.drop, {
                    selectors: selectors
                });
            }
        }
        return this;
    };
    /**
     * Factory of drop behaviors. It uses lazy instantiation to create instances of
     * the different drop behaviors
     * @param {String} type Type of drop behavior we want to assign to an object,
     * it can be nodrop, container, connection or connectioncontainer
     * @param {Array} selectors Array containing the css selectors that the drop
     * behavior will accept
     * @return {DropBehavior}
     */
    BehavioralElement.prototype.dropBehaviorFactory = function (type, selectors) {
        if (type === "nodrop") {
            if (!this.noDropBehavior) {
                this.noDropBehavior = new PMUI.behavior.NoDropBehavior(selectors);
            }
            return this.noDropBehavior;
        }
        if (type === "container") {
            if (!this.containerDropBehavior) {
                this.containerDropBehavior = new PMUI.behavior.ContainerDropBehavior(selectors);
            }
            return this.containerDropBehavior;
        }
        if (type === "connection") {
            if (!this.connectionDropBehavior) {
                this.connectionDropBehavior = new PMUI.behavior.ConnectionDropBehavior(selectors);
            }
            return this.connectionDropBehavior;
        }
        if (type === "connectioncontainer") {
            if (!this.connectionContainerDropBehavior) {
                this.connectionContainerDropBehavior =
                    new PMUI.behavior.ConnectionContainerDropBehavior(selectors);
            }
            return this.connectionContainerDropBehavior;
        }
    };
    /**
     * Sets the container behavior of an object, using the same options as
     * the factory
     * @param {String} behavior the container behavior we want the factory to
     * assign, it can be regular, or nocontainer
     * @chainable
     */
    BehavioralElement.prototype.setContainerBehavior = function (behavior) {
        // update the saved object
        // added by mauricio to save the container behavior of this
        $.extend(true, this.savedOptions, {container: behavior});
        this.containerBehavior = this.containerBehaviorFactory(behavior);
        return this;
    };

    /**
     * Factory of container behaviors. It uses lazy instantiation to create
     * instances of the different container behaviors
     * @param {String} type An string that specifies the container behavior we want
     * an instance to have, it can be regular or nocontainer
     * @return {ContainerBehavior}
     */
    BehavioralElement.prototype.containerBehaviorFactory = function (type) {
        if (type === "regular") {
            if (!this.regularContainerBehavior) {
                this.regularContainerBehavior = new PMUI.behavior.RegularContainerBehavior();
            }
            return this.regularContainerBehavior;

        }
        if (!this.noContainerBehavior) {
            this.noContainerBehavior = new PMUI.behavior.NoContainerBehavior();
        }
        return this.noContainerBehavior;
    };
    /**
     * Updates the children positions of a container given the x and y difference
     * @param {Number} diffX x difference
     * @param {Number} diffY y difference
     * @chainable
     * // TODO make this method recursive
     */
    BehavioralElement.prototype.updateChildrenPosition = function (diffX, diffY) {
        var children = this.getChildren(),
            child,
            i,
            updatedChildren = [],
            previousValues = [],
            newValues = [];
        for (i = 0; i < children.getSize(); i += 1) {
            child = children.get(i);
            if ((diffX !== 0 || diffY !== 0) && !this.canvas.currentSelection.contains(child)) {
                updatedChildren.push(child);
                previousValues.push({
                    x: child.x,
                    y: child.y
                });
                newValues.push({
                    x: child.x + diffX,
                    y: child.y + diffY
                });
            }
            child.setPosition(child.x + diffX, child.y + diffY);
        }
        if (updatedChildren.length > 0) {
            this.canvas.triggerPositionChangeEvent(updatedChildren, previousValues,
                newValues);
        }
        return this;
    };
    /**
     * Returns whether the instance is a container or not
     * @return {Boolean}
     */
    BehavioralElement.prototype.isContainer = function () {
        return this.containerBehavior &&
            this.containerBehavior.type !== "NoContainerBehavior";
    };

    /**
     * Encapsulates the functionality of adding an element this element according
     * to its container behavior
     * @param {PMUI.draw.Shape} shape Shape we want to add to the element
     * @param {Number} x x coordinate where the shape will be positionated relative
     * to this element
     * @param {Number} y y coordinate where the shape will be positionated relative
     * to this element
     * @param {Boolean} topLeftCorner determines if the drop position should be
     * calculated from the top left corner of the shape or, from its center
     * @chainable
     */
    BehavioralElement.prototype.addElement = function (shape, x, y,
                                                       topLeftCorner) {
        this.containerBehavior.addToContainer(this, shape, x, y, topLeftCorner);
        return this;
    };
    /**
     * Encapsulates the functionality of removing an element this element according
     * to its container behavior
     * @param {PMUI.draw.Shape} shape shape to be removed from this element
     * @chainable
     */
    BehavioralElement.prototype.removeElement = function (shape) {
        this.containerBehavior.removeFromContainer(shape);
        return this;
    };
    /**
     * Swaps a shape from this container to a different one
     * @param {PMUI.draw.Shape} shape shape to be swapped
     * @param {PMUI.draw.BehavioralElement} otherContainer the other container the shape will
     * be swapped to
     * @param {Number} x x coordinate where the shape will be positionated relative
     * to this element
     * @param {Number} y y coordinate where the shape will be positionated relative
     * to this element
     * @param {Boolean} topLeftCorner determines if the drop position should be
     * calculated from the top left corner of the shape or, from its center
     * @chainable
     */
    BehavioralElement.prototype.swapElementContainer = function (shape,
                                                                 otherContainer, x,
                                                                 y, topLeftCorner) {
        var newX = !x ? shape.getX() : x,
            newY = !y ? shape.getY() : y;
        shape.changedContainer = true;
        this.removeElement(shape);
        otherContainer.addElement(shape, newX, newY, topLeftCorner);
        return this;
    };
    /**
     * Returns the list of children belonging to this shape
     * @returns {PMUI.util.ArrayList}
     */
    BehavioralElement.prototype.getChildren = function () {
        return this.children;
    };
    /**
     * Updates the dimensions and position of this shape (note: 'this' is a shape)
     * @param {Number} margin the margin for this element to consider towards the
     * shapes near its borders
     * @chainable
     */
    BehavioralElement.prototype.updateDimensions = function (margin) {
        // update its size (if an child grew out of the shape)
        // only if it's not the canvas
        if (this.family !== 'Canvas') {
            this.updateSize(margin);
            this.refreshConnections();
            // updates JQueryUI's options (minWidth and minHeight)
            PMUI.behavior.ResizeBehavior.prototype.updateResizeMinimums(this);
            BehavioralElement.prototype.updateDimensions.call(this.parent, margin);
        }
        return this;
    };

    /**
     * Sets the selectors of the current drop behavior
     * @param {Array} selectors new css selectors for the drop behavior
     * @param {Boolean} overwrite determines whether the default selectors will
     * be erased
     * @chainable
     */
    BehavioralElement.prototype.setDropAcceptedSelectors = function (selectors,
                                                                     overwrite) {
        if (selectors && selectors.hasOwnProperty('length')) {
            this.drop.updateSelectors(this, selectors, overwrite);
        }
        return this;
    };
    /**
     * Attach the drop behavior to the element, if there is such
     * @chainable
     */
    BehavioralElement.prototype.updateBehaviors = function () {
        if (this.dropBehavior) {
            this.dropBehavior.attachDropBehavior(this);
            this.dropBehavior.updateSelectors(this);
        }
        return this;
    };
    /**
     * Stringifies the container and drop behavior of this object
     * @return {Object}
     */
    BehavioralElement.prototype.stringify = function () {
        var inheritedJSON = BehavioralElement.superclass.prototype.stringify.call(this),
            thisJSON = {
                container: this.savedOptions.container,
                drop: this.savedOptions.drop
            };
        $.extend(true, inheritedJSON, thisJSON);
        return inheritedJSON;
    };

    PMUI.extendNamespace('PMUI.draw.BehavioralElement', BehavioralElement);

    if (typeof exports !== 'undefined') {
        module.exports = BehavioralElement;
    }

}());

(function () {
    /**
     * @class  PMUI.behavior.BehaviorFactory
     * @extend PMUI.util.Factory
     * Extends the factory class to produce Behavior instances
     *
     * @constructor
     * Creates a new instance od the class
     * @param {Object} options
     */
    var BehaviorFactory = function (options) {
        BehaviorFactory.superclass.call(this, options);
    };

    PMUI.inheritFrom('PMUI.util.Factory', BehaviorFactory);

    /**
     * Defines the object's type
     * @type {String}
     */
    BehaviorFactory.prototype.type = 'BehaviorFactory';

    /**
     * Overwrite the make function to accept strings
     * @param  {Object/String} type
     * @return {Object}
     */
    BehaviorFactory.prototype.make = function (obj) {
        var behaviorInstance,
            behaviorType = obj.pmType;

        if (this.isValidClass(obj)) {
            behaviorInstance = obj;
        } else if (this.isValidName(behaviorType)) {
            behaviorInstance = this.build(behaviorType, obj);
        } else if (this.isValidName(obj)) {
            behaviorInstance = this.build(obj, {});
        } else {
            behaviorInstance = this.build(this.defaultProduct, obj);
        }
        return behaviorInstance;
    };

    PMUI.extendNamespace('PMUI.behavior.BehaviorFactory', BehaviorFactory);

    if (typeof exports !== 'undefined') {
        module.exports = BehaviorFactory;
    }

}());
(function () {
    /**
     * @abstract
     * @class PMUI.behavior.DragBehavior
     * Abstract class that encapsulates the drag behavior of an object
     *
     * @constructor Creates a new instance of the class
     *
     */
    var DragBehavior = function () {
    };

    /**
     * Type of the object
     * @property {String}
     */
    DragBehavior.prototype.type = "DragBehavior";
    /**
     * Family of the object
     * @property {String}
     */
    DragBehavior.prototype.family = "DragBehavior";


    /**
     * Attach the drag listener and its corresponding ui properties to the shape
     * @param {PMUI.draw.Shape} shape
     */
    DragBehavior.prototype.attachDragBehavior = function (shape) {
        var dragOptions,
        $shape = $(shape.getHTML());
        dragOptions = {
            revert: false,
            helper: "none",
            cursorAt: false,
            revertDuration: 0,
            grid: [1, 1],
            start: this.onDragStart(shape),
            drag: this.onDrag(shape),
            stop: this.onDragEnd(shape)
        };
        $shape.draggable(dragOptions);
    };
    /**
     * @event dragStart
     * @abstract drag start handler, function that runs when the drag start event occurs,
     * it should return a function so that any implementation should go inside the
     * return
     * @param {PMUI.draw.Shape} shape current shape being dragged
     * @template
     * @protected
     */
    DragBehavior.prototype.onDragStart = function (shape) {
        return function (e, ui) {
        };
    };

    /**
     * @event drag
     * Drag handler, function that runs when dragging is occurring,
     * it should return a function so that any implementation should go inside the
     * return
     * @param {PMUI.draw.Shape} shape shape being dragged
     * @template
     * @protected
     */
    DragBehavior.prototype.onDrag = function (shape) {
        return function (e, ui) {
        };
    };

    /**
     * @event dragEnd
     * Drag end handler, function that runs when the drag end event occurs,
     * it should return a function so that any implementation should go inside the
     * return
     * @param {PMUI.draw.Shape} shape
     * @template
     * @protected
     */
    DragBehavior.prototype.onDragEnd = function (shape) {
        return function (e, ui) {
        };
    };
    /**
     * @abstract Executes the hook Function for the drag start event
     * @template
     * @protected
     */
    DragBehavior.prototype.dragStartHook = function (hookFunction) {
    };

    /**
     * @abstract Executes the hook function for the drag event
     * @template
     * @protected
     */
    DragBehavior.prototype.dragHook = function (hookFunction) {
    };
    /**
     * @abstract Executes the hook function for the drag end event
     * @template
     * @protected
     */
    DragBehavior.prototype.dragEndHook = function () {
    };

    PMUI.extendNamespace('PMUI.behavior.DragBehavior', DragBehavior);

    if (typeof exports !== 'undefined') {
        module.exports = DragBehavior;
    }

}());
(function () {
    /**
     * @class PMUI.behavior.CustomShapeDragBehavior
     * Encapsulates the drag behavior of a custom shape (with ports and connections)
     * , it also encapsulates the behavior for multiple drag
     * @extends PMUI.behavior.DragBehavior
     *
     * @constructor Creates a new instance of the class
     *
     */
    var CustomShapeDragBehavior = function () {
    };

    PMUI.inheritFrom('PMUI.behavior.DragBehavior', CustomShapeDragBehavior);
    /**
     * Type of the instances
     * @property {String}
     */
    CustomShapeDragBehavior.prototype.type = "CustomShapeDragBehavior";
    /**
     * Attach the drag behavior and ui properties to the corresponding shape
     * @param {PMUI.draw.CustomShape} customShape
     */
    CustomShapeDragBehavior.prototype.attachDragBehavior = function (customShape) {
        var dragOptions,
            $customShape = $(customShape.getHTML());
        dragOptions = {
            revert: false,
            helper: "none",
            cursorAt: false,
            revertDuration: 0,
            disable: false,
            grid: [1, 1],
            start: this.onDragStart(customShape),
            drag: this.onDrag(customShape, true),
            stop: this.onDragEnd(customShape, true)
        };
        $customShape.draggable({'cursor':"move"});
        $customShape.draggable(dragOptions);
    };

    //TODO Encapsulates behaviors for multiple drag, and simple custom shape drag
    //TODO Initialize all oldX and oldY values
    /**
     * On drag start handler, it uses the {@link PMUI.behavior.RegularDragBehavior}.onDragStart
     * method to initialize the drag, but also initializes other properties
     * @param {PMUI.draw.CustomShape} customShape
     * @return {Function}
     */
    CustomShapeDragBehavior.prototype.onDragStart = function (customShape) {
        return function (e, ui) {
            if(customShape.canvas.currentSelection.asArray().length == 0){
               customShape.canvas.addToSelection(customShape);                
            }
            PMUI.behavior.RegularDragBehavior.prototype.onDragStart.call(this,
                customShape)(e, ui);

            customShape.previousXDragPosition = customShape.getX();
            customShape.previousYDragPosition = customShape.getY();
            if (customShape.canvas.snapToGuide) {
                //init snappers
                customShape.canvas.startSnappers(e);
            }
            customShape.canvas.isDragging = true;
        };
    };
    /**
     * Procedure executed while dragging, it takes care of multiple drag, moving
     * connections, updating positions and children of the shapes being dragged
     * @param {PMUI.draw.CustomShape} customShape shape being dragged
     * @param {boolean} root return whether this is the shape where the drag started
     * @param {number} childDiffX x distance needed for the non-root shapes to move
     * @param {number} childDiffY y distance needed for the non-root shapes to move
     * @param {Object} e jQuery object containing the properties when a drag event
     * occur
     * @param {Object} ui JQuery UI object containing the properties when a drag
     * event occur
     */
    CustomShapeDragBehavior.prototype.onDragProcedure = function (customShape, root, childDiffX, childDiffY, e, ui) {
        var i,
            j,
            sibling,
            diffX,
            diffY,
            port,
            child,
            connection,
            shape1,
            shape2,
            canvas = customShape.canvas,
            k,
            uiOffset,
            positionsX1= [];
        
        uiOffset = {};
        uiOffset.x = ui.helper.position().left / canvas.zoomFactor;
        uiOffset.y = ui.helper.position().top / canvas.zoomFactor;
        uiOffset.diffX = customShape.x - uiOffset.x;
        uiOffset.diffY = customShape.y - uiOffset.y;

        for (k = 0; k < customShape.canvas.currentSelection.getSize(); k += 1) {
            sibling = customShape.canvas.currentSelection.get(k);
            if (sibling.id !== customShape.id) {
                positionsX1.push(sibling.x + uiOffset.diffX);
            }
        }

        // shapes
        if (root) {
            // Commented for problem on snappers
            
            if (customShape.canvas.snapToGuide) {
                customShape.canvas.processGuides(e, ui, customShape);
            }
            customShape.setPosition(ui.helper.position().left / canvas.zoomFactor,
                ui.helper.position().top / canvas.zoomFactor);

            diffX = customShape.x - customShape.previousXDragPosition;
            diffY = customShape.y - customShape.previousYDragPosition;

            customShape.previousXDragPosition = customShape.x;
            customShape.previousYDragPosition = customShape.y;

            for (i = 0; i < customShape.canvas.currentSelection.getSize(); i += 1) {
                sibling = customShape.canvas.currentSelection.get(i);
                if (sibling.id !== customShape.id) {
                    if (((sibling.x + diffX) >= 0) && ((sibling.y + diffY) >= 0)){
                        sibling.setPosition(sibling.x + diffX, sibling.y + diffY);
                    } else {
                        e.preventDefault();
                    }
                }
            }
        } else {
            customShape.setPosition(customShape.x, customShape.y);
        }

        // children
        if (root) {
            for (i = 0; i < customShape.canvas.currentSelection.getSize(); i += 1) {
                sibling = customShape.canvas.currentSelection.get(i);
                for (j = 0; j < sibling.children.getSize(); j += 1) {
                    child = sibling.children.get(j);
                    PMUI.behavior.CustomShapeDragBehavior.prototype.onDragProcedure.call(this, child,
                        false, diffX, diffY, e, ui);
                }
            }
        } else {
            for (i = 0; i < customShape.children.getSize(); i += 1) {
                child = customShape.children.get(i);
                PMUI.behavior.CustomShapeDragBehavior.prototype.onDragProcedure.call(this, child,
                    false, childDiffX, childDiffY, e, ui);
            }
        }

        // connections
        if (root) {
            for (i = 0; i < customShape.canvas.currentSelection.getSize(); i += 1) {
                sibling = customShape.canvas.currentSelection.get(i);
                for (j = 0; j < sibling.ports.getSize(); j += 1) {
                    //for each port update its absolute position and repaint its connection
                    port = sibling.ports.get(j);
                    connection = port.connection;
                    port.setPosition(port.x, port.y);

                    if (customShape.canvas.sharedConnections.
                            find('id', connection.getID())) {
                        // move the segments of this connections
                        if (connection.srcPort.parent.getID() === sibling.getID()) {
                            // to avoid moving the connection twice
                            // (two times per shape), move it only if the shape
                            // holds the sourcePort
                            connection.move(diffX * canvas.zoomFactor,
                                diffY * canvas.zoomFactor);
                        }
                    } else {
                        connection
                            .setSegmentColor(PMUI.util.Color.GREY, false)
                            .setSegmentStyle("regular", false)
                            .disconnect()
                            .connect();
                    }
                }
            }
        } else {
            for (i = 0; i < customShape.ports.getSize(); i += 1) {
                //for each port update its absolute position and repaint its connection
                port = customShape.ports.get(i);
                connection = port.connection;
                shape1 = connection.srcPort.parent;
                shape2 = connection.destPort.parent;

                port.setPosition(port.x, port.y);

                if (customShape.canvas.sharedConnections.
                        find('id', connection.getID())) {
                    // to avoid moving the connection twice
                    // (two times per shape), move it only if the shape
                    // holds the sourcePort
                    if (connection.srcPort.parent.getID() ===
                            customShape.getID()) {
                        connection.move(childDiffX * canvas.zoomFactor,
                                childDiffY * canvas.zoomFactor);
                    }
                } else {
                    connection
                        .setSegmentColor(PMUI.util.Color.GREY, false)
                        .setSegmentStyle("regular", false)
                        .disconnect()
                        .connect();
                }
            }
        }
    };
    /**
     * On drag handler, calls the drag procedure while the dragging is occurring,
     * and also takes care of the snappers
     * @param {PMUI.draw.CustomShape} customShape shape being dragged
     * @param {boolean} root return whether this is the shape where the drag started
     * @param {number} childDiffX x distance needed for the non-root shapes to move
     * @param {number} childDiffY y distance needed for the non-root shapes to move
     * @return {Function}
     */
    CustomShapeDragBehavior.prototype.onDrag = function (customShape, root, childDiffX, childDiffY) {
        var self = this;
        return function (e, ui) {
            // call to dragEnd procedure
            self.onDragProcedure(customShape, root, childDiffX,
                childDiffY, e, ui);
        };
    };
    /**
     * Procedure executed on drag end, it takes care of multiple drag, moving
     * connections, updating positions and children of the shapes being dragged
     * @param {PMUI.draw.CustomShape} customShape shape being dragged
     * @param {boolean} root return whether this is the shape where the drag started
     * @param {Object} e jQuery object containing the properties when a drag event
     * occur
     * @param {Object} ui JQuery UI object containing the properties when a drag
     * event occur
     */
    CustomShapeDragBehavior.prototype.dragEndProcedure = function (customShape, root, e, ui) {
        var i,
            j,
            sibling,
            port,
            child,
            connection,
            shape1,
            shape2,
            canvas = customShape.canvas;

        // shapes
        if (root) {

            // the difference between this segment of code and the segment of code
            // found in dragProcedure is that it's not needed to move the shapes
            // anymore using differentials
            customShape.setPosition(ui.helper.position().left / canvas.zoomFactor,
                ui.helper.position().top / canvas.zoomFactor);
            customShape.wasDragged = true;
            customShape.canvas.isDragging = false;
            for (i = 0; i < customShape.canvas.currentSelection.getSize(); i += 1) {
                sibling = customShape.canvas.currentSelection.get(i);
                sibling.setPosition(sibling.x, sibling.y);
            }
        } else {
            customShape.setPosition(customShape.x, customShape.y);
        }

        // children
        if (root) {
            for (i = 0; i < customShape.canvas.currentSelection.getSize();
                 i += 1) {
                sibling = customShape.canvas.currentSelection.get(i);
                for (j = 0; j < sibling.children.getSize(); j += 1) {
                    child = sibling.children.get(j);
                    child.changedContainer = true;
                    PMUI.behavior.CustomShapeDragBehavior.prototype.dragEndProcedure.call(this,
                        child, false, e, ui);
                }
            }
        } else {
            for (i = 0; i < customShape.children.getSize(); i += 1) {
                child = customShape.children.get(i);
                PMUI.behavior.CustomShapeDragBehavior.prototype.dragEndProcedure.call(this,
                    child, false, e, ui);
            }
        }

        // connections
        if (root) {
            for (i = 0; i < customShape.canvas.currentSelection.getSize();
                 i += 1) {
                sibling = customShape.canvas.currentSelection.get(i);
                for (j = 0; j < sibling.ports.getSize(); j += 1) {

                    // for each port update its absolute position and repaint
                    // its connection
                    port = sibling.ports.get(j);
                    connection = port.connection;

                    port.setPosition(port.x, port.y);

                    if (customShape.canvas.sharedConnections.
                            find('id', connection.getID())) {
                        // move the segments of this connections
                        if (connection.srcPort.parent.getID() ===
                                sibling.getID()) {
                            // to avoid moving the connection twice
                            // (two times per shape), move it only if the shape
                            // holds the sourcePort
                            connection.disconnect(true).connect({
                                algorithm: 'user',
                                points: connection.points,
                                dx: parseFloat($(connection.html).css('left')),
                                dy: parseFloat($(connection.html).css('top'))
                            });
                            connection.checkAndCreateIntersectionsWithAll();
                        }
                    } else {
                        connection
                            .setSegmentColor(connection.originalSegmentColor, false)
                            .setSegmentStyle(connection.originalSegmentStyle, false)
                            .disconnect()
                            .connect();
                        connection.setSegmentMoveHandlers();
                        connection.checkAndCreateIntersectionsWithAll();
                    }
                }
            }
        } else {
            for (i = 0; i < customShape.ports.getSize(); i += 1) {
                //for each port update its absolute position and repaint
                //its connection
                port = customShape.ports.get(i);
                connection = port.connection;
                shape1 = connection.srcPort.parent;
                shape2 = connection.destPort.parent;

                port.setPosition(port.x, port.y);
                if (customShape.canvas.sharedConnections.
                        find('id', connection.getID())) {
                    // to avoid moving the connection twice
                    // (two times per shape), move it only if the shape
                    // holds the sourcePort
                    if (connection.srcPort.parent.getID() ===
                            customShape.getID()) {
                        connection.checkAndCreateIntersectionsWithAll();
                    }
                } else {
                    connection
                        .setSegmentColor(connection.originalSegmentColor, false)
                        .setSegmentStyle(connection.originalSegmentStyle, false)
                        .disconnect()
                        .connect();
                    connection.setSegmentMoveHandlers();
                    connection.checkAndCreateIntersectionsWithAll();
                }
            }
        }

    };
    /**
     * On drag end handler, ot calls drag end procedure, removes the snappers and,
     * fires the command move if necessary
     * @param {PMUI.draw.CustomShape} customShape
     * @return {Function}
     */
    CustomShapeDragBehavior.prototype.onDragEnd = function (customShape) {
        var command,
            self = this;
        return function (e, ui) {

            // call to dragEnd procedure
            self.dragEndProcedure(customShape, true, e, ui);

            customShape.dragging = false;

            // hide the snappers
            customShape.canvas.verticalSnapper.hide();
            customShape.canvas.horizontalSnapper.hide();

            if (!customShape.changedContainer) {

                command = new PMUI.command.CommandMove(customShape.canvas.currentSelection);
                command.execute();
                customShape.canvas.commandStack.add(command);
            }
            customShape.changedContainer = false;

            // decrease the zIndex of the oldParent of customShape
            customShape.decreaseParentZIndex(customShape.oldParent);
            customShape.canvas.emptyCurrentSelection();
        };
    };

    PMUI.extendNamespace('PMUI.behavior.CustomShapeDragBehavior', CustomShapeDragBehavior);

    if (typeof exports !== 'undefined') {
        module.exports = CustomShapeDragBehavior;
    }
    
}());

(function () {
    /**
     * @class PMUI.behavior.RegularDragBehavior
     * Class that encapsulates the regular drag behavior of a shape
     * @extends PMUI.behavior.DragBehavior
     *
     * @constructor Creates a new instance of the class
     *
     */
    var RegularDragBehavior = function () {
    };

    PMUI.inheritFrom('PMUI.behavior.DragBehavior', RegularDragBehavior);
    /**
     * Type of the object
     * @property {String}
     */
    RegularDragBehavior.prototype.type = "RegularDragBehavior";
    /**
     * Attach the drag behavior to a given shape
     * @param {PMUI.draw.Shape} shape
     */
    RegularDragBehavior.prototype.attachDragBehavior = function (shape) {
        var $shape = $(shape.getHTML());
        RegularDragBehavior.superclass.prototype.attachDragBehavior.call(this, shape);
        $shape.draggable({'cursor':"move"});
    };
    /**
     * On drag start handler, initializes everything that is needed for a shape to
     * be dragged
     * @param {PMUI.draw.Shape} shape
     * @return {Function}
     */
    RegularDragBehavior.prototype.onDragStart = function (shape) {
        return function (e, ui) {
            var canvas = shape.canvas,
                currentLabel = canvas.currentLabel,
                selectedShape,
                i;

            // hide the current connection if there was one
            canvas.hideCurrentConnection();
            if (currentLabel) {
                currentLabel.loseFocus();
                $(currentLabel.textField).focusout();
            }

            // sort the data of the snappers (a shape might have been inserted in
            // the customShape arrayList or the regularShapes)
            canvas.fixSnapData();

            if (!canvas.currentSelection.contains(shape)) {
                canvas.emptyCurrentSelection();
                /* Also decreases the z-index */
                canvas.addToSelection(shape);
            }

            // added by mauricio
            // these lines must be here and not in the top (currentSelection
            // is updated in the if above)
            for (i = 0; i < canvas.currentSelection.getSize(); i += 1) {
                selectedShape = canvas.currentSelection.get(i);
                selectedShape.setOldX(selectedShape.getX());
                selectedShape.setOldY(selectedShape.getY());
                selectedShape.setOldParent(selectedShape.getParent());
            }

            // increase shape's ancestors zIndex
            shape.increaseParentZIndex(shape.getParent());
            shape.canvas.isDragging = true;
            return true;
        };
    };
    /**
     * On drag handler, sets the position of the shape to current position of the
     * shape in the screen
     * @param {PMUI.draw.Shape} shape
     * @return {Function}
     */
    RegularDragBehavior.prototype.onDrag = function (shape) {
        return function (e, ui) {
            shape.setPosition(ui.helper.position().left,
                ui.helper.position().top);

            // show or hide the snappers
            shape.canvas.showOrHideSnappers(shape);
        };
    };
    /**
     * On drag end handler, set the final position of the shape and fires the
     * command move
     * @param {PMUI.draw.Shape} shape
     * @return {Function}
     */
    RegularDragBehavior.prototype.onDragEnd = function (shape) {
        return function (e, ui) {
            var command;
            shape.setPosition(ui.helper.position().left / shape.canvas.zoomFactor,ui.helper.position().top / shape.canvas.zoomFactor);
            // decrease the zIndex of the oldParent of this shape
            shape.decreaseParentZIndex(shape.oldParent);

            shape.dragging = false;
            shape.canvas.isDragging = false;
            // hide the snappers
            shape.canvas.verticalSnapper.hide();
            shape.canvas.horizontalSnapper.hide();
            if (!shape.changedContainer) {
                command = new PMUI.command.CommandMove(shape.canvas.currentSelection);
                command.execute();
                shape.canvas.commandStack.add(command);
            }
            shape.changedContainer = false;
        };
    };

    PMUI.extendNamespace('PMUI.behavior.RegularDragBehavior', RegularDragBehavior);

    if (typeof exports !== 'undefined') {
        module.exports = RegularDragBehavior;
    }
}());

(function () {
    /**
     * @class PMUI.behavior.ConnectionDragBehavior
     * Class that encapsulates the behavior for a connection drag.
     * A connection drag behavior means that instead of moving a shape when dragging
     * occurs, it creates a connection segment that let's us connect to shapes
     * @extends PMUI.behavior.DragBehavior
     *
     * @constructor Creates a new instance of the class
     *
     */
    var ConnectionDragBehavior = function () {
    };

    PMUI.inheritFrom('PMUI.behavior.DragBehavior', ConnectionDragBehavior);

    /**
     * Type of the instances
     * @property {String}
     */
    ConnectionDragBehavior.prototype.type = "ConnectionDragBehavior";
    /**
     * Attach the drag behavior and ui properties to the corresponding shape
     * @param {PMUI.draw.Shape} shape
     */
    ConnectionDragBehavior.prototype.attachDragBehavior = function (shape) {
        var $shape = $(shape.getHTML()),
            dragOptions;

        dragOptions = {
            helper: shape.createDragHelper,
            cursorAt: {top: 0, left: 0},
            revert: true
        };
        ConnectionDragBehavior.superclass.prototype.attachDragBehavior.call(this, shape);
        $shape.draggable(dragOptions);
        $shape.draggable('enable');
    };
    /**
     * On drag start handler, initializes all properties needed to start a
     * connection drag
     * @param {PMUI.draw.CustomShape} customShape
     * @return {Function}
     */
    ConnectionDragBehavior.prototype.onDragStart = function (customShape) {
        return function (e, ui) {
            var canvas = customShape.canvas,
                currentLabel = canvas.currentLabel,
                realPoint = canvas.relativePoint(e),
                startPortX = e.pageX - customShape.getAbsoluteX(),
                startPortY = e.pageY - customShape.getAbsoluteY();            

            customShape.startConnectionPoint.portX = startPortX;
            customShape.startConnectionPoint.portY = startPortY;
            customShape.canvas.emptyCurrentSelection();

            if (currentLabel) {
                currentLabel.loseFocus();
                $(currentLabel.textField).focusout();
            }
            if (customShape.family !== "CustomShape") {
                return false;
            }
            customShape.setOldX(customShape.getX());
            customShape.setOldY(customShape.getY());
            customShape.startConnectionPoint.x = customShape.canvas.zoomFactor * realPoint.x;
            customShape.startConnectionPoint.y = customShape.canvas.zoomFactor * realPoint.y;
            return true;

        };
    };
    /**
     * On drag handler, creates a connection segment from the shape to the current
     * mouse position
     * @param {PMUI.draw.CustomShape} customShape
     * @return {Function}
     */
    ConnectionDragBehavior.prototype.onDrag = function (customShape) {
        return function (e, ui) {
            var canvas = customShape.getCanvas(),
                endPoint = new PMUI.util.Point(),
                realPoint = canvas.relativePoint(e);

            if (canvas.connectionSegment) {
                $(canvas.connectionSegment.getHTML()).remove();
            }
            
            // Determine the point where the mouse currently is
            endPoint.x = realPoint.x * customShape.canvas.zoomFactor;
            endPoint.y = realPoint.y * customShape.canvas.zoomFactor;

            // Creates a new segment from where the helper was created to the
            // currently mouse location

            canvas.connectionSegment = new PMUI.draw.Segment({
                startPoint: customShape.startConnectionPoint,
                endPoint: endPoint,
                parent: canvas,
                zOrder: PMUI.util.Style.MAX_ZINDEX * 2
            });

            // We make the connection segment point to helper in order to get
            // information when the drop occurs
            canvas.connectionSegment.pointsTo = customShape;

            // Create HTML and paint
            canvas.connectionSegment.paint();
        };

    };
    /**
     * On drag end handler, deletes the connection segment created while dragging
     * @param {PMUI.draw.CustomShape} customShape
     * @return {Function}
     */
    ConnectionDragBehavior.prototype.onDragEnd = function (customShape) {
        return function (e, ui) {
            if (customShape.canvas.connectionSegment) {
                $(customShape.canvas.connectionSegment.getHTML()).remove();
            }
            customShape.setPosition(customShape.getOldX(), customShape.getOldY());
            customShape.dragging = false;
        };
    };

    PMUI.extendNamespace('PMUI.behavior.ConnectionDragBehavior', ConnectionDragBehavior);

    if (typeof exports !== 'undefined') {
        module.exports = ConnectionDragBehavior;
    }
    
}());

(function () {
    /**
     * @class PMUI.behavior.NoDragBehavior
     * Class that encapsulates the drag behavior corresponding to the elements that
     * cannot be dragged
     * @extends PMUI.behavior.DragBehavior
     *
     * @constructor Creates a new instance of the class
     *
     */
    var NoDragBehavior = function () {
    };

    PMUI.inheritFrom('PMUI.behavior.DragBehavior', NoDragBehavior);
    /**
     * Type of the instances
     * @property {String}
     */
    NoDragBehavior.prototype.type = "NoDragBehavior";

    /**
     * On drag start handler, this method prevents drag from occurring
     * @param {PMUI.draw.Shape} shape
     * @return {Function}
     */
    NoDragBehavior.prototype.onDragStart = function (shape) {
        // hide the current connection if there was one
        return function (e, ui) {
            shape.canvas.hideCurrentConnection();
            return false;
        };
    };

    PMUI.extendNamespace('PMUI.behavior.NoDragBehavior', NoDragBehavior);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = NoDragBehavior;
    }
}());

(function () {
    /**
     * @abstract
     * @class PMUI.behavior.DropBehavior
     * Abstract class where all the drop behavior classes inherit from
     * Strategy Pattern
     * @constructor
     * Creates a new instance of the class
     * @param {Object} [options] css selectors that the drop behavior
     * will accept
     */
    var DropBehavior = function (options) {
        /**
         * css selectors that the used for the drop behaviors beside the defaults
         * @property {Array}
         */
        this.selectors = [];
        DropBehavior.prototype.init.call(this, options);
    };
    /**
     * Type of the instances
     * @property {String}
     */
    DropBehavior.prototype.type = "DropBehavior";
    /**
     * Family of the instances
     * @property {String}
     */
    DropBehavior.prototype.family = "DropBehavior";
    /**
     * Default css selectors for the drop behavior
     * @property {String}
     */
    DropBehavior.prototype.defaultSelector = "";

    /**
     * @private
     * Initializes the object with the default options
     * @param  {[type]} options 
     */
    DropBehavior.prototype.init = function (options) {
        var defaults = {
            selectors: []
        };
        jQuery.extend(true, defaults, options);
        this.setSelectors(defaults.selectors);
    };
    /**
     * Attach the drop behaviors and assign the handlers to the corresponding shape
     * @param {PMUI.draw.Shape} shape
     */
    DropBehavior.prototype.attachDropBehavior = function (shape) {
        var $shape = $(shape.getHTML()),
            dropOptions = {
                accept:  this.defaultSelector,
                drop: this.onDrop(shape),
                over: this.onDragEnter(shape),
                out: this.onDragLeave(shape),
                greedy: true
            };
        $shape.droppable(dropOptions);
    };

    /**
     * @event dragEnter
     * @abstract Handler for the drag enter event
     * @param {PMUI.draw.Shape} shape
     * @template
     * @protected
     */
    DropBehavior.prototype.onDragEnter = function (shape) {
        return function (e, ui) {
        };
    };

    /**
     * @event dragLeave
     * @abstract Handler for the drag leave event
     * @param {PMUI.draw.Shape} shape
     * @template
     * @protected
     */
    DropBehavior.prototype.onDragLeave = function (shape) {
        return function (e, ui) {
        };
    };

    /**
     * @event drop
     * @abstract Handler for the on drop event
     * @param {PMUI.draw.Shape} shape
     * @template
     * @protected
     */
    DropBehavior.prototype.onDrop = function (shape) {
        return function (e, ui) {
        };
    };
    /**
     * Sets the selectors that the drop behavior will accept
     * @param {Array} selectors css selectors
     * @param {boolean} overwrite determines whether the default selectors will be
     * overridden or not
     * @chainable
     */
    DropBehavior.prototype.setSelectors = function (selectors, overwrite) {
        var currentSelectors = "",
            index,
            i;
        if (selectors) {
            this.selectors = selectors;
        }
        if (!overwrite) {
            currentSelectors = this.defaultSelector;
            index = 0;
        } else if (selectors.length > 0) {
            currentSelectors = selectors[0];
            index = 1;
        }
        for (i = index; i < selectors.length; i += 1) {
            currentSelectors += "," + this.selectors[i];
        }
        return this;
    };
    /**
     * Updates the accepted drop selectors
     * @param {PMUI.draw.Shape} shape
     * @param {Array} selectors
     * @chainable
     */
    DropBehavior.prototype.updateSelectors = function (shape, selectors) {
        var $shape = $(shape.getHTML()),
            currentSelectors,
            i;
        if (selectors) {
            this.selectors = selectors;
        }
        if (this.selectors.length > 0) {
            currentSelectors = this.selectors[0];
        }
        for (i = 1; i < this.selectors.length; i += 1) {
            currentSelectors += ',' + this.selectors[i];
        }
        $shape.droppable({"accept":  currentSelectors});
        return this;
    };

    /**
     * Hook for the drag enter handler
     * @template
     * @protected
     */
    DropBehavior.prototype.dragEnterHook = function () {
        return true;
    };

    /**
     * Hook for the drag leave handler
     * @template
     * @protected
     */
    DropBehavior.prototype.dragLeaveHook = function () {
        return true;
    };

    /**
     * Hook for the drop handler, executes before the on drop handler logic
     * @param {PMUI.draw.Shape} shape
     * @param {Object} e jQuery object that contains the properties on the
     * drop event
     * @param {Object} ui jQuery object that contains the properties on the
     * drop event
     * @template
     * @protected
     */
    DropBehavior.prototype.dropStartHook = function (shape, e, ui) {
        return true;
    };
    /**
     * Hook for the on drop handler
     * @param {PMUI.draw.Shape} shape
     * @param {Object} e jQuery object that contains the properties on the
     * drop event
     * @param {Object} ui jQuery object that contains the properties on the
     * drop event
     * @template
     * @protected
     */
    DropBehavior.prototype.dropHook = function (shape, e, ui) {
        return true;
    };
    /**
     * Hook for the on drop handler, executes after the drop logic has concluded
     * @param {PMUI.draw.Shape} shape
     * @param {Object} e jQuery object that contains the properties on the
     * drop event
     * @param {Object} ui jQuery object that contains the properties on the
     * drop event
     * @template
     * @protected
     */
    DropBehavior.prototype.dropEndHook = function (shape, e, ui) {
        return true;
    };

    PMUI.extendNamespace('PMUI.behavior.DropBehavior', DropBehavior);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = DropBehavior;
    }
}());

(function () {
    /**
     * @class PMUI.behavior.ConnectionDropBehavior
     * Class that encapsulates the drop behavior for dropped connections in shapes
     * @extends PMUI.behavior.DropBehavior
     *
     * @constructor
     * Creates a new instance of the class
     * @param selectors
     */
    var ConnectionDropBehavior = function (selectors) {
        ConnectionDropBehavior.superclass.call(this, selectors);
    };

    PMUI.inheritFrom('PMUI.behavior.DropBehavior', ConnectionDropBehavior);

    /**
     * Type of the instances
     * @property {String}
     */
    ConnectionDropBehavior.prototype.type = "ConnectionDropBehavior";
    /**
     * Defaults selectors for this drop behavior
     * @property {String}
     */
    ConnectionDropBehavior.prototype.defaultSelector = ".custom_shape,.port";

    /**
     * Sets the selectors for this drop behavior including the defaults
     * @param selectors
     * @param overwrite
     * @return {*}
     */
    ConnectionDropBehavior.prototype.setSelectors = function (selectors, overwrite) {
        ConnectionDropBehavior.superclass.prototype.setSelectors.call(this, selectors, overwrite);
        this.selectors.push(".port");
        this.selectors.push(".custom_shape");

        return this;
    };
    /**
     * Drag enter hook for this drop behavior, marks that a shape is over a
     * droppable element
     * @param {PMUI.draw.Shape} shape
     * @return {Function}
     */
    ConnectionDropBehavior.prototype.onDragEnter = function (shape) {
        return function (e, ui) {
            shape.entered = true;
        };
    };

    /**
     * Drag leave hook for this drop behavior, marks that a shape has left a
     * droppable element
     * @param {PMUI.draw.Shape} shape
     * @return {Function}
     */
    ConnectionDropBehavior.prototype.onDragLeave = function (shape) {
        return function (e, ui) {
            shape.entered = false;
        };
    };
    /**
     * On drop handler for this drop behavior, creates a connection between the
     * droppable element and the dropped element, or move ports among those shapes
     * @param {PMUI.draw.Shape} shape
     * @return {Function}
     */
    ConnectionDropBehavior.prototype.onDrop = function (shape) {
        var that = this;
        return function (e, ui) {
            var canvas = shape.getCanvas(),
                id = ui.draggable.attr('id'),
                x,
                y,
                currLeft,
                currTop,
                startPoint,
                sourceShape,
                sourcePort,
                realPoint,
                endPort,
                endPortXCoord,
                endPortYCoord,
                connection,
                currentConnection = canvas.currentConnection,
                srcPort,
                dstPort,
                port,
                success = false,
                command;
            shape.entered = false;
            if (!shape.dropBehavior.dropStartHook(shape, e, ui)) {
                return false;
            }
            if (shape.getConnectionType() === "none") {
                return true;
            }
            if (currentConnection) {
                srcPort = currentConnection.srcPort;
                dstPort = currentConnection.destPort;
                if (srcPort.id === id) {
                    port = srcPort;
                } else if (dstPort.id === id) {
                    port = dstPort;
                } else {
                    port = null;
                }
            }
            if (ui.helper && ui.helper.attr('id') === "drag-helper") {

                // If its the helper then we need to create two ports and draw a
                // connection.
                // We get the points and the corresponding shapes involved
                startPoint = shape.canvas.connectionSegment.startPoint;
                sourceShape = shape.canvas.connectionSegment.pointsTo;
                // Determine the points where the helper was created
                if (sourceShape.parent && sourceShape.parent.id === shape.id) {
                    return true;
                }
                sourceShape.setPosition(sourceShape.oldX, sourceShape.oldY);

                // Create the ports
                sourcePort = new PMUI.draw.Port({
                    width: 8,
                    height: 8
                });
                endPort = new PMUI.draw.Port({
                    width: 8,
                    height: 8
                });

                realPoint = shape.canvas.relativePoint(e);

                // Determine the position where the helper was dropped
                endPortXCoord = ui.offset.left - shape.canvas.getX() -
                    shape.getAbsoluteX() + shape.canvas.getLeftScroll();
                endPortYCoord = ui.offset.top - shape.canvas.getY() -
                    shape.getAbsoluteY() + shape.canvas.getTopScroll();

                // Add ports to the corresponding shapes
                // AddPort() determines the position of the ports
                sourceShape.addPort(sourcePort, startPoint.x, startPoint.y);
                shape.addPort(endPort, endPortXCoord, endPortYCoord,
                    false, sourcePort);

                // Create the connection
                connection = new PMUI.draw.Connection({
                    srcPort: sourcePort,
                    destPort: endPort,
                    canvas: shape.canvas,
                    segmentStyle: shape.connectionType
                });

                // Set its decorators
                connection.setSrcDecorator(new PMUI.draw.ConnectionDecorator({
                    width: 11,
                    height: 11,
                    canvas: canvas,
                    decoratorPrefix: "con_normal",
                    decoratorType: "source",
                    parent: connection
                }));
                connection.setDestDecorator(new PMUI.draw.ConnectionDecorator({
                    width: 11,
                    height: 11,
                    canvas: canvas,
                    decoratorPrefix: "con_normal",
                    decoratorType: "target",
                    parent: connection
                }));

                connection.canvas.commandStack.add(new PMUI.command.CommandConnect(connection));

                // Connect the two ports
                connection.connect();
                connection.setSegmentMoveHandlers();

                // Add the connection to the canvas, that means insert its html to
                // the DOM and adding it to the connections array
                canvas.addConnection(connection);

                // Now that the connection was drawn try to create the intersections
                connection.checkAndCreateIntersectionsWithAll();

                // Attaching port listeners
                sourcePort.attachListeners(sourcePort);
                endPort.attachListeners(endPort);

                // Finally trigger createEvent
                canvas.triggerCreateEvent(connection, []);
            } else if (port) {

                port.setOldParent(port.getParent());
                port.setOldX(port.getX());
                port.setOldY(port.getY());

                x = ui.position.left;
                y = ui.position.top;
                port.setPosition(x, y);
                shape.dragging = false;
                if (shape.getID() !== port.parent.getID()) {
                    port.parent.removePort(port);
                    currLeft = ui.offset.left - canvas.getX() -
                        shape.absoluteX + shape.canvas.getLeftScroll();
                    currTop = ui.offset.top - canvas.getY() -
                        shape.absoluteY + shape.canvas.getTopScroll();
                    shape.addPort(port, currLeft, currTop, true);
                    canvas.regularShapes.insert(port);
                } else {
                    shape.definePortPosition(port, port.getPoint(true));
                }

                // LOGIC: when portChangeEvent is triggered it gathers the state
                // of the connection but since at this point there's only a segment
                // let's paint the connection, gather the state and then disconnect
                // it (the connection is later repainted on, I don't know how)
                port.connection.connect();
                canvas.triggerPortChangeEvent(port);
                port.connection.disconnect();

                command = new PMUI.command.CommandReconnect(port);
                port.canvas.commandStack.add(command);
            }
            return false;
        };
    };

    PMUI.extendNamespace('PMUI.behavior.ConnectionDropBehavior', ConnectionDropBehavior);
}());

(function () {
    /**
     * @class PMUI.behavior.ContainerDropBehavior
     * Encapsulates the drop behavior of a container
     * @extends PMUI.behavior.DropBehavior
     *
     * @constructor
     * Creates a new instance of the class
     * @param {Array} [selectors=[]] css selectors that this drop behavior will
     * accept
     */
    var ContainerDropBehavior = function (selectors) {
        ContainerDropBehavior.superclass.call(this, selectors);
    };

    PMUI.inheritFrom('PMUI.behavior.DropBehavior', ContainerDropBehavior);
    /**
     * Type of the instances
     * @property {String}
     */
    ContainerDropBehavior.prototype.type = "ContainerDropBehavior";
    /**
     * Default selectors for this drop behavior
     * @property {String}
     */
    ContainerDropBehavior.prototype.defaultSelector = ".custom_shape";

    /**
     * On drop handler for this drop behavior, creates shapes when dropped from the
     * toolbar, or move shapes among containers
     * @param {PMUI.draw.Shape} shape
     * @return {Function}
     */
    ContainerDropBehavior.prototype.onDrop = function (shape) {
        return function (e, ui) {
            var customShape = null,
                canvas = shape.getCanvas(),
                selection,
                sibling,
                i,
                command,
                coordinates,
                id,
                shapesAdded = [],
                containerBehavior = shape.containerBehavior;
            if (canvas.readOnly) {
                return false;
            }

            shape.entered = false;
            if (ui.helper && ui.helper.attr('id') === "drag-helper") {
                return false;
            }
            id = ui.draggable.attr('id');
            
            customShape = canvas.shapeFactory(id);
            if (customShape === null) {

                customShape = canvas.customShapes.find('id', id);
                if (!customShape || !shape.dropBehavior.dropHook(shape, e, ui)) {
                    return false;
                }
                
                if (!(customShape.parent &&
                    customShape.parent.id === shape.id)) {

                    selection = canvas.currentSelection;
                    for (i = 0; i < selection.getSize(); i += 1) {
                        sibling = selection.get(i);
                        coordinates = PMUI.getPointRelativeToPage(sibling);
                        coordinates = PMUI.pageCoordinatesToShapeCoordinates(shape, null,
                                coordinates.x, coordinates.y, customShape);
                        shapesAdded.push({
                            shape: sibling,
                            container: shape,
                            x: coordinates.x,
                            y: coordinates.y,
                            topLeft: false
                        });
                    }
                    command = new PMUI.command.CommandSwitchContainer(shapesAdded);
                    command.execute();
                    canvas.commandStack.add(command);
                    canvas.multipleDrop = true;

                }

                // Fix resize minWidth and minHeight and also fix the dimension
                // of this shape (if a child made it grow)
                shape.updateDimensions(10);
                canvas.updatedElement = null;

            } else {
                coordinates = PMUI.pageCoordinatesToShapeCoordinates(shape, e,null,null,customShape);
                if (PMUI.validCoordinatedToCreate(shape, e, customShape)) {
                    shape.addElement(customShape, coordinates.x, coordinates.y,
                        customShape.topLeftOnCreation);

                    // Since it is a new element in the designer, we triggered the
                    // custom on create element event
                    canvas.updatedElement = customShape;

                    // Create the command for this new shape
                    command = new PMUI.command.CommandCreate(customShape);
                    canvas.commandStack.add(command);
                    command.execute();
                    canvas.hideAllFocusLabels();
                    if (customShape.label) {
                        customShape.label.getFocus();
                    }
                }
            }
        };
    };

    PMUI.extendNamespace('PMUI.behavior.ContainerDropBehavior', ContainerDropBehavior);
}());

(function () {
    /**
     * @class PMUI.behavior.ConnectionContainerDropBehavior
     * Class that encapsulates the drop behaviors for containers that can also be
     * connected
     * @extends PMUI.behavior.DropBehavior
     *
     * @constructor
     * Creates a new instance of the class
     * @param {Array} [selectors=[]] css selectors that this drop behavior will
     * accept
     */
    var ConnectionContainerDropBehavior = function (selectors) {
        ConnectionContainerDropBehavior.superclass.call(this, selectors);
    };

    PMUI.inheritFrom('PMUI.behavior.DropBehavior', ConnectionContainerDropBehavior);

    /**
     * Type of the instances
     * @property {String}
     */
    ConnectionContainerDropBehavior.prototype.type = "ConnectionContainerDropBehavior";
    /**
     * Default selectors for this drop behavior
     * @property {String}
     */
    ConnectionContainerDropBehavior.prototype.defaultSelector = ".custom_shape,.port";
    /**
     * Set the selectors for this drop behavior including the default selectors
     * @param {Array} selectors css selectors
     * @param {boolean} overwrite
     * @return {*}
     */
    ConnectionContainerDropBehavior.prototype.setSelectors = function (selectors, overwrite) {
        ConnectionContainerDropBehavior.superclass.prototype
            .setSelectors.call(this, selectors, overwrite);
        this.selectors.push(".port");
        this.selectors.push(".custom_shape");
        return this;
    };
    /**
     * On drop handler for this drop behavior, determines whether to create a
     * connection or add a shape to the container that is using this drop behavior
     * @param {PMUI.draw.Shape} shape
     * @return {Function}
     */
    ConnectionContainerDropBehavior.prototype.onDrop = function (shape) {
        return function (e, ui) {
            if (!PMUI.behavior.ConnectionDropBehavior.prototype.onDrop.call(this, shape)(e, ui)) {
                PMUI.behavior.ContainerDropBehavior.prototype.onDrop.call(this, shape)(e, ui);
            }
        };
    };

    PMUI.extendNamespace('PMUI.behavior.ConnectionContainerDropBehavior',
        ConnectionContainerDropBehavior);
}());



(function () {
    /**
     * @class  PMUI.behavior.NoDropBehavior
     * Encapsulates the drop behavior representing an object that can't be droppable
     * @extends PMUI.behavior.DropBehavior
     *
     * @constructor
     * Creates a new instance of the class
     */
    var NoDropBehavior = function (selectors) {
        NoDropBehavior.superclass.call(this, selectors);
    };

    PMUI.inheritFrom('PMUI.behavior.DropBehavior', NoDropBehavior);
    /**
     * Type of the instances
     * @property {String}
     */
    NoDropBehavior.prototype.type = "NoDropBehavior";
    /**
     * Attach the drop behavior, sets the accepted elements to none
     * @param {PMUI.draw.Shape} shape
     */
    NoDropBehavior.prototype.attachDropBehavior = function (shape) {
        //var $shape = $(shape.getHTML());
        //NoDropBehavior.superclass.prototype.attachDropBehavior.call(this, shape);
        //$(shape).droppable({ accept:  "" });
    };

    PMUI.extendNamespace('PMUI.behavior.NoDropBehavior', NoDropBehavior);

    if (typeof exports !== 'undefined') {
        module.exports = NoDropBehavior;
    }

}());

(function () {
    /**
     * @abstract
     * @class PMUI.behavior.ContainerBehavior
     * Object that encapsulates the container of shapes, this is an abstract class,
     * so all its methods should be implemented by its subclasses

     * @constructor
     * Creates a new instance of the class
     */
    var ContainerBehavior = function () {
    };
    /**
     * Type of the instances
     * @property {String}
     */
    ContainerBehavior.prototype.type = "ContainerBehavior";
    /**
     * Family of the instances
     * @property {String}
     */
    ContainerBehavior.prototype.family = "ContainerBehavior";
    /**
     * @abstract
     * Sets a shape's container to a given container
     * @param {PMUI.draw.BehavioralElement} container element using this behavior
     * @param {PMUI.draw.Shape} shape shape to be added
     * @template
     * @protected
     */
    ContainerBehavior.prototype.addToContainer = function (container, shape, x, y, topLeftCorner) {
    };
    /**
     * @abstract
     * Removes shape from its current container
     * @param {PMUI.draw.Shape} shape shape to be removed
     * @template
     * @protected
     */
    ContainerBehavior.prototype.removeFromContainer = function (shape) {
    };
    /**
     * @abstract
     * Adds a shape to a given container
     * @param {PMUI.draw.BehavioralElement} container container element using this behavior
     * @param {PMUI.draw.Shape} shape shape to be added to the container
     * @template
     * @protected
     */
    ContainerBehavior.prototype.addShape = function (container, shape, x, y) {
    };
    /**
     * Returns whether a shape is a container or not
     * @return {boolean}
     */
    ContainerBehavior.prototype.isContainer = function () {
        return false;
    };

    PMUI.extendNamespace('PMUI.behavior.ContainerBehavior', ContainerBehavior);

    if (typeof exports !== 'undefined') {
        module.exports = ContainerBehavior;
    }

}());

(function () {
    /**
     * @class PMUI.behavior.RegularContainerBehavior
     * Encapsulates the behavior of a regular container
     * @extends PMUI.behavior.ContainerBehavior
     *
     * @constructor
     * Creates a new instance of the class
     */
    var RegularContainerBehavior = function () {
    };

    PMUI.inheritFrom('PMUI.behavior.ContainerBehavior', RegularContainerBehavior);

    /**
     * Type of the instances
     * @property {String}
     */
    RegularContainerBehavior.prototype.type = "RegularContainerBehavior";
    /**
     * Adds a shape to a given container given its coordinates
     * @param {PMUI.draw.BehavioralElement} container container using this behavior
     * @param {PMUI.draw.Shape} shape shape to be added
     * @param {number} x x coordinate where the shape will be added
     * @param {number} y y coordinate where the shape will be added
     * @param {boolean} topLeftCorner Determines whether the x and y coordinates
     * will be considered from the top left corner or from the center
     */
    RegularContainerBehavior.prototype.addToContainer = function (container, shape, x, y, topLeftCorner) {
        var shapeLeft = 0,
            shapeTop = 0,
            shapeWidth,
            shapeHeight,
            canvas,
            topLeftFactor = (topLeftCorner === true) ? 0 : 1;

        if (container.family === "Canvas") {
            canvas = container;
        } else {
            canvas = container.canvas;
        }


        shapeWidth = shape.getZoomWidth();
        shapeHeight = shape.getZoomHeight();

        shapeLeft += x - (shapeWidth / 2) * topLeftFactor;
        shapeTop += y - (shapeHeight / 2) * topLeftFactor;

        shapeLeft /= canvas.zoomFactor;
        shapeTop /= canvas.zoomFactor;

        shape.setParent(container);
        container.getChildren().insert(shape);
        this.addShape(container, shape, shapeLeft, shapeTop);

        // fix the zIndex of this shape and it's children
        shape.fixZIndex(shape, 0);

        // fix resize minWidth and minHeight and also fix the dimension
        // of this shape (if a child made it grow)
        container.updateDimensions(10);

        // adds the shape to either the customShape arrayList or the regularShapes
        // arrayList if possible
        canvas.addToList(shape);
    };
    /**
     * Removes a shape from the container implementing this behavior
     * @param {PMUI.draw.Shape} shape shape to be removed
     */
    RegularContainerBehavior.prototype.removeFromContainer = function (shape) {
        var parent = shape.parent;
        parent.getChildren().remove(shape);
        if (parent.isResizable()) {
            parent.resizeBehavior.updateResizeMinimums(shape.parent);
        }
        shape.parent = null;
    };
    /**
     * Sets the position of the shape, and append its html
     * @param {PMUI.draw.BehavioralElement} container element implementing this behavior
     * @param {PMUI.draw.Shape} shape shape added to the container
     * @param {number} x x coordinate of the position that will be set relative to
     * the container
     * @param {number} y y coordinate of the position that will be set relative to
     * the container
     * @chainable
     */
    RegularContainerBehavior.prototype.addShape = function (container, shape, x, y) {
        shape.setPosition(x, y);
        //insert the shape HTML to the DOM
        container.getHTML().appendChild(shape.getHTML());

        shape.updateHTML();
        shape.paint();
        shape.applyBehaviors();
        shape.attachListeners();
        return this;

    };

    PMUI.extendNamespace('PMUI.behavior.RegularContainerBehavior', RegularContainerBehavior);

    if (typeof exports !== 'undefined') {
        module.exports = RegularContainerBehavior;
    }
    
}());


(function () {
    /**
     * @class PMUI.behavior.NoContainerBehavior
     * Encapsulates the behavior of elements that has no container behavior, useful
     * for implementing the strategy pattern
     * @extends PMUI.behavior.ContainerBehavior
     *
     *
     * @constructor
     * Creates a new instance of the class
     */
    var NoContainerBehavior = function () {
    };

    PMUI.inheritFrom('PMUI.behavior.ContainerBehavior', NoContainerBehavior);
    /**
     * Type of the instances
     * @property {String}
     */
    NoContainerBehavior.prototype.type = "NoContainerBehavior";

    PMUI.extendNamespace('PMUI.behavior.NoContainerBehavior', NoContainerBehavior);

    if (typeof exports !== 'undefined') {
        module.exports = NoContainerBehavior;
    }
    
}());

(function () {
    /**
     * @abstract
     * @class PMUI.behavior.ResizeBehavior
     * Abstract class which inherited classes' instances are used for delegation of the resize behavior of a shape.
     *
     * @constructor Creates an instance of the class ResizeBehavior
     */
    var ResizeBehavior = function () {
    };

    /**
     * The type of each instance of this class.
     * @property {String}
     */
    ResizeBehavior.prototype.type = "ResizeBehavior";

    /**
     * The family of each instance of this class.
     * @property {String}
     */
    ResizeBehavior.prototype.family = "ResizeBehavior";


    /**
     * Initialize JQueryUI's resize plugin
     * @param {PMUI.draw.Shape} shape
     */
    ResizeBehavior.prototype.init = function (shape) {
        var $shape = $(shape.getHTML()),
            shapeResizeOptions = {
                handles: shape.getHandlesIDs(),
                disable: false,
                start: this.onResizeStart(shape),
                resize: this.onResize(shape),
                stop: this.onResizeEnd(shape)
            };
        $shape.resizable(shapeResizeOptions);
    };

    /**
     * @abstract
     * @event resizeStart
     * Abstract method to be implemented in inherited classes
     * @param {PMUI.draw.Shape} shape
     */
    ResizeBehavior.prototype.onResizeStart = function (shape) {
    };
    /**
     * @abstract
     * @event resize
     * Abstract method to be implemented in inherited classes
     * @param {PMUI.draw.Shape} shape
     */
    ResizeBehavior.prototype.onResize = function (shape) {
    };
    /**
     * @abstract
     * @event resizeEnd
     * Abstract method to be implemented in inherited classes
     * @param {PMUI.draw.Shape} shape
     */
    ResizeBehavior.prototype.onResizeEnd = function (shape) {
    };
    /**
     * Updates the minimum height and maximum height of the JQqueryUI's resizable plugin.
     * @param {PMUI.draw.Shape} shape
     * @chainable
     */
    ResizeBehavior.prototype.updateResizeMinimums = function (shape) {
        var minW,
            minH,
            children = shape.getChildren(),
            limits = children.getDimensionLimit(),
            margin = 15,
            $shape = $(shape.getHTML());

        // TODO:  consider the labels width and height
        minW = limits[1] + margin;
        minH = limits[2] + margin;

        // update jQueryUI's minWidth and minHeight
        $shape.resizable();
        $shape.resizable('option', 'minWidth', minW);
        $shape.resizable('option', 'minHeight', minH);
        return this;
    };

    PMUI.extendNamespace('PMUI.behavior.ResizeBehavior', ResizeBehavior);

    if (typeof exports !== 'undefined') {
        module.exports = ResizeBehavior;
    }

}());

(function () {
    /**
     * @class PMUI.behavior.NoResizeBehavior
     * Class that encapsulates the regular resize behavior of a shape when it's not supposed to be resizable
     * @extends PMUI.behavior.ResizeBehavior
     *
     * @constructor Creates a new instance of the class RegularResizeBehavior
     */
    var NoResizeBehavior = function () {
    };

    PMUI.inheritFrom('PMUI.behavior.ResizeBehavior', NoResizeBehavior);

    /**
     * The type of each instance of this class.
     * @property {String}
     */
    NoResizeBehavior.prototype.type = "NoResizeBehavior";

    /**
     * Initialize JQueryUI's resize plugin (disables the resizable plugin).
     * @param {PMUI.draw.Shape} shape
     */
    NoResizeBehavior.prototype.init = function (shape) {
        var $shape = $(shape.getHTML());
        NoResizeBehavior.superclass.prototype.init.call(this, shape);
        $shape.resizable('disable');
        $shape
            .removeClass('ui-state-disabled');
        shape.applyStyleToHandlers('nonResizableStyle');
        shape.showOrHideResizeHandlers(false);
    };

    /**
     * Overwrites the method {@link PMUI.behavior.ResizeBehavior#updateResizeMinimums} since
     * a shape that is not resizable shouldn't update its resize minimums.
     * @param {PMUI.draw.Shape} shape
     */
    NoResizeBehavior.prototype.updateResizeMinimums = function (shape) {
    };

    PMUI.extendNamespace('PMUI.behavior.NoResizeBehavior', NoResizeBehavior);

    if (typeof exports !== 'undefined') {
        module.exports = NoResizeBehavior;
    }
    
}());

(function () {
    /**
     * @class PMUI.behavior.RegularResizeBehavior
     * Class that encapsulates the regular resize behavior of a shape
     * @extends PMUI.behavior.ResizeBehavior
     *
     * @constructor Creates a new instance of the class RegularResizeBehavior
     */
    var RegularResizeBehavior = function () {
    };

    PMUI.inheritFrom('PMUI.behavior.ResizeBehavior', RegularResizeBehavior);

    /**
     * The type of each instance of this class
     * @property {String}
     */
    RegularResizeBehavior.prototype.type = "RegularResizeBehavior";

    /**
     * Initialize JQueryUI's resizable plugin
     * @param {PMUI.draw.Shape} shape
     */
    RegularResizeBehavior.prototype.init = function (shape) {
        var $shape = $(shape.getHTML());
        RegularResizeBehavior.superclass.prototype.init.call(this, shape);
        $shape.resizable('enable');
        shape.applyStyleToHandlers('resizableStyle');

        // hide its handles (jQueryUI's resizable shows the handles by default)
        shape.showOrHideResizeHandlers(false);
    };

    /**
     * @event resizeStart
     * ResizeStart event fired when the user resizes a shape.
     * It does the following:
     *
     * - Save old values (for the undo-redo stack)
     * - Empties the {@link PMUI.draw.Canvas#property-currentSelection}, and adds `shape` to that arrayList
     * - Hides the resize handlers of the shape
     *
     * @param {PMUI.draw.Shape} shape
     */
    RegularResizeBehavior.prototype.onResizeStart = function (shape) {
        return function (e, ui) {
            shape.resizing = true;
            shape.canvas.isResizing = true;
            shape.dragging = false;

            shape.oldWidth = shape.width;
            shape.oldHeight = shape.height;
            shape.oldX = shape.x;
            shape.oldY = shape.y;
            shape.oldAbsoluteX = shape.absoluteX;
            shape.oldAbsoluteY = shape.absoluteY;

            if (shape.ports) {
                shape.initPortsChange();
            }

            if (shape.canvas.currentSelection.getSize() > 1) {
                shape.canvas.emptyCurrentSelection();
                shape.canvas.addToSelection(shape);
            }
            shape.showOrHideResizeHandlers(false);
            shape.calculateLabelsPercentage();
            shape.canvas.hideAllFocusLabels();
            return true;
        };
    };

    /**
     * @event resize
     * Resize event fired when the user is resizing a shape.
     * It does the following:
     *
     * - Sets the position and dimensions of the shape
     * - Fixes the ports of `shape` and from the its children (recursively)
     * - Updates the position of its labels
     *
     * @param {PMUI.draw.Shape} shape
     */
    RegularResizeBehavior.prototype.onResize = function (shape) {
        return function (e, ui) {
            var i,
                port,
                canvas = shape.canvas;
            shape.setPosition(ui.position.left / canvas.zoomFactor,
                    ui.position.top / canvas.zoomFactor);
            shape.setDimension(ui.size.width / canvas.zoomFactor,
                    ui.size.height / canvas.zoomFactor);

            // fix the position of the shape's ports (and the positions and port
            // position of its children)
            // parameters (shape, resizing, root)
            shape.fixConnectionsOnResize(shape.resizing, true);
            // fix the labels positions on resize (on x = true and y = true)
            shape.updateLabelsPosition();
            shape.canvas.hideAllFocusLabels();
        };
    };

    /**
     * @event resizeEnd
     * ResizeEnd event fired when the user stops resizing a shape.
     * It does the following:
     *
     * - Shows the handlers of `shape`
     * - Updates the dimension of its parent (this shape might have outgrown the shape)
     * - Creates an instance of {@link PMUI.command.CommandResize} to add it to the undo-redo stack
     *
     * @param {PMUI.draw.Shape} shape
     */
    RegularResizeBehavior.prototype.onResizeEnd = function (shape) {
        return function (e, ui) {
            var i,
                label,
                command;
            shape.resizing = false;
            shape.canvas.isResizing = false;
            // last resize
            RegularResizeBehavior.prototype.onResize.call(this, shape)(e, ui);

            // show the handlers again
            shape.showOrHideResizeHandlers(true);

            // update the dimensions of the parent if possible (a shape might
            // have been resized out of the dimensions of its parent)
            shape.parent.updateDimensions(10);

            if (shape.ports) {
                shape.firePortsChange();
            }

            // TESTING COMMANDS
            command = new PMUI.command.CommandResize(shape);
            shape.canvas.commandStack.add(command);
            command.execute();
            for (i = 0; i < shape.labels.getSize(); i += 1) {
                label = shape.labels.get(i);
                label.setLabelPosition(label.location, label.diffX, label.diffY);
            }

            return true;
        };
    };

    PMUI.extendNamespace('PMUI.behavior.RegularResizeBehavior', RegularResizeBehavior);

    if (typeof exports !== 'undefined'){
        module.exports = RegularResizeBehavior;
    }

}());

(function() {
    var DataControlDropBehavior = function(settings) {
        DataControlDropBehavior.superclass.call(this, settings);
        this.targetObject = null;
        this.scope = null;
        this.hoverClass = null;
        DataControlDropBehavior.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.behavior.DropBehavior', DataControlDropBehavior);

    DataControlDropBehavior.prototype.init = function(settings) {
        var defaults = {
            scope: 'pmui-datacontrol-dragdrop-scope',
            hoverClass: 'pmui-dataControl-drag-over'
        };

        jQuery.extend(true, defaults, settings);

        if(!defaults.targetObject) {
            throw new Error("The target object wasn't specified.");
        }

        this.targetObject = defaults.targetObject;
        this.hoverClass = defaults.hoverClass;
        this.setScope(defaults.scope);
    };

    DataControlDropBehavior.prototype.setScope = function(scope) {
        if(typeof scope === 'string') {
            this.scope = scope;
        } else {
            throw new Error("setScope(): the parameter must be a string.");
        }

        return this;
    };

    DataControlDropBehavior.prototype.attachDropBehavior = function() {
        var html = this.targetObject.getHTML();

        $(html).droppable({
            greedy: true,
            scope: this.scope,
            hoverClass: this.hoverClass,
            drop: this.onDrop(),
            over: this.onDragEnter(),
            out: this.onDragLeave()
        });

        return this;
    };

    DataControlDropBehavior.prototype.onDragLeave = function() {
        return (this.targetObject.onDragLeaveHandler && this.targetObject.onDragLeaveHandler()) || null;
    };

    DataControlDropBehavior.prototype.onDragEnter = function() {
        return (this.targetObject.onDragEnterHandler && this.targetObject.onDragEnterHandler()) || null;
    };

    DataControlDropBehavior.prototype.onDrop = function() {
        var targetObject = this.targetObject;
        return function(e, ui) {
            var data = $(ui.draggable).data('pmui-datacontrol-data');
            data.currentContainer.removeItem(data.index);
            ui.helper.remove();
            targetObject.addItem(data.data);
        };
    };

    PMUI.extendNamespace('PMUI.behavior.DataControlDropBehavior', DataControlDropBehavior);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = DataControlDropBehavior;
    }
}());
(function() {
    var DataControlNoDropBehavior = function(settings) {
        DataControlNoDropBehavior.superclass.call(this, settings);
        this.targetObject = null;        
        DataControlNoDropBehavior.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.behavior.NoDropBehavior', DataControlNoDropBehavior);

    DataControlNoDropBehavior.prototype.init = function(settings) {
        if(!settings.targetObject) {
            throw new Error("The target object wasn't specified.");
        }

        this.targetObject = settings.targetObject;
    };

    DataControlNoDropBehavior.prototype.attachDropBehavior = function() {
        var html = this.targetObject.getHTML();

        try {
            $(html).droppable('destroy');
        } catch(e) {}

        return this;
    };

    PMUI.extendNamespace('PMUI.behavior.DataControlNoDropBehavior', DataControlNoDropBehavior);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = DataControlNoDropBehavior;
    }
}());
(function () {
    var DataControlDragBehavior = function (settings) {
        DataControlDragBehavior.superclass.call(this, settings);
        this.targetObject = null;
        this.scope = null;
        this.helper = null;
        DataControlDragBehavior.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.behavior.DragBehavior', DataControlDragBehavior);

    DataControlDragBehavior.prototype.init = function (settings) {
        var defaults = {
            scope: 'pmui-datacontrol-dragdrop-scope',
            helper: 'original'
        };

        jQuery.extend(true, defaults, settings);

        //TODO: uncomment the following validation when the GridPanel's rows are handled through objects.
        //by now it will accept html elements as the targetObject option.
        /*if(!defaults.targetObject || typeof defaults.targetObject !== 'object') {
         throw new Error("The target object is null or invalid.");
         }*/

        if (!defaults.targetObject) {
            throw new Error("The target object is null or invalid.");
        }
        this.targetObject = defaults.targetObject;
        this.helper = defaults.helper;
        this.scope = defaults.scope;
    };

    DataControlDragBehavior.prototype.onReceiveHandler = function () {
        var targetObject = this.targetObject;
        return function (e, ui) {
            var row,
                index,
                newIndex,
                diff,
                globalIndex,
                grid,
                auxArray;
            row = ui.item.data("pmui-datacontrol-data");
            grid = row.parent;
            //remove the item from its previous grid
            index = grid.getRowIndex(row);
            grid.items.asArray().splice(index, 1);
            grid.goToPage(grid.currentPage);
            //append the object to the new grid
            newIndex = $(targetObject.dom.tbody).find('tr').index(ui.item);
            $(ui.item).detach();
            targetObject.addItem(row, newIndex);
            row.setCells();
        };
    };

    DataControlDragBehavior.prototype.onDropHandler = function () {
        return function (e, ui) {
            if (ui.item.parent().get(0) === this) {
                var row,
                    index,
                    newIndex,
                    diff,
                    globalIndex,
                    grid,
                    auxArray;
                row = ui.item.data("pmui-datacontrol-data");
                grid = row.parent;
                index = row.parent.indexInPage(row);
                newIndex = $(grid.dom.tbody).find('tr').index(ui.item);
                diff = newIndex - index;
                newIndex = index + diff;
                globalIndex = grid.getRowIndex(row);
                row = grid.items.asArray().splice(globalIndex, 1)[0];
                newIndex = globalIndex + diff;
                grid.items.asArray().splice(newIndex, 0, row);
            }
        };
    };

    DataControlDragBehavior.prototype.attachDragBehavior = function () {
        var $html,
            i,
            items,
            item,
            index,
            row,
            object = this.targetObject;
        items = this.targetObject.getItems();
        for (i = 0; i < items.length; i += 1) {
            item = $(items[i].getHTML()).data("pmui-datacontrol-data", (function (j) {
                return object.items.get(j);
            }(i)));
            if (!$html) {
                $html = jQuery(item);
            } else {
                $html = $html.add(item);
            }
        }

        try {
            $(this.targetObject.dom.tbody).sortable("destroy");
        } catch (e) {
        }

        $(this.targetObject.dom.tbody).sortable({
            connectWith: '.pmui-gridpanel-tbody',
            update: this.onDropHandler(),
            receive: this.onReceiveHandler()
        });

        return this;
    };

    PMUI.extendNamespace('PMUI.behavior.DataControlDragBehavior', DataControlDragBehavior);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = DataControlDragBehavior;
    }
}());
(function () {
    var DataControlNoDragBehavior = function (settings) {
        DataControlNoDragBehavior.superclass.call(this, settings);
        this.targetObject = null;
        DataControlNoDragBehavior.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.behavior.NoDragBehavior', DataControlNoDragBehavior);

    DataControlNoDragBehavior.prototype.init = function (settings) {
        //TODO: uncomment the following validation when the GridPanel's rows are handled through objects.
        //by now it will accept html elements as the targetObject option.
        /*if(!defaults.targetObject || typeof defaults.targetObject !== 'object') {
         throw new Error("The target object is null or invalid.");
         }*/
        this.targetObject = settings.targetObject;
    };

    DataControlNoDragBehavior.prototype.attachDragBehavior = function () {
        var $html,
            i,
            items,
            item;

        if (this.targetObject instanceof PMUI.panel.GridPanel) {
            html = $(this.targetObject.getHTML()).find('tr');
        } else {
            items = this.targetObject.getItems();
            for (i = 0; i < items.length; i += 1) {
                item = $(items[i].getHTML()).data("'pmui-datacontrol-data'", items[i]);
                if (!$html) {
                    $html = jQuery(item);
                } else {
                    $html = $html.add(item);
                }
            }
        }
        try {
            $(html).draggable("destroy");
        } catch (e) {
        }

        return this;
    };

    PMUI.extendNamespace('PMUI.behavior.DataControlNoDragBehavior', DataControlNoDragBehavior);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = DataControlNoDragBehavior;
    }
}());
(function () {
    /**
     * @class PMUI.behavior.ContainerItemBehavior
     * Abstract class that encapsulates common properties ans functionality for the specific behaviors that can be
     * attached to the Container's subclasses.
     * @extends {PMUI.core.Base}
     * @abstract
     *
     * @cfg {PMUI.core.Container} targetObject The object the behavior will be applied to.
     * @cfg {String} [scope="pmui-containeritem-behavior"] A string that identifies group sets of items that can
     * interact with this target object's behavior.
     * @cfg {String} [handle=null] If specified, restricts dragging from starting unless the mousedown occurs on the
     * specified element(s). Only elements that descend from the draggable element are permitted.
     */
    var ContainerItemBehavior = function (settings) {
        ContainerItemBehavior.superclass.call(this, settings);
        /**
         * The object the behavior will be applied to.
         * @type {PMUI.core.Container}
         */
        this.targetObject = null;
        /**
         * The string that identifies group sets of items that can interact with this target object's behavior.
         * @type {String}
         */
        this.scope = null;
        /**
         * If the behavior has been attached to its
         * {@link PMUI.behavior.ContainerItemContainer#property-targetObject targetObject}.
         * @type {Boolean}
         * @readonly
         */
        this.behaviorAttached = null;
        /**
         * The JQuery Object the JQuery UI behavior is currently applied to.
         * @type {Object}
         * @private
         */
        this.targetHTML = null;
        /**
         * The selector for the sortable items in case a related behavior is applied.
         * @type {String}
         * @private
         */
        this.sortableItems = null;
        /**
         * If the behavior is disabled or not.
         * @type {Boolean}
         * @readonly
         */
        this.disabled = null;
        /**
         * The current object that currently is being dragged.
         * @type {PMUI.core.Element}
         * @readonly
         */
        this.draggedObject = null;
        /**
         * The class for the placeholder used in some behaviors.
         * @type {String}
         * @readonly
         */
        this.placeholderClass = null;
        /**
         * The class to be applied to the element when it is droppable and has a draggable element being dragged over it.
         * @type {String}
         * @readonly
         */
        this.hoverClass = null;

        this.sortItem = null;

        this.sortIObject = null;

        /**
         * Restricts dragging/sorting from starting unless the mousedown occurs on the specified element(s).
         * Only elements that descend from the draggable element are permitted.
         * @type {String}
         */
        this.handle = null;

        ContainerItemBehavior.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Base', ContainerItemBehavior);

    ContainerItemBehavior.prototype.init = function (settings) {
        var defaults = {
            scope: 'pmui-containeritem-behavior',
            disabled: false,
            placeholderClass: 'pmui-containeritembehavior-placeholder',
            hoverClass: 'pmui-containeritembehavior-hover',
            handle: null
        };

        $.extend(true, defaults, settings);

        this.scope = defaults.scope;
        this.placeholderClass = defaults.placeholderClass;
        this.hoverClass = defaults.hoverClass;
        this.setTargetObject(defaults.targetObject);
        this.handle = defaults.handle;

        if (defaults.disabled) {
            this.disable();
        } else {
            this.enable();
        }
    };
    /**
     * Updates the behavior status (enabled/disabled).
     * @chainable
     * @abstract
     * @private
     */
    ContainerItemBehavior.prototype.updateBehaviorAvailability = function () {
    };
    /**
     * Enables the behavior.
     * @chainable
     */
    ContainerItemBehavior.prototype.enable = function () {
        this.disabled = false;
        if (this.targetHTML) {
            this.updateBehaviorAvailability();
        }
        return this;
    };
    /**
     * Disables the behavior.
     * @chainable
     */
    ContainerItemBehavior.prototype.disable = function () {
        this.disabled = true;
        if (this.targetHTML) {
            this.updateBehaviorAvailability();
        }
        return this;
    };
    /**
     * Sets the object the behavior will be applied to.
     * @param {PMUI.core.Container} targetObject
     * @private
     */
    ContainerItemBehavior.prototype.setTargetObject = function (targetObject) {
        if (targetObject instanceof PMUI.core.Container) {
            this.targetObject = targetObject;
            this.sortableItems = this.targetObject.sortableItems;
        } else {
            throw new Error("setTargetObject(): the parameter must be an instance of PMUI.core.Container.");
        }

        return this;
    };
    /**
     * Returns the target object the behavior will be applied to.
     * @return {PMUI.core.Container}
     */
    ContainerItemBehavior.prototype.getTargetObject = function () {
        return this.targetObject;
    };
    /**
     * Returns true if the behavior has been attached to its
     * {@link PMUI.behavior.ContainerItemContainer#property-targetObject targetObject}.
     * @return {Boolean}
     */
    ContainerItemBehavior.prototype.isBehaviorAttached = function () {
        return this.behaviorAttached;
    };
    /**
     * Defines the handler to be executed when the
     * {@link #property-targetObject targetObject}'s item dragging starts.
     * @return {Function} The handler.
     * @private
     */
    ContainerItemBehavior.prototype.onStart = function () {
        var that = this;
        return function (e, ui) {
            var targetObject = that.targetObject;
            ui.helper.get(0).style.width = $(e.target).width() + "px";
            $(this).hide();
            that.draggedObject = PMUI.getPMUIObject(this);
            if (typeof targetObject.onDragStart === 'function') {
                targetObject.onDragStart(targetObject, that.draggedObject);
            }
        };
    };
    /**
     * Defines the handler to be executed while a {@link #property-targetObject targetObject}'s item is being dragged.
     * @return {Function} The handler.
     * @private
     */
    ContainerItemBehavior.prototype.onDrag = function () {
        var that = this;
        return function (e, ui) {
        };
    };
    /**
     * Defines the handler to be executed when the
     * {@link #property-targetObject targetObject}'s item dragging stops.
     * @return {Function} The handler.
     * @private
     */
    ContainerItemBehavior.prototype.onStop = function () {
        var that = this;
        return function (e, ui) {
            var targetObject = that.targetObject;
            that.draggedObject = PMUI.getPMUIObject(this);
            if (typeof targetObject.onStopDrag === 'function') {
                targetObject.onStopDrag(targetObject, that.draggedObject);
            }
            targetObject = $(this);
            if (targetObject.parent().length) {
                targetObject.show();
            } else {
                targetObject.remove();
            }
            that.draggedObject = null;
        };
    };
    /**
     * Defines the handler to be executed when an accepted draggable is dropped on the
     * {@link #property-targetObject targetObject}.
     * @return {Function} The handler.
     * @private
     */
    ContainerItemBehavior.prototype.onDrop = function () {
        var that = this;
        return function (e, ui) {
            var item = PMUI.getPMUIObject(ui.draggable.get(0)),
                data,
                placeholder = ui.draggable.data().sortableItem && ui.draggable.data().sortableItem.placeholder[0],
                targetObject = that.targetObject, performDrop;

            jQuery(ui.helper).remove();

            if (!item || targetObject.isDirectParentOf(item)) {
                return;
            }

            if (typeof targetObject.onBeforeDrop === 'function') {
                targetObject.onBeforeDrop(targetObject, item, targetObject.items.getSize());
            }

            if (typeof targetObject.onDrop === 'function') {
                performDrop = targetObject.onDrop(targetObject, item, targetObject.items.getSize() - 1);
            }
            if (performDrop || performDrop === undefined) {
                item.getParent().behaviorObject.onDropOut(item, targetObject);
            }
            //The next lines avoid error when the dropped element is a sortable element
            if (placeholder) {
                placeholder.style.display = 'none';
                targetObject.getContainmentArea().appendChild(placeholder);
            }
        };
    };
    /**
     * The function to be perform when a draggable item is drop out from the container. Only applicable the behavior id
     * of the drag type.
     * @param {PMUI.core.Element} item The draggable item involved in the action.
     * @param {PMUI.core.Container} destiny The container the draggable item was dropped on.
     * @chainable
     * @private
     */
    ContainerItemBehavior.prototype.onDropOut = function (item, destiny, index) {
        var targetObject = this.targetObject;
        targetObject.removeItem(item);
        if (destiny.canContain(item)) {
            destiny.addItem(item, index);
            item.setVisible(true);
        } else {
            data = item.getData();
            destiny.addDataItem(data, index);
        }
        if (typeof targetObject.onDropOut === 'function') {
            targetObject.onDropOut(item, targetObject, destiny);
        }
        return this;
    };
    /**
     * Attaches event handlers for the mouse events triggered by the draggable/sortableItems.
     * @chainable
     * @private
     */
    ContainerItemBehavior.prototype.attachOnDraggableMouseEvents = function () {
        var that = this;
        this.detachOnDraggableMouseEvents();
        this.targetHTML.on('mouseover.pmui-containeritemdrag', function () {
            var pmuiObject = PMUI.getPMUIObject(this);
            that.targetObject.onDraggableMouseOver(pmuiObject);
            return true;
        }).on('mouseout.pmui-containeritemdrag', function () {
            var pmuiObject = PMUI.getPMUIObject(this);
            that.targetObject.onDraggableMouseOut(pmuiObject);
            return true;
        });
        return this;
    };
    /**
     * Attaches event handlers for the mouse events triggered by the draggable/sortableItems.
     * @chainable
     * @private
     */
    ContainerItemBehavior.prototype.detachOnDraggableMouseEvents = function () {
        this.targetHTML.off('mouseover.pmui-containeritemdrag')
            .off('mouseout.pmui-containeritemdrag');
        return this;
    };
    /**
     * Attaches the behavior to its
     * {@link PMUI.behavior.ContainerItemContainer#property-targetObject targetObject}.
     * @chainable
     */
    ContainerItemBehavior.prototype.attachBehavior = function () {
        this.behaviorAttached = true;
        this.updateBehaviorAvailability();
        return this;
    };
    /**
     * Detaches the behavior from  its
     * {@link PMUI.behavior.ContainerItemContainer#property-targetObject targetObject}.
     * @chainable
     */
    ContainerItemBehavior.prototype.detachBehavior = function () {
        this.behaviorAttached = false;
        return this;
    };

    PMUI.extendNamespace('PMUI.behavior.ContainerItemBehavior', ContainerItemBehavior);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = ContainerItemBehavior;
    }
}());
(function() {
    /** 
     * @class PMUI.behavior.ContainerItemNoBehavior
     * @extends PMUI.behavior.ContainerItemBehavior
     */
    var ContainerItemNoBehavior = function(settings) {
        ContainerItemNoBehavior.superclass.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.behavior.ContainerItemBehavior', ContainerItemNoBehavior);
    /** [attachBehavior description] */
    ContainerItemNoBehavior.prototype.attachBehavior = function() {
        var $html,
            i,
            items,
            item,
            targetObject = this.targetObject;
        if(!this.behaviorAttached) {
            items = targetObject.getItems();
            for(i = 0; i < items.length; i += 1) {
                item = items[i].getHTML();
                if(!$html) {
                    $html = jQuery(item);
                } else {
                    $html = $html.add(item);
                }
            }

            try {
                $html.draggable('destroy');
            } catch(e) {}

            try {
                $(targetObject.getContainmentArea()).droppable('destroy');
            } catch(e) {}

            try {
                $(targetObject.getContainmentArea()).sortable('destroy');
            } catch(e) {}
        }
        return ContainerItemNoBehavior.superclass.prototype.attachBehavior.call(this);
    };

    PMUI.extendNamespace('PMUI.behavior.ContainerItemNoBehavior', ContainerItemNoBehavior);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = ContainerItemNoBehavior;
    }
}());
(function () {
    /**
     * @class PMUI.behavior.ContainerItemDragBehavior
     * @extends PMUI.behavior.ContainerItemBehavior
     * Class that encapsulates the drag behavior for the items of a {@link PMUI.core.Container Container}. The object
     * that applies this behavior will be able to be dragged to another Containers that are applying dropping behaviors
     * ({@link PMUI.behavior.ContainerItemDropBehavior drop},
     * {@link PMUI.behavior.ContainerItemDragDropBehavior dragdrop},
     * {@link PMUI.behavior.ContainerItemDropSortBehavior dropsort},
     * {@link PMUI.behavior.ContainerItemDragDropSortBehavior dragdropsort}).
     *
     * Since this class is created through the
     * {@link PMUI.behavior.ContainerItemBehaviorFactory ContainerItemBehaviorFactory} it shouldn't be instantiated in
     * most cases (it is instantiated internally by the object of the Container class).
     * If you want to apply this behavior to a {@link PMUI.core.Container Container} object, just call its
     * {@link PMUI.core.Container#method-setBehavior setBehavior()} method with the string parameter "drag" or set it
     * at instantiation time using its {@link PMUI.core.Container#cfg-behavior behavior} config option.
     *
     * Usage example:
     *
     *      @example
     *      var tree = new PMUI.panel.TreePanel({
     *          items: [
     *              {
     *                  label: "draggable item #1"
     *              }, {
     *                  label: "draggable item #2"
     *              }, {
     *                  label: "draggable item #3"
     *              }, {
     *                  label: "draggable item #4"
     *              }, {
     *                  label: "draggable item #5"
     *              }, {
     *                  label: "draggable item #6"
     *              }
     *          ]
     *      }), tree2 = new PMUI.panel.TreePanel({
     *          style: {
     *              cssClasses: ["droppable-list"]
     *          },
     *          items: [
     *              {
     *                  label: 'Drop Here!'
     *              }
     *          ]
     *      });
     *
     *      tree.setBehavior("drag");
     *      tree2.setBehavior("drop");
     *      document.body.appendChild(tree.getHTML());
     *      //Add a separator
     *      document.body.appendChild(document.createElement('hr'));
     *      document.body.appendChild(tree2.getHTML());
     */
    var ContainerItemDragBehavior = function (settings) {
        ContainerItemDragBehavior.superclass.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.behavior.ContainerItemBehavior', ContainerItemDragBehavior);
    /**
     * @inheritdoc
     */
    ContainerItemDragBehavior.prototype.updateBehaviorAvailability = function () {
        this.targetHTML.draggable(this.disabled ? 'disable' : 'enable');
        if (this.disabled) {
            this.detachOnDraggableMouseEvents();
        } else {
            this.attachOnDraggableMouseEvents();
        }
        return this;
    };
    /**
     * Returns the items from the {@link PMUI.behavior.ContainerItemDragBehavior#property-targetObject targetObject}
     * related to the behavior.
     * @return {Object} A jQuery object.
     * @private
     */
    ContainerItemDragBehavior.prototype.getTargetObjectItems = function () {
        var $html = $(null),
            i,
            item,
            items,
            targetObject = this.targetObject;
        items = targetObject.getBehavioralItems();
        for (i = 0; i < items.length; i += 1) {
            item = items[i].getHTML();
            $html = $html.add(item);
        }
        return $html;
    };
    /**
     * @inheritdoc
     */
    ContainerItemDragBehavior.prototype.onDropOut = function (item, targetObject, index) {
        var aux;
        ContainerItemDragBehavior.superclass.prototype.onDropOut.apply(this, arguments);
        //The next lines avoid an uncontrolable error generated on the jquery ui's stop callback
        //when it try to access certain data that will be undefined because of the destruction of the
        //drag behavior when it is added to the new container in which it was dropped on.
        //What it does is add some data in the jquery data of the object's html.
        this.draggedObject = item;
        aux = jQuery(this.draggedObject.html).data('ui-draggable');
        if (!aux) {
            aux = {};
            jQuery(this.draggedObject.html).data('ui-draggable', aux);
        }
        if (!aux.sortables) {
            aux.sortables = [];
        }
        if (!aux.options) {
            aux.options = {};
        }
        return this;
    };
    /**
     * Attaches the behavior to the {@link PMUI.behavior.ContainerItemDragBehavior#property-targetObject targetObject}.
     * @chainable
     */
    ContainerItemDragBehavior.prototype.attachBehavior = function (index) {
        var $html,
            i,
            items,
            item,
            targetObject = this.targetObject,
            behaviorAttached;
        if (index !== undefined) {
            $html = $(this.targetObject.items.get(index).getHTML());
            behaviorAttached = this.targetObject.items.get(index).behaviorObject.behaviorAttached;
        } else {
            $html = this.getTargetObjectItems();
            behaviorAttached = this.behaviorAttached;
        }
        if (!behaviorAttached) {
            if (index !== undefined) {
                $html = $(this.targetObject.items.get(index).getHTML());
            } else {
                $html = this.getTargetObjectItems();
            }
            this.targetHTML = $html;
            $html.draggable({
                appendTo: document.body,
                connectToSortable: '.pmui-containeritembehavior-sort',
                helper: 'clone',
                revert: "invalid",
                scope: this.scope,
                drag: this.onDrag(),
                start: this.onStart(),
                stop: this.onStop(),
                items: this.draggableItems,
                handle: this.handle || false,
                zIndex: 1000
            });
            this.attachOnDraggableMouseEvents();
            if (index !== undefined) {
                this.targetObject.items.get(index).behaviorObject.behaviorAttached = true;
                this.targetObject.items.get(index).behaviorObject.updateBehaviorAvailability();
            } else {
                ContainerItemDragBehavior.superclass.prototype.attachBehavior.call(this);
            }
        }
        return this;
    };
    /**
     * Detaches the behavior from the
     * {@link PMUI.behavior.ContainerItemDragBehavior#property-targetObject targetObject}.
     * @chainable
     */
    ContainerItemDragBehavior.prototype.detachBehavior = function () {
        if (this.behaviorAttached) {
            try {
                this.targetHTML.draggable('destroy');
                this.detachOnDraggableMouseEvents();
            } catch (e) {
            }
        }
        return ContainerItemDragBehavior.superclass.prototype.detachBehavior.call(this);
    };

    PMUI.extendNamespace('PMUI.behavior.ContainerItemDragBehavior', ContainerItemDragBehavior);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = ContainerItemDragBehavior;
    }
}());
(function() {
    /**
     * @class PMUI.behavior.ContainerItemDragCloneBehavior
     * @extends PMUI.behavior.ContainerItemDragBehavior
     * Class that encapsulates the drag behavior for the items of a {@link PMUI.core.Container Container}. The object 
     * that applies this behavior will be able to create a clone item to be dragged to another Containers that are 
     * applying dropping behaviors
     * ({@link PMUI.behavior.ContainerItemDropBehavior drop}, 
     * {@link PMUI.behavior.ContainerItemDragDropBehavior dragdrop}, 
     * {@link PMUI.behavior.ContainerItemDropSortBehavior dropsort}, 
     * {@link PMUI.behavior.ContainerItemDragDropSortBehavior dragdropsort}).
     * 
     * Since this class is created through the 
     * {@link PMUI.behavior.ContainerItemBehaviorFactory ContainerItemBehaviorFactory} it shouldn't be instantiated in 
     * most cases (it is instantiated internally by the object of the Container class).
     * If you want to apply this behavior to a {@link PMUI.core.Container Container} object, just call its 
     * {@link PMUI.core.Container#method-setBehavior setBehavior()} method with the string parameter "dragclone" or set it 
     * at instantiation time using its {@link PMUI.core.Container#cfg-behavior behavior} config option.
     *
     * Usage example:
     *
     *      @example
     *      var tree = new PMUI.panel.TreePanel({
     *          items: [
     *              {
     *                  label: "draggable item #1"
     *              }, {
     *                  label: "draggable item #2"
     *              }, {
     *                  label: "draggable item #3"
     *              }, {
     *                  label: "draggable item #4"
     *              }, {
     *                  label: "draggable item #5"
     *              }, {
     *                  label: "draggable item #6"
     *              }
     *          ]
     *      }), tree2 = new PMUI.panel.TreePanel({
     *          style: {
     *              cssClasses: ["droppable-list"]
     *          },
     *          items: [
     *              {
     *                  label: 'Drop Here!'
     *              }
     *          ]
     *      });
     *
     *      tree.setBehavior("dragclone");
     *      tree2.setBehavior("drop");
     *      document.body.appendChild(tree.getHTML());
     *      //Add a separator
     *      document.body.appendChild(document.createElement('hr'));
     *      document.body.appendChild(tree2.getHTML());
     */
    var ContainerItemDragCloneBehavior = function(settings) {
        ContainerItemDragCloneBehavior.superclass.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.behavior.ContainerItemDragBehavior', ContainerItemDragCloneBehavior);

    /**
     * @inheritdoc
     */
    ContainerItemDragCloneBehavior.prototype.onStart = function() {
        var that = this;
        return function(e, ui) {
            var targetObject = that.targetObject;
            ui.helper.get(0).style.width = $(e.target).width() + "px";
            that.draggedObject = PMUI.getPMUIObject(this);
            if(typeof targetObject.onDragStart === 'function') {
                targetObject.onDragStart(targetObject, that.draggedObject);
            }
        };
    };
    /**
     * @inheritodc
     */
    ContainerItemDragCloneBehavior.prototype.onDropOut = function(item, destiny, index) {
        var targetObject = this.targetObject;
        data = item.getData();
        destiny.addDataItem(data, index);
        if(typeof targetObject.onDropOut === 'function') {
            targetObject.onDropOut(item, targetObject, destiny);
        }
        return this;
    };

    PMUI.extendNamespace('PMUI.behavior.ContainerItemDragCloneBehavior', ContainerItemDragCloneBehavior);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = ContainerItemDragCloneBehavior;
    }
}());
(function() {
    /**
     * @class PMUI.behavior.ContainerItemDropBehavior
     * @extends PMUI.behavior.ContainerItemBehavior
     * Class that encapsulates the drop behavior for {@link PMUI.core.Container Container} objects. The object that 
     * applies this behavior will be able to accept draggable items from other Containers which ones are applying 
     * dragging behaviors ({@link PMUI.behavior.ContainerItemDragBehavior drag}, 
     * {@link PMUI.behavior.ContainerItemDragDropBehavior dragdrop}, 
     * {@link PMUI.behavior.ContainerItemDragSortBehavior dragsort}, 
     * {@link PMUI.behavior.ContainerItemDragDropSortBehavior dragdropsort}). .
     * 
     * Since this class is created through the 
     * {@link PMUI.behavior.ContainerItemBehaviorFactory ContainerItemBehaviorFactory} it shouldn't be instantiated in 
     * most cases (it is instantiated internally by the object of the Container class).
     * If you want to apply this behavior to a {@link PMUI.core.Container Container} object, just call its 
     * {@link PMUI.core.Container#method-setBehavior setBehavior()} method with the string parameter "drop" or set it 
     * at instantiation time using its {@link PMUI.core.Container#cfg-behavior behavior} config option.
     *
     * Usage example:
     *
     *      @example
     *      var tree = new PMUI.panel.TreePanel({
     *          items: [
     *              {
     *                  label: "draggable item #1"
     *              }, {
     *                  label: "draggable item #2"
     *              }, {
     *                  label: "draggable item #3"
     *              }, {
     *                  label: "draggable item #4"
     *              }, {
     *                  label: "draggable item #5"
     *              }, {
     *                  label: "draggable item #6"
     *              }
     *          ]
     *      }), tree2 = new PMUI.panel.TreePanel({
     *          style: {
     *              cssClasses: ["droppable-list"]
     *          },
     *          items: [
     *              {
     *                  label: 'Drop Here!'
     *              }
     *          ]
     *      });
     *
     *      tree.setBehavior("drag");
     *      tree2.setBehavior("drop");
     *      document.body.appendChild(tree.getHTML());
     *      //Add a separator
     *      document.body.appendChild(document.createElement('hr'));
     *      document.body.appendChild(tree2.getHTML());
     */
    var ContainerItemDropBehavior = function(settings) {
        ContainerItemDropBehavior.superclass.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.behavior.ContainerItemBehavior', ContainerItemDropBehavior);
    /**
     * @inheritdoc
     */
    ContainerItemDropBehavior.prototype.updateBehaviorAvailability = function() {
        this.targetHTML.droppable(this.disabled ? 'disable' : 'enable');
        return this;
    };
    /**
     * Defines the handler to be executed when an accepted draggable is dragged over the 
     * {@link #property-targetObject targetObject}.
     * @return {Function} The handler.
     * @private
     */
    ContainerItemDropBehavior.prototype.onOver = function() {
        var that = this;
        return function(e, ui) {
            (that.targetObject.onDragOver()());
        };
    };
    /**
     * Defines the handler to be executed when an accepted draggable is dragged out of the 
     * {@link PMUI.behavior.ContainerItemDragBehavior#property-targetObject targetObject}.
     * @return {Function} The handler.
     * @private
     */
    ContainerItemDropBehavior.prototype.onOut = function() {
        var that = this;
        return function(e, ui) {

        };
    };
    /**
     * Attaches the behavior to the {@link #property-targetObject targetObject}.
     * @chainable
     */
    ContainerItemDropBehavior.prototype.attachBehavior = function() {
        var $html,
            i,
            items,
            item,
            targetObject = this.targetObject;
        if(!this.behaviorAttached) {
            this.targetHTML = $(this.targetObject.getContainmentArea());
            this.targetHTML.droppable({
                scope: this.scope,
                drop: this.onDrop(),
                out: this.onOut(),
                over: this.onOver(),
                hoverClass: this.hoverClass,
                tolerance: 'pointer'
            });
        }
        return ContainerItemDropBehavior.superclass.prototype.attachBehavior.call(this);
    };
    /**
     * Detaches the behavior from the 
     * {@link #property-targetObject targetObject}.
     * @chainable
     */
    ContainerItemDropBehavior.prototype.detachBehavior = function() {
        if(this.behaviorAttached) {
            try {
                this.targetHTML.droppable('destroy');
            } catch(e) {}
        }
        return ContainerItemDropBehavior.superclass.prototype.detachBehavior.call(this);
    };

    PMUI.extendNamespace('PMUI.behavior.ContainerItemDropBehavior', ContainerItemDropBehavior);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = ContainerItemDropBehavior;
    }
}());
(function() {
    /**
     * @class PMUI.behavior.ContainerItemSortBehavior
     * @extends PMUI.behavior.ContainerItemDragBehavior
     * Class that encapsulates the sort behavior for {@link PMUI.core.Container Container} objects. This behavior 
     * enables the {@link #property-targetObject targetObject}'s' items to be sortable.
     * 
     * Since this class is created through the 
     * {@link PMUI.behavior.ContainerItemBehaviorFactory ContainerItemBehaviorFactory} it shouldn't be instantiated in 
     * most cases (it is instantiated internally by the object of the Container class).
     * If you want to apply this behavior to a {@link PMUI.core.Container Container} object, just call its 
     * {@link PMUI.core.Container#method-setBehavior setBehavior()} method with the string parameter "sort" or set it 
     * at instantiation time using its {@link PMUI.core.Container#cfg-behavior behavior} config option.
     *
     * Usage example:
     *
     *      @example
     *      var tree = new PMUI.panel.TreePanel({
     *          items: [
     *              {
     *                  label: "sortable item #1"
     *              }, {
     *                  label: "sortable item #2"
     *              }, {
     *                  label: "sortable item #3"
     *              }, {
     *                  label: "sortable item #4"
     *              }, {
     *                  label: "sortable item #5"
     *              }, {
     *                  label: "sortable item #6"
     *              }
     *          ]
     *      });
     *
     *      tree.setBehavior("sort");
     *      document.body.appendChild(tree.getHTML());
     */
    var ContainerItemSortBehavior = function(settings) {
        ContainerItemSortBehavior.superclass.call(this, settings);
        this.alignment = null;
        ContainerItemSortBehavior.prototype.init.call(this,settings);
    };

    PMUI.inheritFrom('PMUI.behavior.ContainerItemDragBehavior', ContainerItemSortBehavior);/**
     * @inheritdoc
     */

    ContainerItemSortBehavior.prototype.init = function (settings) {
        var defaults = {
            alignment : 'vertical'
        };

        jQuery.extend(true, defaults, settings);
        this.setAlignment(defaults.alignment);
    };

    ContainerItemSortBehavior.prototype.setAlignment = function (alignment) {
        if (!(typeof alignment === "string" && (alignment === "vertical" || alignment === "horizontal"))){
            throw new Error  ("ContainerItemSortBehavior.setAlignment(): the parameter is no valid, must be a vertical or horizontal value"); 
        }
        this.alignment = alignment;
        return this;
    };

    ContainerItemSortBehavior.prototype.updateBehaviorAvailability = function() {
        this.targetHTML.sortable(this.disabled ? 'disable' : 'enable');
        return this;
    };
    /**
     * Defines the handler to be executed when the
     * {@link #property-targetObject targetObject}'s item sorting starts.
     * @return {Function} The handler.
     * @private
     */
    ContainerItemSortBehavior.prototype.onStart = function() {
        var that = this;
        return function(e, ui) {
            var targetObject,
                o;
            that.cancelledDrop = null;
            o = PMUI.getPMUIObject(ui.item.get(0));
            
            if(that.targetObject.isDirectParentOf(o)) {
                targetObject = that.targetObject;
                
                if ( that.alignment == "vertical" ) {
                    ui.helper.get(0).style.width = $(e.target).width() + "px";
                }
                that.sortIObject = o;
                if(typeof targetObject.onSortStart === 'function') {
                    targetObject.onSortStart(targetObject, that.sortIObject);
                }
            }
        };
    };
    /**
     * Defines the handler to be executed when the
     * {@link #property-targetObject targetObject}'s item sorting stops.
     * @return {Function} The handler.
     * @private
     */
    ContainerItemSortBehavior.prototype.onStop = function() {
        var that = this;
        return function() {};
    };
    /**
     * Defines the handler to be executed when the user stopped sorting and the 
     * {@link #property-targetObject targetObject}'s items positions have 
     * changed.
     * @return {Function} The handler.
     * @private
     */
    ContainerItemSortBehavior.prototype.onUpdate = function() {
        return this.targetObject.onSortingChange();
    };
    /**
     * Attaches the behavior to the {@link #property-targetObject targetObject}.
     * @chainable
     */
    ContainerItemSortBehavior.prototype.attachBehavior = function() {
        var $html,
            i,
            items,
            item,
            targetObject = this.targetObject;
        if(!this.behaviorAttached) {
            this.targetHTML = jQuery(this.targetObject.getContainmentArea());
            this.targetHTML.sortable({
                containment: 'parent',
                sort: this.onDrag(),
                start: this.onStart(),
                stop: this.onStop(),
                update: this.onUpdate(),
                handle: this.handle || false
            });
        }
        return ContainerItemSortBehavior.superclass.superclass.prototype.attachBehavior.call(this);
    };
    /**
     * Detaches the behavior from the 
     * {@link #property-targetObject targetObject}.
     * @chainable
     */
    ContainerItemSortBehavior.prototype.detachBehavior = function() {
        if(this.behaviorAttached) {
            try {
                targetHTML.sortable('destroy');
            } catch(e) {}
        }
        return ContainerItemSortBehavior.superclass.superclass.prototype.detachBehavior.call(this);
    };

    PMUI.extendNamespace('PMUI.behavior.ContainerItemSortBehavior', ContainerItemSortBehavior);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = ContainerItemSortBehavior;
    }
}());
(function() {
    /**
     * @class PMUI.behavior.ContainerItemDragDropBehavior
     * @extends PMUI.behavior.ContainerItemBehavior
     * Class that encapsulates the drop behavior for a {@link PMUI.core.Container Container} object and the drag 
     * behavior for its items. The object that applies this behavior, will be able to:
     * 
     * - Accept draggable items from other Containers which ones are applying dragging behaviors 
     * ({@link PMUI.behavior.ContainerItemDragBehavior drag}, 
     * {@link PMUI.behavior.ContainerItemDragDropBehavior dragdrop}, 
     * {@link PMUI.behavior.ContainerItemDragSortBehavior dragsort}, 
     * {@link PMUI.behavior.ContainerItemDragDropSortBehavior dragdropsort}). 
     * - The object's items will be able to be dragged to another Containers that are applying dropping behaviors
     * ({@link PMUI.behavior.ContainerItemDropBehavior drop}, 
     * {@link PMUI.behavior.ContainerItemDragDropBehavior dragdrop}, 
     * {@link PMUI.behavior.ContainerItemDropSortBehavior dropsort}, 
     * {@link PMUI.behavior.ContainerItemDragDropSortBehavior dragdropsort}).
     * 
     * Since this class is created through the 
     * {@link PMUI.behavior.ContainerItemBehaviorFactory ContainerItemBehaviorFactory} it shouldn't be instantiated in 
     * most cases (it is instantiated internally by the object of the Container class).
     * If you want to apply this behavior to a {@link PMUI.core.Container Container} object, just call its 
     * {@link PMUI.core.Container#method-setBehavior setBehavior()} method with the string parameter "dragdrop" or set it 
     * at instantiation time using its {@link PMUI.core.Container#cfg-behavior behavior} config option.
     *
     * Usage example:
     *
     *      @example
     *      var tree = new PMUI.panel.TreePanel({
     *          items: [
     *              {
     *                  label: "draggable item #1"
     *              }, {
     *                  label: "draggable item #2"
     *              }, {
     *                  label: "draggable item #3"
     *              }, {
     *                  label: "draggable item #4"
     *              }, {
     *                  label: "draggable item #5"
     *              }, {
     *                  label: "draggable item #6"
     *              }
     *          ]
     *      }), tree2 = new PMUI.panel.TreePanel({
     *          style: {
     *              cssClasses: ["droppable-list"]
     *          },
     *          items: [
     *              {
     *                  label: "draggable item #7"
     *              }, {
     *                  label: "draggable item #8"
     *              }, {
     *                  label: "draggable item #9"
     *              }, {
     *                  label: "draggable item #10"
     *              }, {
     *                  label: "draggable item #11"
     *              }, {
     *                  label: "draggable item #12"
     *              }
     *          ]
     *      });
     *
     *      tree.setBehavior("dragdrop");
     *      tree2.setBehavior("dragdrop");
     *      document.body.appendChild(tree.getHTML());
     *      //Add a separator
     *      document.body.appendChild(document.createElement('hr'));
     *      document.body.appendChild(tree2.getHTML());
     */
    var ContainerItemDragDropBehavior = function(settings) {
        ContainerItemDragDropBehavior.superclass.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.behavior.ContainerItemBehavior', ContainerItemDragDropBehavior);
    /**
     * @inheritdoc
     */
    ContainerItemDragDropBehavior.prototype.updateBehaviorAvailability = function() {
        var d = this.disabled ? 'disable' : 'enable';
        this.targetHTML.droppable(d);
        jQuery(this.getTargetObjectItems()).draggable(d);
        if(this.disabled) {
            this.detachOnDraggableMouseEvents();
        } else {
            this.attachOnDraggableMouseEvents();
        }
        return this;
    };
    /**
     * Returns the html from the items of the {@link #property-targetObject targetObject} 
     * @return {Object} A jQuery object with all the target object' items html.
     */
    ContainerItemDragDropBehavior.prototype.getTargetObjectItems = function() {
        var $html = $(null),
            i,
            item,
            items,
            targetObject = this.targetObject;
        items = targetObject.getBehavioralItems();
        for(i = 0; i < items.length; i += 1) {
            item = items[i].getHTML();
            $html = $html.add(item);
        }
        return $html;
    };
    /**
     * @inheritdoc
     */
    ContainerItemDragDropBehavior.prototype.onDropOut = function() {
        var aux;
        ContainerItemDragDropBehavior.superclass.prototype.onDropOut.apply(this, arguments);
        //The next lines avoid an uncontrolable error generated on the jquery ui's stop callback
        //when it try to access certain data that will be undefined because of the destruction of the
        //drag behavior when it is added to the new container in which it was dropped on.
        //What it does is add some data in the jquery data of the object's html.
        aux = jQuery(this.draggedObject.html).data('ui-draggable');
        if(!aux) {
            aux = {};
            jQuery(this.draggedObject.html).data('ui-draggable', aux);
        }
        if(!aux.sortables) {
            aux.sortables = [];
        }
        if(!aux.options) {
            aux.options = {};
        }
        return this;
    };
    /**
     * Defines the handler to be executed when an accepted draggable is dragged over the 
     * {@link #property-targetObject targetObject}.
     * @return {Function} The handler.
     * @private
     */
    ContainerItemDragDropBehavior.prototype.onOver = function() {
        var that = this;
        return function(e, ui) {
            (that.targetObject.onDragOver()());
        };
    };
    /**
     * Defines the handler to be executed when an accepted draggable is dragged out of the 
     * {@link #property-targetObject targetObject}.
     * @return {Function} The handler.
     * @private
     */
    ContainerItemDragDropBehavior.prototype.onOut = function() {
        var that = this;
        return function(e, ui) {

        };
    };
    /**
     * Attaches the behavior to the {@link #property-targetObject targetObject}.
     * @chainable
     */
    ContainerItemDragDropBehavior.prototype.attachBehavior = function() {
        var $html,
            i,
            items,
            item,
            targetObject = this.targetObject;
        if(!this.behaviorAttached) {
            $html = this.getTargetObjectItems();

            $html.draggable({
                appendTo: document.body,
                connectToSortable: '.pmui-containeritembehavior-sort',
                helper: 'clone',
                revert: "invalid",
                drag: this.onDrag(),
                start: this.onStart(),
                stop: this.onStop(),
                scope: this.scope,
                handle: this.handle || false
            });
            this.targetHTML = jQuery(this.targetObject.getContainmentArea());
            this.targetHTML.droppable({
                drop: this.onDrop(),
                out: this.onOut(),
                over: this.onOver(),
                tolerance: 'pointer',
                scope: this.scope
            });
            this.attachOnDraggableMouseEvents();
        }
        return ContainerItemDragDropBehavior.superclass.prototype.attachBehavior.call(this);
    };
    /**
     * Detaches the behavior from the 
     * {@link #property-targetObject targetObject}.
     * @chainable
     */
    ContainerItemDragDropBehavior.prototype.detachBehavior = function() {
        if(this.behaviorAttached) {
            try {
                this.getTargetObjectItems().draggable('destroy');
                this.targetHTML.droppable('destroy');
                this.detachOnDraggableMouseEvents();
            } catch(e) {}
        }
        return ContainerItemDragDropBehavior.superclass.prototype.detachBehavior.call(this);
    };

    PMUI.extendNamespace('PMUI.behavior.ContainerItemDragDropBehavior', ContainerItemDragDropBehavior);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = ContainerItemDragDropBehavior;
    }
}());
(function() {
    /**
     * @class PMUI.behavior.ContainerItemDragSortBehavior
     * @extends PMUI.behavior.ContainerItemDragBehavior
     * Class that encapsulates the drag and sort behaviors for a {@link PMUI.core.Container Container} object items. 
     * The items of the object that applies this behavior, will be able to:
     * 
     * - Be dragged to another Containers that are applying dropping behaviors
     * ({@link PMUI.behavior.ContainerItemDropBehavior drop}, 
     * {@link PMUI.behavior.ContainerItemDragDropBehavior dragdrop}, 
     * {@link PMUI.behavior.ContainerItemDropSortBehavior dropsort}, 
     * {@link PMUI.behavior.ContainerItemDragDropSortBehavior dragdropsort}). 
     * - Be sortable.
     * 
     * Since this class is created through the 
     * {@link PMUI.behavior.ContainerItemBehaviorFactory ContainerItemBehaviorFactory} it shouldn't be instantiated in 
     * most cases (it is instantiated internally by the object of the Container class).
     * If you want to apply this behavior to a {@link PMUI.core.Container Container} object, just call its 
     * {@link PMUI.core.Container#method-setBehavior setBehavior()} method with the string parameter "dragsort" or set it 
     * at instantiation time using its {@link PMUI.core.Container#cfg-behavior behavior} config option.
     *
     * Usage example:
     *
     *      @example
     *      var tree = new PMUI.panel.TreePanel({
     *          items: [
     *              {
     *                  label: "draggable item #1"
     *              }, {
     *                  label: "draggable item #2"
     *              }, {
     *                  label: "draggable item #3"
     *              }, {
     *                  label: "draggable item #4"
     *              }, {
     *                  label: "draggable item #5"
     *              }, {
     *                  label: "draggable item #6"
     *              }
     *          ]
     *      }), tree2 = new PMUI.panel.TreePanel({
     *          style: {
     *              cssClasses: ["droppable-list"]
     *          },
     *          items: [
     *              {
     *                  label: 'Drop Here!'
     *              }
     *          ]
     *      });
     *
     *      tree.setBehavior("dragsort");
     *      tree2.setBehavior("drop");
     *      document.body.appendChild(tree.getHTML());
     *      //Add a separator
     *      document.body.appendChild(document.createElement('hr'));
     *      document.body.appendChild(tree2.getHTML());
     */
    var ContainerItemDragSortBehavior = function(settings) {
        ContainerItemDragSortBehavior.superclass.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.behavior.ContainerItemDragBehavior', ContainerItemDragSortBehavior);
    /**
     * @inheritdoc
     */
    ContainerItemDragSortBehavior.prototype.updateBehaviorAvailability = function() {
        this.targetHTML.sortable(this.disabled ? 'disable' : 'enable');
        if(this.disabled) {
            this.detachOnDraggableMouseEvents();
        } else {
            this.attachOnDraggableMouseEvents();
        }
        return this;
    };
    /**
     * @inheritdoc
     */
    ContainerItemDragSortBehavior.prototype.onStart = function() {
        var that = this;
        return function(e, ui) {
            var targetObject, o;
            //Since the jQuery sortable's start callback is executed when the sorting starts and not neccesarily when
            //one of its own items starts dragging we need to do the next validation:
            //check if the item dragged belongs to the sortable html. In that case we fire the onDragStart callback.
            o = PMUI.getPMUIObject(ui.item.get(0));
            if(that.targetObject.isDirectParentOf(o)) {
                targetObject = that.targetObject;
                ui.helper.get(0).style.width = $(e.target).width() + "px";
                ui.item.hide();
                that.draggedObject = o;
                if(typeof targetObject.onDragStart === 'function') {
                    targetObject.onDragStart(targetObject, that.draggedObject);
                }
            }
        };
    };
    /**
     * Defines the handler to be executed when the user stopped sorting and the 
     * {@link #property-targetObject targetObject}'s items positions have 
     * changed.
     * @return {Function} The handler.
     * @private
     */
    ContainerItemDragSortBehavior.prototype.onUpdate = function() {
        var that = this;
        return function(e, ui) {
            var parentHTML = that.targetObject.getHTML(),
                item = PMUI.getPMUIObject(ui.item.get(0));

            if(item && jQuery(parentHTML).has(item.getHTML()).length) {
                (that.targetObject.onSortingChange()(e, ui));
            }
        };
    };
    /**
     * Attaches the behavior to the {@link #property-targetObject targetObject}.
     * @chainable
     */
    ContainerItemDragSortBehavior.prototype.attachBehavior = function() {
        var $html,
            i,
            items,
            item,
            targetObject = this.targetObject;
        if(!this.behaviorAttached) {
            this.targetHTML = jQuery(targetObject.getContainmentArea());
            this.targetHTML.sortable({
                appendTo: document.body,
                connectWith: '.pmui-containeritembehavior-sort',
                helper: 'clone',
                revert: "invalid",
                start: this.onStart(),
                update: this.onUpdate(),
                items: this.sortableItems,
                placeholder: this.placeholderClass,
                handle: this.handle || false,
                zIndex: 1000
            });
            this.attachOnDraggableMouseEvents();
        }
        return ContainerItemDragSortBehavior.superclass.superclass.prototype.attachBehavior.call(this);
    };
    /**
     * Detaches the behavior from the 
     * {@link #property-targetObject targetObject}.
     * @chainable
     */
    ContainerItemDragSortBehavior.prototype.detachBehavior = function() {
        if(this.behaviorAttached) {
            try {
                this.targetHTML.sortable('destroy');
            } catch(e) {}
        }
        return ContainerItemDragSortBehavior.superclass.prototype.detachBehavior.call(this);
    };

    PMUI.extendNamespace('PMUI.behavior.ContainerItemDragSortBehavior', ContainerItemDragSortBehavior);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = ContainerItemDragSortBehavior;
    }
}());
(function() {
    /**
     * @class PMUI.behavior.ContainerItemDropSortBehavior
     * @extends PMUI.behavior.ContainerItemDropBehavior
     * Class that encapsulates the drop behavior for a {@link PMUI.core.Container Container} object and the drag 
     * behavior for its items. The object that applies this behavior, will be able to:
     *
     * - Accept draggable items from other Containers which ones are applying dragging behaviors 
     * ({@link PMUI.behavior.ContainerItemDragBehavior drag}, 
     * {@link PMUI.behavior.ContainerItemDragDropBehavior dragdrop}, 
     * {@link PMUI.behavior.ContainerItemDragSortBehavior dragsort}, 
     * {@link PMUI.behavior.ContainerItemDragDropSortBehavior dragdropsort}). 
     * 
     * - The object items will be sortable.
     * 
     * Since this class is created through the 
     * {@link PMUI.behavior.ContainerItemBehaviorFactory ContainerItemBehaviorFactory} it shouldn't be instantiated in 
     * most cases (it is instantiated internally by the object of the Container class).
     * If you want to apply this behavior to a {@link PMUI.core.Container Container} object, just call its 
     * {@link PMUI.core.Container#method-setBehavior setBehavior()} method with the string parameter "dropsort" or set it 
     * at instantiation time using its {@link PMUI.core.Container#cfg-behavior behavior} config option.
     *
     * Usage example:
     *
     *      @example
     *      var tree = new PMUI.panel.TreePanel({
     *          items: [
     *              {
     *                  label: "sortable item #1"
     *              }, {
     *                  label: "sortable item #2"
     *              }, {
     *                  label: "sortable item #3"
     *              }, {
     *                  label: "sortable item #4"
     *              }, {
     *                  label: "sortable item #5"
     *              }, {
     *                  label: "sortable item #6"
     *              }
     *          ]
     *      }), tree2 = new PMUI.panel.TreePanel({
     *          style: {
     *              cssClasses: ["droppable-list"]
     *          },
     *          items: [
     *              {
     *                  label: "draggable item #1"
     *              }, {
     *                  label: "draggable item #2"
     *              }, {
     *                  label: "draggable item #3"
     *              }, {
     *                  label: "draggable item #4"
     *              }, {
     *                  label: "draggable item #5"
     *              }, {
     *                  label: "draggable item #6"
     *              }
     *          ]
     *      });
     *
     *      tree.setBehavior("dropsort");
     *      tree2.setBehavior("drag");
     *      document.body.appendChild(tree.getHTML());
     *      //Add a separator
     *      document.body.appendChild(document.createElement('hr'));
     *      document.body.appendChild(tree2.getHTML());
     */
    var ContainerItemDropSortBehavior = function(settings) {
        ContainerItemDropSortBehavior.superclass.call(this, settings);
        this.dropped = null;
        this.cancelledDrop = null;
    };

    PMUI.inheritFrom('PMUI.behavior.ContainerItemDropBehavior', ContainerItemDropSortBehavior);
    /**
     * @inheritdoc
     */
    ContainerItemDropSortBehavior.prototype.updateBehaviorAvailability = function() {
        this.targetHTML.sortable(this.disabled ? 'disable' : 'enable');
        return this;
    };
    /**
     * Defines the handler to be executed when an accepted draggable is dropped on the 
     * {@link #property-targetObject targetObject}.
     * @return {Function} The handler.
     * @private
     */
    ContainerItemDropSortBehavior.prototype.onDrop = function() {
        var that = this;
        return function(e, ui) {
            var droppable = $(this),
                targetObject = that.targetObject,
                item = PMUI.getPMUIObject(ui.item.get(0)),
                index = !targetObject.getItems().length ? 0 : droppable.find('>*').index(item.getHTML()),
                data,
                performDrop;

            if(index < 0) {
                index = droppable.find('>*').index(droppable.find('.ui-draggable'));
            }
            if(typeof targetObject.onBeforeDrop === 'function') {
                targetObject.onBeforeDrop(targetObject, item, index);
            }
            if(typeof targetObject.onDrop === 'function') {
                performDrop = targetObject.onDrop(targetObject, item, index);
            }
            if(performDrop || performDrop === undefined) {
                item.getParent().behaviorObject.onDropOut(item, targetObject, index);
                that.dropped = true;
                if(!targetObject.canContain(item)) {
                    $(e.target).find('.ui-draggable').detach();
                }
                that.cancelledDrop = false;
            } else {
                //if the dropped object came from a container with draggable or sortable behavior
                if(ui.helper) {
                    ui.sender.draggable('cancel');
                } else {
                    ui.sender.sortable('cancel');
                }
                that.cancelledDrop = true;
            }
        };
    };
    /**
     * Defines the handler to be executed when the user stopped sorting and the 
     * {@link #property-targetObject targetObject}'s items positions have 
     * changed.
     * @return {Function} The handler.
     * @private
     */
    ContainerItemDropSortBehavior.prototype.onUpdate = function() {
        var that = this;
        return function(e, ui) {
            var parentHTML = that.targetObject.getHTML(),
                item = PMUI.getPMUIObject(ui.item.get(0));

            if(that.dropped) {
                that.dropped = false;
                return;
            }

            if(item && jQuery(parentHTML).has(item.getHTML()).length) {
                (that.targetObject.onSortingChange()(e, ui));
            }
        };
    };
    /**
     * @inheritdoc
     */
    ContainerItemDropSortBehavior.prototype.onStop = function() {
        var that = this;
        return function(e, ui) {
            if(that.cancelledDrop) {
                ui.item.remove();
            }
            that.cancelledDrop = null;
        };
    };
    /**
     * @inherit
     */
    ContainerItemDropSortBehavior.prototype.onStart = function() {
        var that = this;
        return function(e, ui) {
            that.cancelledDrop = null;
        };
    };
    /**
     * Attaches the behavior to the {@link #property-targetObject targetObject}.
     * @chainable
     */
    ContainerItemDropSortBehavior.prototype.attachBehavior = function() {
        var $html,
            i,
            items,
            item,
            targetObject = this.targetObject;
        if(!this.behaviorAttached) {
            this.targetHTML = jQuery(targetObject.getContainmentArea());
            this.targetHTML.addClass('pmui-containeritembehavior-sort').sortable({
                containment: targetObject.getContainmentArea(),
                receive: this.onDrop(),
                start: this.onStart(),
                update: this.onUpdate(),
                items: this.sortableItems,
                stop: this.onStop(),
                tolerance: 'pointer',
                handle: this.handle || false,
                placeholder: this.placeholderClass
            });
        }
        return ContainerItemDropSortBehavior.superclass.superclass.prototype.attachBehavior.call(this);
    };
    /**
     * Detaches the behavior from the 
     * {@link #property-targetObject targetObject}.
     * @chainable
     */
    ContainerItemDropSortBehavior.prototype.detachBehavior = function() {
        if(this.behaviorAttached) {
            try {
                this.targetHTML.removeClass('pmui-containeritembehavior-sort').sortable('destroy');
            } catch(e) {}
        }
        return ContainerItemDropSortBehavior.superclass.prototype.detachBehavior.call(this);
    };

    PMUI.extendNamespace('PMUI.behavior.ContainerItemDropSortBehavior', ContainerItemDropSortBehavior);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = ContainerItemDropSortBehavior;
    }
}());
(function() {
    /**
     * @class PMUI.behavior.ContainerItemDragDropSortBehavior
     * @extends PMUI.behavior.ContainerItemDragDropBehavior
     * Class that encapsulates the drop behavior for a {@link PMUI.core.Container Container} object and the drag and 
     * sort behaviors for its items. The object that applies this behavior, will be able to:
     * 
     * - Accept draggable items from other Containers which ones are applying dragging behaviors 
     * ({@link PMUI.behavior.ContainerItemDragBehavior drag}, 
     * {@link PMUI.behavior.ContainerItemDragDropBehavior dragdrop}, 
     * {@link PMUI.behavior.ContainerItemDragSortBehavior dragsort}, 
     * {@link PMUI.behavior.ContainerItemDragDropSortBehavior dragdropsort}).
     * 
     * - The object's items will be able to be dragged to another Containers that are applying dropping behaviors
     * ({@link PMUI.behavior.ContainerItemDropBehavior drop}, 
     * {@link PMUI.behavior.ContainerItemDragDropBehavior dragdrop}, 
     * {@link PMUI.behavior.ContainerItemDropSortBehavior dropsort}, 
     * {@link PMUI.behavior.ContainerItemDragDropSortBehavior dragdropsort}).
     *
     * - The object's items will be enabled for sorting.
     * 
     * Since this class is created through the 
     * {@link PMUI.behavior.ContainerItemBehaviorFactory ContainerItemBehaviorFactory} it shouldn't be instantiated in 
     * most cases (it is instantiated internally by the object of the Container class).
     * If you want to apply this behavior to a {@link PMUI.core.Container Container} object, just call its 
     * {@link PMUI.core.Container#method-setBehavior setBehavior()} method with the string parameter "dragdropsort" or set it 
     * at instantiation time using its {@link PMUI.core.Container#cfg-behavior behavior} config option.
     *
     * Usage example:
     *
     *      @example
     *      var tree = new PMUI.panel.TreePanel({
     *          items: [
     *              {
     *                  label: "draggable item #1"
     *              }, {
     *                  label: "draggable item #2"
     *              }, {
     *                  label: "draggable item #3"
     *              }, {
     *                  label: "draggable item #4"
     *              }, {
     *                  label: "draggable item #5"
     *              }, {
     *                  label: "draggable item #6"
     *              }
     *          ]
     *      }), tree2 = new PMUI.panel.TreePanel({
     *          style: {
     *              cssClasses: ["droppable-list"]
     *          },
     *          items: [
     *              {
     *                  label: "draggable item #7"
     *              }, {
     *                  label: "draggable item #8"
     *              }, {
     *                  label: "draggable item #9"
     *              }, {
     *                  label: "draggable item #10"
     *              }, {
     *                  label: "draggable item #11"
     *              }, {
     *                  label: "draggable item #12"
     *              }
     *          ]
     *      });
     *
     *      tree.setBehavior("dragdropsort");
     *      tree2.setBehavior("dragdropsort");
     *      document.body.appendChild(tree.getHTML());
     *      //Add a separator
     *      document.body.appendChild(document.createElement('hr'));
     *      document.body.appendChild(tree2.getHTML());
     */
    var ContainerItemDragDropSortBehavior = function(settings) {
        ContainerItemDragDropSortBehavior.superclass.call(this, settings);
        this.dropped = null;
        this.cancelledDrop = null;
    };

    PMUI.inheritFrom('PMUI.behavior.ContainerItemDragDropBehavior', ContainerItemDragDropSortBehavior);
    /**
     * @inheritdoc
     */
    ContainerItemDragDropSortBehavior.prototype.updateBehaviorAvailability = function() {
        this.targetHTML.sortable(this.disabled ? 'disable' : 'enable');
        if(this.disabled) {
            this.detachOnDraggableMouseEvents();
        } else {
            this.attachOnDraggableMouseEvents();
        }
        return this;
    };
    /**
     * Defines the handler to be executed when the user stopped sorting and the 
     * {@link #property-targetObject targetObject}'s items positions have 
     * changed.
     * @return {Function} The handler.
     * @private
     */
    ContainerItemDragDropSortBehavior.prototype.onUpdate = function() {
        var that = this;
        return function(e, ui) {
            var parentHTML = that.targetObject.getHTML(),
                item = PMUI.getPMUIObject(ui.item.get(0));
            if(that.dropped) {
                that.dropped = false;
                return;
            }

            if(!item) {
                return;
            }

            if(jQuery(parentHTML).has(item.getHTML()).length) {
                (that.targetObject.onSortingChange()(e, ui));
            }
        };
    };
    /**
     * @inheritdoc
     */
    ContainerItemDragDropSortBehavior.prototype.onStart = function() {
        var that = this;
        return function(e, ui) {
            var targetObject, o;
            that.cancelledDrop = null;
            //Since the jQuery sortable's start callback is executed when the sorting starts and not neccesarily when
            //one of its own items starts dragging we need to do the next validation:
            //check if the item dragged belongs to the sortable html. In that case we fire the onDragStart callback.
            o = PMUI.getPMUIObject(ui.item.get(0));
            if(that.targetObject.isDirectParentOf(o)) {
                targetObject = that.targetObject;
                ui.helper.get(0).style.width = $(e.target).width() + "px";
                ui.item.hide();
                that.draggedObject = o;
                if(typeof targetObject.onDragStart === 'function') {
                    targetObject.onDragStart(targetObject, that.draggedObject);
                }
            }
        };
    };
    /**
     * Defines the handler to be executed when an accepted draggable is dropped on the 
     * {@link #property-targetObject targetObject}.
     * @return {Function} The handler.
     * @private
     */
    ContainerItemDragDropSortBehavior.prototype.onDrop = function() {
        var that = this;
        return function(e, ui) {
            var targetObject = that.targetObject,
                droppable = $(this),
                item = PMUI.getPMUIObject(ui.item.get(0)),
                data,
                index = !targetObject.getItems().length ? 0 
                    : droppable.find('>*').index(item.getHTML()),
                performDrop;

            if(index < 0) {
                index = droppable.find('>*').index(droppable.find('.ui-draggable'));
            }
            if(typeof targetObject.onBeforeDrop === 'function') {
                targetObject.onBeforeDrop(targetObject, item, index);
            }
            if(typeof targetObject.onDrop === 'function') {
                performDrop = targetObject.onDrop(targetObject, item, index);
            }
            if(performDrop || performDrop === undefined) {
                item.getParent().behaviorObject.onDropOut(item, targetObject, index);
                that.dropped = true;
                if(!targetObject.canContain(item)) {
                    $(e.target).find('.ui-draggable').detach();   
                }
                that.cancelledDrop = false;
            } else {
                //if the dropped object came from a container with draggable or sortable behavior
                if(ui.helper) {
                    //from draggable
                    ui.sender.draggable('cancel');
                } else {
                    //from sortable
                    ui.sender.sortable('cancel');
                }
                that.cancelledDrop = true;
            }
        };
    };
    /**
     * @inheritdoc
     */
    ContainerItemDragDropSortBehavior.prototype.onStop = function() {
        var that = this;
        return function(e, ui) {
            if(that.cancelledDrop) {
                ui.item.remove();
            }
            that.cancelledDrop = null;
        };
    };
    /**
     * Attaches the behavior to the {@link #property-targetObject targetObject}.
     * @chainable
     */
    ContainerItemDragDropSortBehavior.prototype.attachBehavior = function() {
        var $html,
            i,
            items,
            item,
            targetObject = this.targetObject;
        if(!this.behaviorAttached) {
            this.targetHTML = jQuery(this.targetObject.getContainmentArea());
            this.targetHTML.addClass('pmui-containeritembehavior-sort').sortable({
                appendTo: document.body,
                connectWith: '.pmui-containeritembehavior-sort',
                helper: 'clone',
                sort: this.onDrag(),
                start: this.onStart(),
                stop: this.onStop(),
                receive: this.onDrop(),
                out: this.onOut(),
                over: this.onOver(),
                update: this.onUpdate(),
                items: this.sortableItems,
                tolerance: 'pointer',
                placeholder: this.placeholderClass,
                handle: this.handle || false,
                zIndex: 1000
            });
            this.attachOnDraggableMouseEvents();
        }
        return ContainerItemDragDropSortBehavior.superclass.superclass.prototype.attachBehavior.call(this);
    };
    /**
     * Detaches the behavior from the 
     * {@link #property-targetObject targetObject}.
     * @chainable
     */
    ContainerItemDragDropSortBehavior.prototype.detachBehavior = function() {
        if(this.behaviorAttached) {
            try {
                this.targetHTML.removeClass('pmui-containeritembehavior-sort').sortable('destroy');
                this.detachOnDraggableMouseEvents();
            } catch(e) {}
        }
        return ContainerItemDragDropSortBehavior.superclass.prototype.detachBehavior.call(this);
    };

    PMUI.extendNamespace('PMUI.behavior.ContainerItemDragDropSortBehavior', ContainerItemDragDropSortBehavior);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = ContainerItemDragDropSortBehavior;
    }
}());
(function() {
    /**
     * @class PMUI.behavior.ContainerItemBehaviorFactory
     * A factory that produces the behavior objects for {@link PMUI.core.Container Container} objects.
     *
     * It accepts the following pmTypes: 
     * 
     * - using "nobehavior" you get a {@link PMUI.behavior.ContainerItemNoBehavior ContainerItemNoBehavior object}.
     * - using "drag" you get a {@link PMUI.behavior.ContainerItemDragBehavior ContainerItemDragBehavior object}.
     * - using "drop" you get a {@link PMUI.behavior.ContainerItemDropBehavior ContainerItemDropBehavior object}.
     * - using "sort" you get a {@link PMUI.behavior.ContainerItemSortBehavior ContainerItemSortBehavior object}.
     * - using "dragdrop" you get a {@link PMUI.behavior.ContainerItemDragDropBehavior ContainerItemDragDropBehavior object}.
     * - using "dragsort" you get a {@link PMUI.behavior.ContainerItemDragSortBehavior ContainerItemDragSortBehavior object}.
     * - using "dropsort" you get a {@link PMUI.behavior.ContainerItemDropSortBehavior ContainerItemDropSortBehavior object}.
     * - using "dragdropsort" you get a {@link PMUI.behavior.ContainerItemDragDropSortBehavior ContainerItemDragDropSortBehavior object}.
     *
     * The default pmType is 'nobehavior'.
     * @extends {PMUI.util.Factory}
     */
    var ContainerItemBehaviorFactory = function(settings) {
        ContainerItemBehaviorFactory.superclass.call(this, settings);
        ContainerItemBehaviorFactory.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.util.Factory', ContainerItemBehaviorFactory);
    /**
     * Initializes the new instance.
     * @param  {Object} settings An object with the config options.
     * @private
     */
    ContainerItemBehaviorFactory.prototype.init = function(settings) {
        var defaults = {
            products: {
                'nobehavior': PMUI.behavior.ContainerItemNoBehavior,
                'drag': PMUI.behavior.ContainerItemDragBehavior,
                'dragclone': PMUI.behavior.ContainerItemDragCloneBehavior,
                'drop': PMUI.behavior.ContainerItemDropBehavior,
                'sort': PMUI.behavior.ContainerItemSortBehavior,
                'dragdrop': PMUI.behavior.ContainerItemDragDropBehavior,
                'dragsort': PMUI.behavior.ContainerItemDragSortBehavior,
                'dropsort': PMUI.behavior.ContainerItemDropSortBehavior,
                'dragdropsort': PMUI.behavior.ContainerItemDragDropSortBehavior
            },
            defaultProduct: 'nobehavior'
        };

        jQuery.extend(true, defaults, settings);

        this.setProducts(defaults.products)
            .setDefaultProduct(defaults.defaultProduct);
    };

    PMUI.extendNamespace('PMUI.behavior.ContainerItemBehaviorFactory', ContainerItemBehaviorFactory);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = ContainerItemBehaviorFactory;
    }
}());
(function () {
    /**
     * @class PMUI.command.Command
     * Abstract class command which declares some abstract methods such as
     * execute (redo) and inverseExecute (undo) a command.
     *
     * A command is implemented in the library as follows:
     *
     * - The command must define a method execute which does the operation desired (e.g. commandDelete's execute
     *      method deletes shapes and connections from a canvas).
     * - The command must define a method undo which undoes what the method execute did.
     * - The command must define a method redo which simply calls the execute method (redo must do the
     *      same operation as execute).
     *
     * Finally to execute and save the command let's use the 
     {@link PMUI.draw.Canvas#property-commandStack} property that any
     * canvas has so:
     *
     *      // e.g.
     *      // let's assume that canvas is an instance of the class Canvas
     *      // let's create an instance of commandDelete
     *      // let's assume that config has the correct configuration options of this command
     *      var command = new PMUI.command.CommandDelete(config)
     *      // let's add the command to the canvas's commandStack
     *      canvas.commandStack.add(command);
     *      // finally let's execute the command
     *      command.execute();      // this line actually removes the shapes!
     *
     *      // if we want to undo the last command
     *      canvas.commandStack.undo();     // this line recreates the shapes
     *
     *      // if we want to redo the last command
     *      canvas.commandStack.redo();     // this line removes the shapes again
     *
     * @abstract
     * @constructor Creates an instance of the class command
     * @param {Object} receiver The object that will execute the command
     */
    var Command = function (receiver) {

        /**
         * The object that executes the command
         * @property {Object}
         */
        this.receiver = receiver;
        /**
         * Reference to the canvas
         * @property {PMUI.draw.Canvas}
         */
        
        this.canvas = this.getCanvas(receiver);    
        
    };

    /**
     * Family of this command
     * @property {String}
     */
    Command.prototype.family = "Command";

    /**
     * Executes the command
     * @template
     * @protected
     */
    Command.prototype.execute = function (stopTrigger) {
    };

    /**
     * InverseExecutes the command (a.k.a. undo)
     * @template
     * @protected
     */
    Command.prototype.undo = function (stopTrigger) {
    };

    /**
     * Executes the command (a.k.a. redo)
     * @template
     * @protected
     */
    Command.prototype.redo = function (stopTrigger) {
    };

    Command.prototype.getCanvas = function (obj) {
        var aux,
            canvas;
            
        if (obj.getSize) {
            aux = obj.get(0);
            canvas = aux.getCanvas();
        } else if (obj.getCanvas) {
            canvas = obj.getCanvas();
        } else {
            canvas = null;
        }
        return canvas;
    };

    // extend the namespace
    PMUI.extendNamespace('PMUI.command.Command', Command);
}());

(function () {
    /**
     * @class PMUI.command.CommandConnect
     * Class CommandConnect determines the actions executed when a connection is created (redo) and the actions
     * executed when it's destroyed (undo).
     *
     * Instances of this class are created in {@link PMUI.draw.Canvas#removeElements}.
     *
     * @extends PMUI.command.Command
     *
     * @constructor Creates an instance of the class CommandConnect.
     * @param {Object} receiver The object that will execute the command
     */
    var CommandConnect = function (receiver) {
        CommandConnect.superclass.call(this, receiver);
    };
    PMUI.inheritFrom('PMUI.command.Command', CommandConnect);
    /**
     * Type of command
     * @property {String}
     */
    CommandConnect.prototype.type = "CommandConnect";
    /**
     * Build the connection.
     * The steps are:
     *
     * 1. Insert the ports in their respective parents (shapes)
     * 2. Append the html of the ports
     * 3. Add the connection html to the canvas
     * 4. Trigger the create event *
     * @chainable
     */
    CommandConnect.prototype.buildConnection = function() {
        var connection = this.receiver,
            canvas = connection.canvas,
            srcPort = connection.getSrcPort(),
            destPort = connection.getDestPort();

        // save the ports in its parents' ports array
        srcPort.parent.ports.insert(srcPort);
        destPort.parent.ports.insert(destPort);

        // append the html of the ports to its parents (customShapes)
        srcPort.parent.html.appendChild(srcPort.getHTML());
        destPort.parent.html.appendChild(destPort.getHTML());

        // add the connection to the canvas (its html is appended)
        canvas.addConnection(connection);
        canvas.updatedElement = connection;
        return connection;
    };
    /**
     * Executes the command.
     * The steps are:
     *
     * 1. Insert the ports in their respective parents (shapes)
     * 2. Append the html of the ports
     * 3. Add the connection html to the canvas
     * 4. Trigger the create event
     *
     * @chainable
     */
    CommandConnect.prototype.execute = function () {
        var connection = this.receiver,
            canvas = connection.canvas,
            srcPort = connection.getSrcPort(),
            destPort = connection.getDestPort();
        // save the ports in its parents' ports array
        srcPort.parent.ports.insert(srcPort);
        destPort.parent.ports.insert(destPort);
        // append the html of the ports to its parents (customShapes)
        srcPort.parent.html.appendChild(srcPort.getHTML());
        destPort.parent.html.appendChild(destPort.getHTML());
        // add the connection to the canvas (its html is appended)
        canvas.addConnection(connection);
        connection.checkAndCreateIntersectionsWithAll()
        canvas.updatedElement = connection;
        canvas.triggerCreateEvent(connection, []);
        return this;
    };
    /**
     * Inverse executes the command a.k.a. undo.
     * The steps are:
     *
     * 1. Save the connection (detach it from the DOM)
     * 2. Trigger the remove event
     *
     * @chainable
     */
    CommandConnect.prototype.undo = function () {
        this.receiver.saveAndDestroy();
        this.receiver.canvas.triggerRemoveEvent(this.receiver, []);
        return this;
    };
    /**
     * Executes the command a.k.a. redo by calling `this.execute`
     * @chainable
     */
    CommandConnect.prototype.redo = function () {
        this.execute();
        return this;
    };

    // extend the namespace
    PMUI.extendNamespace('PMUI.command.CommandConnect', CommandConnect);
}());

(function () {
    /**
     * @class PMUI.command.CommandCreate
     * Class CommandCreate determines the actions executed when some shapes are created (redo) and the actions
     * executed when they're destroyed (undo).
     *
     * Instances of this class are created in {@link PMUI.behavior.ConnectionDropBehavior#onDrop}.
     * @extends PMUI.command.Command
     *
     * @constructor Creates an instance of the class CommandCreate
     * @param {Object} receiver The object that will execute the command
     *
     */
    var CommandCreate = function (receiver) {
        CommandCreate.superclass.call(this, receiver);

        /**
         * Object that represents the state of the receiver before
         * it was created
         * @property {Object}
         */
        this.before = null;

        /**
         * Object that represents the state of the receiver after
         * it was created
         * @property {Object}
         */
        this.after = null;

        CommandCreate.prototype.initObject.call(this, receiver);
    };

    PMUI.inheritFrom('PMUI.command.Command', CommandCreate);

    /**
     * Type of command
     * @property {String}
     */
    CommandCreate.prototype.type = "CommandCreate";

    /**
     * Instance initializer which uses options to extend the config options to initialize the instance
     * @param {Object} receiver
     * @private
     */
    CommandCreate.prototype.initObject = function (receiver) {
        this.before = {};
        this.after = {
            x: receiver.getX(),
            y: receiver.getY(),
            parent: receiver.getParent()
        };
    };

    /**
     * Executes the command.
     * The steps are:
     *
     * 1. Insert the current shape to the children of its parent if it's possible
     * 2. Append it to the HTML of its parent
     * 3. Add the shape to either `canvas.customShapes` or `canvas.regularShapes`
     * 4. Trigger the create event
     *
     * @chainable
     */
    CommandCreate.prototype.execute = function () {

        // execute the trigger
        var shape = this.receiver,
            parent = shape.parent;

        // append the html to its parent
        // NOTE: in the first execution (in containerDropBehavior) the html is
        // already in the parent so the following line appends it again (html
        // is not created)

        // note that during the execution of this command the next line may called twice (one in
        // RegularContainerBehavior.addToContainer and the other here) so check if it's not
        // already in its children
        if (!parent.getChildren().contains(shape)) {
            parent.getChildren().insert(shape);
        }
        this.after.parent.html.appendChild(shape.getHTML());
        shape.canvas.addToList(shape);
        shape.showOrHideResizeHandlers(false);
        shape.canvas.triggerCreateEvent(shape, []);
        return this;
    };

    /**
     * Inverse executes the command a.k.a. undo
     *
     * The steps are:
     *
     * 1. Remove the current shape from the children of its parent if it's possible
     * 2. Remove its HTML (detach it from the DOM)
     * 4. Trigger the remove event
     *
     * @chainable
     */
    CommandCreate.prototype.undo = function () {
        this.receiver.parent.getChildren().remove(this.receiver);
        this.receiver.saveAndDestroy();
        this.receiver.canvas.triggerRemoveEvent(this.receiver, []);
        return this;
    };

    /**
     * Executes the command a.k.a redo
     * @chainable
     */
    CommandCreate.prototype.redo = function () {
        this.execute();
        return this;
    };

    // extend the namespace
    PMUI.extendNamespace('PMUI.command.CommandCreate', CommandCreate);
}());

(function () {
    /**
     * @class PMUI.command.CommandDelete
     * Class CommandDelete determines the actions executed when some shapes are deleted (redo) and the actions
     * executed when they're recreated (undo).
     *
     * Instances of this class are created in {@link PMUI.draw.Canvas#removeElements}.
     * @extends PMUI.command.Command
     *
     * @constructor Creates an instance of the class CommandDelete
     * @param {Object} receiver The object that will execute the command
     */
    var CommandDelete = function (receiver) {
        CommandDelete.superclass.call(this, receiver);
        /**
         * A stack of commandsConnect
         * @property {Array}
         */
        this.stackCommandConnect = [];
        /**
         * ArrayList that represents the selection that was active before deleting the elements
         * @property {PMUI.util.ArrayList}
         */
        this.currentSelection = new PMUI.util.ArrayList();
        /**
         * Reference to the current connection in the canvas
         * @property {PMUI.draw.Connection}
         */
        this.currentConnection = null;
        /**
         * List of all the elements related to the commands
         * @property {Array}
         */
        this.relatedElements = [];
        CommandDelete.prototype.initObject.call(this, receiver);
    };
    PMUI.inheritFrom('PMUI.command.Command', CommandDelete);
    /**
     * Type of command
     * @property {String}
     */
    CommandDelete.prototype.type = "CommandDelete";
    /**
     * Instance initializer which uses options to extend the config options to initialize the instance
     * @param {Object} receiver The object that will execute the command
     * @private
     */
    CommandDelete.prototype.initObject = function (receiver) {
        var i,
            shape;
        // move the current selection to this.currentSelection array
        for (i = 0; i < receiver.getCurrentSelection().getSize() > 0; i += 1) {
            shape = receiver.getCurrentSelection().get(i);
            this.currentSelection.insert(shape);
        }
        // save the currentConnection of the canvas if possible
        if (receiver.currentConnection) {
            this.currentConnection = receiver.currentConnection;
        }
    };
    /**
     * Saves and destroys connections and shapes
     * @private
     * @param {Object} shape
     * @param {boolean} root True if `shape` is a root element in the tree
     * @param {boolean} [fillArray] If set to true it'll fill `this.relatedElements` with the objects erased
     * @return {boolean}
     */
    CommandDelete.prototype.saveAndDestroy = function (shape, root, fillArray) {
        var i,
            child,
            parent,
            children = null,
            connection,
            canvas = shape.canvas;
        if (shape.hasOwnProperty("children")) {
            children = shape.children;
        }
        // special function to be called as an afterwards
        // BIG NOTE: doesn't have to delete html
        if (shape.destroy) {
            shape.destroy();
        }
        for (i = 0; i < children.getSize(); i += 1) {
            child = children.get(i);
            this.saveAndDestroy(child, false, fillArray);
        }
        while (shape.ports && shape.ports.getSize() > 0) {
            connection = shape.ports.getFirst().connection;
            if (fillArray) {
                this.relatedElements.push(connection);
            }
            this.stackCommandConnect.push(
                new PMUI.command.CommandConnect(connection)
            );
            connection.saveAndDestroy();
        }
        // remove from the children array of its parent
        if (root) {
            parent = shape.parent;
            parent.getChildren().remove(shape);
            if (parent.isResizable()) {
                parent.resizeBehavior.updateResizeMinimums(shape.parent);
            }
            // remove from the currentSelection and from either the customShapes
            // arrayList or the regularShapes arrayList
            canvas.removeFromList(shape);
            // remove the html only from the root
            shape.html = $(shape.html).detach()[0];
        }
        if (fillArray) {
            this.relatedElements.push(shape);
        }
        return true;
    };
    /**
     * Executes the command
     * The steps are:
     *
     * 1. Retrieve the old currentSelection (saved in `this.initObject()`)
     * 2. Remove the shapes (detaching them from the DOM)
     * 3. Remove the currentConnection if there's one
     * 4. Trigger the remove event
     *
     * @chainable
     */
    CommandDelete.prototype.execute = function () {
        var shape,
            i,
            canvas = this.receiver,
            currentConnection,
            stringified,
            fillArray = false,
            mainShape = null;
        if (this.relatedElements.length === 0) {
            fillArray = true;
        }
        canvas.emptyCurrentSelection();
        // copy from this.currentConnection
        for (i = 0; i < this.currentSelection.getSize(); i += 1) {
            shape = this.currentSelection.get(i);
            canvas.addToSelection(shape);
        }
        if (canvas.currentSelection.getSize() === 1) {
            mainShape = shape;
        }

        // remove the elements in the canvas current selection
        stringified = [];
        while (canvas.getCurrentSelection().getSize() > 0) {
            shape = canvas.getCurrentSelection().getFirst();
            this.saveAndDestroy(shape, true, fillArray);
            stringified.push(shape.stringify());
        }

        // destroy the currentConnection
        canvas.currentConnection = this.currentConnection;
        currentConnection = canvas.currentConnection;
        if (currentConnection) {
            // add to relatedElements just in the case when only a connection is
            // selected and deleted
            this.relatedElements.push(currentConnection);
            this.stackCommandConnect.push(
                new PMUI.command.CommandConnect(currentConnection)
            );
            currentConnection.saveAndDestroy();
            currentConnection = null;
        }
        canvas.triggerRemoveEvent(mainShape, this.relatedElements);
        return this;
    };
    /**
     * Inverse executes the command a.k.a. undo
     *
     * The steps are:
     *
     * 1. Retrieve the old currentSelection (saved in `this.initObject()`)
     * 2. Restore the shapes (attaching them to the DOM)
     * 3. Restore the currentConnection if there was one
     * 4. Trigger the create event
     *
     * @chainable
     */
    CommandDelete.prototype.undo = function () {
        // undo recreates the shapes
        var i,
            shape,
            mainShape = this.receiver.currentSelection.getFirst();
        for (i = 0; i < this.currentSelection.getSize(); i += 1) {
            shape = this.currentSelection.get(i);
            // add to the canvas array of regularShapes and customShapes
            shape.canvas.addToList(shape);
            // add to the children of the parent
            shape.parent.getChildren().insert(shape);
            shape.parent.html.appendChild(shape.getHTML());
            shape.showOrHideResizeHandlers(false);
        }
        // reconnect using the stack of commandConnect
        for (i = this.stackCommandConnect.length - 1; i >= 0; i -= 1) {
            this.stackCommandConnect[i].redo();
        }
        this.receiver.triggerCreateEvent(mainShape, this.relatedElements);
        return this;
    };
    /**
     * Executes the command (a.k.a redo)
     * @chainable
     */
    CommandDelete.prototype.redo = function () {
        this.execute();
        return this;
    };
    // extend namespace
    PMUI.extendNamespace('PMUI.command.CommandDelete', CommandDelete);
}());

(function () {
    /**
     * @class PMUI.command.CommandEditLabel
     * Encapsulates the action of editing a label
     *
     *                  //e.g.
     *                  // var command = new PMUI.command.CommandEditLabel(label, "new message");
     * @extends PMUI.command.Command
     *
     * @constructor
     * Creates an instance of this command
     * @param {PMUI.draw.Label} receiver The object that will perform the action
     * @param {String} newMessage
     */
    var CommandEditLabel = function (receiver, newMessage) {
        CommandEditLabel.superclass.call(this, receiver);
        this.before = null;
        this.after = null;
        CommandEditLabel.prototype.initObject.call(this, receiver, newMessage);
    };

    PMUI.inheritFrom('PMUI.command.Command', CommandEditLabel);

    /**
     * Type of the instances
     * @property {String}
     */
    CommandEditLabel.prototype.type = "CommandEditLabel";
    /**
     * Initializes the command
     * @param {PMUI.draw.Label} receiver The object that will perform the action
     * @param {String} newMessage
     */
    CommandEditLabel.prototype.initObject = function (receiver, newMessage) {
        var parentHeight = 0,
            parentWidth = 0;
        if (receiver.parent) {
            parentHeight = receiver.parent.height;
            parentWidth = receiver.parent.width;
        }
        this.before = {
            message: receiver.message,
            width: receiver.width,
            height: receiver.height,
            parentHeight: parentHeight,
            parentWidth: parentWidth
        };
        this.after = {
            message: newMessage,
            width: 0,
            height: 0,
            parentHeight: parentWidth,
            parentWidth: parentHeight
        };
    };
    /**
     * Executes the command, sets the new message updates the dimensions and its,
     * parent if necessary
     */
    CommandEditLabel.prototype.execute = function (stopTrigger) {
        this.receiver.setMessage(this.after.message);
        this.receiver.updateDimension();
        if (this.after.width === 0) {
            this.after.width = this.receiver.width;
            this.after.height = this.receiver.height;
            if (this.after.parentWidth !== 0) {
                this.after.parentWidth = this.receiver.parent.width;
                this.after.parentHeight = this.receiver.parent.height;
            }
        }
        this.receiver.paint();
        if (!stopTrigger) {
            this.receiver.canvas.triggerTextChangeEvent(
                this.receiver,
                this.before.message,
                this.after.message
            );
            if ((this.after.parentWidth !== this.before.parentWidth) &&
                    (this.before.parentHeight !== this.after.parentHeight)) {
                this.receiver.canvas.triggerDimensionChangeEvent(
                    this.receiver.parent,
                    this.before.parentWidth,
                    this.before.parentHeight,
                    this.after.parentWidth,
                    this.after.parentHeight
                );
            }
        }
    };
    /**
     * Returns to the previous state before executing the command
     */
    CommandEditLabel.prototype.undo = function (stopTrigger) {
        this.receiver.setMessage(this.before.message);

        if (this.receiver.parent) {
            this.receiver.parent.setDimension(this.before.parentWidth, this.before.parentHeight);
        }
        this.receiver.setDimension(this.before.width, this.before.height);
        this.receiver.updateDimension();
        this.receiver.paint();
        this.receiver.canvas.triggerTextChangeEvent(
            this.receiver,
            this.after.message,
            this.before.message
        );
        if ((this.after.parentWidth !== this.before.parentWidth) &&
                (this.before.parentHeight !== this.after.parentHeight)) {
            this.receiver.canvas.triggerDimensionChangeEvent(
                this.receiver.parent,
                this.after.parentWidth,
                this.after.parentHeight,
                this.before.parentWidth,
                this.before.parentHeight
            );
        }
    };
    /**
     * Executes the command again after an undo action has been done
     */
    CommandEditLabel.prototype.redo = function () {
        this.execute();
    };

    PMUI.extendNamespace('PMUI.command.CommandEditLabel', CommandEditLabel);
}());

(function () {
    /**
     * @class PMUI.command.CommandMove
     * Encapsulates the action of moving an element
     *
     *              //e.g.
     *              var command = new PMUI.command.CommandMove(shape);
     * @extends PMUI.command.Command
     *
     * @constructor
     * Creates an instance of CommandMove
     * @param {Object} receiver The object that will perform the action
     */
    var CommandMove = function (receiver) {
        CommandMove.superclass.call(this, receiver);
        this.before = null;
        this.after = null;
        this.relatedShapes = [];
        CommandMove.prototype.initObject.call(this, receiver);
    };

    PMUI.inheritFrom('PMUI.command.Command', CommandMove);
    /**
     * Type of the instances of this class
     * @property {String}
     */
    CommandMove.prototype.type = "CommandMove";

    /**
     * Initializes the command parameters
     * @param {PMUI.draw.Core} receiver The object that will perform the action
     */
    CommandMove.prototype.initObject = function (receiver) {
        var i,
            beforeShapes = [],
            afterShapes = [];
        for (i = 0; i < receiver.getSize(); i += 1) {
            this.relatedShapes.push(receiver.get(i));
            beforeShapes.push({
                x: receiver.get(i).getOldX(),
                y: receiver.get(i).getOldY()
            });
            afterShapes.push({
                x: receiver.get(i).getX(),
                y: receiver.get(i).getY()
            });
        }
        this.before = {
            shapes: beforeShapes
        };
        this.after = {
            shapes: afterShapes
        };
        this.moveUndo = false;
    };

    /**
     * Executes the command, changes the position of the element, and if necessary
     * updates the position of its children, and refreshes all connections
     */
    CommandMove.prototype.execute = function () {
        var i,
            j,
            max,
            connection,
            shape,
            delta;
        //clear used array
        PMUI.getActiveCanvas().refreshArray.clear();
        PMUI.getActiveCanvas().connToRefresh.clear();
        for (i = 0; i < this.relatedShapes.length; i += 1) {
            shape = this.relatedShapes[i];
            //delta to move user connections
            delta = {
                dx: this.after.shapes[i].x - this.before.shapes[i].x,
                dy: this.after.shapes[i].y - this.before.shapes[i].y
            };
            shape.setPosition(this.after.shapes[i].x, this.after.shapes[i].y)
                .refreshChildrenPositions(true, delta);

            for (j = 0, max = this.canvas.connToRefresh.getSize(); j < max; j += 1) {
                connection = this.canvas.connToRefresh.get(j);
                connection.reconectSwitcher(delta, false);
            }
            shape.refreshConnections(false, this.relatedShapes, delta);

        }
        this.canvas.triggerPositionChangeEvent(this.relatedShapes,
            this.before.shapes, this.after.shapes);
        if (this.moveUndo) {
            for (j = 0; j < PMUI.getActiveCanvas().refreshArray.getSize(); j += 1) {
                connection = PMUI.getActiveCanvas().refreshArray.get(j);
                connection.reconnectUser(delta, false);
                connection.setSegmentMoveHandlers()
                    .checkAndCreateIntersectionsWithAll();
                PMUI.getActiveCanvas().triggerConnectionStateChangeEvent(connection);
            }
            this.moveUndo = false;
        }

    };

    /**
     * Returns to the state before the command was executed
     */
    CommandMove.prototype.undo = function () {
        var i,
            delta,
            shape,
            connection,
            j, max;
        PMUI.getActiveCanvas().refreshArray.clear();
        PMUI.getActiveCanvas().connToRefresh.clear();
        for (i = 0; i < this.relatedShapes.length; i += 1) {
            shape = this.relatedShapes[i];
            delta = {
                dx: this.before.shapes[i].x - this.after.shapes[i].x,
                dy: this.before.shapes[i].y - this.after.shapes[i].y
            };
            shape.setPosition(this.before.shapes[i].x, this.before.shapes[i].y)
                .refreshChildrenPositions(true, delta);

            for (j = 0, max = this.canvas.connToRefresh.getSize(); j < max; j += 1) {
                connection = this.canvas.connToRefresh.get(j);
                connection.reconectSwitcher(delta, false);
            }
            shape.refreshConnections(false, this.relatedShapes, delta);

        }
        this.canvas.triggerPositionChangeEvent(this.relatedShapes,
            this.after.shapes, this.before.shapes);

        for (j = 0; j < PMUI.getActiveCanvas().refreshArray.getSize(); j += 1) {
            connection = PMUI.getActiveCanvas().refreshArray.get(j);
            connection.reconnectUser(delta, false);
            connection.setSegmentMoveHandlers()
                .checkAndCreateIntersectionsWithAll();
            PMUI.getActiveCanvas()
                .triggerConnectionStateChangeEvent(connection);
        }
        this.moveUndo = true;
    };

    /**
     *  Executes the command again after an undo action has been done
     */
    CommandMove.prototype.redo = function () {
        this.execute();
    };

    PMUI.extendNamespace('PMUI.command.CommandMove', CommandMove);
}());

(function () {
    /**
     * @class PMUI.command.CommandPaste
     * Class CommandPaste determines the actions executed when some shapes are pasted (redo) and the actions
     * executed when they're removed (undo).
     *
     * Instances of this class are created in {@link PMUI.draw.Canvas#paste}.
     * @extends PMUI.command.Command
     *
     * @constructor Creates an instance of the class CommandPaste
     * @param {Object} receiver The object that will execute the command
     * @param {Object} options Initialization options
     * @cfg {Array} [stackCommandConnect=[]] Array of commands connect
     * @cfg {Array} [stackCommandCreate=[]] Array of commands create
     */
    var CommandPaste = function (receiver, options) {

        CommandPaste.superclass.call(this, receiver);

        /**
         * A stack of commandsConnect (for connections)
         * @property {Array}
         */
        this.stackCommandConnect = [];

        /**
         * A stack of commandsCreate (for shapes)
         * @property {Array}
         */
        this.stackCommandCreate = [];

        CommandPaste.prototype.initObject.call(this, receiver, options);
    };

    PMUI.inheritFrom('PMUI.command.Command', CommandPaste);

    /**
     * Type of command
     * @property {String}
     */
    CommandPaste.prototype.type = "CommandPaste";

    /**
     * Instance initializer which uses options to extend the config options to initialize the instance
     * @param {Object} receiver The object that will execute the command
     * @private
     */
    CommandPaste.prototype.initObject = function (receiver, options) {
        var i,
            shape,
            defaults = {
                stackCommandConnect: [],
                stackCommandCreate: []
            };

        $.extend(true, defaults, options);

        this.stackCommandConnect = defaults.stackCommandConnect;
        this.stackCommandCreate = defaults.stackCommandCreate;

    };

    /**
     * Executes the command.
     * The steps are:
     *
     * 1. Execute the redo operation for each command create
     * 2. Execute the redo operation for each command connect
     *
     * @chainable
     */
    CommandPaste.prototype.execute = function () {
        var i,
            command;
        for (i = 0; i < this.stackCommandCreate.length; i += 1) {
            command = this.stackCommandCreate[i];
            command.redo();
        }
        for (i = 0; i < this.stackCommandConnect.length; i += 1) {
            command = this.stackCommandConnect[i];
            command.redo();
        }
        return this;
    };

    /**
     * Inverse executes the command a.k.a. undo.
     * The steps are:
     *
     * 1. Execute the undo operation for each command create
     * 2. Execute the undo operation for each command connect
     *
     * @chainable
     */
    CommandPaste.prototype.undo = function () {
        var i,
            command;
        for (i = 0; i < this.stackCommandCreate.length; i += 1) {
            command = this.stackCommandCreate[i];
            command.undo();
        }
        for (i = 0; i < this.stackCommandConnect.length; i += 1) {
            command = this.stackCommandConnect[i];
            command.undo();
        }
        return this;
    };

    /**
     * Executes the command a.k.a redo
     * @chainable
     */
    CommandPaste.prototype.redo = function () {
        this.execute();
        return this;
    };

    PMUI.extendNamespace('PMUI.command.CommandPaste', CommandPaste);
}());

(function () {
    /**
     * @class PMUI.command.CommandReconnect
     * Class CommandReconnect determines the actions executed when a connection is reconnected, e.g. when a connection
     * source port or end port are dragged to another shape or another position in the same shape (redo)
     * and the actions executed to revert the last drag to another shape or another position in the same shape (undo).
     *
     * Instances of this class are created in {@link PMUI.behavior.ConnectionDropBehavior#onDrop}.
     * @extends PMUI.command.Command
     *
     * @constructor Creates an instance of the class CommandReconnect
     * @param {Object} receiver The object that will execute the command
     */
    var CommandReconnect = function (receiver) {
        CommandReconnect.superclass.call(this, receiver);

        /**
         * Object that represents the state of the shape before changing
         * its dimension
         * @property {Object}
         */
        this.before = {
            x: this.receiver.getOldX(),
            y: this.receiver.getOldY(),
            parent: this.receiver.getOldParent()
        };

        /**
         * Object that represents the state of the shape after changing
         * its dimension
         * @property {Object}
         */
        this.after = {
            x: this.receiver.getX(),
            y: this.receiver.getY(),
            parent: this.receiver.getParent()
        };
    };

    PMUI.inheritFrom('PMUI.command.Command', CommandReconnect);

    /**
     * Type of command.
     * @property {String}
     */
    CommandReconnect.prototype.type = "CommandReconnect";

    /**
     * Executes the command
     * The steps are:
     *
     * 1. Hide the currentConnection of the canvas if there's one
     * 2. If the new parent of the dragged port is different than the old parent
     *      - Remove the port from its old parent
     *      - Add the port to the new parent
     * 3. If the new parent of the dragged port is equal to the old parent
     *      - Redefine its position in the shape
     * 4. Reconnect the connection (using the new ports) and check for intersections
     * 4. Trigger the port change event
     *
     * @chainable
     */
    CommandReconnect.prototype.execute = function () {

        var port = this.receiver,
            parent = this.after.parent,
            oldParent = this.before.parent;

        // hide the connection if its visible
        if (parent.canvas.currentConnection) {
            parent.canvas.currentConnection.hidePortsAndHandlers();
            parent.canvas.currentConnection = null;
        }

        if (parent.getID() !== oldParent.getID()) {
            oldParent.removePort(port);
            parent.addPort(port, this.after.x, this.after.y, true);
            port.oldParent = oldParent;
        } else {
            parent.definePortPosition(port,
                new PMUI.util.Point(this.after.x, this.after.y));
        }

        port.connection
            .disconnect()
            .connect()
            .setSegmentMoveHandlers()
            .checkAndCreateIntersectionsWithAll();

        // custom trigger
        this.receiver.canvas.triggerPortChangeEvent(port);
        return this;
    };

    /**
     * Inverse executes a command e.g. undo.
     * The steps are:
     *
     * 1. Hide the currentConnection of the canvas if there's one
     * 2. If the old parent of the port is different than the new parent
     *      - Remove the port from its new parent
     *      - Add the port to the old parent
     * 3. If the old parent of the port is equal to the new parent
     *      - Redefine its position in the shape
     * 4. Reconnect the connection (using the new ports) and check for intersections
     * 4. Trigger the port change event
     *
     * @chainable
     */
    CommandReconnect.prototype.undo = function () {
        var port = this.receiver,
            parent = this.after.parent,
            oldParent = this.before.parent;

        // hide the connection if its visible
        if (parent.canvas.currentConnection) {
            parent.canvas.currentConnection.hidePortsAndHandlers();
            parent.canvas.currentConnection = null;
        }

        if (parent.getID() !== oldParent.getID()) {
            parent.removePort(port);
            oldParent.addPort(port, this.before.x, this.before.y, true);
            port.canvas.regularShapes.insert(port);
            port.oldParent = parent;
        } else {
            parent.definePortPosition(port,
                new PMUI.util.Point(this.before.x, this.before.y));
        }

        port.connection
            .disconnect()
            .connect()
            .setSegmentMoveHandlers()
            .checkAndCreateIntersectionsWithAll();

        // custom trigger
        this.receiver.canvas.triggerPortChangeEvent(port);
        return this;
    };

    /**
     * Inverse executes a command e.g. undo
     * @chainable
     */
    CommandReconnect.prototype.redo = function () {
        this.execute();
        return this;
    };

    PMUI.extendNamespace('PMUI.command.CommandReconnect', CommandReconnect);
}());
(function () {
    /**
     * @class PMUI.command.CommandResize
     * Class CommandResize determines the actions executed when some shapes are resized (redo) and the actions
     * executed when they're resized back (undo).
     *
     * Instances of this class are created in {@link PMUI.behavior.RegularResizeBehavior#event-resizeEnd}.
     * @extends PMUI.command.Command
     *
     * @constructor Creates an instance of the class CommandResize
     * @param {Object} receiver The object that will execute the command
     */
    var CommandResize = function (receiver) {
        CommandResize.superclass.call(this, receiver);

        /**
         * Object that represents the state of the shape before changing
         * its dimension
         * @property {Object}
         */
        this.before = {
            x: this.receiver.getOldX(),
            y: this.receiver.getOldY(),
            width: this.receiver.getOldWidth(),
            height: this.receiver.getOldHeight()
        };

        /**
         * Object that represents the state of the shape after changing
         * its dimension
         * @property {Object}
         */
        this.after = {
            x: this.receiver.getX(),
            y: this.receiver.getY(),
            width: this.receiver.getWidth(),
            height: this.receiver.getHeight()
        };
    };

    PMUI.inheritFrom('PMUI.command.Command', CommandResize);

    /**
     * Type of command of this object
     * @property {String}
     */
    CommandResize.prototype.type = "CommandResize";

    /**
     * Executes the command.
     * The steps are:
     *
     * 1. Set the new position and dimension of the shape (using `this.after`)
     * 2. Fix its connections on resize
     * 3. Trigger the dimension change event
     * 4. Trigger the position change event
     *
     * @chainable
     */
    CommandResize.prototype.execute = function () {
        var shape = this.receiver,
            canvas = shape.getCanvas();
        shape.setPosition(this.after.x, this.after.y)
            .setDimension(this.after.width, this.after.height);
        canvas.triggerDimensionChangeEvent(shape, this.before.width,
            this.before.height, this.after.width, this.after.height);
        if ((this.after.x !== this.before.x) || (this.after.y !== this.before.y)) {
            canvas.triggerPositionChangeEvent(
                [shape],
                [
                    {
                        x: this.before.x,
                        y: this.before.y
                    }
                ],
                [
                    {
                        x: this.after.x,
                        y: this.after.y
                    }
                ]
            );
        }
        return this;
    };

    /**
     * Inverse executes a command a.k.a undo.
     * The steps are:
     *
     * 1. Set the new position and dimension of the shape (using `this.before`)
     * 2. Fix its connections on resize
     * 3. Trigger the dimension change event
     * 4. Trigger the position change event
     *
     * @chainable
     */
    CommandResize.prototype.undo = function () {
        var shape = this.receiver,
            canvas = shape.getCanvas();
        shape.setPosition(this.before.x, this.before.y)
            .setDimension(this.before.width, this.before.height);
        shape.fixConnectionsOnResize(shape.resizing, true);
        canvas.triggerDimensionChangeEvent(shape, this.after.width,
            this.after.height, this.before.width, this.before.height);
        if ((this.after.x !== this.before.x) || (this.after.y !== this.before.y)) {
            canvas.triggerPositionChangeEvent(
                [shape],
                [
                    {
                        x: this.after.x,
                        y: this.after.y
                    }
                ],
                [
                    {
                        x: this.before.x,
                        y: this.before.y
                    }
                ]
            );
        }
        return this;
    };

    /**
     * Executes the command a.k.a redo.
     * @chainable
     */
    CommandResize.prototype.redo = function () {
        this.execute();
        return this;
    };

    PMUI.extendNamespace('PMUI.command.CommandResize', CommandResize);
}());

(function () {
    /**
     * @class PMUI.command.CommandSegmentMove
     * Class CommandSegmentMove determines the actions executed when a segment is moved through its move handler (redo)
     * and the actions executed when the segment is moved back (undo).
     *
     * Instances of this class are created in {@link PMUI.draw.SegmentMoveHandler#event-dragEnd}.
     * @extends PMUI.command.Command
     *
     * @constructor Creates an instance of the class CommandSegmentMove
     * @param {PMUI.draw.Connection} receiver The object that will execute the command
     * @param {Object} options Initialization options
     * @cfg {Array} [oldPoints=[]] Array of old points of the connection
     * @cfg {Array} [newPoints=[]] Array of new points of the connection
     */
    var CommandSegmentMove = function (receiver, options) {
        CommandSegmentMove.superclass.call(this, receiver);

        /**
         * Array of points that represent the state of the connection before moving the segment move handler
         * @property {Array}
         */
        this.oldPoints = [];

        /**
         *  Array of points that represent the state of the connection after moving the segment move handler
         * @property {Array}
         */
        this.newPoints = [];

        CommandSegmentMove.prototype.initObject.call(this, options);
    };

    PMUI.inheritFrom('PMUI.command.Command', CommandSegmentMove);

    /**
     * Type of command of this object
     * @property {String}
     */
    CommandSegmentMove.prototype.type = "CommandResize";

    /**
     * Instance initializer which uses options to extend the config options to initialize the instance
     * @param {Object} options The object that contains old points and new points
     * @private
     */
    CommandSegmentMove.prototype.initObject = function (options) {
        var defaults = {
                oldPoints: [],
                newPoints: []
            },
            i,
            point;
        $.extend(true, defaults, options);
        this.oldPoints = [];
        for (i = 0; i < defaults.oldPoints.length; i += 1) {
            point = defaults.oldPoints[i];
            this.oldPoints.push(new PMUI.util.Point(point.x, point.y));
        }
        this.newPoints = [];
        for (i = 0; i < defaults.newPoints.length; i += 1) {
            point = defaults.newPoints[i];
            this.newPoints.push(new PMUI.util.Point(point.x, point.y));
        }
    };

    /**
     * There's a common behavior between execute and inverseExecute in
     * this command so merge both files and use a parameter to choose
     * between execute and inverseExecute.
     * The steps are:
     *
     * 1. Select the connection by triggering a click in its destination decorator
     * 2. Hide the ports and handlers of the connection and reconnect the connection using points
     * 3. Check and create the intersections and show the ports of the connection
     * 4. Trigger the segment move event
     *
     * @private
     * @chainable
     */
    CommandSegmentMove.prototype.common = function (action) {
        var connection = this.receiver;
        // trigger targetSpriteDecorator onClick
        $(connection.destDecorator.getHTML()).trigger('click');

        connection.hidePortsAndHandlers();
        connection.disconnect(true).connect({
            algorithm: 'user',
            points: this[action]
        });

        connection.setSegmentMoveHandlers();

        // create intersections with all other connections
        connection.checkAndCreateIntersectionsWithAll();

        // show the ports and handlers again
        connection.showPortsAndHandlers();

        // trigger event
        connection.canvas.triggerConnectionStateChangeEvent(connection);
        return this;
    };

    /**
     * Executes a command
     * @chainable
     */
    CommandSegmentMove.prototype.execute = function () {
        this.common("newPoints");
        return this;
    };

    /**
     * Inverse executes the command a.k.a. undo
     * @chainable
     */
    CommandSegmentMove.prototype.undo = function () {
        this.common("oldPoints");
        return this;
    };

    /**
     * Executes the command a.k.a. redo
     * @chainable
     */
    CommandSegmentMove.prototype.redo = function () {
        this.execute();
        return this;
    };

    PMUI.extendNamespace('PMUI.command.CommandSegmentMove', CommandSegmentMove);
}());

(function () {
    /**
     * @class PMUI.command.CommandStack
     * Command stack stores the commands executed to perform undos and redos, it consists of 2 stacks:
     *
     * - undoStack (represented as an array)
     * - redoStack (represented as an array)
     *
     * Every time an undo or redo action is executed the stacks automatically get updated and the function passed
     * during the instantiation is called.
     *
     *      // e.g.
     *      // let's assume that commandCreateInstance is an instance of CommandCreate
     *      // let's assume that commandResizeInstance is an instance of CommandResize
     *
     *      // first let's create the stacks (max size of the redo stack is 5)
     *      var commandStack = new PMUI.command.CommandStack(5);
     *
     *      // commandStack.add() inserts the command to the undoStack (emptying the redo stack too)
     *      commandStack.add(commandCreateInstance);
     *      commandStack.add(commandResizeInstance);
     *      commandStack.add(commandResizeInstance);
     *      commandStack.add(commandResizeInstance);
     *      commandStack.add(commandResizeInstance);
     *
     *      // at this point the redo stack is full (we defined a max size of 5), so the following add will remove the
     *      // last element of the stack (which is commandCreateInstance) and the undoStack will only consist of
     *      // command resize instances
     *      commandStack.add(commandResizeInstance);
     *
     *      // whenever an undo operation is executed in the commandStack, the first command (which is the last command
     *      // in the undoStack) executes its undo operation and the command is removed from the undoStack and pushed to
     *      // the redoStack, graphically:
     *
     *      // Let's define an stack graphically as '[['
     *      // if an element (e1) is pushed to the stack the stack becomes: [[e1
     *      // if an element (e2) is pushed to the stack the stack becomes: [[e1, e2
     *      // if an element (e3) is pushed to the stack the stack becomes: [[e1, e2, e3
     *      // if an element is removed from the stack the stack becomes: [[e1, e2
     *      // Note the direction of the stack, if it's defined as ']]'
     *      // the operations executed above turn the stack into:
     *      // e1]]; e2, e1]]; e3, e2, e1]]; e2, e1]]
     *
     *      // Let's alias commandResizeInstance as cRI and commandCreateInstance cCI.
     *      // With the example defined above of commandResizeInstance, the following line does:
     *      // pre state:
     *      // undoStack = [[cRI_1, cRI_2, cRI_3, cRI_4, cRI_5
     *      // redoStack = ]]
     *      // post state:
     *      // undoStack = [[cRI_1, cRI_2, cRI_3, cRI_4
     *      // redoStack = cRI_5]]
     *      commandStack.undo();
     *
     *      // executing undo again leads to:
     *      // pre state:
     *      // undoStack = [[cRI_1, cRI_2, cRI_3, cRI_4
     *      // redoStack = cRI_5]]
     *      // post state:
     *      // undoStack = [[cRI_1, cRI_2, cRI_3
     *      // redoStack = cRI_4, cRI_5]]
     *      commandStack.undo();
     *
     *      // executing redo leads to:
     *      // pre state:
     *      // undoStack = [[cRI_1, cRI_2, cRI_3
     *      // redoStack = cRI_4, cRI_5]]
     *      // post state:
     *      // undoStack = [[cRI_1, cRI_2, cRI_3, cRI_4
     *      // redoStack = cRI_5]]
     *      commandStack.redo();
     *
     *      // adding a new command to the stack empties the redo stack so:
     *      // pre state:
     *      // undoStack = [[cRI_1, cRI_2, cRI_3, cRI_4
     *      // redoStack = cRI_5]]
     *      // post state:
     *      // undoStack = [[cRI_1, cRI_2, cRI_3, cRI_4, cCI_1
     *      // redoStack = ]]
     *      commandStack.add(commandCreateInstance);
     *
     * @constructor Creates an instance of the class CommandStack
     * @param {number} stackSize The maximum number of operations to be saved
     * @param {Function} successCallback Function to be executed after add, undo or redo,
     * `this` will refer to the object itself, not the constructor
     */
    var CommandStack = function (stackSize, successCallback) {
        var undoStack,
            redoStack,
            maxSize;
        /**
         * Stacks that contains commands (when pushed to the undoStack)
         * @property {Array} [undoStack=[]]
         * @private
         */
        undoStack = [];
        /**
         * Stacks that contains commands (when pushed to the redoStack)
         * @property {Array} [redoStack=[]]
         * @private
         */
        redoStack = [];
        /**
         * Maximum size of the undo stack
         * @property {number} [maxSize=20]
         * @private
         */
        maxSize = stackSize || 20;
        /**
         * Empties the redo stack (when a new event is added to the undoStack)
         * @private
         */
        function emptyRedoStack() {
            redoStack = [];
        }

        /**
         * Handler to be called when a special action occurs
         */
        function onSuccess() {
        }

        if (successCallback && {}.toString.call(successCallback) === '[object Function]') {
            onSuccess = successCallback;
        }

        return {
            /**
             * Adds an action (command) to the undoStack
             * @param {PMUI.command.Command} action
             */
            add: function (action) {
                emptyRedoStack();
                undoStack.push(action);
                if (undoStack.length > maxSize) {
                    // got to the max size of the stack
                    undoStack.shift();
                }
                onSuccess();
            },
            /**
             * Adds an action (command) to the redoStack
             * @param {PMUI.command.Command} action
             */
            addToRedo: function (action) {
                redoStack.push(action);
            },
            /**
             * Undoes the last action executing undoStack's first item undo
             * @return {boolean}
             */
            undo: function () {
                var action;     // action to be inverse executed
                if (undoStack.length === 0) {
                    //console.log("undo(): can't undo because there are no " +
                    //    "actions to undo");
                    return false;
                }
                action = undoStack.pop();
                // inverse execute the action
                action.undo();
                redoStack.unshift(action);

                // execute on success handler
                onSuccess();
                return true;
            },
            /**
             * Redoes the last action executing redoStack's first item redo
             * @return {boolean}
             */
            redo: function () {
                var action;     // action to be inverse executed
                if (redoStack.length === 0) {
                    return false;
                }
                action = redoStack.shift();
                // execute the action
                action.redo();
                undoStack.push(action);

                // execute on success handler
                onSuccess();
                return true;
            },
            /**
             * Clear both stacks
             */
            clearStack: function () {
                redoStack = [];
                undoStack = [];
            },
            /**
             * Debugging method to show the state of each stack
             * @param {boolean} showDetailed
             */
            debug: function (showDetailed) {
                var i;
                if (showDetailed) {
                    for (i = 0; i < undoStack.length; i += 1) {
                        console.log((i + 1) + ") " + undoStack[i].type);
                    }
                }
                if (showDetailed) {
                    for (i = 0; i < redoStack.length; i += 1) {
                        console.log((i + 1) + ") " + redoStack[i].type);
                    }
                }
            },
            /**
             * Gets the size of the redo stack
             * @return {Number}
             */
            getRedoSize: function () {
                return redoStack.length;
            },
            /**
             * Gets the size of the redo stack
             * @return {Number}
             */
            getUndoSize: function () {
                return undoStack.length;
            },
            /**
             * Sets the onSuccess handler of this object
             * @param successCallback
             * @chainable
             */
            setHandler: function (successCallback) {
                if (successCallback && {}.toString.call(successCallback) === '[object Function]') {
                    onSuccess = successCallback;
                }
                return this;
            }
        };
    };

    // extend the namespace
    PMUI.extendNamespace('PMUI.command.CommandStack', CommandStack);

}());

(function () {
    /**
     * @class PMUI.command.CommandSwitchContainer
     * Class that encapsulates the action of switching containers
     *
     *              //e.g.
     *              var command = new PMUI.command.CommandSwitchContainer(arrayOfShapes);
     * @extends PMUI.command.Command
     *
     * @constructor
     * Creates an instance of this command
     * @param {Array} shapesAdded array of shapes that are going to switch container
     */
    var CommandSwitchContainer = function (shapesAdded) {
        CommandSwitchContainer.superclass.call(this, shapesAdded[0].shape);
        /**
         * Properties of the object before the command is executed
         * @property {Object}
         */
        this.before = null;
        /**
         * Properties of the object after the command is executed
         * @property {Object}
         */
        this.after = null;
        /**
         * Reference to all objects involved in this command
         * @type {Array}
         */
        this.relatedShapes = [];
        CommandSwitchContainer.prototype.initObject.call(this, shapesAdded);
    };

    PMUI.inheritFrom('PMUI.command.Command', CommandSwitchContainer);

    /**
     * Type of the instances of this command
     * @property {String}
     */
    CommandSwitchContainer.prototype.type = "CommandSwitchContainer";

    /**
     * Initializer of the command
     * @param {Array} shapesAdded array of shapes that are going to switch container
     */
    CommandSwitchContainer.prototype.initObject = function (shapesAdded) {
        var i,
            shape,
            beforeShapes = [],
            afterShapes = [];

        for (i = 0; i < shapesAdded.length; i += 1) {
            shape = shapesAdded[i];
            this.relatedShapes.push(shape.shape);
            beforeShapes.push({
                parent: shape.shape.parent,
                x: shape.shape.getOldX() * shape.shape.canvas.getZoomFactor(),
                y: shape.shape.getOldY() * shape.shape.canvas.getZoomFactor(),
                topLeft: true
            });
            afterShapes.push({
                parent: shape.container,
                x: shape.x,
                y: shape.y,
                topLeft: shape.topLeft
            });
        }

        this.before = {
            shapes: beforeShapes
        };

        this.after = {
            shapes: afterShapes
        };
        this.containerUndo = false;
    };

    /**
     * The command execution implementation, updates the parents, and if necessary,
     * updates the children positions and connections.
     */
    CommandSwitchContainer.prototype.execute = function () {
        var i,
            j,
            max,
            connection,
            shape,
            delta;

        for (i = 0; i < this.relatedShapes.length; i += 1) {
            shape = this.relatedShapes[i];
            delta = {
                dx: this.before.shapes[i].x - this.after.shapes[i].x,
                dy: this.before.shapes[i].y - this.after.shapes[i].y
            };
            this.before.shapes[i].parent.swapElementContainer(
                shape,
                this.after.shapes[i].parent,
                this.after.shapes[i].x,
                this.after.shapes[i].y,
                this.after.shapes[i].topLeft
            );
            shape.refreshChildrenPositions(true, delta);
            shape.refreshConnections(false, this.relatedShapes, delta);
        }
        this.canvas.triggerParentChangeEvent(this.relatedShapes,
            this.before.shapes, this.after.shapes);
        if (this.containerUndo) {
            for (j = 0; j < PMUI.getActiveCanvas().refreshArray.getSize(); j += 1) {
                connection = PMUI.getActiveCanvas().refreshArray.get(j);
                connection.reconnectManhattah(false);
                connection.setSegmentMoveHandlers()
                    .checkAndCreateIntersectionsWithAll();
                PMUI.getActiveCanvas()
                    .triggerConnectionStateChangeEvent(connection);
            }
            this.containerUndo = false;
        }

    };

    /**
     * Returns to the state before this command was executed
     */
    CommandSwitchContainer.prototype.undo = function () {
        var i,
            delta,
            shape,
            connection,
            j,
            max;
        for (i = 0; i < this.relatedShapes.length; i += 1) {
            shape = this.relatedShapes[i];
            delta = {
                dx: this.before.shapes[i].x - this.after.shapes[i].x,
                dy: this.before.shapes[i].y - this.after.shapes[i].y
            };
            this.before.shapes[i].parent.swapElementContainer(
                shape,
                this.before.shapes[i].parent,
                this.before.shapes[i].x,
                this.before.shapes[i].y,
                this.before.shapes[i].topLeft
            );
            shape.refreshChildrenPositions(true, delta);
            shape.refreshConnections(false, this.relatedShapes, delta);
        }

        this.canvas.triggerParentChangeEvent(this.relatedShapes,
            this.after.shapes, this.before.shapes);


        for (j = 0; j < PMUI.getActiveCanvas().refreshArray.getSize(); j += 1) {
            connection = PMUI.getActiveCanvas().refreshArray.get(j);
            connection.reconnectManhattah(false);
            connection.setSegmentMoveHandlers()
                .checkAndCreateIntersectionsWithAll();
            PMUI.getActiveCanvas()
                .triggerConnectionStateChangeEvent(connection);
        }
        this.containerUndo = true;

    };


    /**
     *  Executes the command again after an undo action has been done
     */
    CommandSwitchContainer.prototype.redo = function () {
        this.execute();
    };

    PMUI.extendNamespace('PMUI.command.CommandSwitchContainer', CommandSwitchContainer);
}());

(function () {
    /**
     * @class PMUI.draw.Canvas
     * Is the object where all the shapes and drawings will be placed on, in addition it handles zoom operations
     * and also the triggering of events.
     *
     * Below are some unique characteristics of the instances of this class:
     *
     * - Each instance of this class has an instance of the following:
     *      - {@link PMUI.command.CommandStack Command stack}
     *      - {@link PMUI.draw.Snapper Two snappers} (one horizontal snapper and one vertical snapper).
     *      - {@link PMUI.draw.MultipleSelectionContainer} to select multiple shapes.
     *      - {@link PMUI.draw.Segment}. To create connections between shapes.
     *      - {@link PMUI.draw.Canvas#property-customShapes CustomShapes arrayList}. To save all the custom shapes
     *          created in this canvas.
     *      - {@link PMUI.draw.Canvas#property-connections Connections arrayList}. To save all the connections
     *          created in this canvas.
     *      - {@link PMUI.draw.Canvas#property-currentSelection Current selection arrayList}.
     To save all the custom shapes
     *          that are select (by clicking, ctrl clicking them or selecting them using the
     *          multipleSelectionContainer instance).
     *      - {@link PMUI.draw.Canvas#property-currentConnection Current connection}. A pointer to the selected
     *          connection.
     *      - {@link PMUI.draw.Canvas#property-currentLabel Current label}. A pointer to the active label.
     *
     * Besides this class does the following:
     *
     * - Parses the JSON retrieved from the database (through {@link PMUI.draw.Canvas#parse})
     * - Creates some custom events (defined in {@link PMUI.draw.Canvas#attachListeners})
     * - Creates, stores and executes the commands (through its {@link PMUI.draw.Canvas#property-commandStack}) property
     *
     * Below is an example of instantiation of this class:
     *
     *      // The canvas needs an object containing the reference to existing classes outside the library
     *      // e.g. let's define two classes
     *      var BpmnActivity = function (options) {
     *          ...
     *      };
     *      var BpmnEvent = function (options) {
     *          ...
     *      };
     *
     *      // Next, the canvas needs a factory function to create custom shapes dragged from a toolbar
     *      // this function needs an ID to create the shape
     *      function toolbarFactory (id) {
     *          var customShape = null;
     *          switch(id) {
     *              case: 'BpmnActivity':
     *                  customShape = new PMUI.draw.BpmnActivity({
     *                      ....
     *                  });
     *                  break;
     *              case: 'BpmnEvent':
     *                  customShape = new PMUI.draw.BpmnEvent({
     *                      ....
     *                  });
     *                  break;
     *          }
     *          return customShape;
     *      }
     *
     *      // finally an instance of this class can be defined
     *      var canvas = new PMUI.draw.Canvas({
     *          width: 4000,
     *          height: 4000,
     *          toolbarFactory: toolbarFactory,
     *          copyAndPasteReferences: {
     *              bpmnActivity: BpmnActivity,
     *              bpmnEvent: BpmnEvent
     *          }
     *      });
     *
     * @extends PMUI.draw.BehavioralElement
     *
     * @constructor
     * Creates an instance of the class
     * @param {Object} options configuration options of the canvas
     * @cfg {number} [width=4000] Width of this canvas.
     * @cfg {number} [height=4000] Height of this canvas.
     * @cfg {Function} toolbarFactory Function that will handle object creation
     * from a custom toolbar
     * @cfg {Object} [copyAndPasteReferences={}] References to the constructors of the classes
     * (so that a shape is easily created from the canvas)
     * @cfg {boolean} [readOnly=false] Property that determines the permission a
     * user has over the canvas
     */
    var Canvas = function (options) {
        Canvas.superclass.call(this, options);
        /**
         * Variable that points to the HTML in the DOM of this object.
         * @property {HTMLElement} [html=null]
         */
        this.html = null;
        /**
         * A list of all the custom shapes in the canvas.
         * @property {PMUI.util.ArrayList}
         */
        this.customShapes = null;
        /**
         * A list of all the regular shapes in the canvas.
         * @property {PMUI.util.ArrayList}
         */
        this.regularShapes = null;
        /**
         * A list of all the connections in the canvas.
         * @property {PMUI.util.ArrayList}
         */
        this.connections = null;
        /**
         * A list of all the shapes that are currently selected.
         * @property {PMUI.util.ArrayList}
         */
        this.currentSelection = null;
        /**
         * A list of all the connections that will not be repainted (using the ManhattanRouter algorithm),
         * but will be moved only.
         * @property {PMUI.util.ArrayList}
         */
        this.sharedConnections = null;
        /**
         * Left Scroll coordinate of the canvas
         * @property {number} [leftScroll=0]
         */
        this.leftScroll = 0;
        /**
         * Top scroll coordinate of the canvas
         * @property {number} [topScroll=0]
         */
        this.topScroll = 0;
        /**
         * Reference to the current selected connection in the canvas
         * @property {PMUI.draw.Connection}
         */
        this.currentConnection = null;
        /**
         * Pointer to the last connection selected in the canvas
         * (this variable is set from the commandDelete)
         * @property {PMUI.draw.Connection}
         */
        this.oldCurrentConnection = null;
        /**
         * Instance of the class {@link PMUI.draw.Segment} used to make connections in the canvas.
         * @property {PMUI.draw.Segment}
         */
        this.connectionSegment = null;
        /**
         * Instance of the class {@link PMUI.draw.MultipleSelectionContainer} created to do multiple selection
         * @property {PMUI.draw.MultipleSelectionContainer}
         */
        this.multipleSelectionHelper = null;
        /**
         * Instance of the class {@link PMUI.draw.Snapper} which represents the horizontal line used for snapping
         * @property {PMUI.draw.Snapper}
         */
        this.horizontalSnapper = null;
        /**
         * Instance of the class {@link PMUI.draw.Snapper} which represents the vertical line used for snapping
         * @property {PMUI.draw.Snapper}
         */
        this.verticalSnapper = null;
        /**
         * Current zoom Factor of the diagram
         * @property {number} [zoomFactor=1]
         */
        this.zoomFactor = 1;
        /**
         * Index for the zoom properties for shapes corresponding to the current
         * zoom factor
         * @property {number} [zoomPropertiesIndex=2]
         */
        this.zoomPropertiesIndex = 2;
        /**
         * zOrder of the HTML Representation
         * @property {number} [zOrder=0]
         */
        this.zOrder = 0;
        /**
         * Boolean set true if the {@link PMUI.draw.Canvas#event-mousedown} event of the canvas is fired,
         * it's set to false in the {@link PMUI.draw.Canvas#event-mouseup} event.
         * @property {boolean} [isMouseDown=false]
         */
        this.isMouseDown = false;
        /**
         * Current selected shape
         * @property {PMUI.draw.Shape}
         */
        this.currentShape = null;
        /**
         * True if the {@link PMUI.draw.Canvas#event-mousedown} event of the canvas is triggered and
         * the {@link PMUI.draw.Canvas#event-mousemove} event is triggered, it's set to false in mouseUp event.
         * @property {boolean} [isMouseDownAndMove=false]
         */
        this.isMouseDownAndMove = false;
        /**
         * Denotes if there's been a multiple drop prior to a drag end.
         * @property {boolean} [multipleDrop=false]
         */
        this.multipleDrop = false;
        /**
         * Denotes if a segment move handler is being dragged. in order not to
         * trigger events in the canvas [draggingASegmentHandler=false]
         * @property {boolean}
         */
        this.draggingASegmentHandler = false;
        /**
         * Elements that was added, changed or deleted in the canvas.
         * @property {Object}
         */
        this.updatedElement = null;
        /**
         * Determines if the canvas has been right clicked at {@link PMUI.draw.Canvas#event-mousedown}
         * @property {boolean} [rightClick=false]
         */
        this.rightClick = false;
        /**
         * Each time a shape is moved using the cursors, the following code is executed:
         *
         *      // for each 'connection' that is not in this.sharedConnection and that it's being
         *      // recalculated (using ManhattanRouter algorithm)
         *      connection.disconnect().connect()
         *                  .setSegmentMoveHandlers()
         *                  .checkAndCreateIntersectionsWithAll();
         *
         *  So to avoid these operations for each key press of the cursors, let's create a timeout,
         *  so that only after that timeout has expired the code above will run.
         *  This variable is a reference to that timeout.
         * @property {Object}
         */
        this.intersectionTimeout = null;
        /**
         * Point to the current label that is being edited
         * @property {Object}
         */
        this.currentLabel = null;
        /**
         * Instance of the class {@link PMUI.command.CommandStack} to be used in this canvas
         * @property {PMUI.command.CommandStack}
         */
        this.commandStack = null;
        /**
         * Array which contains a list of all the objects that were duplicated
         * (during copy)
         * @property {Array} [shapesToCopy=[]]
         */
        this.shapesToCopy = [];
        /**
         * Array which contains a list of all the connections that were duplicated
         * (during copy)
         * @property {Array} [connectionsToCopy=[]]
         */
        this.connectionsToCopy = [];
        /**
         * Property that determines the permissions a user has over the canvas
         * @property {boolean} [readOnly=false]
         */
        this.readOnly = false;
        /**
         * Layer that prevents the canvas to be altered
         * @property {PMUI.draw.ReadOnlyLayer}
         */
        this.readOnlyLayer = null;
        /**
         * Object which holds references to the constructors of the classes
         * (so that a shape is easily created from the canvas)
         * @property {Object} [copyAndPasteReferences={}]
         */
        this.copyAndPasteReferences = {};
        /**
         * Previous zoom properties index
         * @property {number} [prevZoom=1]
         */
        this.prevZoom = 1;
        /**
         * Initializer for labels, so that jQuery can measure the width of a message
         * in the first time its created
         * @type {HTMLElement}
         */
        this.dummyLabelInitializer = null;
        this.isDragging = false;
        this.isResizing = false;
        this.refreshArray = new PMUI.util.ArrayList();
        this.connToRefresh = new PMUI.util.ArrayList();
        Canvas.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.draw.BehavioralElement', Canvas);

    /**
     * Type of the instances
     * @property {String}
     */
    Canvas.prototype.type = "Canvas";
    /**
     * Family of the instances, this attribute must not be overridden
     * @property {String}
     * @readonly
     */
    Canvas.prototype.family = "Canvas";
    /**
     * Instance initializer which uses options to extend the config options to initialize the instance.
     * The following properties are instantiated in this method:
     *
     *      this.children = new PMUI.util.ArrayList();
     *      this.customShapes = new PMUI.util.ArrayList();
     *      this.regularShapes = new PMUI.util.ArrayList();
     *      this.connections = new PMUI.util.ArrayList();
     *      this.currentSelection = new PMUI.util.ArrayList();
     *      this.sharedConnections = new PMUI.util.ArrayList();
     *      this.commandStack = new PMUI.command.CommandStack(20);
     *      this.multipleSelectionHelper = new PMUI.draw.MultipleSelectionContainer(this);
     *      this.horizontalSnapper = new PMUI.draw.Snapper({orientation: 'horizontal', canvas: this});
     *      this.verticalSnapper = new PMUI.draw.Snapper({orientation: 'vertical', canvas: this});
     *
     * @param {Object} options The object that contains the config
     * @private
     */
    Canvas.prototype.init = function (options) {
        var canvasPosition,
            defaults;
        defaults = {
            left: 0,
            top: 0,
            width: 4000,
            height: 4000,
            copyAndPasteReferences: (options && options.copyAndPasteReferences) || {},
            readOnly: false
        };
        jQuery.extend(true, defaults, options);
        if (options) {
            this.children = new PMUI.util.ArrayList();
            this.customShapes = new PMUI.util.ArrayList();
            this.regularShapes = new PMUI.util.ArrayList();
            this.connections = new PMUI.util.ArrayList();
            this.currentSelection = new PMUI.util.ArrayList();
            this.sharedConnections = new PMUI.util.ArrayList();
            this.commandStack = new PMUI.command.CommandStack(20);
            this.multipleSelectionHelper = new PMUI.draw.MultipleSelectionContainer({
                canvas: this
            });
            this.copyAndPaste = false;
            this.copyAndPasteReferences = defaults.copyAndPasteReferences;
            this.setShapeFactory(defaults.shapeFactory);
            this.setPosition(defaults.left, defaults.top)
                .setDimension(defaults.width, defaults.height)
                .setCanvas(this)
                .setReadOnly(defaults.readOnly);
            // create snappers
            this.horizontalSnapper = new PMUI.draw.Snapper({
                orientation: 'horizontal',
                canvas: this
            });
            this.verticalSnapper = new PMUI.draw.Snapper({
                orientation: 'vertical',
                canvas: this
            });

            if (defaults.absoluteX) {
                this.absoluteX = defaults.absoluteX;
            }
            if (defaults.absoluteY) {
                this.absoluteY = defaults.absoluteY;
            }
            if (defaults.readOnly) {
            }
        }
    };

    /**
     * Overwrite createHTML method
     * @return {HTMLElement}
     */
    Canvas.prototype.createHTML = function () {
        Canvas.superclass.prototype.createHTML.call(this);
        this.addElement(this.multipleSelectionHelper, 0, 0, true);
        return this.html;
    };
    /**
     * Sets the read and write permissions of the canvas.
     * @param {boolean} readOnly Determines if the canvas will be set to read only
     * or if it will be editable
     * @chainable
     */
    Canvas.prototype.setReadOnly = function (readOnly) {
        if (readOnly) {
            this.readOnly = readOnly;
        }
        return this;
    };
    /**
     * Sets the canvas to readOnly mode by creating a ReadOnlyLayer instance and appending its html to
     * this html
     * @chainable
     */
    Canvas.prototype.setToReadOnly = function () {
        var readOnlyLayer = this.readOnlyLayer;
        if (readOnlyLayer && readOnlyLayer.html) {
            this.html.appendChild(this.readOnlyLayer.html);
        } else {
            this.readOnlyLayer = new PMUI.draw.ReadOnlyLayer({
                width: this.width,
                height: this.height
            });
            this.html.appendChild(this.readOnlyLayer.html);
        }
        this.readOnly = true;
        return this;
    };
    /**
     * Sets the canvas to read and write mode.
     * @chainable
     */
    Canvas.prototype.unsetReadOnly = function () {
        var readOnlyLayer = this.readOnlyLayer;
        this.html.removeChild(readOnlyLayer.getHTML());
        this.readOnly = false;
        return this;
    };
    /**
     * Sets the position of the canvas.
     * @param {number} x x coordinate relative to where the canvas is contained
     * @param {number} y y coordinate relative to where the canvas is contained
     * @chainable
     */
    Canvas.prototype.setPosition = function (x, y) {
        this.setX(x);
        this.setY(y);
        return this;
    };
    /**
     * Sets the x coordinate of the canvas, its zoomX and absoluteX to an equal value.
     * @param {number} newX new x coordinate to be applied in the canvas
     * @chainable
     */
    Canvas.prototype.setX = function (newX) {
        this.x = this.zoomX = newX;
        this.absoluteX = 0;
        return this;
    };
    /**
     * Set the y coordinate of the canvas, its zoomY and absoluteY to an equal value
     * @param {number} newY new y coordinate to be applied in the canvas
     * @chainable
     */
    Canvas.prototype.setY = function (newY) {
        this.y = this.zoomY = newY;
        this.absoluteY = 0;
        return this;
    };
    /**
     * Retrieves the div element that has this canvas id
     * @return {HTMLElement}
     */
    Canvas.prototype.createHTMLDiv = function () {
        return document.getElementById(this.id);
    };
    /**
     * Default shape factory for creating shapes.
     * @param {String} id
     * @return {PMUI.draw.CustomShape}
     * @template
     */
    Canvas.prototype.shapeFactory = function (id) {
        var customShape = null;
        return customShape;
    };
    /**
     * Identifies the family of the shape (which might be *"CustomShape"* or *"RegularShape"*)
     * and adds `shape` to either `this.customShapes` or `this.regularShapes`.
     * @param {PMUI.draw.Shape} shape
     * @chainable
     */
    Canvas.prototype.addToList = function (shape) {
        switch (shape.family) {
            case "CustomShape":
                if (!this.customShapes.contains(shape)) {
                    this.customShapes.insert(shape);
                }
                break;
            case "RegularShape":
                if (!this.regularShapes.contains(shape)) {
                    this.regularShapes.insert(shape);
                }
                break;
            default:
        }
        return this;
    };
    /**
     * Hides `this.currentConnection` if there is one.
     * @chainable
     */
    Canvas.prototype.hideCurrentConnection = function () {
        // hide the current connection if there was one
        if (this.currentConnection) {
            this.currentConnection.hidePortsAndHandlers();
            this.currentConnection = null;
        }
        return this;
    };
    /**
     * Applies a zoom scale to the canvas and all its elements
     * @param {number} scale numbered from 1 to n
     * @chainable
     */
    Canvas.prototype.applyZoom = function (scale) {
        // TODO Implement Zoom Constants in utils
        var i,
            shape,
            max,
            conn;

        if (scale > 0) {
            scale -= 1;
            this.prevZoom = this.zoomPropertiesIndex;
            this.zoomPropertiesIndex = scale;
            this.zoomFactor = (scale * 25 + 50) / 100;
        }
        for (i = 0; i < this.customShapes.getSize(); i += 1) {
            shape = this.customShapes.get(i);
            shape.applyZoom();
            shape.paint();
        }
        //connection zoom
        this.canvas.refreshArray.clear();
        for (i = 0, max = this.connections.getSize(); i < max; i += 1) {
            conn = this.connections.get(i);
            conn.applyZoom();
            conn.savePoints();
        }
        for (i = 0; i < this.regularShapes.getSize(); i += 1) {
            shape = this.regularShapes.get(i);
            shape.applyZoom();
            shape.paint();
        }
        return this;
    };
    /**
     * Adds a connection to the canvas, appending its html to the DOM and inserting
     * it in the list of connections
     * @param {PMUI.draw.Connection} conn
     * @chainable
     */
    Canvas.prototype.addConnection = function (conn) {
        this.html.appendChild(conn.getHTML());
        this.connections.insert(conn);
        this.updatedElement = conn;
        return this;
    };
    /**
     * Remove all selected elements, it destroy the shapes and all references to them.
     * @chainable
     */
    Canvas.prototype.removeElements = function () {
        // destroy the shapes (also destroy all the references to them)
        var shape,
            command;
        command = new PMUI.command.CommandDelete(this);
        this.commandStack.add(command);
        command.execute();
        return this;
    };
    /**
     * Moves all the connections of the children of this shape (shape was moved using the cursors but the children
     * connections don't know that so move those connections), this method is called from #moveElements.
     * @param {PMUI.draw.Shape} shape
     * @chainable
     */
    Canvas.prototype.moveAllChildConnections = function (shape) {
        var i,
            child,
            j,
            port;
        if (shape.child !== null) {
            for (i = 0; i < shape.children.getSize(); i += 1) {
                child = shape.children.get(i);
                child.setPosition(child.x, child.y);
                for (j = 0; j < child.getPorts().getSize(); j += 1) {
                    port = child.getPorts().get(j);
                    port.setPosition(port.x, port.y);
                    port.connection.disconnect();
                    port.connection.connect();
                    port.connection.savePoints();
                }
                this.moveAllChildConnections(child);
            }
        }
        return this;
    };
    /**
     * Move all selected elements in one direction, used mainly for keyboard events
     * @param {PMUI.draw.Canvas} canvas
     * @param {string} direction The direction to move the shapes to
     * @param {Function} [hook] Hook used to determine which shapes can be moved with the keyboard,
     * the function must receive a shape as its parameter and return true if the shape can be moved, false
     * otherwise (if this function is not defined then it's assumed that all shapes are valid
     * to be moved).
     *      // e.g.
     *      hook = function(shape) {
     *          return shape.isValidToMove();
     *      }
     *
     * @chainable
     */
    Canvas.prototype.moveElements = function (canvas, direction, hook) {
        var i, j,
            shape,
            hfactor = 0,
            vfactor = 0,
            port,
            currentSelection = [],
            canMove = true,
            selection;
        switch (direction) {
            case 'LEFT':
                hfactor = -1;
                break;
            case 'RIGHT':
                hfactor = 1;
                break;
            case 'TOP':
                vfactor = -1;
                break;
            case 'BOTTOM':
                vfactor = 1;
                break;
        }
        selection = canvas.getCurrentSelection();
        if (selection.getSize() > 0) {
            selection.sort(function (shape1, shape2) {
                return shape1.y > shape2.y;
            });
            if (selection.get(0).y - 4 + vfactor < 0) {
                canMove = false;
            }

            selection.sort(function (shape1, shape2) {
                return shape1.x > shape2.x;
            });
            if (selection.get(0).x - 3 + hfactor < 0) {
                canMove = false;
            }

            for (i = 0; i < selection.getSize(); i += 1) {
                shape = selection.get(i);
                currentSelection.push(shape);
                if (hook && typeof hook === "function" && !hook(shape)) {
                    canMove = false;
                }
                if (canMove) {
                    shape.oldX = shape.x;
                    shape.oldY = shape.y;
                    shape.oldAbsoluteX = shape.absoluteX;
                    shape.oldAbsoluteY = shape.absoluteY;

                    shape.setPosition(shape.getX() + hfactor, shape.getY() + vfactor);
                    shape.changePosition(shape.oldX, shape.oldY, shape.oldAbsoluteX,
                        shape.oldAbsoluteY);
                    for (j = 0; j < shape.ports.getSize(); j += 1) {
                        // for each port update its absolute position and repaint its
                        port = shape.ports.get(j);
                        port.setPosition(port.x, port.y);
                        port.connection.disconnect().connect();
                        port.connection.savePoints();
                    }
                    this.moveAllChildConnections(shape);
                }
            }
            clearTimeout(this.intersectionTimeout);
            this.intersectionTimeout = window.setTimeout(function (currentSelection) {
                var stack = [],
                    selection = currentSelection || [];
                for (i = 0; i < selection.length; i += 1) {
                    shape = selection[i];
                    stack.push(shape);
                }
                while (stack.length > 0) {
                    shape = stack.pop();
                    // add the children to the stack
                    for (i = 0; i < shape.getChildren().getSize(); i += 1) {
                        stack.push(shape.getChildren().get(i));
                    }
                    for (j = 0; j < shape.ports.getSize(); j += 1) {
                        // for each port update its absolute position and repaint its
                        port = shape.ports.get(j);
                        port.connection.disconnect().connect();
                        port.connection.setSegmentMoveHandlers();
                        port.connection.checkAndCreateIntersectionsWithAll();
                    }
                }
            }, 1000, currentSelection);
        }
        return this;
    };
    /**
     * Removes `shape` from the its corresponding list in the canvas (the shape has a reference either in
     * `this.customShapes` or `this.regularShapes`).
     * @param {PMUI.draw.Shape} shape
     * @chainable
     */
    Canvas.prototype.removeFromList = function (shape) {
        // remove from the current selection
        this.currentSelection.remove(shape);
        if (shape.family === "CustomShape") {
            this.customShapes.remove(shape);
        } else if (shape.family === "RegularShape") {
            this.regularShapes.remove(shape);
        }
        return this;
    };
    /**
     * Fixes the data of the snappers recreating the arrays and sorting them,
     * this method is called from {@link PMUI.behavior.RegularDragBehavior#onDragStart} (it might
     * be an overrided method `onDragStart` if the instance of {@link PMUI.behavior.RegularDragBehavior} was changed).
     * @chainable
     */
    Canvas.prototype.fixSnapData = function () {
        this.horizontalSnapper.createSnapData();
        this.verticalSnapper.createSnapData();
        this.horizontalSnapper.sortData();
        this.verticalSnapper.sortData();
        return this;
    };
    /**
     * Shows or hides the snappers according to this criteria:
     *
     * - To show the vertical snapper
     *      - `shape.absoluteX` must equal a value in the data of `this.verticalSnapper`
     *      - `shape.absoluteX + shape.width` must equal a value in the data of `this.verticalSnapper`
     *
     * - To show the horizontal snapper
     *      - `shape.absoluteY` must equal a value in the data of `this.horizontalSnapper`
     *      - `shape.absoluteY + shape.height` must equal a value in the data of `this.horizontalSnapper`
     *
     * @param {PMUI.draw.Shape} shape
     * @chainable
     */
    Canvas.prototype.showOrHideSnappers = function (shape) {
        var hSnapper = this.horizontalSnapper,
            vSnapper = this.verticalSnapper,
            x = shape.getAbsoluteX(),
            y = shape.getAbsoluteY(),
            width = shape.getZoomWidth(),
            height = shape.getZoomHeight();
        if (hSnapper.binarySearch(y)) {
            hSnapper.setPosition(
                this.getLeftScroll() / this.zoomFactor,
                y / this.zoomFactor
            );
            hSnapper.show();
        } else if (hSnapper.binarySearch(y + height)) {
            hSnapper.setPosition(
                this.getLeftScroll() / this.zoomFactor,
                (y + height) / this.zoomFactor
            );
            hSnapper.show();
        } else {
            hSnapper.hide();
        }
        if (vSnapper.binarySearch(x)) {
            vSnapper.setPosition(
                x / this.zoomFactor - this.absoluteX,
                this.getTopScroll() / this.zoomFactor
            );
            vSnapper.show();
        } else if (vSnapper.binarySearch(x + width)) {
            vSnapper.setPosition(
                (x + width) / this.zoomFactor,
                this.getTopScroll() / this.zoomFactor
            );
            vSnapper.show();
        } else {
            vSnapper.hide();
        }
        return this;
    };
    /**
     * Empties `this.currentSelection` arrayList, thus hiding the resize handlers
     * of each shape that was in it, it also clears `this.sharedConnections` array
     * (there's a direct relationship between them).
     * @chainable
     */
    Canvas.prototype.emptyCurrentSelection = function () {
        var i,
            shape;
        while (this.currentSelection.getSize() > 0) {
            shape = this.currentSelection.get(0);
            this.removeFromSelection(shape);
        }
        // also clear the sharedConnections
        this.sharedConnections.clear();
        return this;
    };
    /**
     * Determines if it's possible to select `newShape` using `referenceShape` as a reference (`newShape` is a valid
     * shape to be added to the selection if it has the same parent as `referenceShape`).
     * @param {PMUI.draw.Shape} referenceShape shape which parent will be taken as reference
     * @param {PMUI.draw.Shape} newShape new selected shape
     * @return {boolean}
     */
    Canvas.prototype.isValidSelection = function (referenceShape, newShape) {
        if (referenceShape.parent === null) {
            return newShape.parent === null;
        }
        if (newShape.parent === null) {
            return false;
        }
        return newShape.parent.id === referenceShape.parent.id;
    };
    /**
     * Adds `shape` to `this.currentSelection` if it meets one of the following rules:
     *
     * - If `this.currentSelection` is empty then add it to the arrayList
     * - If `this.currentSelection` is not empty then check if this candidate shape
     *      has the same parent as any element in `this.currentSelection`, if so then add it to
     *      the arrayList.
     *
     * This method also shows the resize handlers of the shape and adds its connections
     * to `this.sharedConnections` if possible.
     * @param {PMUI.draw.Shape} shape
     * @chainable
     */
    Canvas.prototype.addToSelection = function (shape) {
        var currentSelection = this.currentSelection,
            firstSelected,
            valid,
            isEmpty = currentSelection.isEmpty();
        if (!isEmpty) {
            firstSelected = currentSelection.get(0);
            valid = this.isValidSelection(firstSelected, shape);
        } else {
            valid = true;
        }
        if (!currentSelection.contains(shape) && valid) {
            // increase this shape zIndex
            shape.increaseZIndex();
            currentSelection.insert(shape);
            // add the connections from this shape that are connected
            // to another shape in the currentSelection to the
            // canvas sharedConnections array
            // NOTE: the shape is passed as an argument but its
            // connections are stored
            if (shape.family === "CustomShape") {
                this.addToSharedConnections(shape);
            }
            shape.selected = true;
            shape.showOrHideResizeHandlers(true);
        }
        return this;
    };
    /**
     * Removes `shape` from `this.currentSelection` (also hiding its resize handlers).
     * @param {PMUI.draw.Shape} shape
     * @chainable
     */
    Canvas.prototype.removeFromSelection = function (shape) {
        shape.decreaseZIndex();
        this.removeFromSharedConnections(shape);
        this.currentSelection.remove(shape);
        shape.selected = false;
        shape.showOrHideResizeHandlers(false);
        return this;
    };
    /**
     * Removes all the shared connections between `customShape` and every shape
     * found in `this.currentSelection`, also the connections inside `customShape` are removed from
     * `this.sharedConnections` array.
     * @param {PMUI.draw.CustomShape} customShape
     * @chainable
     */
    Canvas.prototype.removeFromSharedConnections = function (customShape) {
        var i,
            child,
            connection,
            sharedConnections = this.sharedConnections;
        for (i = 0; i < customShape.getChildren().getSize(); i += 1) {
            child = customShape.getChildren().get(i);
            this.removeFromSharedConnections(child);
        }
        if (customShape.ports) {
            for (i = 0; i < customShape.ports.getSize(); i += 1) {
                connection = customShape.ports.get(i).connection;
                if (sharedConnections.find('id', connection.getID())) {
                    this.sharedConnections.remove(connection);
                }
            }
        }
        return this;
    };
    /**
     * Checks if an ancestor of `shape` is in `this.currentSelection`.
     * @return {boolean}
     */
    Canvas.prototype.findAncestorInCurrentSelection = function (shape) {
        if (this.currentSelection.find('id', shape.getID())) {
            return true;
        }
        if (!shape.parent) {
            return false;
        }
        return this.findAncestorInCurrentSelection(shape.parent);
    };
    /**
     * Adds all the connections between `customShape` and another shape in the
     * currentSelection to the `sharedConnections` arrayList, also the connections inside
     * `customShape` are added to `this.sharedConnections` array.
     * @param {PMUI.draw.CustomShape} customShape
     * @chainable
     */
    Canvas.prototype.addToSharedConnections = function (customShape) {
        var i,
            child,
            connection,
            sourceShape,
            destShape,
            sharedConnections = this.sharedConnections;
        for (i = 0; i < customShape.getChildren().getSize(); i += 1) {
            child = customShape.getChildren().get(i);
            this.addToSharedConnections(child);
        }
        if (customShape.ports) {
            for (i = 0; i < customShape.ports.getSize(); i += 1) {
                connection = customShape.ports.get(i).connection;
                sourceShape = connection.srcPort.parent;
                destShape = connection.destPort.parent;
                if (this.findAncestorInCurrentSelection(sourceShape) &&
                    this.findAncestorInCurrentSelection(destShape) && !sharedConnections.find('id', connection.getID())) {
                    sharedConnections.insert(connection);
                }
            }
        }
        return this;
    };
    /**
     * Removes a connection from `this.connections`.
     * @param {PMUI.draw.Connection} conn
     * @chainable
     */
    Canvas.prototype.removeConnection = function (conn) {
        //this.currentSelection.remove(conn);
        this.connections.remove(conn);
        return this;
    };
    /**
     * Attaches event listeners to this canvas, it also creates some custom triggers
     * used to save the data (to send it to the database later).
     *
     * The events attached to this canvas are:
     *
     * - {@link PMUI.draw.Canvas#event-mousedown Mouse down event}
     * - {@link PMUI.draw.Canvas#event-mousemove Mouse move event}
     * - {@link PMUI.draw.Canvas#event-mouseup Mouse up event}
     * - {@link PMUI.draw.Canvas#event-click Click event}
     * - {@link PMUI.draw.Canvas#event-scroll Scroll event}
     *
     * The custom events are:
     *
     * - {@link PMUI.draw.Canvas#event-createelement Create element event}
     * - {@link PMUI.draw.Canvas#event-removeelement Remove element event}
     * - {@link PMUI.draw.Canvas#event-changeelement Change element event}
     * - {@link PMUI.draw.Canvas#event-selectelement Select element event}
     * - {@link PMUI.draw.Canvas#event-rightclick Right click event}
     *
     * This method also initializes jQueryUI's droppable plugin (instantiated as `this.dropBehavior`)
     * @chainable
     */
    Canvas.prototype.attachListeners = function () {
        var $canvas = $(this.html).click(this.onClick(this)),
            $canvasContainer = $canvas.parent();
        $canvas.mousedown(this.onMouseDown(this));
        $canvasContainer.scroll(this.onScroll(this, $canvasContainer));
        $canvas.mousemove(this.onMouseMove(this));
        $canvas.mouseup(this.onMouseUp(this));
        $canvas.on("createelement", this.onCreateElement(this));
        $canvas.on("removeelement", this.onRemoveElement(this));
        $canvas.on("changeelement", this.onChangeElement(this));
        $canvas.on("selectelement", this.onSelectElement(this));
        $canvas.on("rightclick", this.onRightClick(this));
        $canvas.on("contextmenu", function (e) {
            e.preventDefault();
        });
        this.updateBehaviors();
        return this;
    };
    /**
     * This is a hook that will be executed after an element has been created in
     * the canvas.
     * This hook will be executed every time a shape, a connection, or an
     * independent label is created.
     * @param {Object} updatedElement
     * @param {string} [updatedElement.id] ID of the updated element
     * @param {string} [updatedElement.type] Type of the updated element
     * @param {PMUI.draw.Shape} [updatedElement.relatedObject] The updated element
     * @param {Array} [updatedElement.relatedElements] An array with all the other elements created
     *  e.g. When executing {@link PMUI.command.CommandDelete#undo CommandDelete.undo()}, multiple elements are created
     *  at once, so this property will contain all those shapes.
     * @template
     * @protected
     */
    Canvas.prototype.onCreateElementHandler = function (updatedElement) {
    };
    /**
     * @event createelement
     * Handler for the custom event createelement, this event fires when an element
     * has been created. It executes the hook #onCreateElementHandler
     * @param {PMUI.draw.Canvas} canvas
     */
    Canvas.prototype.onCreateElement = function (canvas) {
        return function (e, ui) {
            canvas.onCreateElementHandler(canvas.updatedElement);
        };
    };
    /**
     * This is a hook that will be executed after an element has been deleted in
     * the canvas.
     * This hook will be executed every time a shape, a connection, or an
     * independent label is deleted
     * @param {Object} updatedElement
     * @param {string} [updatedElement.id] ID of the removed element
     * @param {string} [updatedElement.type] Type of the removed element
     * @param {PMUI.draw.Shape} [updatedElement.relatedObject] The removed element
     * @param {Array} [updatedElement.relatedElements] An array with all the other elements removed
     *  e.g. When executing {@link PMUI.command.CommandDelete#execute PMUI.command.CommandDelete.execute()},
     multiple elements are created
     *  at once, so this property will contain all those shapes.
     * @template
     * @protected
     */
    Canvas.prototype.onRemoveElementHandler = function (updatedElement) {
        return true;
    };
    /**
     * @event removeelement
     * Handler for the custom event removeelement, this event fires when an element
     * has been deleted. It executes the hook #onRemoveElementHandler
     * @param {PMUI.draw.Canvas} canvas
     */
    Canvas.prototype.onRemoveElement = function (canvas) {
        return function (e, ui) {
            canvas.onRemoveElementHandler(canvas.updatedElement.relatedElements);
        };
    };
    /**
     * This is a hook that will be executed after an element has been changed in
     * the canvas.
     * This hook will be executed every time a shape, a connection, or an
     * independent label is changed.
     * `arguments[0]` is an array with all the elements that were updated,
     * the structure of each element of the array is described below:
     *
     *      {
     *          id: #,      // the id of the updated element
     *          type: #     // the type of the updated element
     *          fields: [
     *              {
     *                  field: #        // the field that was updated in this element
     *                  oldVal: #       // the old value of this shape
     *                  newVal: #       // the new value of this shape
     *              },
     *              ...
     *          ]
     *      }
     *
     * @param {Array} updatedElements Array with all the elements that were updated.
     * @template
     * @protected
     */
    Canvas.prototype.onChangeElementHandler = function (updatedElements) {
    };
    /**
     * @event changeelement
     * Handler for the custom event changeeelement, this event fires when an element
     * has been changed. It executes the hook #onChangeElementHandler
     * @param {PMUI.draw.Canvas} canvas
     */
    Canvas.prototype.onChangeElement = function (canvas) {
        return function (e, ui) {
            canvas.onChangeElementHandler(canvas.updatedElement);
        };
    };
    /**
     * This is a hook that will be executed after an element has been selected in
     * the canvas.
     * This hook will be executed every time a shape, a connection, or an
     * independent label is selected
     * `arguments[0]` is an array with all the elements that were selected,
     * the structure of each element of the array is described below:
     *
     *      {
     *          id: #,              // the id of the selected element
     *          type: #             // the type of the selected element
     *          relatedObject       // the selected element
     *      }
     * @param {Array} updatedElements Array with the selected elements
     * @protected
     * @template
     */
    Canvas.prototype.onSelectElementHandler = function (updatedElements) {
    };
    /**
     * @event selectelement
     * Handler for the custom event selectelement, this event fires when an element
     * has been selected. It executes the hook #onSelectElementHandler
     * @param {PMUI.draw.Canvas} canvas
     */
    Canvas.prototype.onSelectElement = function (canvas) {
        return function (e, ui) {
            canvas.onSelectElementHandler(canvas.updatedElement);
        };
    };
    /**
     * This is a hook that will be executed after an element has been right clicked
     * in the canvas or the canvas's been right clicked itself.
     * This hook will be executed every time a shape, a connection, an
     * independent label or the canvas is right clicked
     * @param {Object} updatedElement Reference to the last element that was
     * right clicked in the canvas
     * @param {Object} points x coordinate where the mouse was pressed
     * @template
     * @protected
     */
    Canvas.prototype.onRightClickHandler = function (updatedElement, points) {
    };
    /**
     * @event rightclick
     * Handler for the custom event rightclick, this event fires when an element
     * has been right clicked. It executes the hook #onRightClickHandler
     * @param {PMUI.draw.Canvas} canvas
     */
    Canvas.prototype.onRightClick = function (canvas) {
        return function (event, e, element) {
            var realPoint = canvas.relativePoint(e);
            canvas.updatedElement = element;
            canvas.onRightClickHandler(canvas.updatedElement, {
                canvas: realPoint,
                page: {
                    x: e.pageX,
                    y: e.pageY
                }
            });
        };
    };
    /**
     * @event click
     * Click event handler, which makes `this.currentLabel` lose its focus.
     * @param {PMUI.draw.Canvas} canvas
     */
    Canvas.prototype.onClick = function (canvas) {
        return function (e, ui) {
            var currentLabel = canvas.currentLabel,
                figure,
                realPoint,
                realPoint,
                oldConnection;

            if (currentLabel) {
                currentLabel.loseFocus();
                $(currentLabel.textField).focusout();
            }
            realPoint = canvas.relativePoint(e);
            figure = canvas.getBestConnecion(realPoint);

            if (figure !== null) {
                oldConnection = canvas.currentConnection;
                canvas.emptyCurrentSelection();
                if (oldConnection) {
                    oldConnection.hidePortsAndHandlers();
                }
                figure.showPortsAndHandlers();
                canvas.currentConnection = figure;
            }
        };
    };

    Canvas.prototype.getBestConnecion = function (point, lineToIgnore) {
        var count = this.getConnections().asArray().length,
            i,
            line;
        for (i = 0; i < count; i += 1) {
            line = this.getConnections().get(i);
            if (line.hitTest(point)) {
                return line;
            }
        }
        return null;
    }
    /**
     * @event mousedown
     * MouseDown Handler of the canvas. It does the following:
     *
     * - Trigger the {@link PMUI.draw.Canvas#event-rightclick Right Click event} if it detects a right click
     * - Empties `canvas.currentSelection`
     * - Hides `canvas.currentConnection` if there's one
     * - Resets the position of `canvas.multipleSelectionContainer` making it visible and setting its
     *      `[x, y]` to the point where the user did mouse down in the `canvas`.
     *
     * @param {PMUI.draw.Canvas} canvas
     */
    Canvas.prototype.onMouseDown = function (canvas) {
        return function (e, ui) {
            var x = e.pageX - canvas.getX() + canvas.getLeftScroll() - canvas.getAbsoluteX(),
                y = e.pageY - canvas.getY() + canvas.getTopScroll() - canvas.getAbsoluteY();

            e.preventDefault();
            if (e.which === 3) {
                canvas.rightClick = true;
                $(canvas.html).trigger("rightclick", [e, canvas]);
            }
            canvas.isMouseDown = true;
            canvas.isMouseDownAndMove = false;
            // do not create the rectangle selection if a segment handler
            // is being dragged
            if (canvas.draggingASegmentHandler) {
                return;
            }
            // clear old selection
            canvas.emptyCurrentSelection();
            // hide the currentConnection if there's one
            canvas.hideCurrentConnection();
            canvas.multipleSelectionHelper.reset();
            canvas.multipleSelectionHelper.setPosition(x / canvas.zoomFactor,
                y / canvas.zoomFactor);
            canvas.multipleSelectionHelper.oldX = x;
            canvas.multipleSelectionHelper.oldY = y;
            canvas.multipleSelectionHelper.setVisible(true);
            canvas.multipleSelectionHelper.changeOpacity(0.2);
        };
    };
    /**
     * @event mousemove
     * MouseMove handler of the canvas, it does the following:
     *
     * - Updates the position and dimension of `canvas.multipleSelectionContainer`
     *
     * @param {PMUI.draw.Canvas} canvas
     */
    Canvas.prototype.onMouseMove = function (canvas) {
        return function (e, ui) {
            if (canvas.isMouseDown && !canvas.rightClick) {
                canvas.isMouseDownAndMove = true;
                var x = e.pageX - canvas.getX() + canvas.getLeftScroll() - canvas.getAbsoluteX(),
                    y = e.pageY - canvas.getY() + canvas.getTopScroll() - canvas.getAbsoluteY(),
                    topLeftX,
                    topLeftY,
                    bottomRightX,
                    bottomRightY;
                topLeftX = Math.min(x, canvas.multipleSelectionHelper.oldX);
                topLeftY = Math.min(y, canvas.multipleSelectionHelper.oldY);
                bottomRightX = Math.max(x, canvas.multipleSelectionHelper.oldX);
                bottomRightY = Math.max(y, canvas.multipleSelectionHelper.oldY);
                canvas.multipleSelectionHelper.setPosition(
                    topLeftX / canvas.zoomFactor,
                    topLeftY / canvas.zoomFactor
                );
                canvas.multipleSelectionHelper.setDimension(
                    (bottomRightX - topLeftX) / canvas.zoomFactor,
                    (bottomRightY - topLeftY) / canvas.zoomFactor
                );
            }
        };
    };
    /**
     * @event mouseup
     * MouseUp handler of the canvas. It does the following:
     *
     * - Wraps the elements that are inside `canvas.multipleSelectionContainer`
     * - Resets the state of `canvas.multipleSelectionContainer`
     (see {@link PMUI.draw.MultipleSelectionContainer#reset})
     *
     * @param {PMUI.draw.Canvas} canvas
     */
    Canvas.prototype.onMouseUp = function (canvas) {
        return function (e, ui) {
            var realPoint,
                x,
                y;

            if (canvas.isMouseDownAndMove) {
                realPoint = canvas.relativePoint(e);
                x = realPoint.x;
                y = realPoint.y;
                canvas.multipleSelectionHelper.setPosition(
                    Math.min(x, canvas.multipleSelectionHelper.zoomX) / canvas.zoomFactor,
                    Math.min(y, canvas.multipleSelectionHelper.zoomY) / canvas.zoomFactor
                );
                if (canvas.multipleSelectionHelper) {
                    canvas.multipleSelectionHelper.wrapElements();
                }
            } else {
                if (!canvas.multipleSelectionHelper.wasDragged) {
                    canvas.multipleSelectionHelper.reset().setVisible(false);
                }
                if (canvas.isMouseDown) {
                    canvas.onClickHandler(canvas, x, y);
                }
            }
            canvas.isMouseDown = false;
            canvas.isMouseDownAndMove = false;
            canvas.rightClick = false;
        };
    };
    /**
     * click handler
     * MouseClick handler of the canvas. It does the following:
     *
     * @param {Canvas} canvas
     * @param {Number} x
     * @param {Number} y
     */
    Canvas.prototype.onClickHandler = function () {
    };
    /**
     * @event scroll
     * Handler for scrolling, sets the scroll values to the canvas
     * @param {PMUI.draw.Canvas} canvas
     * @param {Object} $canvasContainer jQuery element that is the container of the `canvas`
     */
    Canvas.prototype.onScroll = function (canvas, $canvasContainer) {
        return function (e, ui) {
            canvas.setLeftScroll($canvasContainer.scrollLeft())
                .setTopScroll($canvasContainer.scrollTop());
        };
    };
    /**
     * Fires the {@link PMUI.draw.Canvas#event-selectelement} event,
     and elaborates the structure of the object that will
     * be passed to the handlers.
     * @param {Array} selection The `currentSelection` ArrayList of some canvas
     * @chainable
     */
    Canvas.prototype.triggerSelectEvent = function (selection) {
        var i,
            elements = [],
            current;
        for (i = 0; i < selection.length; i += 1) {
            current = selection[i];
            elements.push({
                id: current.id,
                type: current.type,
                relatedObject: current
            });
        }
        this.updatedElement = elements;
        $(this.html).trigger('selectelement');
        return this;
    };
    /**
     * Fires the {@link PMUI.draw.Canvas#event-rightclick} event and elaborates the structure
     * of the object that will be passed to the event.
     * @param {PMUI.draw.CustomShape} element The object that's been right clicked on.
     * @chainable
     */
    Canvas.prototype.triggerRightClickEvent = function (element) {
        this.updatedElement = {
            id: element.id,
            type: element.type,
            relatedObject: element
        };
        $(this.html).trigger('rightclick');
        return this;
    };
    /**
     * Fires the {@link PMUI.draw.Canvas#event-createelement}
     event, and elaborates the structure of the object that will
     * be passed to the handlers.
     * @param {Object} shape The shape created
     * @param {Array} relatedElements The array with the other elements created
     * @chainable
     */
    Canvas.prototype.triggerCreateEvent = function (shape, relatedElements) {
        this.updatedElement = {
            id: (shape && shape.id) || null,
            type: (shape && shape.type) || null,
            relatedObject: shape,
            relatedElements: relatedElements
        };
        if (!this.items.find("id", shape.id)) { 
            $(this.html).trigger('createelement'); 
        }
        return this;
    };
    /**
     * Fires the {@link PMUI.draw.Canvas#event-removeelement} event,
     and elaborates the structure of the object that will
     * be passed to the handlers.
     * @param {PMUI.draw.CustomShape} shape The shape created
     * @param {Array} relatedElements The array with the other elements created
     * @chainable
     */
    Canvas.prototype.triggerRemoveEvent = function (shape, relatedElements) {
        this.updatedElement = {
            id: (shape && shape.id) || null,
            type: (shape && shape.type) || null,
            relatedObject: shape,
            relatedElements: relatedElements
        };
        $(this.html).trigger('removeelement');
        return this;
    };

    /**
     * Fires the {@link PMUI.draw.Canvas#event-changeelement} event,
     and elaborates the structure of the object that will
     * be passed to the handlers, the structure contains the following
     fields (considering old values and new values):
     *
     * - width
     * - height
     *
     * @param {PMUI.draw.CustomShape} shape The shape that updated its dimension
     * @param {number} oldWidth The old width of `shape`
     * @param {number} oldHeight The old height of `shape`
     * @param {number} newWidth The new width of `shape`
     * @param {number} newHeight The old height of `shape`
     * @chainable
     */
    Canvas.prototype.triggerDimensionChangeEvent = function (shape, oldWidth,
                                                             oldHeight, newWidth, newHeight) {
        this.updatedElement = [{
            id: shape.id,
            type: shape.type,
            fields: [
                {
                    field: "width",
                    oldVal: oldWidth,
                    newVal: newWidth
                },
                {
                    field: "height",
                    oldVal: oldHeight,
                    newVal: newHeight
                }
            ],
            relatedObject: shape
        }];
        $(this.html).trigger('changeelement');
        return this;
    };

    /**
     * Fires the {@link PMUI.draw.Canvas#event-changeelement} event,
     and elaborates the structure of the object that will
     * be passed to the handlers, the structure contains the following fields
     (considering old values and new values):
     *
     * - x
     * - y
     * - parent (the shape that is parent of this shape)
     * - state (of the connection)
     *
     * @param {PMUI.draw.Port} port The port updated
     * @chainable
     */
    Canvas.prototype.triggerPortChangeEvent = function (port) {
        this.updatedElement = [{
            id: port.getID(),
            type: port.type,
            fields: [
                {
                    field: 'x',
                    oldVal: port.getOldX(),
                    newVal: port.getX()
                },
                {
                    field: 'y',
                    oldVal: port.getOldY(),
                    newVal: port.getY()
                },
                {
                    field: 'parent',
                    oldVal: port.getOldParent().getID(),
                    newVal: port.getParent().getID()
                },
                {
                    field: 'state',
                    oldVal: port.connection.getOldPoints(),
                    newVal: port.connection.savePoints() &&
                    port.connection.getPoints()
                }
            ],
            relatedObject: port
        }];

        $(this.html).trigger('changeelement');
        return this;
    };

    /**
     * Fires the {@link PMUI.draw.Canvas#event-changeelement} event,
     and elaborates the structure of the object that will
     * be passed to the handlers, the structure contains the following
     fields (considering old values and new values):
     *
     * - state (of the connection)
     *
     * @param {PMUI.draw.Connection} connection The connection updated
     * @chainable
     */
    Canvas.prototype.triggerConnectionStateChangeEvent = function (connection) {
        var points = [],
            Point = PMUI.util.Point,
            point,
            i;
        connection.savePoints();
        for (i = 0; i < connection.points.length; i += 1) {
            point = connection.points[i];
            points.push(new Point(point.x / this.zoomFactor, point.y / this.zoomFactor));
        }
        this.updatedElement = [{
            id: connection.getID(),
            type: connection.type,
            fields: [
                {
                    field: 'state',
                    oldVal: connection.getOldPoints(),
                    newVal: points
                }
            ],
            relatedObject: connection
        }];
        return this;
    };

    /**
     * Fires the {@link PMUI.draw.Canvas#event-changeelement} event,
     and elaborates the structure of the object that will
     * be passed to the handlers, the structure contains the following
     fields (considering old values and new values):
     *
     * - x
     * - y
     *
     * @param {Array} shapes The shapes that were updated
     * @param {Array} before The state of the shapes before they were repositioned
     * @param {Array} after The state of the shapes after they were repositioned
     * @chainable
     */
    Canvas.prototype.triggerPositionChangeEvent = function (shapes, before, after) {
        var i,
            elements = [];
        for (i = 0; i < shapes.length; i += 1) {
            elements.push({
                id: shapes[i].getID(),
                type: shapes[i].type,
                fields: [
                    {
                        field: "x",
                        oldVal: before[i].x,
                        newVal: after[i].x
                    },
                    {
                        field: "y",
                        oldVal: before[i].y,
                        newVal: after[i].y
                    }
                ],
                relatedObject: shapes[i]
            });
        }
        this.updatedElement = elements;
        $(this.html).trigger('changeelement');
        return this;
    };
    /**
     * Fires the {@link PMUI.draw.Canvas#event-changeelement} event,
     and elaborates the structure of the object that will
     * be passed to the handlers, the structure contains the following
     fields (considering old values and new values):
     *
     * - message
     *
     * @param {PMUI.draw.CustomShape} element The shape that updated one of ots labels
     * @param {string} oldText The old text of the label
     * @param {string} newText The new text of the label
     * @chainable
     */
    Canvas.prototype.triggerTextChangeEvent = function (element, oldText, newText) {
        this.updatedElement = [{
            id: element.id,
            type: element.type,
            parent: element.parent,
            fields: [
                {
                    field: "message",
                    oldVal: oldText,
                    newVal: newText
                }
            ],
            relatedObject: element
        }];
        $(this.html).trigger('changeelement');
        return this;
    };
    /**
     * Fires the {@link PMUI.draw.Canvas#event-changeelement} event,
     and elaborates the structure of the object that will
     * be passed to the handlers, the structure contains the following
     fields (considering old values and new values):
     *
     * - parent
     * - x
     * - y
     *
     * @param {Array} shapes The shapes that were updated
     * @param {Array} before The state of the shapes before they were repositioned
     * @param {Array} after The state of the shapes after they were repositioned
     * @chainable
     */
    Canvas.prototype.triggerParentChangeEvent = function (shapes, before, after) {

        var i,
            elements = [];
        for (i = 0; i < shapes.length; i += 1) {
            elements.push({
                id: shapes[i].getID(),
                type: shapes[i].type,
                fields: [
                    {
                        field: "parent",
                        oldParent: before[i].parent,
                        newVal: after[i].parent

                    },
                    {
                        field: "x",
                        oldVal: before[i].x,
                        newVal: after[i].x
                    },
                    {
                        field: "y",
                        oldVal: before[i].y,
                        newVal: after[i].y
                    }
                ],
                relatedObject: shapes[i]
            });
        }

        this.updatedElement = elements;
        $(this.html).trigger('changeelement');
        return this;
    };

    /**
     * Sets the top scroll of this canvas.
     * @param {number} newScroll
     * @chainable
     */
    Canvas.prototype.setTopScroll = function (newScroll) {
        this.topScroll = newScroll;
        return this;
    };
    /**
     * Sets the left scroll of this canvas.
     * @param {number} newScroll
     * @chainable
     */
    Canvas.prototype.setLeftScroll = function (newScroll) {
        this.leftScroll = newScroll;
        return this;
    };
    /**
     * Sets the zoom Factor applied in the canvas
     * @param {number} newZoom
     * @chainable
     */
    Canvas.prototype.setZoomFactor = function (newZoom) {
        if (typeof newZoom === "number" && newZoom % 25 === 0 && newZoom > 0) {
            this.zoomFactor = newZoom;
        }
        return this;
    };
    /**
     * Sets the currentConnection of this canvas.
     * @param {PMUI.draw.Connection} newConnection
     * @chainable
     */
    Canvas.prototype.setCurrentConnection = function (newConnection) {
        if (newConnection.type === "Connection") {
            this.currentConnection = newConnection;
        }
        return this;
    };
    /**
     * Assigns `newFunction` as `Canvas.prototype.toolbarShapeFactory` so that
     * the canvas has a reference to the shapes that will be created when they
     * are dragged from the toolbar.
     * @param {Function} newFunction
     * @chainable
     */
    Canvas.prototype.setShapeFactory = function (newFunction) {
        Canvas.prototype.shapeFactory = newFunction;
        return this;
    };
    /**
     * Gets the current zoom factor applied in the canvas
     * @return {number}
     */
    Canvas.prototype.getZoomFactor = function () {
        return this.zoomFactor;
    };
    /**
     * Gets the index where the zoom properties are located for the current
     * zoom factor.
     * @return {number}
     */
    Canvas.prototype.getZoomPropertiesIndex = function () {
        return this.zoomPropertiesIndex;
    };
    /**
     * Gets the segment used to make connections in the canvas.
     * @return {PMUI.draw.Segment}
     */
    Canvas.prototype.getConnectionSegment = function () {
        return this.connectionSegment;
    };
    /**
     * Gets the left scroll position of the canvas.
     * @return {number}
     */
    Canvas.prototype.getLeftScroll = function () {
        return this.leftScroll;
    };
    /**
     * Gets the top scroll position of the canvas.
     * @return {number}
     */
    Canvas.prototype.getTopScroll = function () {
        return this.topScroll;
    };
    /**
     * Gets the current connection stored in this canvas.
     * @return {PMUI.draw.Connection}
     */
    Canvas.prototype.getCurrentConnection = function () {
        return this.currentConnection;
    };
    /**
     * Gets the current selection of this canvas.
     * @return {PMUI.util.ArrayList}
     */
    Canvas.prototype.getCurrentSelection = function () {
        return this.currentSelection;
    };
    /**
     * Gets all the connections of this canvas.
     * @return {PMUI.util.ArrayList}
     */
    Canvas.prototype.getConnections = function () {
        return this.connections;
    };
    /**
     * Gets all the shared connections stored in this canvas.
     * @return {PMUI.util.ArrayList}
     */
    Canvas.prototype.getSharedConnections = function () {
        return this.sharedConnections;
    };
    /**
     * Gets all the custom shapes of the canvas.
     * @return {PMUI.util.ArrayList}
     */
    Canvas.prototype.getCustomShapes = function () {
        return this.customShapes;
    };
    /**
     * Gets all the regular shapes of the canvas.
     * @return {PMUI.util.ArrayList}
     */
    Canvas.prototype.getRegularShapes = function () {
        return this.regularShapes;
    };
    /**
     * Gets the multiple selection container instance.
     * @return {PMUI.draw.MultipleSelectionContainer}
     */
    Canvas.prototype.getMultipleSelectionHelper = function () {
        return this.multipleSelectionHelper;
    };
    /**
     * Gets the horizontal snapper of this canvas.
     * @return {PMUI.draw.Snapper}
     */
    Canvas.prototype.getHorizontalSnapper = function () {
        return this.horizontalSnapper;
    };
    /**
     * Gets the vertical snapper of this canvas.
     * @return {PMUI.draw.Snapper}
     */
    Canvas.prototype.getVerticalSnapper = function () {
        return this.verticalSnapper;
    };
    /**
     * Gets the last updated element in the canvas
     * @return {Mixed}
     */
    Canvas.prototype.getUpdatedElement = function () {
        return this.updatedElement;
    };
    /**
     * Any instance of the class Canvas is not resizable so this method
     * will always return false.
     * @return {boolean}
     */
    Canvas.prototype.isResizable = function () {
        return false;
    };
    /**
     * Gets a reference to itself.
     * @return {PMUI.draw.Canvas}
     */
    Canvas.prototype.getCanvas = function () {
        return this;
    };
    /**
     * Undoes the last action in the canvas by calling `this.commandStack.undo`.
     * @chainable
     */
    Canvas.prototype.undo = function () {
        this.commandStack.undo();
        return this;
    };
    /**
     * Redoes the last action in the canvas by calling `this.commandStack.redo`.
     * @chainable
     */
    Canvas.prototype.redo = function () {
        this.commandStack.redo();
        return this;
    };
    /**
     * Serializes this canvas by serializing its customShapes, regularShapes and connections.
     * @return {Object}
     * @return {Object} return.customShapes See {@link PMUI.draw.CustomShape#stringify}
     * @return {Object} return.regularShapes See {@link PMUI.draw.Shape#stringify}
     * @return {Object} return.connections See {@link PMUI.draw.Connection#stringify}
     */
    Canvas.prototype.stringify = function () {
        var i,
            customShapes = [],
            regularShapes = [],
            connections = [],
            inheritedJSON,
            thisJSON,
            Shape;
        // serialize custom shapes
        for (i = 0; i < this.customShapes.getSize(); i += 1) {
            customShapes.push(this.customShapes.get(i).stringify());
        }
        // serialize regular shapes
        for (i = 0; i < this.regularShapes.getSize(); i += 1) {
            regularShapes.push(this.regularShapes.get(i).stringify());
        }
        // serialize connections shapes
        for (i = 0; i < this.connections.getSize(); i += 1) {
            connections.push(this.connections.get(i).stringify());
        }
        inheritedJSON = Shape.prototype.stringify.call(this);
        thisJSON = {
            customShapes: customShapes,
            regularShapes: regularShapes,
            connections: connections
        };
        $.extend(true, inheritedJSON, thisJSON);
        return inheritedJSON;
    };
    /**
     * Adds shape and its children to `this.shapesToCopy` array so that later
     * they can be pasted in the canvas.
     * @param {PMUI.draw.Shape} shape
     * @chainable
     */
    Canvas.prototype.addToShapesToCopy = function (shape) {
        var i,
            child,
            shapeCustomized = shape.stringify();
        shapeCustomized.extendedType = shape.extendedType;
        this.shapesToCopy.push(shapeCustomized);
        for (i = 0; i < shape.getChildren().getSize(); i += 1) {
            child = shape.getChildren().get(i);
            this.addToShapesToCopy(child);
        }
        return this;
    };

    /**
     * Duplicates the `this.sharedConnection` array and the shapes stored in `this.currentSelection`
     * array (saving them in `this.shapesToCopy` and `this.connectionsToCopy` respectively) so
     * that they can be pasted later in the canvas.
     * @chainable
     */
    Canvas.prototype.copy = function () {
        var i,
            shape,
            connection;
        // duplicate shapes
        this.shapesToCopy = [];
        for (i = 0; i < this.getCurrentSelection().getSize(); i += 1) {
            shape = this.getCurrentSelection().get(i);
            this.addToShapesToCopy(shape);
        }
        // duplicate connections
        this.connectionsToCopy = [];
        for (i = 0; i < this.getSharedConnections().getSize(); i += 1) {
            connection = this.getSharedConnections().get(i);
            this.connectionsToCopy.push(connection.stringify());
        }
        return this;
    };
    /**
     * Pastes the shapes saved in `this.shapesToCopy` and the connections saved in `this.connectionsToCopy`
     * by calling the #parse method.
     *
     * Currently the parser is called with these arguments:
     *
     *       {
     *          shapes: this.shapesToCopy,
     *          connections: this.connectionsToCopy,
     *          createCommand: true,
     *          uniqueID: true,
     *          selectAfterFinish: true,
     *          prependMessage: "Copy of ",
     *          diffX: 100,
     *          diffY: 100
     *      }
     *
     * @chainable
     */
    Canvas.prototype.paste = function () {
        this.parse({
            shapes: this.shapesToCopy,
            connections: this.connectionsToCopy,
            createCommand: true,
            uniqueID: true,
            selectAfterFinish: true,
            prependMessage: "Copy of ",
            diffX: 100,
            diffY: 100
        });
        return this;
    };
    /**
     * Default copy paste factory which creates new instances of {@link PMUI.draw.CustomShape}, its main purpose
     * is to create instances using `this.copyAndPasteReferences` (passed through the config options of the canvas)
     * which are reference variables to the constructor of some class (a class declared outside the library).
     *
     *      // let's assume that there's a class declared outside the library called BpmnActivity
     *      var BpmnActivity = function (options) {
     *          ...
     *      };
     *
     *      // in the config options of this canvas, we passed a reference to the constructor like this
     *      var canvas = new PMUI.draw.Canvas({
     *          ...
     *          copyAndPasteReferences: {
     *              bpmnActivity: BpmnActivity
     *          }
     *          ...
     *      });
     *
     *      // so the copyAndPasteFactory will create an instance of the class BpmnActivity
     *      // using that reference
     *      // e.g.
     *      // let's assume that options are the correct configuration options for BpmnActivity
     *      var bpmnActivityInstance = Canvas.prototype.copyAndPasteFactory('bpmnActivity', options);
     *
     *
     * @param {string} type The type of the shape to be created
     * @param {Object} options The config options to be passed to the constructor of the shape
     * @return {PMUI.draw.CustomShape} A custom shape or shape created using the reference created before.
     */
    Canvas.prototype.shapeFactory = function (type, options) {
        if (this.copyAndPasteReferences[type]) {
            return new this.copyAndPasteReferences[type](options);
        }
        return new PMUI.draw.CustomShape(options);
    };
    /**
     * Factory to create connections
     * @param {string} type
     * @param {Object} options
     * @return {Object}
     */
    Canvas.prototype.connectionFactory = function (type, options) {
        if (type && this.copyAndPasteReferences[type]) {
            return new this.copyAndPasteReferences[type](options);
        }
        return new PMUI.draw.Connection(options);
    };

    /**
     * Transforms an array of objects, each of the form `{id: #, parent: #}` to a tree like object.
     * The structure of the returned object (which represents a tree) is:
     *
     *      {
     *          id_1: [child_1_of_id1, child_2_of_id1, ...],
     *          id_2: [child_1_of_id2, child_2_of_id2, ...],
     *          ...
     *      }
     *
     * @param {Array} nodes
     * @return {Object}
     */
    Canvas.prototype.transformToTree = function (nodes) {
        var tree = {},
            node,
            i;
        for (i = 0; i < nodes.length; i += 1) {
            // node = {id: #, parent: #, order: #}
            node = nodes[i];
            // create the children of node.id
            if (!tree[node.id]) {
                tree[node.id] = [];
            }
            // insert to the children of its parent
            if (node.parent) {
                // check if the node exists
                if (!tree[node.parent]) {
                    tree[node.parent] = [];
                }
                // add node to the children of node's parent
                tree[node.parent][node.order] = node.id;
            }
        }
        return tree;
    };
    /**
     * Given a tree (with the structure proposed in #transformToTree)
     * and a pointer to the root node, perform a levelOrder traversal (BFS)
     * of the tree and returning an array with the IDs of each node.
     * @param {Object} tree
     * @param {String} [root=canvas.getID()] The ID of the root node (might be canvas)
     * @return {Array} An array with the IDs of the nodes of the tree in level order traversal
     */
    Canvas.prototype.levelOrderTraversal = function (tree, root) {
        var queue = [],
            processed = [],
            top,
            realRoot = root || this.getID(),
            i;
        queue.push(realRoot);
        while (queue.length > 0) {
            top = queue.shift();
            // push the json of the node
            processed.push(top);
            // push to the queue
            for (i = 0; i < tree[top].length; i += 1) {
                queue.push(tree[top][i]);
            }
        }
        // return the IDs
        return processed;
    };
    /**
     * Parses `options` creating shapes and connections and placing them in this canvas.
     * It does the following:
     *
     * - Creates each shape (in the same order as it is in the array `options.shapes`)
     * - Creates each connection (in the same order as it is in the array `options.connections`)
     * - Creates the an instance of {@link PMUI.command.CommandPaste} (if possible)
     *
     * @param {Object} options
     * @param {Array} [options.shapes=[]] The config options of each shape to be placed in this canvas.
     * @param {Array} [options.connections=[]] The config options of each connection to be placed in this canvas.
     * @param {boolean} [options.uniqueID=false] If set to true, it'll assign a unique ID to each shape created.
     * @param {boolean} [options.selectAfterFinish=false] If set to true, it'll add the shapes that are
     * direct children of this canvas to `this.currentSelection` arrayList.
     * @param {string} [options.prependMessage=""] The message to be prepended to each shape's label.
     * @param {boolean}  [options.createCommand=true] If set to true it'll create a command for each creation
     * of a shape and connection (see {@link PMUI.command.CommandCreate},
     {@link PMUI.command.CommandConnect}) and save them in
     * a {@link PMUI.command.CommandPaste} (for undo-redo purposes).
     * @param {number} [options.diffX=0] The number of pixels on the x-coordinate to move the shape on creation
     * @param {number} [options.diffY=0] The number of pixels on the y-coordinate to move the shape on creation
     * @chainable
     */
    Canvas.prototype.parse = function (options) {
        var defaults = {
                shapes: [],
                connections: [],
                uniqueID: false,
                selectAfterFinish: false,
                prependMessage: "",
                createCommand: true,
                diffX: 0,
                diffY: 0
            },
            i,
            j,
            id,
            oldID,
            shape,
            points,
            shapeOptions,
            connection,
            connectionOptions,
            sourcePort,
            sourcePortOptions,
            sourceShape,
            sourceBorder,
            destPort,
            destPortOptions,
            destShape,
            destBorder,
            command,
            diffX,
            diffY,
            stackCommandCreate = [],
            stackCommandConnect = [],
            canvasID = this.getID(),
            mapOldId = {},
            map = {};
        $.extend(true, defaults, options);
        // set the differentials (if the shapes are pasted in the canvas)
        diffX = defaults.diffX;
        diffY = defaults.diffY;
        // map the canvas
        map[canvasID] = this;
        mapOldId[canvasID] = canvasID;
        // empty the current selection and sharedConnections as a consequence
        // (so that the copy is selected after)
        if (defaults.selectAfterFinish) {
            this.emptyCurrentSelection();
        }
        for (i = 0; i < defaults.shapes.length; i += 1) {
            shapeOptions = {};
            $.extend(true, shapeOptions, defaults.shapes[i]);

            // set the canvas of <shape>
            shapeOptions.canvas = this;

            // create a map of the current id with a new id
            oldID = shapeOptions.id;

            // generate a unique id on user request
            if (defaults.uniqueID) {
                shapeOptions.id = PMUI.generateUniqueId();
            }
            mapOldId[oldID] = shapeOptions.id;

            // change labels' messages (using prependMessage)
            if (shapeOptions.labels) {
                for (j = 0; j < shapeOptions.labels.length; j += 1) {
                    shapeOptions.labels[j].message = defaults.prependMessage +
                        shapeOptions.labels[j].message;
                }
            }

            // create an instance of the shape based on its type
            shape = this.shapeFactory(shapeOptions.extendedType, shapeOptions);

            // map the instance with its id
            map[shapeOptions.id] = shape;

            // if the shapes don't have a valid parent then set the parent
            // to be equal to the canvas
            // TODO: ADD shapeOptions.topLeftOnCreation TO EACH SHAPE
            if (!mapOldId[shapeOptions.parent]) {
                this.addElement(shape,
                    shapeOptions.x + diffX, shapeOptions.y + diffY, true);
            } else if (shapeOptions.parent !== canvasID) {
                // get the parent of this shape
                map[mapOldId[shapeOptions.parent]].addElement(shape, shapeOptions.x,
                    shapeOptions.y, true);
            } else {
                // move the shapes a little (so it can be seen that
                // they were duplicated)
                map[mapOldId[shapeOptions.parent]].addElement(shape,
                    shapeOptions.x + diffX, shapeOptions.y + diffY, true);
            }

            // perform some extra actions defined for each shape
            shape.parseHook();

            shape.attachListeners();
            // execute command create but don't add it to the canvas.commandStack
            command = new PMUI.command.CommandCreate(shape);
            command.execute();
            stackCommandCreate.push(command);
        }
        for (i = 0; i < defaults.connections.length; i += 1) {
            connectionOptions = {};
            $.extend(true, connectionOptions, defaults.connections[i]);

            // state of the connection
            points = connectionOptions.state || [];

            // determine the shapes
            sourcePortOptions = connectionOptions.srcPort;
            sourceShape = map[mapOldId[sourcePortOptions.parent]];
            sourceBorder = sourceShape.getBorderConsideringLayers();

            destPortOptions = connectionOptions.destPort;
            destShape = map[mapOldId[destPortOptions.parent]];
            destBorder = destShape.getBorderConsideringLayers();

            // populate points if points has no info (backwards compatibility,
            // e.g. the flow state is null)
            if (points.length === 0) {
                points.push({
                    x: sourcePortOptions.x + sourceShape.getAbsoluteX(),
                    y: sourcePortOptions.y + sourceShape.getAbsoluteY()
                });
                points.push({
                    x: destPortOptions.x + destShape.getAbsoluteX(),
                    y: destPortOptions.y + destShape.getAbsoluteY()
                });
            }

            //create the ports
            sourcePort = new PMUI.draw.Port({
                width: 8,
                height: 8
            });
            destPort = new PMUI.draw.Port({
                width: 8,
                height: 8
            });
            // add the ports to the shapes
            // LOGIC: points is an array of points relative to the canvas.
            // CustomShape.addPort() requires that the point passed as an argument
            // is respect to the shape, so transform the point's coordinates (also
            // consider the border)
            sourceShape.addPort(
                sourcePort,
                points[0].x + diffX + sourceBorder -
                sourceShape.getAbsoluteX(),
                points[0].y + diffX + sourceBorder -
                sourceShape.getAbsoluteY()
            );
            destShape.addPort(
                destPort,
                points[points.length - 1].x + diffX + destBorder -
                destShape.getAbsoluteX(),
                points[points.length - 1].y + diffY + destBorder -
                destShape.getAbsoluteY(),
                false,
                sourcePort
            );

            connection = this.connectionFactory(
                connectionOptions.type,
                {
                    srcPort: sourcePort,
                    destPort: destPort,
                    canvas: this,
                    segmentStyle: connectionOptions.segmentStyle
                }
            );
            connection.id = connectionOptions.id || PMUI.generateUniqueId();
            if (defaults.uniqueID) {
                connection.id = PMUI.generateUniqueId();
            }
            //set its decorators
            connection.setSrcDecorator(new PMUI.draw.ConnectionDecorator({
                width: 11,
                height: 11,
                canvas: this,
                decoratorPrefix: connectionOptions.srcDecoratorPrefix,
                decoratorType: "source",
                parent: connection
            }));
            connection.setDestDecorator(new PMUI.draw.ConnectionDecorator({
                width: 11,
                height: 11,
                canvas: this,
                decoratorPrefix: connectionOptions.destDecoratorPrefix,
                decoratorType: "target",
                parent: connection
            }));

            command = new PMUI.command.CommandConnect(connection);
            stackCommandConnect.push(command);

            // connect the two ports
            if (points.length >= 3) {
                connection.connect({
                    algorithm: 'user',
                    points: connectionOptions.state,
                    dx: defaults.diffX,
                    dy: defaults.diffY
                });
            } else {
                // use manhattan
                //            console.log("manhattan");
                connection.connect();
            }
            connection.setSegmentMoveHandlers();

            // add the connection to the canvas, that means insert its html to
            // the DOM and adding it to the connections array
            this.addConnection(connection);

            // now that the connection was drawn try to create the intersections
            connection.checkAndCreateIntersectionsWithAll();

            //attaching port listeners
            sourcePort.attachListeners(sourcePort);
            destPort.attachListeners(destPort);

            this.triggerCreateEvent(connection, []);
        }

        // finally add to currentSelection each shape if possible (this method is
        // down here because of the zIndex problem with connections)
        if (defaults.selectAfterFinish) {
            for (id in map) {
                if (map.hasOwnProperty(id)) {
                    if (map[id].family !== 'Canvas') {
                        this.addToSelection(map[id]);
                    }
                }
            }
        }

        // create command if possible
        if (defaults.createCommand) {
            this.commandStack.add(new PMUI.command.CommandPaste(this, {
                stackCommandCreate: stackCommandCreate,
                stackCommandConnect: stackCommandConnect
            }));
        }
        return this;
    };

    Canvas.prototype.getRelativeX = function () {
        return this.x + this.absoluteX;
    };

    Canvas.prototype.getRelativeY = function () {
        return this.y + this.absoluteY;
    };
    Canvas.prototype.hideAllFocusLabels = function () {
        var size = this.customShapes.getSize(),
            i,
            shape;
        for (i = 0; i < size; i += 1) {
            shape = this.customShapes.get(i);
            shape.labels.get(0).loseFocus();
        }
        return true;
    };

    PMUI.extendNamespace('PMUI.draw.Canvas', Canvas);

    if (typeof exports !== 'undefined') {
        module.exports = Canvas;
    }

}());

(function () {
    /**
     * @abstract
     * @class PMUI.draw.Shape
     * Represents a shape in the PMDraw framework, shapes can be:
     *
     * - **Regular shapes** (Ovals, rectangles, polygons)
     * - **Custom shapes** (these kind of shapes can have sprites)
     *
     * A shape has the following characteristics:
     *
     * - It has a dragBehavior (inherited from {@link PMUI.draw.Core})
     * - It has a dropBehavior (inherited from {@link PMUI.draw.BehavioralElement})
     * - It has a containerBehavior (inherited from {@link PMUI.draw.BehavioralElement})
     * - It has a resizeBehavior (instantiated in this class)
     *
     * This class cannot be instantiated.
     *
     * @extend PMUI.draw.BehavioralElement
     * @constructor Creates an instance of the class ConnectionDecorator
     * @param {Object} options Initialization options
     * @cfg {boolean} [topLeft=false] If set to true then when this shape is dragged from the toolbar it'll be created
     * and placed in its topLeft coordinate otherwise it'll use the center as its topLeft coordinate
     * @cfg {string} [resizeBehavior="no"] Default resize behavior used to create the correct instance in the factory
     * @cfg {Object} [resizeHandlers={
     *      type: "None",
     *      total: 4,
     *          resizableStyle: {
     *              cssProperties: {
     *                  'background-color': "rgb(0, 255, 0)",
     *                  'border': '1px solid black'
     *              }
     *          },
     *          nonResizableStyle: {
     *              cssProperties: {
     *                  'background-color': "white",
     *                  'border': '1px solid black'
     *              }
     *          }
     *      }] Default styles to create the instances of the class Style
     * @cfg {string} [drag="disabled"] Default drag behavior used to create the correct instance in the factory
     */
    var Shape = function (options) {

        Shape.superclass.call(this, options);

        /**
         * Array built when setting the dimension of the shape to store the
         * x coordinate of the div corners in clockwise order starting at top left
         * @property {Array}
         */
        this.xCorners = [0, 0, 0, 0];
        /**
         * Array built when setting the dimension of the shape to store the
         * y coordinate of the div corners in clockwise order starting at top left
         * @property {Array}
         */
        this.yCorners = [0, 0, 0, 0];
        /**
         * Array built when setting the dimension of the shape to store the
         * x coordinate of the midpoints of each div border in clockwise order
         * starting at the top border
         * @property {Array}
         */
        this.xMidPoints = [0, 0, 0, 0];
        /**
         * Array built when setting the dimension of the shape to store the
         * y coordinate of the midpoints of each div border in clockwise order
         * starting at the top border
         * @property {Array}
         */
        this.yMidPoints = [0, 0, 0, 0];
        /**
         * List containing the resize Points located in the corner of a div
         * @property {PMUI.util.ArrayList}
         */
        this.cornerResizeHandlers = new PMUI.util.ArrayList();
        /**
         * List containing the resize Points located in the middle of a border
         * @property {PMUI.util.ArrayList}
         */
        this.midResizeHandlers = new PMUI.util.ArrayList();

        /**
         * Center point of the shape (in the case of a polygon).
         * @property {PMUI.util.Point}
         */
        this.center = null;
        /**
         * The parent of this shape.
         * @property {PMUI.draw.Shape}
         */
        this.parent = null;
        /**
         * Old parent of this shape (useful to check the previous
         * container of this shape).
         * @property {PMUI.draw.Shape}
         */
        this.oldParent = null;
        /**
         * Default zOrder of the shape.
         * @property {number} [defaultZOrder=1]
         */
        this.defaultZOrder = 1;
        /**
         * Denotes whether this shape is being dragged.
         * @property {boolean} [dragging=false]
         */
        this.dragging = false;
        /**
         * Denotes whether this shape was dragged.
         * @property {boolean} [wasDragged=false]
         */
        this.wasDragged = false;
        /**
         * Denotes whether this shape was entered by a draggable element.
         * @property {boolean} [entered=false]
         */
        this.entered = false;
        /**
         * Determines the resizeBehavior that this object has.
         * @property {PMUI.behavior.ResizeBehavior}
         */
        this.resizeBehavior = null;
        /**
         * Determines whether the shape is being resized or not.
         * @property {boolean} [resizing=false]
         */
        this.resizing = false;
        /**
         * This shape was repainted.
         * @property {boolean}
         */
        this.repainted = false;
        /**
         * Determines whether a shape has fixed Dimensions or not
         * @property boolean
         */
        this.fixed = true;
        /**
         * Determines if the shape's been dropped to a different container
         * @property {boolean}
         */
        this.changedContainer = false;
        /**
         * Determines whether this shape will be created considering its top-left
         * coordinates or its center
         * @property {boolean}
         */
        this.topLeftOnCreation = false;
        /**
         * Determines wether the events ["mousedown, mouseup, click"] will be assigned to component.
         * @property {boolean}
         */
        this.attachEvents = true
        // set defaults
        Shape.prototype.init.call(this, options);
    };

    // inherits from BehavioralElement
    PMUI.inheritFrom('PMUI.draw.BehavioralElement', Shape);

    /**
     * Type of each instance of this class
     * @property {String}
     */
    Shape.prototype.type = "Shape";

    /**
     * Family of each instance of this class
     * @property {String}
     */
    Shape.prototype.family = "Shape";

    /**
     * Instance of RegularDragBehavior (avoiding the creation of multiple same instances)
     * @property {PMUI.behavior.DragBehavior} [noDragBehavior=null]
     */
    Shape.prototype.noDragBehavior = null;

    /**
     * Instance of RegularDragBehavior (avoiding the creation of multiple same instances)
     * @property {PMUI.behavior.DragBehavior} [regularDragBehavior=null]
     */
    Shape.prototype.regularDragBehavior = null;

    /**
     * Instance of ConnectionDragBehavior (avoiding the creation of multiple same instances)
     * @property {PMUI.behavior.ConnectionDragBehavior} [connectionDragBehavior=null]
     */
    Shape.prototype.connectionDragBehavior = null;

    /**
     * Instance of CustomShapeDragBehavior (avoiding the creation of multiple same instances)
     * @property {PMUI.behavior.CustomShapeDragBehavior} [customShapeDragBehavior=null]
     */
    Shape.prototype.customShapeDragBehavior = null;

    /**
     * Corner resize identifiers (for jQueryUI Resizable handles)
     * @property {Array} [cornersIdentifiers=['nw', 'ne', 'se', 'sw']]
     */
    Shape.prototype.cornersIdentifiers = ['nw', 'ne', 'se', 'sw'];

    /**
     * Mid resize identifiers (for jQueryUI Resizable handles)
     * @property {Array} [midPointIdentifiers=['n', 'e', 's', 'w']]
     */
    Shape.prototype.midPointIdentifiers = ['n', 'e', 's', 'w'];

    /**
     * Constant for the maximum z-index
     * @property {number} [MAX_ZINDEX=100]
     */
    Shape.prototype.MAX_ZINDEX = 100;

    /**
     * Constant for the default radius used in the class Arc
     * @property {number} [DEFAULT_RADIUS=6]
     */
    Shape.prototype.DEFAULT_RADIUS = 6;

    /**
     * Instance initializer which uses options to extend the config options to initialize the instance
     * @param {Object} options The object that contains old points and new points
     * @private
     */
    Shape.prototype.init = function (options) {
        var defaults = {
            topLeft: false,
            resizeBehavior: "no",
            resizeHandlers: {
                type: "None",
                total: 4,
                resizableStyle: {
                    cssProperties: {
                        'background-color': "rgb(0, 255, 0)",
                        'border': '1px solid black'
                    }
                },
                nonResizableStyle: {
                    cssProperties: {
                        'background-color': "white",
                        'border': '1px solid black'
                    }
                }
            },
            drag: "disabled",
            attachEvents: true
        };

        $.extend(true, defaults, options);

        if (defaults.drag !== "disabled") {
            this.setDragBehavior(defaults.drag);
        } else {
            this.setDragBehavior("nodrag");
        }
        this.setResizeBehavior(defaults.resizeBehavior);
        this.createHandlers(defaults.resizeHandlers.type,
            defaults.resizeHandlers.total,
            defaults.resizeHandlers.resizableStyle,
            defaults.resizeHandlers.nonResizableStyle);
        this.topLeftOnCreation = defaults.topLeft;
        this.attachEvents = defaults.attachEvents;
    };

    /**
     * Creates handlers according to the `number` of handlers, the `type` of handlers (currently only Rectangle
     * is supported), the `resizableStyle` (created in `this.initObject`) and the `nonResizableStyle`
     * (created in `this.initObject`).
     * @param {string} type
     * @param {number} number
     * @param {Object} resizableStyle
     * @param {Object} nonResizableStyle
     * @chainable
     */
    Shape.prototype.createHandlers = function (type, number, resizableStyle, nonResizableStyle) {
        if (type === "Rectangle") {

            var i;

            //First determine how many ResizeHandlers we are to create
            if (!number || (number !== 8 &&
                number !== 4 && number !== 0)) {
                number = 4;
            }
            //Then insert the corners first
            for (i = 0; i < number && i < 4; i += 1) {
                this.cornerResizeHandlers.insert(
                    new PMUI.draw.ResizeHandler({
                        parent: this,
                        zOrder: PMUI.util.Style.MAX_ZINDEX + 3,
                        representation: new PMUI.draw.Rectangle(),
                        orientation: this.cornersIdentifiers[i],
                        resizableStyle: resizableStyle,
                        nonResizableStyle: nonResizableStyle
                    })
                );
            }
            //subtract 4 just added resize points to the total
            number -= 4;
            //add the rest to the mid list
            for (i = 0; i < number; i += 1) {
                this.midResizeHandlers.insert(
                    new PMUI.draw.ResizeHandler({
                        parent: this,
                        zOrder: PMUI.util.Style.MAX_ZINDEX + 3,
                        representation: new PMUI.draw.Rectangle(),
                        orientation: this.midPointIdentifiers[i],
                        resizableStyle: resizableStyle,
                        nonResizableStyle: nonResizableStyle
                    })
                );
            }
        }
        return this;
        //console.log(this.cornerResizeHandlers.asArray());
        //console.log(this.midResizeHandlers.asArray());
    };

    /**
     * Updates the position of the handlers using `this.cornerResizeHandlers` and `this.midResizeHandlers`.
     * NOTE:  There's a prerequisite to call this method, `this.setDimensions` must be called first
     * because it updated the arrays used by this method.
     * @chainable
     */
    Shape.prototype.updateHandlers = function () {
        var handler,
            i;
        for (i = 0; i < this.cornerResizeHandlers.getSize(); i += 1) {
            handler = this.cornerResizeHandlers.get(i);
            handler.setPosition(this.xCorners[i] -
                Math.round(handler.width / 2) - 1,
                this.yCorners[i] - Math.round(handler.height / 2) - 1);
        }
        for (i = 0; i < this.midResizeHandlers.getSize(); i += 1) {
            handler = this.midResizeHandlers.get(i);
            handler.setPosition(this.xMidPoints[i] -
                Math.round(handler.width / 2) - 1,
                this.yMidPoints[i] - Math.round(handler.height / 2) - 1);
        }
        return this;
    };

    /**
     * Sets the visibility of the resize handlers
     * @param {boolean} visible
     * @chainable
     */
    Shape.prototype.showOrHideResizeHandlers = function (visible) {

        var i;
        if (!visible) {
            visible = false;
        }
        for (i = 0; i < this.cornerResizeHandlers.getSize(); i += 1) {
            this.cornerResizeHandlers.get(i).setVisible(visible);
        }

        for (i = 0; i < this.midResizeHandlers.getSize(); i += 1) {
            this.midResizeHandlers.get(i).setVisible(visible);
        }
        return this;
    };

    /**
     * Applies a predefined style to its handlers (which can be resizable style or non resizable style)
     * @param {string} styleType
     * @chainable
     */
    Shape.prototype.applyStyleToHandlers = function (styleType) {
        var i;
        for (i = 0; i < this.cornerResizeHandlers.getSize(); i += 1) {
            this.cornerResizeHandlers.get(i)[styleType].applyStyle();
        }

        for (i = 0; i < this.midResizeHandlers.getSize(); i += 1) {
            this.midResizeHandlers.get(i)[styleType].applyStyle();
        }
        return this;
    };

    /**
     * Attaches events to this shape (currently mousedown, mouseup and click events).
     * This method also instantiates the behaviors defined in the configuration options of the object,
     * the behaviors instantiated are:
     *
     * - drag behavior
     * - drop behavior
     * - resize behavior
     *
     * @chainable
     */
    Shape.prototype.attachListeners = function () {
        var $shape = $(this.html);
        if (this.attachEvents && !this.canvas.readOnly) {
            $shape.on("mousedown", this.onMouseDown(this));
            $shape.on("mouseup", this.onMouseUp(this));
            $shape.on("click", this.onClick(this));
            this.updateBehaviors();
        }
        return this;
    };

    /**
     * @event mousedown
     * Moused down callback fired when the user mouse downs on the `shape`
     * @param {PMUI.draw.Shape} shape
     */
    Shape.prototype.onMouseDown = function (shape) {
        return function (e, ui) {
        };
    };

    /**
     * @event mouseup
     * Moused up callback fired when the user mouse ups on the `shape`
     * @param {PMUI.draw.Shape} shape
     */
    Shape.prototype.onMouseUp = function (shape) {
        return function (e, ui) {
        };
    };

    /**
     * @event click
     * Click callback fired when the user clicks on the `shape`
     * @param {PMUI.draw.Shape} shape
     */
    Shape.prototype.onClick = function (shape) {
        return function (e, ui) {
        };
    };

    /**
     * Creates the HTML representation of the shape, besides calling the method `createHTML` of
     * the method of its parent, it also adds the resize handlers to the DOM.
     * @returns {HTMLElement}
     */
    Shape.prototype.createHTML = function () {
        var i;

        // call the prototype's createHTML
        Shape.superclass.prototype.createHTML.call(this);

        // add the handlers
        for (i = 0; i < this.cornerResizeHandlers.getSize(); i += 1) {
            this.addResizeHandler(this.cornerResizeHandlers.get(i),
                this.xCorners[i], this.yCorners[i]);
        }
        for (i = 0; i < this.midResizeHandlers.getSize(); i += 1) {
            this.addResizeHandler(this.midResizeHandlers.get(i),
                this.xMidPoints[i], this.yMidPoints[i]);
        }
        return this.html;
    };


    /**
     * Returns true if this object is draggable
     * @return {boolean}
     */
    Shape.prototype.isDraggable = function () {
        return this.drag &&
            this.drag.type !== "NoDragBehavior";
    };

    /**
     * Updates the behaviors of this shape (this method is called from `this.attachListeners`).
     * This is the method that actually initializes jQueryUI's plugins (during the creation of the
     * instance of this shapes, the shape's behaviors are initialized but the init that they do
     * initialize jQuery's UI plugins is done through `[behavior].init`).
     * @chainable
     */
    Shape.prototype.updateBehaviors = function () {
        Shape.superclass.prototype.updateBehaviors.call(this);
        if (this.drag) {
            this.drag.attachDragBehavior(this);
        }
        if (this.resizeBehavior) {
            this.resizeBehavior.init(this);
        }
        return this;
    };

    /**
     * Adds a `resizeHandler` to the shape at `[x, y]`
     * @param {PMUI.draw.ResizeHandler} resizeHandler
     * @param {number} x
     * @param {number} y
     * @chainable
     */
    Shape.prototype.addResizeHandler = function (resizeHandler, x, y) {
        if (!this.html) {
            return;
        }
        //console.log(resizeHandler.getHTML());
        this.html.appendChild(resizeHandler.getHTML());

        resizeHandler.setPosition(x - Math.round(resizeHandler.width / 2) - 1,
            y - Math.round(resizeHandler.height / 2) - 1);
        //add resizable validations
        if (this.isResizable()) {
            resizeHandler.setCategory("resizable");
        }

        return this;
    };


    /**
     * Paints the shape performing the following actions:
     *
     * - Paints its resize handlers
     * - Applies the predefined style according to the resize behavior it has
     *
     * @chainable
     */
    Shape.prototype.paint = function () {
        var i,
            styleToApply;

        //    // apply predefined style
        //    this.style.applyStyle();

        for (i = 0; i < this.cornerResizeHandlers.getSize(); i += 1) {
            this.cornerResizeHandlers.get(i).paint();
        }
        for (i = 0; i < this.midResizeHandlers.getSize(); i += 1) {
            this.midResizeHandlers.get(i).paint();
        }

        // apply style to the handlers
        if (this.resizeBehavior) {
            styleToApply = this.resizeBehavior.type === "NoResizeBehavior" ?
                "nonResizableStyle" : "resizableStyle";
            this.applyStyleToHandlers(styleToApply);
        }

        return this;
    };

    Shape.prototype.updateHTML = function () {
        return this;
    };

    /**
     * Detaches `this` HTML from the DOM (also removing it from `canvas.customShapes` or `canvas.regularShapes`)
     * @chainable
     */
    Shape.prototype.saveAndDestroy = function () {
        // save the html but detach it from the DOM
        this.html = $(this.html).detach()[0];
        this.canvas.removeFromList(this);
        return this;
    };

    /**
     * Updates the dimensions of this shape according to the dimensions and
     * positions of its children
     * @param {number} newMargin Padding to be added when a children is near the edge
     * @chainable
     */
    Shape.prototype.updateSize = function (newMargin) {
        var children = this.children,
            limits = children.getDimensionLimit(),
            left = limits[3],
            top = limits[0],
            right = limits[1],
            bottom = limits[2],
            newLeft = this.getX(),
            newTop = this.getY(),
            newWidth = this.getWidth(),
            newHeight = this.getHeight(),
            margin,
            diffX = 0,
            diffY = 0,
            positionShift = false,
            dimensionIncrement = false;

        if (newMargin !== "undefined") {
            margin = newMargin;
        } else {
            margin = 15;
        }

        if (left < 0) {
            diffX = margin - left;
            positionShift = true;
            this.oldX = this.x;
            this.oldAbsoluteX = this.x;
            this.oldY = this.y;
            this.oldAbsoluteY = this.absoluteY;
        }

        if (top < 0) {
            diffY = margin - top;
            positionShift = true;
            this.oldX = this.x;
            this.oldAbsoluteX = this.x;
            this.oldY = this.y;
            this.oldAbsoluteY = this.absoluteY;
        }

        newLeft -= diffX;
        newTop -= diffY;
        newWidth += diffX;
        newHeight += diffY;

        if (right > this.width) {
            newWidth += right - this.width + margin;
            dimensionIncrement = true;
            this.oldWidth = this.width;
        }
        if (bottom > this.height) {
            newHeight += bottom - this.height + margin;
            dimensionIncrement = true;
            this.oldHeight = this.height;
        }

        // move the shape to the new coordinates
        this.setPosition(newLeft, newTop);

        // update the shape's dimension
        this.setDimension(newWidth, newHeight);

        // custom triggers
        if (positionShift) {
            this.changePosition(this.oldX, this.oldY,
                this.absoluteX, this.absoluteY);
        }
        if (dimensionIncrement) {
            this.changeSize(this.oldWidth, this.oldHeight);
        }

        // move the children
        this.updateChildrenPosition(diffX, diffY);

        return this;
    };

    /**
     * Applies the actual zoom scale to the corresponding shape
     * @chainable
     */
    Shape.prototype.applyZoom = function () {
        //    var zoomFactor = this.canvas.getZoomFactor(),
        //        zoomIndex = this.canvas.getZoomPropertiesIndex();

        this.refreshShape();
        return this;
    };

    /**
     * Sets the dimension of this shape, it also updates the arrays `this.xCorners, this.yCorners, this.xMidPoints
     * and this.yMidPoints`
     * @param {number} width
     * @param {number} height
     * @chainable
     */
    Shape.prototype.setDimension = function (width, height) {
        Shape.superclass.prototype.setDimension.call(this, width, height);
        if (this.xCorners) {
            this.xCorners = [0, Math.round(this.zoomWidth), Math.round(this.zoomWidth), 0];
            this.yCorners = [0, 0, Math.round(this.zoomHeight), Math.round(this.zoomHeight)];
            this.xMidPoints = [Math.round(this.zoomWidth / 2), Math.round(this.zoomWidth),
                Math.round(this.zoomWidth / 2), 0];
            this.yMidPoints = [0, Math.round(this.zoomHeight / 2), Math.round(this.zoomHeight),
                Math.round(this.zoomHeight / 2)];
            this.updateHandlers();
        }
        return this;
    };

    /**
     * Sets some variables that store what changed during the process of changing the parent and also
     * triggers `changeElement` using those variables.
     *
     * The variables saved in {@link PMUI.draw.Canvas#updatedElement} are:
     *
     * - x (old x and new x)
     * - y (old y and new y)
     * - absoluteX (old absoluteX and new absoluteX)
     * - absoluteY (old absoluteY and new absoluteY)
     * - parent (old parent and new parent)
     *
     * @param {number} oldX
     * @param {number} oldY
     * @param {number} oldAbsoluteX
     * @param {number} oldAbsoluteY
     * @param {Object} oldParent
     * @param {PMUI.draw.Canvas} canvas
     * @chainable
     */
    Shape.prototype.changeParent = function (oldX, oldY, oldAbsoluteX, oldAbsoluteY, oldParent, canvas) {
        var fields = [
            {
                "field": "x",
                "oldVal": oldX,
                "newVal": this.x
            },
            {
                "field": "y",
                "oldVal": oldY,
                "newVal": this.y
            },
            {
                "field": "absoluteX",
                "oldVal": oldAbsoluteX,
                "newVal": this.absoluteX
            },
            {
                "field": "absoluteY",
                "oldVal": oldAbsoluteY,
                "newVal": this.absoluteY
            },
            {
                "field": "parent",
                "oldVal": oldParent,
                "newVal": this.parent
            }
        ];
        canvas.updatedElement = {
            "id": this.id,
            "type": this.type,
            "fields": fields,
            "relatedObject": this
        };
        $(canvas.html).trigger("changeelement");
        return this;
    };

    /**
     * Sets some variables that store what changed during the process of resizing and also
     * triggers `changeElement` using those variables.
     *
     * The variables saved in {@link PMUI.draw.Canvas#updatedElement} are:
     *
     * - width (old width and new width)
     * - height (old height and new height)
     *
     * @param {number} oldWidth
     * @param {number} oldHeight
     * @chainable
     */
    Shape.prototype.changeSize = function (oldWidth, oldHeight) {
        var canvas = this.canvas,
            fields = [
                {
                    "field": "width",
                    "oldVal": oldWidth,
                    "newVal": this.width
                },
                {
                    "field": "height",
                    "oldVal": oldHeight,
                    "newVal": this.height
                }
            ];
        canvas.updatedElement = {
            "id": this.id,
            "type": this.type,
            "fields": fields,
            "relatedObject": this
        };
        $(canvas.html).trigger("changeelement");
        return this;
    };

    /**
     * Sets some variables that store what changed during the process of changing its position and also
     * triggers `changeElement` using those variables.
     *
     * The variables saved in {@link PMUI.draw.Canvas#updatedElement} are:
     *
     * - x (old x and new x)
     * - y (old y and new y)
     * - absoluteX (old absoluteX and new absoluteX)
     * - absoluteY (old absoluteY and new absoluteY)
     *
     * @param {number} oldX
     * @param {number} oldY
     * @param {number} oldAbsoluteX
     * @param {number} oldAbsoluteY
     * @chainable
     */
    Shape.prototype.changePosition = function (oldX, oldY, oldAbsoluteX, oldAbsoluteY) {
        var canvas = this.canvas,
            fields = [
                {
                    "field": "x",
                    "oldVal": oldX,
                    "newVal": this.x
                },
                {
                    "field": "y",
                    "oldVal": oldY,
                    "newVal": this.y
                },
                {
                    "field": "absoluteX",
                    "oldVal": oldAbsoluteX,
                    "newVal": this.absoluteX
                },
                {
                    "field": "absoluteY",
                    "oldVal": oldAbsoluteY,
                    "newVal": this.absoluteY
                }

            ];
        canvas.updatedElement = [{
            "id": this.id,
            "type": this.type,
            "fields": fields,
            "relatedObject": this
        }];
        $(canvas.html).trigger("changeelement");
        return this;
    };

    /**
     * Sets whether the dimensions are fixed or not
     * @param {boolean} fixed
     * @chainable
     */
    Shape.prototype.setFixed = function (fixed) {
        if (typeof fixed === "boolean") {
            this.fixed = fixed;
        }
        return this;
    };

    /**
     * Adds `value` to the z-index of the shape (considering the z-index of its parent), since a shape might have
     * children, this method must increase the z-index of each child recursively.
     * @param {PMUI.draw.Shape} shape
     * @param {number} value
     * @chainable
     */
    Shape.prototype.fixZIndex = function (shape, value) {

        var i,
            anotherShape,
            port,
            srcShape,
            destShape,
            srcShapeZIndex,
            destShapeZIndex,
            parentZIndex;

        parentZIndex = shape.parent.html.style.zIndex;
        shape.setZOrder(
            parseInt(parentZIndex, 10) + value + parseInt(shape.defaultZOrder, 10)
        );

        // fix children zIndex
        for (i = 0; i < shape.children.getSize(); i += 1) {
            anotherShape = shape.children.get(i);
            anotherShape.fixZIndex(anotherShape, 0);
        }

        // fix connection zIndex
        // only if it has ports
        if (shape.ports) {
            for (i = 0; i < shape.ports.getSize(); i += 1) {
                port = shape.ports.get(i);
                srcShape = port.connection.srcPort.parent;
                destShape = port.connection.destPort.parent;
                srcShapeZIndex = parseInt(srcShape.html.style.zIndex, 10);
                destShapeZIndex = parseInt(destShape.html.style.zIndex, 10);
                port.connection.style.addProperties({
                    zIndex: Math.max(srcShapeZIndex + 1, destShapeZIndex + 1)
                });
            }
        }
        return this;
    };

    /**
     * Increases the zIndex of this shape by Style.MAX_ZINDEX
     * @chainable
     */
    Shape.prototype.increaseZIndex = function () {
        this.fixZIndex(this, PMUI.util.Style.MAX_ZINDEX);
        return this;
    };

    /**
     * Decreases the zIndex of this shape back to normal
     * @chainable
     */
    Shape.prototype.decreaseZIndex = function () {
        this.fixZIndex(this, 0);
        return this;
    };

    /**
     * Increases the z-index of `shapes`'s ancestors by one
     * @param shape
     * @chainable
     */
    Shape.prototype.increaseParentZIndex = function (shape) {
        if (shape.family !== "Canvas") {
            shape.style.addProperties({
                zIndex: parseInt(shape.html.style.zIndex, 10) + 1
            });
            shape.increaseParentZIndex(shape.parent);
        }
        return this;
    };

    /**
     * Decreases the zIndex of `shapes`'s ancestors by one by one
     * @param {PMUI.draw.Shape} shape
     * @chainable
     */
    Shape.prototype.decreaseParentZIndex = function (shape) {
        if (shape && shape.family !== "Canvas") {
            shape.style.addProperties({
                zIndex: parseInt(shape.html.style.zIndex, 10) - 1
            });
            shape.decreaseParentZIndex(shape.parent);
        }
        return this;
    };

    /**
     * Sets the determined resize behavior to `this` by calling `this.resizeBehaviorFactory` (which creates or returns
     * the instance according to `behavior`) and attaches the drag events to `this`.
     * @param {String} behavior
     * @chainable
     */
    Shape.prototype.setResizeBehavior = function (behavior) {
        var factory = new PMUI.behavior.BehaviorFactory({
            products: {
                "regularresize": PMUI.behavior.RegularResizeBehavior,
                "Resize": PMUI.behavior.RegularResizeBehavior,
                "yes": PMUI.behavior.RegularResizeBehavior,
                "resize": PMUI.behavior.RegularResizeBehavior,
                "noresize": PMUI.behavior.NoResizeBehavior,
                "NoResize": PMUI.behavior.NoResizeBehavior,
                "no": PMUI.behavior.NoResizeBehavior
            },
            defaultProduct: "noresize"
        });
        this.resizeBehavior = factory.make(behavior);
        if (this.html) {
            this.resizeBehavior.init(this);
        }
        return this;
    };

    /**
     * Returns whether the shape is resizable or not
     * @return {boolean}
     */
    Shape.prototype.isResizable = function () {
        return this.resizeBehavior &&
            this.resizeBehavior.type !== "NoResizeBehavior";
    };

    /**
     * Updates the position and dimensions of the shape (useful when the parent of this shape
     * has changed positions or dimensions).
     * @chainable
     */
    Shape.prototype.refreshShape = function () {
        this.setPosition(this.x, this.y)
            .setDimension(this.width, this.height);
        return this;
    };

    /**
     * Abstract method intended to refresh the connections of a shapes
     * @abstract
     * @chainable
     */
    Shape.prototype.refreshConnections = function () {
        return this;
    };

    /**
     * Updates the positions of the children of this shape recursively
     * @param {boolean} onCommand
     * @chainable
     */
    Shape.prototype.refreshChildrenPositions = function (onCommand) {
        var i,
            children = this.children,
            child,
            relatedShapes = [],
            coordinates = [];
        for (i = 0; i < children.getSize(); i += 1) {
            child = children.get(i);
            child.setPosition(child.getX(), child.getY());
            if (onCommand) {
                child.refreshConnections(false);
            }
            relatedShapes.push(child);
            coordinates.push({
                x: child.getX(),
                y: child.getY()
            });
            child.refreshChildrenPositions(onCommand);
        }

        return this;
    };

    /**
     * Fix connections ports on resize (a container must call this method on resize to reposition its
     * ports on resize and the ports of its children)
     * @param {boolean} resizing
     * @param {boolean} root The currentShape is root?
     * @chainable
     */
    Shape.prototype.fixConnectionsOnResize = function (resizing, root) {

        var i,
            port,
            child,
            connection,
            zoomFactor = this.canvas.zoomFactor;

        if (root) {
            if (this.ports) {
                // connections
                for (i = 0; i < this.ports.getSize(); i += 1) {
                    port = this.ports.get(i);
                    connection = port.connection;
                    this.recalculatePortPosition(port);

                    connection.disconnect().connect();
                    if (!this.resizing) {
                        connection.setSegmentMoveHandlers();
                        connection.checkAndCreateIntersectionsWithAll();
                    }
                }
            }
        } else {
            if (this.ports) {
                // connections
                for (i = 0; i < this.ports.getSize(); i += 1) {
                    // for each port update its absolute position and
                    // repaint its connections
                    port = this.ports.get(i);
                    connection = port.connection;
                    port.setPosition(port.x, port.y);

                    connection.disconnect().connect();
                    if (!this.resizing) {
                        connection.setSegmentMoveHandlers();
                        connection.checkAndCreateIntersectionsWithAll();
                    }
                }
            }
        }

        // children
        for (i = 0; i < this.children.getSize(); i += 1) {
            child = this.children.get(i);
            child.setPosition(child.x, child.y);
            child.fixConnectionsOnResize(child.resizing, false);
        }
        return this;
    };

    /**
     * Serializes this object.
     *
     * This method adds the following to the object retrieved from {@link PMUI.draw.BehavioralElement#stringify}:
     *
     * - resizeBehavior
     * - resizeHandlers (as defined in the config options)
     *
     * @return {Object}
     */
    Shape.prototype.stringify = function () {
        var inheritedJSON = Shape.superclass.prototype.stringify.call(this),
            type = (this.savedOptions.resizeHandlers &&
                this.savedOptions.resizeHandlers.type) || 'Rectangle',
            total = (this.savedOptions.resizeHandlers &&
                this.savedOptions.resizeHandlers.total) || 4,
            thisJSON = {
                resizeBehavior: this.savedOptions.resizeBehavior,
                resizeHandlers: {
                    type: type,
                    total: total
                }
            };
        $.extend(true, inheritedJSON, thisJSON);
        return inheritedJSON;
    };

    /**
     * Sets the center of the shape
     * @param {number} newCenter
     * @throws {Error} parameter newCenter is not an instance of points
     * @chainable
     */
    Shape.prototype.setCenter = function (newCenter) {
        if (newCenter instanceof PMUI.util.Point) {
            this.center = newCenter;
        } else {
            throw new Error("setCenter():  argument is not an instance of Point");
        }
        return this;
    };

    /**
     * Sets the Parent of the shape (might also trigger the custom event change element if the parameter
     * triggerChange is set to true)
     * @chainable
     * @param {PMUI.draw.Shape} newParent
     * @param {boolean} triggerChange
     */
    Shape.prototype.setParent = function (newParent, triggerChange) {

        if (newParent) {

            if (this.canvas && triggerChange) {
                this.canvas.updatedElement = {
                    "id": this.id,
                    "type": this.type,
                    "fields": [
                        {
                            "field": "parent",
                            "oldVal": this.parent,
                            "newVal": newParent
                        }
                    ]
                };
                $(this.canvas.html).trigger("changeelement");
            }
            this.parent = newParent;
        }

        return this;
    };

    /**
     * Sets the oldParent of the shape.
     * @chainable
     * @param {PMUI.draw.Shape} oldParent
     */
    Shape.prototype.setOldParent = function (oldParent) {
        this.oldParent = oldParent;
        return this;
    };

    /**
     * Gets the center of the shape.
     * @return {PMUI.util.Point}
     */
    Shape.prototype.getCenter = function () {
        return this.center;
    };

    /**
     * Gets the parent of the shape.
     * @return {PMUI.draw.Shape / PMUI.draw.Canvas}
     */
    Shape.prototype.getParent = function () {
        return this.parent;
    };

    /**
     * Gets the oldParent of the shape
     * @return {PMUI.draw.Shape / PMUI.draw.Canvas}
     */
    Shape.prototype.getOldParent = function () {
        return this.oldParent;
    };

    /**
     * Gets the handles IDs used to initialize jQueryUI's resizable plugin
     * @return {Object}
     */
    Shape.prototype.getHandlesIDs = function () {
        var handlesObject = {},
            i;

        for (i = 0; i < this.midPointIdentifiers.length; i += 1) {
            handlesObject[this.midPointIdentifiers[i]] = '#' +
                this.midPointIdentifiers[i] + this.id +
                'resizehandler';
        }
        for (i = 0; i < this.cornersIdentifiers.length; i += 1) {
            handlesObject[this.cornersIdentifiers[i]] = '#' +
                this.cornersIdentifiers[i] + this.id +
                'resizehandler';
        }
        return handlesObject;
    };

    /**
     * Applies all behavior in a Shape
     * @chainable
     */
    Shape.prototype.applyBehaviors = function () {
        if (this.html) {
            if (this.drag) {
                this.drag.attachDragBehavior(this);
            }
            if (this.drop) {
                this.drop.attachDropBehavior(this);
            }
            if (this.resizeBehavior) {
                this.resizeBehavior.init(this);
            }
        }
        return this;
    };

    PMUI.extendNamespace('PMUI.draw.Shape', Shape);

    if (typeof exports !== 'undefined') {
        module.exports = Shape;
    }


}());

(function () {
    /**
     * @class PMUI.draw.Label
     * Creates a an object that can in order to illustrate text in the HTML it can
     * be inside a shape or by its own directly in the canvas
     *
     *              //e.g.
     *              var label = new PMUI.draw.Label({
     *                  //message that the label will display
     *                  message: "This is a label",
     *                  //orientation of the text, can be vertical or horizontal
     *                  orientation: "horizontal",
     *                  //font-family
     *                  fontFamily: "arial",
     *                  //size of the label object not the text
     *                  size: 80,
     *                  //position where it will be located relative to its
     *                  //container
     *                  position: {
     *                  //location can be center, top, bottom among others,
     *                  //relative to its container
     *                      location: "center",
     *                  //How many pixels in the x coordinate and y coordinate
     *                  //we want to move it from its location
     *                      diffX: 2,
     *                      diffY: -1
     *                  },
     *                  //option that determines if the label should update its
     *                  //parent size when it grows
     *                  updateParent: false,
     *                  //label's parent
     *                  parent: canvas
     *
     *              });
     * @extends PMUI.draw.Shape
     *
     * @constructor
     * Creates an instance of the class
     * @param {Object} options configuration options for the label object
     * @cfg {String} [message=""] Message to be displayed
     * @cfg {String} [orientation="horizontal"] Orientation of the text, can be
     * vertical or horizontal
     * @cfg {String} [fontFamily="arial"] Font family we want the message to be
     * displayed with
     * @cfg {number} [size=0] Size of the label object
     * @cfg {Object} [position={
     *     location:  "none",
     *     diffX:  0,
     *     diffY:  0
     * }] Location where we want the label to be positioned relative to its parent
     * @cfg {boolean} [updateParent=false] Determines whether the parent's size
     * should be updated when the label increases its size
     * @cfg {Object} [parent=null] Label's parent
     */
    var Label = function (options) {
        Label.superclass.call(this, options);
        /**
         * The percentage of this label respect to the width of the shape
         * in the range(0, 1)
         * @property {number}
         */
        this.xPercentage = 0;
        /**
         * The percentage of this label respect to the height of the shape
         * in the range(0, 1)
         * @property {number}
         */
        this.yPercentage = 0;
        /**
         * Message that the label will display
         * @property {String}
         */
        this.message = "";
        /**
         * Orientation of the label
         * @property {String}
         */
        this.orientation = "";
        /**
         * HTML span that holds the text display
         * @property {HTMLElement}
         */
        this.text = null;
        /**
         * Determines whether a label's parent should be updated when a label
         * increases its size
         * @property {boolean}
         */
        this.updateParent = false;
        /**
         * Determines the type of overflow this label should have
         * @property {boolean}
         */
        this.overflow = false;
        /**
         * XXX
         * @property {boolean}
         */
        this.onFocus = false;
        /**
         * Determines the location relative to its parent where this label will be
         * positioned
         * @property {String}
         */
        this.location = "";
        /**
         * x direction pixels that the label will be moved from its location
         * @property {number}
         */
        this.diffX = 0;
        /**
         * y direction pixels that the label will be moved from its location
         * @property {number}
         */
        this.diffY = 0;
        /**
         * Determines the font-size to be used in each zoom scale
         * @property {Array}
         */
        this.fontSizeOnZoom = [];
        /**
         * The font-size that this label will use to display the message
         * @property {number}
         */
        this.fontSize = 0;
        /**
         * html text field for text editing
         * @property {HTMLElement}
         */
        this.textField = null;

        /**
         * Initial width
         * @property {number}
         */
        this.initialWidth = null;

        /**
         * Min Height
         * @property {number}
         */
        this.minHeight = null;


        Label.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.draw.Shape', Label);
    /**
     * Type of all label instances
     * @property {String}
     */
    Label.prototype.type = "Label";
    /**
     * Line height to be considered in the label's message
     * @type {number}
     */
    Label.prototype.lineHeight = 20;


    /**
     * Initializer of the object will all the given configuration options
     * @param {Object} options
     */
    Label.prototype.init = function (options) {
        var defaults = {
            message: "New Label",
            orientation: "horizontal",
            fontFamily: "arial",
            size: 0,
            position: {
                location: "none",
                diffX: 0,
                diffY: 0
            },
            overflow: false,
            updateParent: false,
            parent: null
        };
        this.fontSizeOnZoom = [6, 8, 10, 13, 15];
        $.extend(true, defaults, options);
        this.setMessage(defaults.message)
            .setOverflow(defaults.overflow)
            .setMinHeight(defaults.minHeight)
            .setUpdateParent(defaults.updateParent)
            .setOrientation(defaults.orientation)
            .setFontFamily(defaults.fontFamily)
            .setFontSize(defaults.size)
            .setParent(defaults.parent)
            .setLabelPosition(defaults.position.location, defaults.position.diffX,
                defaults.position.diffY);

    };
    /**
     * Attach the corresponding listeners to this label
     * @chainable
     */
    Label.prototype.attachListeners = function () {
        var $label = $(this.html),
            $label = $(this.text);
        var $textField = $(this.textField);
        if (!this.html) {
            return this;
        }
        Label.superclass.prototype.attachListeners.call(this);
        if (!this.getCanvas().readOnly) {
            $($textField).mousedown (function (e) {
                e.stopPropagation();
            });
            $($textField).mouseup (function (e) {
                e.stopPropagation();
            });
            $($textField).click (function (e) {
                e.stopPropagation();
            });

        }
        return this;
    };
    /**
     * Creates the HTML of the label, the input text and the span for displaying the
     * message
     * @return {HTMLElement}
     */
    Label.prototype.createHTML = function () {
        Label.superclass.prototype.createHTML.call(this);
        this.html.style.textAlign = "center";
        this.html.style.align = "center";
        this.html.style.fontFamily = this.fontFamily;
        this.html.style.fontSize = this.fontSize + "pt";
        this.textField = document.createElement("input");
        this.textField.style.width = "200px";
        this.textField.style.position = "absolute";
        this.textField.style.display = "none";
        this.text = document.createElement("span");
        this.text.style.width = "auto";
        this.text.style.height = "auto";
        this.text.style.lineHeight = this.lineHeight * this.canvas.zoomFactor + "px";
        this.text.innerHTML = this.message;
        this.html.appendChild(this.text);
        this.html.appendChild(this.textField);
        this.html.style.zIndex = '120';
        return this.html;
    };
    /**
     * Displays the style of the label and adds the corresponding classes for
     * rotation
     * @chainable
     */
    Label.prototype.paint = function () {
        var $label = $(this.html);

        this.text.style.lineHeight = this.lineHeight * this.canvas.zoomFactor + "px";
        this.textField.value = this.message;
        this.text.innerHTML = this.message;

        this.html.style.verticalAlign = "middle";
        if (this.overflow) {
            this.html.style.overflow = "hidden";
        } else {
            this.html.style.overflow = "none";
        }

        this.displayText(true);
        if (this.orientation === "vertical") {
            $label.addClass('rotateText');
        } else {
            $label.removeClass('rotateText');
        }
        if (this.parent.onUpdateLabel) {
            this.parent.onUpdateLabel();
        }

        return this;

    };
    /**
     * Displays the label's message in its current orientation or the input text
     * @param {boolean} display true if we want to display the label's message or
     * false for the input text
     * @chainable
     */
    Label.prototype.displayText = function (display, e) {
        var y1 = 0,
            y2 = 0,
            diffY;
        if (display) {
            this.text.style.display = "block";
            this.textField.style.display = "none";
            if (this.orientation === "vertical") {
                this.textField.style.left = "0px";
            }
        } else {
            this.textField.style.display = "block";
            if (this.orientation === "vertical") {
                this.textField.style.left = this.width / 2 + "px";
                if (e) {
                    y1 = this.canvas.getTopScroll() + this.parent.getAbsoluteY() + this.parent.getZoomHeight() / 2;
                    y2 = e.pageY - this.canvas.getZoomY();
                    diffY = (y2 - y1);
                    this.textField.style.top = diffY + "px";
                }
            }
            this.text.style.display = "none";
        }
        return this;
    };
    /**
     * Sets the message of this label
     * @param {String} newMessage
     * @chainable
     */
    Label.prototype.setMessage = function (newMessage) {
        this.message = newMessage;
        if (this.text) {
            this.text.innerHTML = this.message;
            this.textField.value = this.message;
        }
        return this;
    };
    /**
     * Retrieves the message that this label is displaying
     * @return {String}
     */
    Label.prototype.getMessage = function () {
        return this.message;
    };
    /**
     * Retrieves the message that this label is displaying
     * @return {String}
     */
    Label.prototype.setMinHeight = function (minHeight) {
        this.minHeight = minHeight;
        return this;
    };
    /**
     * Sets the orientation of the text
     * @param {String} newOrientation It can be vertical or horizontal by default
     * @chainable
     */
    Label.prototype.setOrientation = function (newOrientation) {
        var $label;
        this.orientation = newOrientation;
        if (!this.html) {
            return this;
        }
        $label = $(this.html);
        if (newOrientation === "vertical") {
            $label.addClass("rotateText");
        } else {
            $label.removeClass("rotateText");
        }
        return this;
    };
    /**
     * Retrieves the orientation of this label's text
     * @return {String}
     */
    Label.prototype.getOrientation = function () {
        return this.orientation;
    };
    /**
     * Sets the font family of this label's displayed text
     * @param {String} newFontFamily
     * @chainable
     */
    Label.prototype.setFontFamily = function (newFontFamily) {
        this.fontFamily = newFontFamily;
        if (this.html) {
            this.html.style.fontFamily = this.fontFamily;
        }
        return this;
    };

    /**
     * Sets the font-size of this label's displayed text
     * @param {String} newFontSize
     * @chainable
     */
    Label.prototype.setFontSize = function (newFontSize) {
        if (newFontSize === 0) {
            this.fontSize = this.getZoomFontSize();
        } else {
            this.fontSize = newFontSize;
        }
        if (this.html) {
            this.html.style.fontSize = this.fontSize + "pt";
        }
        return this;
    };
    /**
     * Sets the property to determine if a label should update its parent
     * @param {boolean} newUpdateParent
     * @chainable
     */
    Label.prototype.setUpdateParent = function (newUpdateParent) {
        this.updateParent = newUpdateParent;
        return this;
    };
    /**
     * Sets the overflow property of this label
     * @param {boolean} newOverflow
     * @chainable
     */
    Label.prototype.setOverflow = function (newOverflow) {
        this.overflow = newOverflow;
        return this;
    };
    /**
     * Sets the position of the label regarding its parent, considering the location
     * and x and y differentials
     * @param {String} position location where we want to put the label relative to,
     * its parent, it can be top-left, top, top-right, center-left, center,
     * center-right, bottom-left, bottom, bottom-right
     * @param {number} diffX x coordinate pixels to move from its location
     * @param {number} diffY y coordinate pixels to move from its location
     * @chainable
     */
    Label.prototype.setLabelPosition = function (position, diffX, diffY) {
        var x,
            y,
            i,
            width = this.zoomWidth,
            height = this.zoomHeight,
            parent = this.parent,
            parentWidth,
            parentHeight,
            zoomFactor = this.canvas.zoomFactor,
            bottomHeightFactor = 4 * zoomFactor,
            positionString = [
                'top-left',
                'top',
                'top-right',
                'center-left',
                'center',
                'center-right',
                'bottom-left',
                'bottom',
                'bottom-right'
            ],
            orientation,
            orientationIndex = (this.orientation === "vertical") ? 1 : 0,
            positionCoordinates;
        if (!position || position === "") {
            position = "top-left";
        }
        if (diffX === undefined || diffX === null) {
            diffX = 0;
        }
        if (diffY === undefined || diffY === null) {
            diffY = 0;
        }
        if (parent && parent.family !== "Canvas") {
            parentWidth = parent.getZoomWidth();
            parentHeight = parent.getZoomHeight();
            orientation = [
                {x: width / 2, y: 0},
                {x: 0, y: height / 2}
            ];
            positionCoordinates = [
                {
                    x: -width / 2,
                    y: 0
                },
                {
                    x: parentWidth / 2 - width / 2,
                    y: 0
                },
                {
                    x: parentWidth - width / 2,
                    y: 0
                },
                {
                    x: -width / 2,
                    y: parentHeight / 2 - height / 2
                },
                {
                    // Changed width by this.zoomWidth and Height for apply zoom by canvas
                    x: parentWidth / 2 - this.zoomWidth / 2,
                    y: parentHeight / 2 - this.zoomHeight / 2
                },
                {
                    x: parentWidth - width,
                    y: parentHeight / 2 - height / 2
                },
                {
                    x: -width / 2,
                    y: parentHeight - bottomHeightFactor
                },
                {
                    x: parentWidth / 2 - width / 2,
                    y: parentHeight - bottomHeightFactor
                },
                {
                    x: parentWidth - width / 2,
                    y: parentHeight - bottomHeightFactor
                }
            ];
            for (i = 0; i < 9; i += 1) {
                if (position === positionString[i]) {
                    this.setPosition(
                        positionCoordinates[i].x / zoomFactor + diffX,
                        positionCoordinates[i].y / zoomFactor + diffY
                    );
                    break;
                }
            }

        }
        this.location = position;
        this.diffX = diffX;
        this.diffY = diffY;

        return this;
    };
    /**
     * Hides the span showing the label's message and display the input text ready
     * to be edited
     * @chainable
     */
    Label.prototype.getFocus = function (e) {
        var $textField = $(this.textField);
        this.displayText(false, e);
        this.canvas.currentLabel = this;
        $($textField).select();
        this.onFocus = true;
        $label = $(this.html);
        $label.removeClass("rotateText");
        if (this.parent.corona) {
            this.canvas.hideAllCoronas();
            this.canvas.cancelConnect();
        }
        return this;
    };
    /**
     * Hides the input text and display the label's message, and if the message's
     * changed, then it executes the editlabel command
     * @chainable
     */
    Label.prototype.loseFocus = function () {
        var command;
        this.canvas.currentLabel = null;
        if (this.textField.value !== this.message) {
            command = new PMUI.command.CommandEditLabel(this, this.textField.value);
            command.execute();
            this.canvas.commandStack.add(command);
            this.setLabelPosition(this.location, this.diffX, this.diffY);
        }
        this.paint();
        this.onFocus = false;
        if (this.orientation === "vertical") {
            $label = $(this.html);
            $label.addClass("rotateText");
        }
        return this;
    };
    /**
     * On Click handler, used to stop propagation when a label is being edited or
     * its parent is the canvas
     * @param {PMUI.draw.Label} label
     * @return {Function}
     */
    Label.prototype.onClick = function (label) {
        return function (e, ui) {

            if (label.parent.family === "Canvas") {
                e.stopPropagation();
            }
            if (label.onFocus) {
                e.stopPropagation();
            }
        };
    };
    /**
     * Double Click handler, used in order for this label to get focus and being
     * edited
     * @param {PMUI.draw.Label} label
     * @return {Function}
     */
    Label.prototype.onDblClick = function (label) {
        return function (e, ui) {
            var canvas = label.getCanvas(),
                $label = $(label.html);
            if (canvas.currentLabel) {
                canvas.currentLabel.loseFocus();
            }
            label.getFocus(e);
            e.stopPropagation();
            //e.preventDefault();
        };
    };
    /**
     * Returns the font-size according to the current zoom scale
     * @return {number}
     */
    Label.prototype.getZoomFontSize = function () {
        var canvas = this.canvas;
        this.fontSize = this.fontSizeOnZoom[canvas.zoomPropertiesIndex];
        return this.fontSize;
    };
    /**
     * Parse the messages in words length.
     * It returns an array with the length of all the words in the message
     * @return {Array}
     */
    Label.prototype.parseMessage = function () {
        var i,
            start = 0,
            result = [],
            word;
        while (this.message.charAt(start) === ' ') {
            start += 1;
        }
        word = 0;
        for (i = start; i < this.message.length; i += 1) {

            if (this.message.charAt(i) === ' ') {
                result.push(word);
                word = 0;
            } else {
                word += 1;
            }
        }
        result.push(word);
        return result;
    };
    /**
     * Updates the dimension of the label, according to its message, and if the
     * updateParent property is true then it will call the corresponding method to
     * update its parent according to the label's size
     * @chainable
     */
    Label.prototype.updateDimension = function (firstTime) {
        var divWidth = this.width || $(this.text).width(),
            newWidth,
            newHeight;
        divWidth = (this.orientation === "vertical") ? this.parent.height : this.zoomWidth;
        if (divWidth === this.parent.width) {
            divWidth = divWidth * 0.9;
        }
        if (divWidth < 10) {
            newWidth = this.parent.getWidth() * 0.9;
            this.initialWidth = this.parent.getWidth() * 0.9;
        } else {
            newWidth = divWidth;
        }

        newHeight = $(this.text).height();


        // Old code for this part
        this.setDimension(newWidth / this.canvas.zoomFactor,
            newHeight / this.canvas.zoomFactor);

        if (this.updateParent) {
            this.updateParentDimension();
        }
        this.setLabelPosition(this.location, this.diffX, this.diffY);
        this.width = newWidth;
        return this;

    };
    /**
     * Apply all properties necessary for this label in a given zoom scale
     * @chainable
     */
    Label.prototype.applyZoom = function () {
        var canvas = this.canvas;
        this.setFontSize(0);
        this.updateDimension();
        return this;
    };
    /**
     * Calls the method to update the label's parent dimension according to the
     * label's orientation
     * @chainable
     */
    Label.prototype.updateParentDimension = function () {

        if (this.orientation === "vertical") {
            this.updateVertically();
        } else {
            this.updateHorizontally();
        }
        if (this.parent.html) {
            this.parent.paint();
        }
        return this;
    };
    /**
     * Updates its parent height according to the size of the label
     * @chainable
     */
    Label.prototype.updateVertically = function () {
        var margin = 5,
            parent = this.parent,
            labelWidth = this.zoomWidth,
            newHeight,
            zoomFactor = this.canvas.zoomFactor;

        if (this.minHeight || this.maxHeight) {
            if (this.zoomHeight < this.maxHeight && this.zoomHeight > this.minHeight)
                newHeight = this.zoomHeight;
            if (this.zoomHeight > this.maxHeight)
                newHeight = this.maxHeight;
            if (this.zoomHeight < this.minHeight)
                newHeight = this.minHeight;
            if (this.zoomHeight > this.minHeight)
                newHeight = this.zoomHeight;
        }
        else {
            newHeight = this.zoomHeight;
        }
        parent.setDimension(parent.width, newHeight / zoomFactor);
        parent.updateChildrenPosition(0, 0);
        parent.refreshConnections();
        this.setLabelPosition(this.location, this.diffX, this.diffY);
        return this;
    };
    /**
     * Updates its parent width and height according to the new label's dimension
     * @chainable
     */
    Label.prototype.updateHorizontally = function () {
        var margin = 5,
            parent = this.parent,
            labelWidth = this.zoomWidth,
            labelHeight = this.zoomHeight,
            newWidth,
            newHeight,
            zoomFactor = this.canvas.zoomFactor;
        if (labelWidth > parent.zoomWidth - margin * 2) {
            newWidth = labelWidth + margin * 2;
        } else {
            newWidth = parent.zoomWidth;
        }
        if (labelHeight > parent.zoomHeight - margin * 2) {
            newHeight = labelHeight + margin * 2;
        } else {
            newHeight = parent.zoomHeight;
        }
        parent.refreshConnections();
        this.setLabelPosition(this.location, this.diffX, this.diffY);
        return this;
    };
    /**
     * Serializes this object
     * @return {Object}
     */
    Label.prototype.stringify = function () {
        // TODO:  USE CLASS STYLE IN THE METHODS OF THIS CLASS
        // TODO:  COMPLETE THE JSON
        /**
         * inheritedJSON = {
         *     id:  #
         *     x:  #,
         *     y:  #,
         *     width:  #,
         *     height:  #
         * }
         * @property {Object}
         */
        var inheritedJSON = {},
            thisJSON = {
                id: this.getID(),
                message: this.getMessage(),
                orientation: this.getOrientation(),
                position: {
                    location: this.location,
                    diffX: this.diffX,
                    diffY: this.diffY
                }
            };
        $.extend(true, inheritedJSON, thisJSON);
        return inheritedJSON;

    };

    PMUI.extendNamespace('PMUI.draw.Label', Label);

    if (typeof exports !== 'undefined') {
        module.exports = Label;
    }

}());
(function () {
    /**
     * @class PMUI.draw.CustomShape
     * This is a custom shape, where there can be applied styles, sprites and
     * decoration, it can have connections associated to it by ports, different
     * layers and labels as well
     *              
     *              //e.g.
     *              var customShape = new PMUI.draw.CustomShape({
     *                  //Determines whether the shape will be connected only in its
     *                  //middle points
     *                  connectAtMiddlePoints : true,
     *                  //The layers that will be instantiated with this shape
     *                  layers: [
     *                      {
     *                                                          {
     *                        layerName : "first-layer",
     *                        priority: 2,
     *                        visible: true,
     *                        style: {
     *                        cssClasses: ['bpmn_zoom']
     *                      },
     *                        zoomSprites : ['img_50_start',
     *                        'img_75_start', 'img_100_start',
     *                        'img_125_start', 'img_150_start']
     *                        }, {
     *                        layerName: "second-layer",
     *                        priority: 3,
     *                        visible: true
     *                        }
     *
     *                  ],
     *                  //Labels that belong to this shape
     *                  labels : [
     *                      {
     *                          message: "this is one label",
     *                          position: {
     *                              location : "bottom",
     *                              diffX: 0,
     *                              diffY: 5
     *                          }
     *                      }
     *                  ],
     *                  //The type of connections that are made with this shape,
     *                  //Each type differs of one another for the type of lines
     *                  //used in the connection
     *                  connectionType: "regular"
     *
     *              });
     *
     * @extends PMUI.draw.Shape
     *
     * @constructor
     * Creates an instance of a CustomShape
     * @param {Object} options configuration options used in a custom shape
     * @cfg {Boolean} [connectAtMiddlePoints=true] Determines whether shape's,
     * connections should be created only in the middle points of its sides
     * @cfg {Array} [layers=[]] Configuration options of all layers that will be,
     * instantiated with this shape
     * @cfg {Array} [labels=[]] Configuration options of all labels that will be
     * instantiated with this shape
     * @cfg {String} [connectionType="regular"] Type of lines that will be used in
     * all connections involving this shape
     */
    var CustomShape = function (options) {
        /**
         * List of all the layers associated to this shape
         * @property {PMUI.util.ArrayList}
         */
        this.layers = new PMUI.util.ArrayList();

        CustomShape.superclass.call(this, options);
        /**
         * List of all the ports associated to this shape
         * @property {PMUI.util.ArrayList}
         */
        this.ports = new PMUI.util.ArrayList();

        /**
         * List of all the labels associated to this shape
         * @property {PMUI.util.ArrayList}
         */
        this.labels = new PMUI.util.ArrayList();
        /**
         * List of all the zoom properties in different zoom scales
         * @property {PMUI.util.ArrayList}
         */
        this.zoomProperties = new PMUI.util.ArrayList();

        /**
         * Inner figure for drawing connection limits
         * @property {Array}
         */
        this.limits = [0, 0, 0, 0];
        /**
         * Border to be added to determine the new position of the port
         * @property {Array}
         */
        this.border = [
            {x: 0, y: 0},
            {x: 0, y: 0},
            {x: 0, y: 0},
            {x: 0, y: 0}
        ];
        /**
         * Determines which type of drag behavior should be assigned
         * @property {number}
         */
        this.dragType = this.CANCEL;
        /**
         * Reference to the point where a connection drag is being started
         * @property {PMUI.util.Point}
         */
        this.startConnectionPoint = null;
        /**
         * if set to true, a port will only be added at its middle points
         * @property {Boolean}
         */
        this.connectAtMiddlePoints = null;
        /**
         * Auxiliary property for saving the previous x coordinate in the dragging
         * procedure for multiple drag
         * @property {Number}
         */
        this.previousXDragPosition = 0;
        /**
         * Auxiliary property for saving the previous y coordinate in the dragging
         * procedure for multiple drag
         * @property {Number}
         */
        this.previousYDragPosition = 0;
        /**
         * The type of lines for connections made with this shape
         * @property {String}
         */
        this.connectionType = null;
        // init the custom shape
        CustomShape.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.draw.Shape', CustomShape);
    /**
     * Type the instances of this class
     * @property {String}
     */
    CustomShape.prototype.type = "CustomShape";
    /**
     * Family where this class and all its subclasses belong
     * @property {String}
     * @readonly
     */
    CustomShape.prototype.family = "CustomShape";
    /**
     * Reference to a drop behaviors for containers
     * @property {PMUI.behavior.ContainerDropBehavior}
     */
    CustomShape.prototype.containerDropBehavior = null;
    /**
     * Reference to a drop behavior that allows us to make connections
     * @property {PMUI.behavior.ConnectionDropBehavior}
     */
    CustomShape.prototype.connectionDropBehavior = null;
    /**
     * Reference to a drop behavior that has no acceptable droppables
     * @property {PMUI.behavior.NoDropBehavior}
     */
    CustomShape.prototype.noDropBehavior = null;
    /**
     * Constant that represents that a drag behavior for making connections should
     * be used
     * @property {Number}
     */
    CustomShape.prototype.CONNECT = 1;
    /**
     * Constant that represents that a drag behavior for moving the shape should be
     * used
     * @property {Number}
     */
    CustomShape.prototype.DRAG = 2;
    /**
     * Constant that represents that no drag behavior should be used
     * @property {Number}
     */
    CustomShape.prototype.CANCEL = 0;

    /**
     * Initializes the basic attributes for the custom shape, and also the
     * particular objects the shape needs to instantiate
     * //TODO Base limits on zoom
     * @param options
     */
    CustomShape.prototype.init = function (options) {

        var defaults = {
                connectAtMiddlePoints: true,
                layers: [],
                labels: [],
                connectionType: "regular",
                drag: "customshapedrag"
            },
            i;

        // init the object with NO configurable options
        this.limits = [5, 5, 5, 5, 5];
        this.setStartConnectionPoint(new PMUI.util.Point(0, 0));

        // init the object with configurable options
        $.extend(true, defaults, options);
        for (i = 0; i < defaults.layers.length; i += 1) {
            this.createLayer(defaults.layers[i]);
        }
        for (i = 0; i < defaults.labels.length; i += 1) {
            this.createLabel(defaults.labels[i]);
        }
        this.setConnectAtMiddlePoints(defaults.connectAtMiddlePoints)
            .setConnectionType(defaults.connectionType);
    };
    /**
     * Creates a layer given its configuration options
     * @param {Object} options configuration options
     * @return {PMUI.draw.Layer}
     */
    CustomShape.prototype.createLayer = function (options) {

        var layer;
        options.parent = this;
        layer = new PMUI.draw.Layer(options);
        this.addLayer(layer);
        return layer;
    };
    /**
     * Creates a label given its configuration options
     * @param {Object} options configuration options for instantiating a label
     * @return {PMUI.draw.Label}
     */
    CustomShape.prototype.createLabel = function (options) {
        var label;
        options.canvas = this.canvas;
        options.parent = this;
        if (options.width === 0) {
            options.width = this.width * 0.9;
        }
        label = new PMUI.draw.Label(options);
        this.addLabel(label);
        return label;
    };

    /**
     * Adds a label to the array of labels and also appends its html
     * @param {PMUI.draw.Label} label
        */
    CustomShape.prototype.addLabel = function (label) {
        if (this.html) {
            //so we just append it to the parent
            label.parent = this;
            this.html.appendChild(label.getHTML());
        }
        if (!this.labels.contains(label)) {
            this.labels.insert(label);
        }
    };
    /**
     * Creates the html for the shape, its layers and labels
     * @returns {HTMLElement}
     */
    CustomShape.prototype.createHTML = function () {
        var i,
            label;
        CustomShape.superclass.prototype.createHTML.call(this);

        this.style.addClasses(["custom_shape"]);

        this.layers.sort(PMUI.draw.Layer.prototype.comparisonFunction);

        for (i = 0; i < this.layers.getSize(); i += 1) {
            this.html.appendChild(this.layers.get(i).getHTML());

        }
        for (i = 0; i < this.labels.getSize(); i += 1) {
            label = this.labels.get(i);
            this.addLabel(label);
            label.attachListeners();

        }
        return this.html;
    };

    /**
     * This function will attach all the listeners corresponding to the CustomShape
     * @chainable
     */
    CustomShape.prototype.attachListeners = function () {
        if (this.html === null) {
            return this;
        }

        if (!this.canvas.readOnly) {
            var $customShape = $(this.html)
                .click(this.onClick(this));
            //drag options for the added shapes
            $customShape.on("mousedown", this.onMouseDown(this));
            $customShape.mousemove(this.onMouseMove(this));
            $customShape.mouseup(this.onMouseUp(this));
            $customShape.on("contextmenu", function (e) {
                e.preventDefault();
            });
            this.updateBehaviors();
        }

        return this;

    };

    /**
     * Apply the styles related to the shape, its layers and labels
     * @chainable
     */
    CustomShape.prototype.paint = function () {
        var i,
            label;

        CustomShape.superclass.prototype.paint.call(this);

        //TODO Apply the style of the given shape
        for (i = 0; i < this.layers.getSize(); i += 1) {
            this.layers.get(i).paint();
        }
        for (i = 0; i < this.ports.getSize(); i += 1) {
            this.ports.get(i).paint();
        }
        for (i = 0; i < this.labels.getSize(); i += 1) {
            label = this.labels.get(i);
            label.paint();
        }
        return this;
    };

    /**
     * Updates properties obtained when the HTML is on the DOM
     * @chainable
     */
    CustomShape.prototype.updateHTML = function () {
        var i,
            label;
        this.setDimension(this.width, this.height);
        for (i = 0; i < this.labels.getSize(); i += 1) {
            label = this.labels.get(i);
            label.paint();
            label.updateDimension();
        }
        return this;
    };
    /**
     *  Verify if a shape is into a set of related shapes
     * @param shape {CustmShape} item to test
     * @param relatedShapes {Array} items
     * @returns {boolean}
     */
    CustomShape.prototype.containsElement = function (shape, relatedShapes) {
        var i,
            max;
        if (jQuery.isArray(relatedShapes)) {
            for (i = 0, max = relatedShapes.length; i < max; i += 1) {
                if (relatedShapes[i].getID() === shape.getID()) {
                    return true;
                }
            }
        }
        return false
    };
    /**
     * Repaints connections related to this shape
     * @param {Boolean} inContainer Determines if the points of a connection should
     * be saved for its reconstruction
     * @chainable
     */
    CustomShape.prototype.refreshConnections = function (inContainer,
                                                         relatedShapes,
                                                         delta) {
        var i,
            connection,
            ports = this.ports,
            port,
            srcElem,
            destElem;

        for (i = 0; i < ports.getSize(); i += 1) {
            port = ports.get(i);
            port.setPosition(port.getX(), port.getY());
            connection = port.connection;

            srcElem = connection.getSrcPort().parent;
            destElem = connection.getDestPort().parent;

            if (!(this.containsElement(srcElem, relatedShapes)
                && this.containsElement(destElem, relatedShapes))) {
                    connection.reconnectManhattah(inContainer);
                    connection.setSegmentMoveHandlers()
                        .checkAndCreateIntersectionsWithAll();
                    this.canvas.triggerConnectionStateChangeEvent(connection);
            } else  if (delta) {
                if (!this.canvas.refreshArray.contains(connection)) {
                    connection.canvas.refreshArray.insert(connection);
                }
            }
        }
        return this;
    };
    /**
     * Updates the properties of this shape layers according to the shape itself
     * @chainable
     */
    CustomShape.prototype.updateLayers = function () {
        var i, j,
            layer;
        for (i = 0; i < this.getLayers().getSize(); i += 1) {
            layer = this.getLayers().get(i);
            layer.setProperties();
        }
        return this;
    };
    /**
     * Returns what it should be the next layer if there is such in the DOM tree
     * or null otherwise
     * @param {PMUI.draw.Layer} layer
     * @returns {PMUI.draw.Layer}
     */
    CustomShape.prototype.findLayerPosition = function (layer) {
        var nextLayer = null, //holds the next layer regarding the position where
        // the new layer should be inserted
            minVal = 10000000, //holds the minimum value of all the values greater
        // than the newLayer priority
            i,
            currLayer,
            currPriority;
        // iterate through all the layers and find the minimum priority of all
        // the priorities that are greater than the priority of the current layer
        for (i = 0; i < this.layers.getSize(); i += 1) {
            currLayer = this.layers.get(i);
            currPriority = currLayer.getPriority();
            if (currPriority > layer.getPriority()) {
                if (minVal > currPriority) {
                    minVal = currPriority;
                    nextLayer = currLayer;
                }
            }
        }
        return nextLayer;
    };

    /**
     * Adds a new layer to the corresponding shape
     * @param {PMUI.draw.Layer} newLayer
     * @chainable
     */
    CustomShape.prototype.addLayer = function (newLayer) {
        // gets the layer that would come next the new one
        var nextLayer = this.findLayerPosition(newLayer);
        // if there is none it means that the new layer has the highest priority
        // of all
        if (this.html) {
            if (!nextLayer) {
                //so we just append it to the parent
                this.html.appendChild(newLayer.getHTML());
            } else {
                //otherwise we append it before nextLayer
                this.html.insertBefore(newLayer.getHTML(), nextLayer.getHTML());
            }
            newLayer.paint();
        }
        this.layers.insert(newLayer);

        return this;
    };

    /**
     * Finds a given layer by ID or null of it doesn't exist
     * @param {String} layerID
     * @returns {PMUI.draw.Layer}
     */
    CustomShape.prototype.findLayer = function (layerID) {
        return this.layers.find('id', layerID);
    };

    /**
     * Set the dimension of the customShape
     * @param {Number} newWidth
     * @param {Number} newHeight
     */
    CustomShape.prototype.setDimension = function (newWidth, newHeight) {
        CustomShape.superclass.prototype.setDimension.call(this, newWidth, newHeight);
        this.updateLabels();
        this.updateLayers();
        return this;
    };
    /**
     * Updates the labels properties if necessary
     * @abstract
     * @template
     * @protected
     */
    CustomShape.prototype.updateLabels = function () {

    };
    /**
     * Makes a layer non-visible
     * @param {String} layerID
     * @returns {PMUI.draw.CustomShape}
     */
    CustomShape.prototype.hideLayer = function (layerID) {
        var currLayer;
        if (!layerID || typeof layerID !== "string") {
            return this;
        }
        currLayer = this.findLayer(layerID);
        if (!currLayer) {
            return this;
        }

        currLayer.setVisible(false);
        return this;

    };
    /**
     * Makes a layer visible
     * @param {String} layerID
     * @returns {PMUI.draw.CustomShape}
     */
    CustomShape.prototype.showLayer = function (layerID) {
        var currLayer;
        if (!layerID || typeof layerID !== "string") {
            return this;
        }

        currLayer = this.findLayer(layerID);
        if (!currLayer) {
            return this;
        }
        currLayer.setVisible(true);


        return this;
    };


    /**
     * Adds a port to the Shape
     * @param {PMUI.draw.Port} port
     * @param {Number} xPortCoord
     * @param {Number} yPortCoord
     * @chainable
     */
    CustomShape.prototype.addPort = function (port, xPortCoord, yPortCoord, triggerChange, sourcePort) {


        //where the user is attempting to create the port
        //TODO Fix trowing custom events by using properties of the objects
        var position = new PMUI.util.Point(xPortCoord, yPortCoord);

        //set the corresponding shape where the port would be created
        port.setParent(this);
        port.setCanvas(this.canvas);
        this.definePortPosition(port, position, sourcePort);

        //append the html to the DOM and paint the port
        this.html.appendChild(port.getHTML());

        port.paint();

        //insert the port to the ports array of the shape
        this.ports.insert(port);
        return this;
    };
    /**
     *
     * Removes a port from the Shape
     * @param {PMUI.draw.Port} port
     * @chainable
     */
    CustomShape.prototype.removePort = function (port) {
        this.ports.remove(port);
        return this;
    };
    /**
     * Determines the position where the port will be located
     * @param {PMUI.draw.Port} port
     * @param {PMUI.util.Point} point
     * @param {PMUI.draw.Port} sourcePort
     * @chainable
     */
    CustomShape.prototype.definePortPosition = function (port, point, sourcePort) {
        var canvas = this.canvas,
            directionArray = [this.TOP, this.RIGHT, this.BOTTOM, this.LEFT],
            // midPointArray is used when connectAtMiddlePoints is set to TRUE
            midPointArray = [
                new PMUI.util.Point(Math.round(this.zoomWidth / 2), 0), // TOP
                new PMUI.util.Point(this.zoomWidth, Math.round(this.zoomHeight / 2)), // RIGHT
                new PMUI.util.Point(Math.round(this.zoomWidth / 2), this.zoomHeight), // BOTTOM
                new PMUI.util.Point(0, Math.round(this.zoomHeight / 2))               // LEFT
            ],
            // sideArray is used when connectAtMiddlePoints is set to FALSE
            sideArray = [
                new PMUI.util.Point(point.x, 0), // TOP
                new PMUI.util.Point(this.getZoomWidth(), point.y), // RIGHT
                new PMUI.util.Point(point.x, this.getZoomHeight()), // BOTTOM
                new PMUI.util.Point(0, point.y)                           // LEFT
            ],
            usedArray, // selects either the midPointArray or the side array
            direction,
            i,
            candidateDistance,
            minDistance,
            option,
            border,
            directionBorderMultiplier = [-1, 1, 1, -1],
            rightBorderMultiplier = [0, 1, 0, 0],
            bottomBorderMultiplier = [0, -2, 0, 1];

        // if the shape has the connectAtMiddlePoints flag on then use the midPoints
        usedArray = this.connectAtMiddlePoints ? midPointArray :  sideArray;

        //console.log(this.connectAtMiddlePoints, usedArray, point, sourcePort);
        // if the shape has a source port available then use manhattan distance
        // instead of squaredDistance
        option = "getSquaredDistance";
        if (sourcePort && this.connectAtMiddlePoints) {
            option = "getManhattanDistance";
        }
        direction = undefined;  //obtain location of the port
        minDistance = Infinity;
        // get the minimum distance between 2 points;
        for (i = 0; i < usedArray.length; i += 1) {
            candidateDistance = point[option](usedArray[i]);
            if (minDistance > candidateDistance) {
                minDistance = candidateDistance;
                direction = directionArray[i];
            }
        }

        border = this.getBorderConsideringLayers();
        for (i = 0; i < 4; i += 1) {
            this.border[i].x =
                (border * directionBorderMultiplier[i] +
                    border * rightBorderMultiplier[i]);
            this.border[i].y =
                (border * directionBorderMultiplier[i] +
                    border * bottomBorderMultiplier[i]);
        }
        // because of the zIndex problem move the ports towards the center
        // of the shape (this is done when the destDecorator is selected)
        port.setDirection(direction);
        // setPosition logic: 
        // since the port must face the border of the shape (or the shape if it
        // doesn't have a border) first let's move the port according to the
        // direction of the port (up -> -1 * border, right -> 1 * border, bottom ->
        // 1 * border, left -> -1 * border)
        // after the port will be right in the edge of the shape but now the
        // multiplier has also affected the positioning of the port if it's located
        // in the right or in the bottom (the port will move 2 * border in the
        // y-axis or x-axis) so let's reverse that movement using another array
        port.setPosition(
            (
                usedArray[direction].x
                    - port.getWidth() / 2
            ),
            (
                usedArray[direction].y
                    - port.getHeight() / 2
            )
        );

        // determines the percentage of port in relation with the shape's width or
        // height (useful to determine the new position of the port while resizing)
        port.determinePercentage();

        return this;
    };

    /**
     * Returns the border of this shape or the border of its layers (max)
     * @return {Number}
     */
    CustomShape.prototype.getBorderConsideringLayers = function () {
        var border = parseInt(this.style.getProperty('borderTopWidth') || 0, 10),
            layer,
            i;
        for (i = 0; i < this.getLayers().getSize(); i += 1) {
            layer = this.getLayers().get(i);
            border = Math.max(border, parseInt(
                layer.style.getProperty('borderTopWidth') || 0,
                10
            ));
        }
        return border;
    };

    /**
     * Show  all the ports of the Shape
     * @chainable
     */
    CustomShape.prototype.showPorts = function () {
        var i;
        for (i = 0; i < this.ports.getSize(); i += 1) {
            this.ports.get(i).show();
        }
        return this;
    };

    /**
     * hide  all the ports of the Shape
     * @chainable
     */
    CustomShape.prototype.hidePorts = function () {
        var i;
        for (i = 0; i < this.ports.getSize(); i += 1) {
            this.ports.get(i).hide();
        }
        return this;
    };

    /**
     * Updates the position of the ports regarding the CustomShape and two
     * differentials
     * TODO Improve triggering of events with ports own properties
     * @param {Number} xDiff
     * @param {Number} yDiff
     * @chainable
     */
    CustomShape.prototype.updatePortsPosition = function (xDiff, yDiff) {
        var i,
            port,
            ports = this.ports;
        for (i = 0; i < ports.getSize(); i += 1) {
            port = ports.get(i);
            if (port.direction === this.RIGHT || port.direction === this.BOTTOM) {
                port.oldX = port.x;
                port.oldY = port.y;
                port.oldAbsoluteX = port.absoluteX;
                port.oldAbsoluteY = port.absoluteY;
                port.setPosition(port.x + xDiff, port.y + yDiff, true);
                port.changePosition(port.oldX, port.oldY, port.oldAbsoluteX,
                    port.oldAbsoluteY);
            } else {
                port.setPosition(port.x, port.y, true);
            }
            port.connection.disconnect().connect();
            port.connection.setSegmentMoveHandlers();
        }
        return this;
    };

    /**
     * Recalculates a port position given the port
     * TODO Determine if this method is necessary
     * @param {PMUI.draw.Port} port
     * @chainable
     */
    CustomShape.prototype.recalculatePortPosition = function (port) {
        var x = 0,
            y = 0;

        switch (port.getDirection()) {
            case 2:
                y = this.getZoomHeight();
            case 0:
                x = this.getZoomWidth() / 2;
                break;
            case 1:
                x = this.getZoomWidth();
            case 3:
                y = this.getZoomHeight() / 2;
                break;
        }

        port.setPosition(
            Math.round(x - (port.getWidth() / 2)),
            Math.round(y - (port.getHeight() /2)));
        return this;
    };

    /**
     * Initializes properties to to save the current position of the ports
     * @chainable
     */
    CustomShape.prototype.initPortsChange = function () {
        var i,
            ports = this.ports,
            port;
        for (i = 0; i < ports.getSize(); i += 1) {
            port = ports.get(i);
            port.oldX = port.x;
            port.oldY = port.y;
            port.oldAbsoluteX = port.absoluteX;
            port.oldAbsoluteY = port.absoluteY;
        }
        return this;
    };

    /**
     * Trigger to save the port changes
     * @chainable
     */
    CustomShape.prototype.firePortsChange = function () {
        var i,
            ports = this.ports,
            port;
        for (i = 0; i < ports.getSize(); i += 1) {
            port = ports.get(i);
            // port is not a shape so use call
            CustomShape.superclass.prototype.changePosition.call(this, port.oldX, port.oldY,
                port.oldAbsoluteX, port.oldAbsoluteY);
        }
        return this;
    };
    /**
     * Updates ports and connections of the current shape
     * @chainable
     */
    CustomShape.prototype.refreshShape = function () {
        CustomShape.superclass.prototype.refreshShape.call(this);
        this.updatePortsOnZoom()
            .refreshConnections(false);
        this.paint();
        return this;
    };
    /**
     * Updates the position of the ports after applying a zoom scale
     * @chainable
     */
    CustomShape.prototype.updatePortsOnZoom = function () {
        var i,
            ports = this.ports,
            port,
            zoomFactor = this.canvas.zoomFactor,
            prevZoomFactor = (this.canvas.prevZoom * 25 + 50) / 100,
            portFactor = (ports.getSize() > 0) ? ports.get(0).width / 2 : 0,
            srcDecorator,
            destDecorator,
            xCoords = [
                this.zoomWidth / 2 - portFactor,
                this.zoomWidth - portFactor,
                this.zoomWidth / 2 - portFactor,
                -portFactor
            ],
            yCoords = [
                -portFactor,
                this.zoomHeight / 2 - portFactor,
                this.zoomHeight - portFactor,
                this.zoomHeight / 2 - portFactor
            ];

        for (i = 0; i < ports.getSize(); i += 1) {
            port = ports.get(i);
            if (this.connectAtMiddlePoints) {
                port.setPosition(xCoords[port.direction], yCoords[port.direction]);
            } else {
                port.setPosition(port.x / prevZoomFactor * zoomFactor,
                    port.y / prevZoomFactor * zoomFactor);
            }
            srcDecorator = port.connection.srcDecorator;
            destDecorator = port.connection.destDecorator;
            if (srcDecorator) {
                srcDecorator.applyZoom();
            }
            if (destDecorator) {
                destDecorator.applyZoom();
            }
        }
        return this;
    };
    /**
     * TODO Determine if this method is necessary
     */
    CustomShape.prototype.calculateLabelsPercentage = function () {
        var i, label;
        for (i = 0; i < this.labels.getSize(); i += 1) {
            label = this.labels.get(i);
            label.xPercentage = label.getX() / this.getWidth();
            label.yPercentage = label.getY() / this.getHeight();
        }

    };

    /**
     * Updates the labels position according to its configuration properties
     * @chainable
     */
    CustomShape.prototype.updateLabelsPosition = function () {
        var i,
            label;
        for (i = 0; i < this.labels.getSize(); i += 1) {
            label = this.labels.get(i);
            label.setLabelPosition(label.location, label.diffX, label.diffY);
        }
        return this;
    };

    /**
     * Returns the respective drag behavior according to a given point
     * @return {Number}
     */
    CustomShape.prototype.determineDragBehavior = function (point) {
        // limit to consider inside the shape
        var Point = PMUI.util.Point,
            Geometry = PMUI.draw.Geometry,
            limit = this.limits[this.canvas.zoomPropertiesIndex],

            border = parseInt(this.style.getProperty('border') || 0, 10);

        // if the point is inside the rectangle determine the behavior
        // (drag or connect)
        if (Geometry.pointInRectangle(point, new Point(0, 0),
                new Point(this.zoomWidth + 2 * border,
                    this.zoomHeight + 2 * border))) {
            // if the shape is inside the inner rectangle then drag
            if (Geometry.pointInRectangle(point,
                    new Point(border + limit, border + limit),
                    new Point(this.zoomWidth + border - limit,
                        this.zoomHeight + border - limit))) {
                return this.DRAG;
            }
            return this.CONNECT;
        }

        // if the mouse pointer is outside then return cancel
        return this.CANCEL;
    };
    /**
     * Creates a drag helper for drag and drop operations for the helper property
     * in jquery ui draggable
     * TODO Create a singleton object for this purpose
     * @returns {String} html
     */
    CustomShape.prototype.createDragHelper = function () {
        var html = document.createElement("div");

        // can't use class style here
        html.style.width = 8 + "px";
        html.style.height = 8 + "px";
        html.style.backgroundColor = "black";
        html.style.zIndex = 2 * PMUI.draw.Shape.prototype.MAX_ZINDEX;
        html.id = "drag-helper";
        html.className = "drag-helper";
        return html;
    };

    /**
     * Handler for the onmousedown event, changes the draggable properties
     * according to the drag behavior that is being applied
     * @param {PMUI.draw.CustomShape} CustomShape
     * @returns {Function}
     */
    CustomShape.prototype.onMouseDown = function (customShape) {
        return function (e, ui) {
            var canvas = customShape.canvas;
            if (e.which === 3) {
                $(canvas.html).trigger("rightclick", [e, customShape]);
            } else {

                if (customShape.dragType === customShape.DRAG) {
                    customShape.setDragBehavior("customshapedrag");

                } else if (customShape.dragType === customShape.CONNECT) {
                    customShape.setDragBehavior("connection");
                } else {
                    customShape.setDragBehavior("nodrag");
                }
                customShape.dragging = true;
            }
            e.stopPropagation();
        };
    };


    /**
     * On Mouse Up handler it allows the shape to recalculate drag behavior
     * whenever there was a mouse down event but no drag involved
     * @param {PMUI.draw.CustomShape} customShape
     * @return {Function}
     */
    CustomShape.prototype.onMouseUp = function (customShape) {
        return function (e, ui) {
            customShape.dragging = false;

        };
    };

    /**
     * Handler for the onmousemove event, determines the drag behavior that is
     * being applied, the coordinates where the mouse is currently located and
     * changes the mouse cursor
     * @param {PMUI.draw.CustomShape} customShape
     * @returns {Function}
     */

    CustomShape.prototype.onMouseMove = function (customShape) {
        return function (e, ui) {
            var $customShape,
                canvas,
                realPoint, 
                auxPoint;

            if (customShape.dragging || customShape.entered) {
                return;
            }
    //TODO ADD TO UTILS A FUNCTION TO RETRIEVE A POINT RESPECTING THE SHAPE
            $customShape = $(customShape.html);
            canvas = customShape.getCanvas();

            realPoint = canvas.relativePoint(e);

            customShape.startConnectionPoint.x = realPoint.x - customShape.absoluteX;
            customShape.startConnectionPoint.y = realPoint.y - customShape.absoluteY;
            //console.log(customShape.startConnectionPoint);
            auxPoint = new PMUI.util.Point(e.pageX - canvas.getX() -
                customShape.absoluteX + canvas.getLeftScroll(),
                e.pageY - canvas.getY() -
                customShape.absoluteY + canvas.getTopScroll());

            customShape.dragType = customShape
                .determineDragBehavior(auxPoint);
            //customShape.startConnectionPoint = auxPoint;

            if (customShape.dragType === customShape.DRAG) {
                $customShape.css('cursor', 'move');
            } else if (customShape.dragType === customShape.CONNECT) {
                $customShape.css('cursor', 'crosshair');

            } else {
                $customShape.css('cursor', 'default');
            }
            //e.stopPropagation();
        };
    };

    /**
     * Handler of the onClick Event hides the selected ports and resize Handlers if
     * any and show its corresponding resize handler
     * @param {PMUI.draw.CustomShape} customShape
     * @returns {Function}
     */
    CustomShape.prototype.onClick = function (customShape) {
        return function (e, ui) {
            var isCtrl = false,
                canvas = customShape.canvas,
                currentSelection = canvas.currentSelection,
                currentLabel = canvas.currentLabel;

            if (e.ctrlKey) { // Ctrl is also pressed
                isCtrl = true;
            }

            // hide the current connection if there was one
            customShape.canvas.hideCurrentConnection();

            if (e.which === 3) {        // right click
                e.preventDefault();
                // trigger right click
                customShape.canvas.triggerRightClickEvent(customShape);
            } else {
                if (!customShape.wasDragged) {
                    // if the custom shape was not dragged (this var is set to true
                    // in custom_shape_drag_behavior >> onDragEnd)
                    if (isCtrl) {
                        if (currentSelection.contains(customShape)) {
                            // remove from the current selection
                            canvas.removeFromSelection(customShape);
                        } else {
                            // add to the current selection
                            canvas.addToSelection(customShape);
                        }

                    } else {
                        canvas.emptyCurrentSelection();
                        canvas.addToSelection(customShape);
                    }
                }
                if (!currentSelection.isEmpty()) {
                    canvas.triggerSelectEvent(currentSelection.asArray());
                }
            }

            if (this.helper) {
                $(this.helper.html).remove();
            }

            // if (currentLabel) {
            //     currentLabel.loseFocus();
            //     $(currentLabel.textField).focusout();
            // }
            customShape.wasDragged = false;
    //        customShape.canvas.setCurrentShape(customShape);
            e.stopPropagation();
        };
    };

    /**
     * Empty function to perform some actions when parsing a diagram (called
     * from Canvas.parse)
     * @template
     * @protected
     */
    CustomShape.prototype.parseHook = function () {
    };

    /**
     * Returns a list of ports related to the shape
     * @returns {PMUI.util.ArrayList}
     */
    CustomShape.prototype.getPorts = function () {
        return this.ports;
    };
    /**
     * Returns a list of Layers related to the shape
     * @returns {PMUI.util.ArrayList}
     */
    CustomShape.prototype.getLayers = function () {
        return this.layers;
    };

    /**
     * Returns the labels associated to the current shape
     * @return {PMUI.util.ArrayList}
     */
    CustomShape.prototype.getLabels = function () {
        return this.labels;
    };

    /**
     * Applies the current zoom to the corresponding shape its layers and labels
     * @chainable
     */
    CustomShape.prototype.applyZoom = function () {
        var i,
            label;

        CustomShape.superclass.prototype.applyZoom.call(this);

        for (i = 0; i < this.layers.getSize(); i += 1) {
            this.layers.get(i).applyZoom();
        }

        for (i = 0; i < this.labels.getSize(); i += 1) {
            label = this.labels.get(i);
            label.applyZoom();
            label.setLabelPosition(label.location, label.diffX, label.diffY);
            //label.setPosition(label.x, label.y);
        }

        return this;
    };

    /**
     * Sets the start point of a connection corresponding to this shape
     * @param {PMUI.util.Point} point
     * @chainable
     */
    CustomShape.prototype.setStartConnectionPoint = function (point) {
        this.startConnectionPoint = point;
        return this;
    };
    /**
     * Sets the connectAtMiddlePoints property
     * @param  {Boolean} connect
     * @chainable
     */
    CustomShape.prototype.setConnectAtMiddlePoints = function (connect) {
        this.connectAtMiddlePoints = connect;
        return this;
    };
    /**
     * Returns whether a shape connections will be done only in the middle points of
     * its sides or not
     * @return {Boolean}
     */
    CustomShape.prototype.getConnectAtMiddlePoints = function () {
        return this.connectAtMiddlePoints;
    };
    /**
     * Sets the connection type of the shape
     * @param {String} newConnType
     * @chainable
     */
    CustomShape.prototype.setConnectionType = function (newConnType) {
        this.connectionType = newConnType;
        return this;
    };
    /**
     * Returns the connection type of the shape
     * @return {String}
     */
    CustomShape.prototype.getConnectionType = function () {
        return this.connectionType;
    };
    /**
     * Serializes this object
     * @return {Object}
     */
    CustomShape.prototype.stringify = function () {
        /**
         * inheritedJSON = {
         *     id:  #
         *     x:  #,
         *     y:  #,
         *     width:  #,
         *     height:  #
         * }
         * @property {Object}
         */
        var sLayers = [],
            labels = [],
            i,
            inheritedJSON,
            thisJSON;

        // serialize layers
        for (i = 0; i < this.layers.getSize(); i += 1) {
            sLayers.push(this.layers.get(i).stringify());
        }

        // serialize labels
        for (i = 0; i < this.labels.getSize(); i += 1) {
            labels.push(this.labels.get(i).stringify());
        }

        inheritedJSON = CustomShape.superclass.prototype.stringify.call(this);
        thisJSON = {
            canvas: this.canvas.getID(),
            layers: sLayers,
            labels: labels,
            connectAtMiddlePoints: this.getConnectAtMiddlePoints(),
            connectionType: this.getConnectionType(),
            parent: this.parent.getID()
        };
        $.extend(true, inheritedJSON, thisJSON);
        return inheritedJSON;
    };

    /**
     * Builds a custom shape based on the parameter 'json'
     * @param {String} json
     * @chainable
     */
    CustomShape.prototype.parseJSON = function (json) {
        this.initObject(json);
        return this;
    };

    PMUI.extendNamespace('PMUI.draw.CustomShape', CustomShape);

    if (typeof exports !== 'undefined') {
        module.exports = CustomShape;
    }

}());

(function () {
    /**
     * @abstract
     * @class PMUI.draw.RegularShape
     * @extend PMUI.draw.Shape
     * The class RegularShape represents all
     * regular shapes created in the canvas such as, rectangles, ovals, ports, and
     * handlers
     *
     * This class will hold all the common behavior of regular shapes
     * like rectangles or ovals
     *
     * @constructor
     * Initializes a regular shape
     */
    var RegularShape = function (options) {
        RegularShape.superclass.call(this, options);

        /**
         * color of the shape
         * @property {PMUI.util.Color}
         */
        this.color = new PMUI.util.Color();

        /**
         * Graphics for this regular shape
         */
        this.graphics = null;
    };

    PMUI.inheritFrom('PMUI.draw.Shape', RegularShape);

    /**
     * Type of the shape
     * @property {String}
     */
    RegularShape.prototype.type = "RegularShape";

    /**
     * Returns the color of the shape
     * @returns {PMUI.util.Color}
     */
    RegularShape.prototype.getColor = function () {
        return this.color;
    };

    /**
     * Sets the color of the shape
     * @returns {PMUI.draw.RegularShape}
     * @param {PMUI.util.Color} newColor
     */
    RegularShape.prototype.setColor = function (newColor) {
        if (newColor.type && newColor.type === "Color") {
            this.color = newColor;
        }
        return this;
    };

    PMUI.extendNamespace('PMUI.draw.RegularShape', RegularShape);

    if (typeof exports !== 'undefined') {
        module.exports = RegularShape;
    }

}());
(function () {
    /**
     * @class PMUI.draw.Arc
     * @extend PMUI.draw.RegularShape
     * Class arc that draws arcs using the HTMLElement draw engine.
     * Since the canvas is inverted in the y-axis the angles are:
     *
     * -    TOP    270
     * -    RIGHT  0
     * -    BOTTOM 90
     * -    LEFT   180
     *
     * An arc can be defined with the following elements:
     *
     * - *startAngle* start angle `0 <= startAngle < 360`.
     * - *endAngle* end angle `0 <= endAngle < 360`.
     * - *radius* the radius of the circle.
     *
     * Besides, the HTMLElement draw engine needs another parameter called *step*, this field tells the engine to
     * draw only at '*step*' steps
     *
     *       e.g.
     *      startAngle = 90
     *      endAngle = 150
     *      engine.paint() with step = 10:
     *          90, 100, 110, 120, 130, 140, 150
     *      engine.paint() with step = 20:
     *          90, 110, 130, 150
     *
     * As a consequence of the y-axis being inverted,we start drawing from the end angle towards the start angle,
     * therefore to draw an arc from 0 deg to 90 deg we must invert the parameters
     *
     *          var a = new PMUI.draw.Arc({
     *          center: new PMUI.util.Point(10, 10),
     *          radius: 200,
     *          startAngle: 270,
     *          endAngle: 0
     *      });
     *
     * @param {Object} options Initialization options
     * @cfg {PMUI.util.Point} [center=new PMUI.util.Points(0, 0)] Point representing the center of the arc
     * @cfg {number} [radius=Shape.prototype.DEFAULT_RADIUS] radius of the arc
     * @cfg {number} [startAngle=270] start angle of the arc
     * @cfg {number} [endAngle=90] end angle of the arc
     * @cfg {number} [step=10] steps to jump from end angle (to get to start angle)
     * @constructor Creates a new instance of arc
     */
    var Arc = function (options) {
        Arc.superclass.call(this);

        /**
         * Start angle of this arc
         * @property {number}
         */
        this.startAngle = null;

        /**
         * End angle of this arc
         * @property {number}
         */
        this.endAngle = null;

        /**
         * Radius of the arc
         * @property {number}
         */
        this.radius = null;

        /**
         * Steps to draw the arc
         * @property {number}
         */
        this.step = null;

        // set defaults
        Arc.prototype.init.call(this, options);
    };

    // prototype chaining
    PMUI.inheritFrom('PMUI.draw.RegularShape', Arc);

    /**
     * Type of this shape
     * @property {String}
     */
    Arc.prototype.type = "Arc";

    /**
     * Instance initializer which uses options to extend the config options to
     * initialize the instance
     * @private
     * @param {Object} options
     */
    Arc.prototype.init = function (options) {

        // Default options for the object
        var defaults = {
            center: new PMUI.util.Point(0, 0),
            radius: PMUI.draw.Shape.prototype.DEFAULT_RADIUS,
            startAngle: 270,
            endAngle: 90,
            step: 10
        };

        // extend recursively defaults with the given options
        $.extend(true, defaults, options);

        // call setters using the defaults object
        this.setCenter(defaults.center)
            .setStartAngle(defaults.startAngle)
            .setEndAngle(defaults.endAngle)
            .setRadius(defaults.radius)
            .setStep(defaults.step);

        // change the id (to debug easier)
        this.id += "-ARC";
    };

    /**
     * In charge of the painting / positioning of the figure on
     * the DOM and setting the styles
     * @chainable
     */
    Arc.prototype.paint = function () {

        this.setVisible(this.visible);

        if (this.html) {
            this.style.applyStyle();
            // this.graphic is inherited from RegularShape
            this.graphics = new PMUI.draw.Graphics(this.id);
            this.graphics.setColor("black");
            this.graphics.drawArc(this.center.x, this.center.y, this.radius,
                this.startAngle, this.endAngle, this.step);
            this.graphics.graphics.paint();
        }
        return this;
    };

    /**
     * Creates the HTML representation of the Arc
     * @returns {HTMLElement}
     */
    Arc.prototype.createHTML = function () {
        Arc.superclass.prototype.createHTML.call(this);
        return this.html;
    };

    /**
     * Returns the startAngle of the arc
     * @returns {number}
     */
    Arc.prototype.getStartAngle = function () {
        return this.startAngle;
    };

    /**
     * Sets the startAngle of the arc
     * @param {number} newAngle
     * @chainable
     */
    Arc.prototype.setStartAngle = function (newAngle) {
        this.startAngle = newAngle;
        return this;
    };

    /**
     * Returns the endAngle of the arc
     * @returns {number}
     */
    Arc.prototype.getEndAngle = function () {
        return this.endAngle;
    };

    /**
     * Sets the endAngle of the arc
     * @param {number} newAngle
     * @chainable
     */
    Arc.prototype.setEndAngle = function (newAngle) {
        this.endAngle = newAngle;
        return this;
    };

    /**
     * Returns the radius of the arc
     * @returns {number}
     */
    Arc.prototype.getRadius = function () {
        return this.radius;
    };

    /**
     * Sets the radius of the arc
     * @param {number} newRadius
     * @chainable
     */
    Arc.prototype.setRadius = function (newRadius) {
        this.radius = newRadius;
        return this;
    };

    /**
     * Returns the radius of the arc
     * @returns {number}
     */
    Arc.prototype.getStep = function () {
        return this.step;
    };

    /**
     * Sets the step to draw the arc (steps jumped from startAngle to endAngle)
     * @param {number} newStep
     * @chainable
     */
    Arc.prototype.setStep = function (newStep) {
        this.step = newStep;
        return this;
    };

    PMUI.extendNamespace('PMUI.draw.Arc', Arc);

    if (typeof exports !== 'undefined') {
        module.exports = Arc;
    }

}());

(function () {
    /**
     * @class PMUI.draw.Oval
     * A regular shape that represents an oval, in the PMDraw framework instances of the class Oval
     * are used to represent a Port.
     *
     * Some examples of use:
     *
     *      var oval = new PMUI.draw.Oval({
     *          width: 8,
     *          height: 8,
     *          center: new PMUI.util.Point(100, 100)
     *      });
     *
     * @extend PMUI.draw.RegularShape
     *
     * @constructor Creates an instance of the class Oval
     * @param {Object} options Initialization options
     * @cfg {number} [width=4] The width of this oval
     * @cfg {number} [height=4] The height of this oval
     * @cfg {number} [center=new Center(0, 0)] The center of this oval
     */
    var Oval = function (options) {
        Oval.superclass.call(this, options);
        Oval.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.draw.RegularShape', Oval);

    /**
     * The type of each instance of this class
     * @property {String} [type=Oval]
     */
    Oval.prototype.type = "Oval";

    /**
     * Instance initializer which uses options to extend the config options to initialize the instance
     * @param {Object} options The object that contains the config
     * @private
     */
    Oval.prototype.init = function (options) {
        /**
         * Default options for the object
         * @property {Object}
         */
        var defaults = {
            center: new PMUI.util.Point(0, 0),
            width: 4,
            height: 4
        };

        // extend recursively the defaultOptions with the given options
        $.extend(true, defaults, options);

        // call setters using the defaults object
        this.setCenter(defaults.center)
            .setWidth(defaults.width)
            .setHeight(defaults.height);
    };

    /**
     * Paints a **red** oval using the configuration options (the HTML is not appended to
     * the DOM)
     * @chainable
     */
    Oval.prototype.paint = function () {

        // show or hide the oval
        this.setVisible(this.visible);

        if (this.html) {
            this.style.applyStyle();

            this.graphic = new JSGraphics(this.id);
            this.graphic.setColor("red");
            this.graphic.fillOval(0, 0, this.getWidth(), this.getHeight());
            this.graphic.paint();
        }
        return this;
    };

    /**
     * Creates the HTML representation of the Oval
     * @returns {HTMLElement}
     */
    Oval.prototype.createHTML = function () {
        Oval.superclass.prototype.createHTML.call(this);
        return this.html;
    };

    PMUI.extendNamespace('PMUI.draw.Oval', Oval);

    if (typeof exports !== 'undefined') {
        module.exports = Oval;
    }

}());

(function () {
    /**
     * @class PMUI.draw.Polygon
     * Abstract class polygon to draw simple poly-lines
     *
     * An example of usage:
     *
     *      var polygon = new PMUI.draw.Polygon({
     *          points: []
     *      });
     *
     * @extend PMUI.draw.RegularShape
     *
     * @constructor Creates an instance of the class Polygon
     * @param {Object} options Initialization options
     * @cfg {Array} [points=[]] The points that make the polygon
     */
    var Polygon = function (options) {

        Polygon.superclass.call(this, options);

        /**
         * The points representing this polygon
         * @property {Array}
         */
        this.points = null;

        Polygon.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.draw.RegularShape', Polygon);

    /**
     * The type of each instance of this class
     * @property {String}
     */
    Polygon.prototype.type = "Polygon";

    /**
     * Instance initializer which uses options to extend the config options to initialize the instance
     * @param {Object} options The object that contains the config
     * @private
     */
    Polygon.prototype.init = function (options) {
        var defaults = {
            points: []
        };
        $.extend(true, defaults, options);
        this.setPoints(defaults.points);
    };

    /**
     * Sets the points of this polygon
     * @param {Array} newPoints
     * @chainable
     */
    Polygon.prototype.setPoints = function (newPoints) {
        var i,
            point;
        this.points = [];
        for (i = 0; i < newPoints.length; i += 1) {
            point = newPoints[i];
            this.points.push(new PMUI.util.Point(point.getX(), point.getY()));
        }
    };

    /**
     * Gets the points of this polygon
     * @return {Array}
     */
    Polygon.prototype.getPoints = function () {
        return this.points;
    };

    PMUI.extendNamespace('PMUI.draw.Polygon', Polygon);

    if (typeof exports !== 'undefined') {
        module.exports = Polygon;
    }

}());

(function () {
    /**
     * @class PMUI.draw.Intersection
     * An intersection in the designer is defined as an arc which has only one additional property: `idOtherConnection`
     * and it's the ID of the other connection this segment has intersection with.
     *
     * All the intersection of a segment are stored in `segment.intersections`.
     *
     * An example of instantiation:
     *
     *      // let's assume that 'segment' is an instance of the class Segment
     *      // let's assume that 'otherSegment' is an instance of the class Segment
     *      // let's assume that 'segment' has an intersection with 'otherSegment' at 'ip' (intersection point)
     *      var intersection = new PMUI.draw.Intersection(
     *          ip,
     *          otherSegment.parent,getID(),
     *          segment
     *      );
     *
     * @extends PMUI.draw.Arc
     *
     * @constructor Creates an instance of the class Intersection
     * @param {PMUI.util.Point} center
     * @param {String} idOtherConnection
     * @param {PMUI.draw.Segment} parent
     */
    var Intersection = function (center, idOtherConnection, parent) {

        Intersection.superclass.call(this);
        /**
         * The center of the arc
         * @property {PMUI.util.Point} [center=null]
         */
        this.center = (!center) ? null : center;
        /**
         * Visibility of this arc
         * @property {boolean}
         */
        this.visible = true;
        /**
         * Parent of this intersection is a segment
         * @property {PMUI.draw.Segment}
         */
        this.parent = parent;
        /**
         * Id of the other connection
         * @property {String}
         */
        this.idOtherConnection = idOtherConnection;
    };

    PMUI.inheritFrom('PMUI.draw.Arc', Intersection);

    /**
     * The type of each instance of the class Intersection
     * @property {String}
     */
    Intersection.prototype.type = "Intersection";

    /**
     * Paints this intersection (calling `Arc.paint()`) considering the orientation of its parent.
     *
     * It overwrites the properties `startAngle` and `endAngle` inherited from {@link PMUI.draw.Arc}
     * according to the orientation of its parent (segment), the calculation is as follows:
     *
     * - Segment is vertical
     *      - startAngle = 270
     *      - endAngle = 90
     * - Segment is horizontal
     *      - startAngle = 180
     *      - endAngle = 0
     *
     * @chainable
     */
    Intersection.prototype.paint = function () {

        // NOTE: it's always visible so do not call setVisible()

        if (this.parent.orientation === this.VERTICAL) {
            this.startAngle = 270;
            this.endAngle = 90;
        } else {
            this.startAngle = 180;
            this.endAngle = 0;
        }

        // call the representation (always an arc)
        Intersection.superclass.prototype.paint.call(this);

        // apply predefined style
        this.style.applyStyle();

        return this;
    };

    /**
     * Destroys the intersection by removing its HTML from the DOM.
     * @chainable
     */
    Intersection.prototype.destroy = function () {
        $(this.html).remove();
        return this;
    };

    /**
     * Creates the HTML representation of the Intersection.
     * @returns {HTMLElement}
     */
    Intersection.prototype.createHTML = function () {
        PMUI.draw.Shape.prototype.createHTML.call(this);
        return this.html;
    };

    PMUI.extendNamespace('PMUI.draw.Intersection', Intersection);

    if (typeof exports !== 'undefined') {
        module.exports = Intersection;
    }

}());

(function () {
    /**
     * @class PMUI.draw.Rectangle
     * A regular shape that represents a rectangle, in the PMDraw framework instances of the class Rectangle
     * are used to represent a resize handler and a segment move handler.
     *
     * Some examples of use:
     *
     *      var rectangle = new PMUI.draw.Rectangle();
     *
     * @extend PMUI.draw.Polygon
     *
     * @constructor Creates an instance of the class Rectangle
     * @param {Object} options Initialization options (currently there are no initialization options)
     */
    var Rectangle = function (options) {
        Rectangle.superclass.call(this, options);
        Rectangle.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.draw.Polygon', Rectangle);

    /**
     * The type of each instance of this class
     * @property {String}
     */
    Rectangle.prototype.type = "Rectangle";

    /**
     * Instance initializer which uses options to extend the config options to initialize the instance
     * @param {Object} options The object that contains the config
     * @private
     */
    Rectangle.prototype.init = function (options) {
    };

    /**
     * Paints the rectangle applying the predefined style and adding a background color if
     * it's possible (it's possible if this rectangle has an instance of the class Color).
     * @chainable
     */
    Rectangle.prototype.paint = function () {
        if (this.html) {
            this.style.applyStyle();
            if (this.color) {
                this.style.addProperties({
                    backgroundColor: this.color.getCSS()
                });
            }
        }
        return this;
    };

    /**
     * Creates the HTML representation of the Rectangle
     * @returns {HTMLElement}
     */
    Rectangle.prototype.createHTML = function () {
        PMUI.draw.Shape.prototype.createHTML.call(this);
        return this.html;
    };

    PMUI.extendNamespace('PMUI.draw.Rectangle', Rectangle);

    if (typeof exports !== 'undefined') {
        module.exports = Rectangle;
    }

}());

(function () {
    /**
     * @class PMUI.draw.MultipleSelectionContainer
     * Represents the rectangle created to do multiple selection after firing
     {@link PMUI.draw.Canvas#event-mousedown} and
     * dragging a rectangle over the desired elements in the canvas and firing
     {@link PMUI.draw.Canvas#event-mouseup},
     * currently it can only select shapes that are direct children of the canvas.
     *
     * An example of use:
     *
     *      // let's assume that canvas is an instance of the class Canvas
     *      var multipleSelectionContainer = new PMUI.draw.MultipleSelectionContainer(canvas);
     *
     * @extend PMUI.draw.Rectangle
     *
     * @constructor Creates an instance of the class MultipleSelectionContainer
     * @param {PMUI.draw.Canvas} canvas
     */
    var MultipleSelectionContainer = function (options) {
        MultipleSelectionContainer.superclass.call(this);

        /**
         * The background color of this element
         * @property {PMUI.util.Color}
         */
        this.backgroundColor = null;

        /**
         * Reference to the canvas
         * @property {PMUI.draw.Canvas}
         */
        this.canvas = null;

        // init object
        MultipleSelectionContainer.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.draw.Rectangle', MultipleSelectionContainer);

    /**
     * Type of each instance of this class
     * @property {String}
     */
    MultipleSelectionContainer.prototype.type = "MultipleSelectionContainer";

    /**
     * Instance initializer which uses options to extend the config options to initialize the instance
     * @param {PMUI.draw.Canvas} canvas The canvas of this multiple selection container
     * @private
     */
    MultipleSelectionContainer.prototype.init = function (options) {
        var defaults = {
            canvas: null,
            x: 0,
            y: 0,
            color: new PMUI.util.Color(0, 128, 255, 0.1)
        };
        jQuery.extend(true, defaults, options);
        this.backgroundColor = defaults.color;
        // light blue
        this.canvas = defaults.canvas;
        this.absoluteX = this.canvas.absoluteX;
        this.absoluteY = this.canvas.absoluteY;
    };

    /**
     * Paints the multiple selection container with the color *light blue* (defined in initObject)
     * @chainable
     */
    MultipleSelectionContainer.prototype.paint = function () {
        this.style.addProperties({
            backgroundColor: this.backgroundColor.getCSS()
        });
        return this;
    };

    /**
     * Changes the opacity of this multiple selection container (and repaints it later).
     * @param {number} value
     * @chainable
     */
    MultipleSelectionContainer.prototype.changeOpacity = function (value) {
        this.backgroundColor.setOpacity(value);
        this.paint();
        return this;
    };

    /**
     * Wraps the direct children of the canvas. To call this method it's assumed that this instance has a position
     * and a dimension in the canvas (this method is called from {@link PMUI.draw.Canvas#event-mouseup}).
     * It executes the following actions:
     *
     * 1. Gathers the currentSelection
     * 2. Checks which direct children of the canvas are inside the selection
     *      (done through {@link PMUI.draw.MultipleSelectionContainer#intersectElements}).
     * 3. Fires {@link PMUI.draw.Canvas#triggerSelectEvent} using the new `this.canvas.currentSelection`
     * 4. Resets the state of this instance
     *
     * @chainable
     */
    MultipleSelectionContainer.prototype.wrapElements = function () {
        var currentSelection = this.canvas.currentSelection,
            selection = [];
        this.intersectElements();
        if (!currentSelection.isEmpty()) {
            selection = currentSelection.asArray();
            this.canvas.triggerSelectEvent(selection);
        }
        this.reset();
        this.setVisible(false);
        return this;
    };

    /**
     * Checks which direct children of the canvas are inside `this` (represented as a rectangle in the canvas).
     * The steps are:
     *
     * 1. Empty `this.canvas.currentSelection` by calling {@link PMUI.draw.Canvas#emptyCurrentSelection}.
     * 2. If a child is inside this rectangle then it's added to `this.canvas.currentSelection`.
     *
     * @chainable
     */
    MultipleSelectionContainer.prototype.intersectElements = function () {
        var i,
            shape,
            children;

        //empty the current selection
        this.canvas.emptyCurrentSelection();

        // get all the customShapes
        children = this.canvas.customShapes;
        for (i = 0; i < children.getSize(); i += 1) {
            shape = children.get(i);
            if (shape.parent.family === "Canvas" && this.checkIntersection(shape)) {
                this.canvas.addToSelection(shape);
            }
        }
        return this;
    };

    /**
     * Resets the position and dimensions of this selection container.
     * @chainable
     */
    MultipleSelectionContainer.prototype.reset = function () {
        this.setPosition(0, 0);
        this.setDimension(0, 0);
        return this;
    };

    /**
     * Alias for {@link PMUI.draw.Core#getAbsoluteX}.
     * @property {Function} getLeft
     */
    MultipleSelectionContainer.prototype.getLeft = PMUI.draw.Shape.prototype.getAbsoluteX;

    /**
     * Alias for {@link PMUI.draw.Core#getAbsoluteY}.
     * @property {Function} getTop
     */
    MultipleSelectionContainer.prototype.getTop = PMUI.draw.Shape.prototype.getAbsoluteY;

    /**
     * Checks if `shape` is inside `this`, `shape` is inside `this` if one of its corners
     * its inside `this`.
     * @param {PMUI.draw.Shape} shape
     * @return {boolean}
     */
    MultipleSelectionContainer.prototype.checkIntersection = function (shape) {
        var Point = PMUI.util.Point,
            Geometry = PMUI.draw.Geometry,
            topLeft = new PMUI.util.Point(this.zoomX, this.zoomY),
            bottomRight = new PMUI.util.Point(this.zoomX + this.zoomWidth,
                this.zoomY + this.zoomHeight);
        return Geometry.pointInRectangle(new PMUI.util.Point(shape.getZoomX(), shape.getZoomY()),
                topLeft, bottomRight) ||
            Geometry.pointInRectangle(new PMUI.util.Point(shape.zoomX +
                shape.zoomWidth, shape.zoomY), topLeft, bottomRight) ||
            Geometry.pointInRectangle(new PMUI.util.Point(shape.zoomX, shape.zoomY +
                shape.zoomHeight), topLeft, bottomRight) ||
            Geometry.pointInRectangle(new PMUI.util.Point(shape.zoomX +
                shape.zoomWidth, shape.zoomY + shape.zoomHeight), topLeft, bottomRight);
    };

    PMUI.extendNamespace('PMUI.draw.MultipleSelectionContainer', MultipleSelectionContainer);

    if (typeof exports !== 'undefined') {
        module.exports = MultipleSelectionContainer;
    }

}());

(function () {
    /**
     * @class PMUI.draw.Snapper
     * Class snapper represents the helper shown while moving shapes.
     * @extend PMUI.draw.Core
     *
     * @constructor Creates an instance of the class Snapper
     * @param {Object} options Initialization options
     * @cfg {PMUI.util.Point} [orientation="horizontal"] The default orientation of this snapper
     */
    var Snapper = function (options) {

        Snapper.superclass.call(this, options);

        /**
         * Orientation of this snapper, it can be either "horizontal" or "vertical".
         * @property {string} [orientation=null]
         */
        this.orientation = null;

        /**
         * Data saved to define the positioning of this snapper in the canvas.
         * @property {Array} [data=[]]
         */
        this.data = [];

        /**
         * The visibility of this snapper.
         * @property {boolean} [visible=false]
         */
        this.visible = false;

        Snapper.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.draw.Core', Snapper);

    /**
     * The type of each instance of this class
     * @property {String}
     */
    Snapper.prototype.type = "Snapper";

    /**
     * Instance initializer which uses options to extend the config options to initialize the instance.
     * @param {Object} options The object that contains the config
     * @private
     */
    Snapper.prototype.init = function (options) {

        var defaults = {
            orientation: "horizontal"
        };

        // extend recursively the defaultOptions with the given options
        $.extend(true, defaults, options);

        // call setters using the defaults object
        this.setOrientation(defaults.orientation);
    };

    /**
     * Creates the HTML representation of the snapper.
     * @returns {HTMLElement}
     */
    Snapper.prototype.createHTML = function () {
        Snapper.superclass.prototype.createHTML.call(this);
        return this.html;
    };

    Snapper.prototype.enable = function () {
        if (this.html) {
            this.graphic = new JSGraphics(this.id);
            this.graphic.setColor("#81DAF5");
            if (this.orientation === 'horizontal') {
                this.graphic.drawLine(0, 0, 4000, 0);
            } else {
                this.graphic.drawLine(0, 0, 0, 4000);
            }
            this.graphic.paint();
        }
        return this;
    };

    /**
     * Hides the snapper.
     * @chainable
     */
    Snapper.prototype.hide = function () {
        this.visible = false;
        this.setVisible(this.visible);
        return this;
    };

    /**
     * Shows the snapper.
     * @chainable
     */
    Snapper.prototype.show = function () {
        this.visible = true;
        this.setVisible(this.visible);
        return this;
    };

    /**
     * Fills the data for the snapper (using customShapes and regularShapes).
     * The data considered for each shape is:
     *
     * - Its absoluteX
     * - Its absoluteY
     * - Its absoluteX + width
     * - Its absoluteY + height
     *
     * @chainable
     */
    Snapper.prototype.createSnapData = function () {
        var i,
            index = 0,
            shape,
            border = 0;

        // clear the data before populating it
        this.data = [];

        // populate the data array using the customShapes
        for (i = 0; i < this.canvas.customShapes.getSize(); i += 1) {
            shape = this.canvas.customShapes.get(i);
            border = parseInt($(shape.getHTML()).css('borderTopWidth'), 10);
            if (this.orientation === 'horizontal') {
                this.data[index * 2] = shape.getAbsoluteY() - border;
                this.data[index * 2 + 1] = shape.getAbsoluteY() + shape.getZoomHeight();
            } else {
                this.data[index * 2] = shape.getAbsoluteX() - border;
                this.data[index * 2 + 1] = shape.getAbsoluteX() + shape.getZoomWidth();
            }
            index += 1;
        }

        // populate the data array using the regularShapes
        for (i = 0; i < this.canvas.regularShapes.getSize(); i += 1) {
            shape = this.canvas.regularShapes.get(i);
            border = parseInt($(shape.getHTML()).css('borderTopWidth'), 10);
            if (this.orientation === 'horizontal') {
                this.data[index * 2] = shape.getAbsoluteY() - border;
                this.data[index * 2 + 1] = shape.getAbsoluteY() +
                    shape.getZoomHeight();
            } else {
                this.data[index * 2] = shape.getAbsoluteX() - border;
                this.data[index * 2 + 1] = shape.getAbsoluteX() +
                    shape.getZoomWidth();
            }
            index += 1;
        }
        return this;
    };

    /**
     * Sorts the data using the builtin `sort()` function, so that there's an strictly increasing order.
     * @chainable
     */
    Snapper.prototype.sortData = function () {
        this.data.sort();
        return this;
    };

    /**
     * Performs a binary search for `value` in `this.data`, return true if `value` was found in the data.
     * @param {number} value
     * @return {boolean}
     */
    Snapper.prototype.binarySearch = function (value) {
        var low = 0,
            up = this.data.length - 1,
            mid;

        while (low <= up) {
            mid = parseInt((low + up) / 2, 10);
            if (this.data[mid] === value) {
                return value;
            }
            if (this.data[mid] > value) {
                up = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        return false;
    };

    /**
     * Attaches listeners to this snapper, currently it only has the
     * mouseMove event which hides the snapper.
     * @param {PMUI.draw.Snapper} snapper
     * @chainable
     */
    Snapper.prototype.attachListeners = function (snapper) {
        var $snapper = $(snapper.html).mousemove(
            function () {
                snapper.hide();
            }
        );
        return this;
    };

    /**
     * Sets the orientation of this snapper.
     * @param {string} orientation
     * @chainable
     */
    Snapper.prototype.setOrientation = function (orientation) {
        if (orientation === "horizontal" || orientation === "vertical") {
            this.orientation = orientation;
        } else {
            throw new Error("setOrientation(): parameter is not valid");
        }
        return this;
    };

    /**
     * Gets the orientation of this snapper.
     * @return {string}
     */
    Snapper.prototype.getOrientation = function () {
        return this.orientation;
    };

    PMUI.extendNamespace('PMUI.draw.Snapper', Snapper);

    if (typeof exports !== 'undefined') {
        module.exports = Snapper;
    }

}());

(function () {
    /**
     * @abstract
     * @class PMUI.draw.Router
     * Represents the router used to define the points for a connection.
     * @extend PMUI.draw.Core
     *
     * @constructor Creates an instance of the class Router
     */
    var Router = function () {
        Router.superclass.call(this);
    };

    PMUI.inheritFrom('PMUI.draw.Core', Router);

    /**
     * The type of each instance of this class
     * @property {String}
     */
    Router.prototype.type = "Router";

    /**
     * @abstract Abstract method to create a route (defined in other inherited classes)
     * @returns {boolean}
     */
    Router.prototype.createRoute = function () {
        return true;
    };

    PMUI.extendNamespace('PMUI.draw.Router', Router);

    if (typeof exports !== 'undefined') {
        module.exports = Router;
    }
}());

(function () {
    /**
     * @class PMUI.draw.ManhattanConnectionRouter
     * Class ManhattanConnectionRouter uses the 'ManhattanRouter' algorithm to define the points of the connection.
     * @extends PMUI.draw.Router
     *
     * @constructor Creates an instance of the class ManhattanConnectionRouter
     */
    var ManhattanConnectionRouter = function () {
        ManhattanConnectionRouter.superclass.call(this);
        /**
         * Minimum distance used in the algorithm
         * @property {number} [mindist=20]
         */
        this.mindist = 20;
    };

    PMUI.inheritFrom('PMUI.draw.Router', ManhattanConnectionRouter);

    /**
     * The type of each instance of this class
     * @property {String}
     */
    ManhattanConnectionRouter.prototype.type = "ManhattanConnectionRouter";

    /**
     * Creates the points of `connection` by calling the #route method and using
     * `connection.srcPort` and `connection.destPort` as the start and end points
     * @param {PMUI.draw.Connection} connection
     * @return {Array} An array of points that define the connection.
     */
    ManhattanConnectionRouter.prototype.createRoute = function (connection) {
        var fromPt, fromDir, toPt, toDir, points = [];

        fromPt = connection.srcPort.getPoint(false);
        fromDir = connection.srcPort.direction;

        toPt = connection.destPort.getPoint(false);
        toDir = connection.destPort.direction;

        // draw a line between the two points.
        this.route(connection, toPt, toDir, fromPt, fromDir, points);
        return points;
    };

    /**
     * Implementation of the 'MahattanRouter' algorithm
     * @param {PMUI.draw.Connection} conn Instance of the class Connection
     * @param {PMUI.util.Point} fromPt initial Point
     * @param {number} fromDir route using to begin line
     *        UP = 0; RIGHT= 1; DOWN = 2; LEFT = 3;
     * @param {PMUI.util.Point} toPt final Point
     * @param {number} toDir route using to end line
     *        UP = 0; RIGHT= 1; DOWN = 2; LEFT = 3;
     * @param {Array} points array where points are saved
     */
    ManhattanConnectionRouter.prototype.route = function (conn, fromPt, fromDir, toPt, toDir, points) {
        var TOL,
            TOLxTOL,
            UP,
            RIGHT,
            DOWN,
            LEFT,
            xDiff,
            yDiff,
            nPoint,
            dir,
            pos;

        TOL = 0.1;
        TOLxTOL = 0.01;

        // fromPt is an x,y to start from.
        // fromDir is an angle that the first link must
        UP = 0;
        RIGHT = 1;
        DOWN = 2;
        LEFT = 3;

        xDiff = fromPt.x - toPt.x;
        yDiff = fromPt.y - toPt.y;

        if (((xDiff * xDiff) < (TOLxTOL)) && ((yDiff * yDiff) < (TOLxTOL))) {
            points.push(toPt);
            return;
        }

        if (fromDir === LEFT) {
            if ((xDiff > 0) && ((yDiff * yDiff) < TOL) && (toDir === RIGHT)) {
                nPoint = toPt;
                dir = toDir;
            } else {
                if (xDiff < 0) {
                    nPoint = new PMUI.util.Point(fromPt.x - this.mindist, fromPt.y);
                } else if (((yDiff > 0) && (toDir === DOWN)) || ((yDiff < 0) &&
                    (toDir === UP))) {
                    nPoint = new PMUI.util.Point(toPt.x, fromPt.y);
                } else if (fromDir === toDir) {
                    pos = Math.min(fromPt.x, toPt.x) - this.mindist;
                    nPoint = new PMUI.util.Point(pos, fromPt.y);
                } else {
                    nPoint = new PMUI.util.Point(fromPt.x - (xDiff / 2), fromPt.y);
                }

                if (yDiff > 0) {
                    dir = UP;
                } else {
                    dir = DOWN;
                }
            }
        } else if (fromDir === RIGHT) {
            if ((xDiff < 0) && ((yDiff * yDiff) < TOL) && (toDir === LEFT)) {
                nPoint = toPt;
                dir = toDir;
            } else {
                if (xDiff > 0) {
                    nPoint = new PMUI.util.Point(fromPt.x + this.mindist, fromPt.y);
                } else if (((yDiff > 0) && (toDir === DOWN)) || ((yDiff < 0) &&
                    (toDir === UP))) {
                    nPoint = new PMUI.util.Point(toPt.x, fromPt.y);
                } else if (fromDir === toDir) {
                    pos = Math.max(fromPt.x, toPt.x) + this.mindist;
                    nPoint = new PMUI.util.Point(pos, fromPt.y);
                } else {
                    nPoint = new PMUI.util.Point(fromPt.x - (xDiff / 2), fromPt.y);
                }

                if (yDiff > 0) {
                    dir = UP;
                } else {
                    dir = DOWN;
                }
            }
        } else if (fromDir === DOWN) {
            if (((xDiff * xDiff) < TOL) && (yDiff < 0) && (toDir === UP)) {
                nPoint = toPt;
                dir = toDir;
            } else {
                if (yDiff > 0) {
                    nPoint = new PMUI.util.Point(fromPt.x, fromPt.y + this.mindist);
                } else if (((xDiff > 0) && (toDir === RIGHT)) || ((xDiff < 0) &&
                    (toDir === LEFT))) {
                    nPoint = new PMUI.util.Point(fromPt.x, toPt.y);
                } else if (fromDir === toDir) {
                    pos = Math.max(fromPt.y, toPt.y) + this.mindist;
                    nPoint = new PMUI.util.Point(fromPt.x, pos);
                } else {
                    nPoint = new PMUI.util.Point(fromPt.x, fromPt.y - (yDiff / 2));
                }

                if (xDiff > 0) {
                    dir = LEFT;
                } else {
                    dir = RIGHT;
                }
            }
        } else if (fromDir === UP) {
            if (((xDiff * xDiff) < TOL) && (yDiff > 0) && (toDir === DOWN)) {
                nPoint = toPt;
                dir = toDir;
            } else {
                if (yDiff < 0) {
                    nPoint = new PMUI.util.Point(fromPt.x, fromPt.y - this.mindist);
                } else if (((xDiff > 0) && (toDir === RIGHT)) || ((xDiff < 0) &&
                    (toDir === LEFT))) {
                    nPoint = new PMUI.util.Point(fromPt.x, toPt.y);
                } else if (fromDir === toDir) {
                    pos = Math.min(fromPt.y, toPt.y) - this.mindist;
                    nPoint = new PMUI.util.Point(fromPt.x, pos);
                } else {
                    nPoint = new PMUI.util.Point(fromPt.x, fromPt.y - (yDiff / 2));
                }

                if (xDiff > 0) {
                    dir = LEFT;
                } else {
                    dir = RIGHT;
                }
            }
        }

        this.route(conn, nPoint, dir, toPt, toDir, points);
        points.push(fromPt);
    };

    PMUI.extendNamespace('PMUI.draw.ManhattanConnectionRouter', ManhattanConnectionRouter);

    if (typeof exports !== 'undefined') {
        module.exports = ManhattanConnectionRouter;
    }

}());

(function () {
    /**
     * @class PMUI.draw.Connection
     * Class that represents a connection between two elements in the diagram.
     *
     * A connection is defined with a set of points, there's a segment between two points e.g. `point[i]`
     * and `point[i + 1]` with `i >= 0` and `i < points.length - 1`, there are two ways to paint a connection:
     *
     * - given *2* {@link PMUI.draw.Port Ports}, use the algorithm 'ManhattanConnection' to define the points
     * - given *n* points, make the segments with the rule defined above (but first let's use the first and
     *      the last points to make them {@link PMUI.draw.Port Ports}).
     *
     * Some characteristics of the connection:
     *
     * - The connection has references to its source port and end port.
     * - The `state` of the connection is the set of points that define that connection.
     * - The connections can have a color, the color is an instance of the class Color.
     *
     * The connections can have the following types of segments:
     *
     * - regular (a complete segment)
     * - dotted
     * - segmented
     * - segmented and dotted (mixed)
     *
     * Some examples of the configuration:
     *
     *      // e.g.
     *      // let's assume that there are two shapes (sourceShape and destShape)
     *      // let's assume that srcPort is a port that is stored in sourceShape
     *      // let's assume that destPort is a port that is stored in destShape
     *      // to create an instance of Connection with regular light green segments
     *      var connectionGreen = new PMUI.draw.Connection({
     *          srcPort:  srcPort,
     *          destPort:  destPort,
     *          segmentColor:  new Color(0, 200, 0),
     *          segmentStyle:  "regular"
     *      });
     *      // to create an instance of Connection with dotted red segments
     *      var connectionRed = new PMUI.draw.Connection({
     *          srcPort:  srcPort,
     *          destPort:  destPort,
     *          segmentColor:  new Color(255, 0, 0),
     *          segmentStyle:  "dotted"
     *      });
     *
     * @extend PMUI.draw.Core
     *
     * @constructor Creates an instance of the class Connection
     * @param {Object} options Initialization options
     * @cfg {PMUI.util.Point} [srcPort=new Port()] Source port of the connection
     * @cfg {PMUI.util.Point} [destPort=new Port()] Destination port of the connection
     * @cfg {PMUI.util.Color} [segmentColor=new Color(0, 0, 0)] Color of the connection (by default it's black)
     * @cfg {string} [segmentStyle="regular"] Type of segments as defined above
     */
    var Connection = function (options) {

        Connection.superclass.call(this, options);

        /**
         * The source port of the connection
         * @property {PMUI.draw.Port}
         */
        this.srcPort = null;

        /**
         * The end port of the connection
         * @property {PMUI.draw.Port}
         */
        this.destPort = null;

        /**
         * The decorator of the source of the connection
         * @property {PMUI.draw.ConnectionDecorator}
         */
        this.srcDecorator = null;

        /**
         * The decorator of the target of the connection
         * @property {PMUI.draw.ConnectionDecorator}
         */
        this.destDecorator = null;

        /**
         * List of the lines that forms the connection
         * @property {PMUI.util.ArrayList}
         */
        this.lineSegments = new PMUI.util.ArrayList();

        /**
         * Saves a copy of the line segments' points when a flag is passed to the
         * disconnect method (NOTE:  this array is used in the
         * userDefinedRoute method)
         * @property {PMUI.util.ArrayList}
         */
        this.points = [];

        this.zoomPoints = [];
        /**
         * Saves a copy of the line segments' points when a flag is passed to the
         * disconnect method (NOTE:  this array is used while creating the object
         * updatedElement in Canvas.triggerConnectionStateChangeEvent)
         * @property {PMUI.util.ArrayList}
         */
        this.oldPoints = [];

        /**
         * Current segment style
         * @property {"dotted" / "regular" / "segmented" / "segmentdot"}
         */
        this.segmentStyle = null;

        /**
         * This segment style ej. "dotted", "segmented", "segmentdot" (it's the
         * original style set in `this.initObject()`)
         * @property {"dotted" / "regular" / "segmented" / "segmentdot"}
         */
        this.originalSegmentStyle = null;

        /**
         * Actual color of all the segment in this connection
         * @property {PMUI.util.Color}
         */
        this.segmentColor = null;

        /**
         * Original color of all the segments in this connection (set in `this.initObject()`)
         * @property {PMUI.util.Color}
         */
        this.originalSegmentColor = null;

        /**
         * default zIndex of the connection
         * @property {number}
         */
        this.defaultZOrder = 2;

        /**
         * Current zIndex of the connection
         * @property {number}
         */
        this.zOrder = 2;

        /**
         * ArrayList which contains the ids of the Connections it has an
         * intersection with:
         *
         *      // e.g.
         *      // let's assume that there's an instance of the class Connection called anotherConnection
         *      intersectionWith = new PMUI.util.ArrayList();
         *      intersectionWith.insert(anotherConnection);
         *
         * @property {PMUI.util.ArrayList}
         */
        this.intersectionWith = new PMUI.util.ArrayList();

        this.corona = 10;

        Connection.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.draw.Core', Connection);

    /**
     * The type of each instance of this class
     * @property {String}
     */
    Connection.prototype.type = "Connection";

    /**
     * The family of each instance of this class
     * @property {String}
     */
    Connection.prototype.family = "Connection";

    /**
     * Router associated with the connection
     * @property {PMUI.draw.Router}
     */
    Connection.prototype.router = new PMUI.draw.ManhattanConnectionRouter();

    /**
     * Instance initializer which uses options to extend the config options to initialize the instance.
     * @param {Object} options The object that contains the config
     * @private
     */
    Connection.prototype.init = function (options) {
        var defaultOptions = {
            srcPort: new PMUI.draw.Port(),
            destPort: new PMUI.draw.Port(),
            segmentColor: new PMUI.util.Color(0, 0, 0),
            segmentStyle: "regular"
        };

        // extend recursively the defaultOptions with the given options
        $.extend(true, defaultOptions, options);

        // init
        this.setSrcPort(defaultOptions.srcPort)
            .setDestPort(defaultOptions.destPort)
            .setSegmentStyle(defaultOptions.segmentStyle, false)
            .setSegmentColor(defaultOptions.segmentColor, false);

        // init originals
        this.originalSegmentStyle = defaultOptions.segmentStyle;
        this.originalSegmentColor = defaultOptions.segmentColor;

        // set the connections for each port as this
        this.getSrcPort().setConnection(this);
        this.getDestPort().setConnection(this);
    };

    /**
     * Creates the HTML Representation of the Connection.
     * @returns {HTMLElement}
     */
    Connection.prototype.createHTML = function () {
        this.html = document.createElement('div');
        this.html.id = this.id;

        this.style.addProperties({
            position: "absolute",
            left: 0,
            top: 0,
            height: 0,
            width: 0,
            zIndex: this.zOrder
        });
        return this.html;
    };


    Connection.prototype.attachListeners = function () {
        if (!this.canvas.readOnly) {
            $(this.html).children().click(this.onClick(this));
        }
    };

    /**
     * @event click
     * Click callback fired when the decorator is clicked on.
     * It hides the currentSelection if any and shows the ports and handlers of `decorator` parent
     * (which is a connection).
     * @param {PMUI.draw.Connection} decorator
     */
    Connection.prototype.onClick = function (conn) {
        return function (e, ui) {
            var connection = conn,
                oldConnection = conn.canvas.currentConnection,
                canvas = connection.canvas;

            // HIDE
            // if there were some shapes in the current selection then
            // empty the current selection
            canvas.emptyCurrentSelection();

            // if there was a connection previously select hide its ports
            // and its handlers
            if (oldConnection) {
                oldConnection.hidePortsAndHandlers();
            }

            // SHOW
            // show the ports and the handlers of the new connection
            connection.showPortsAndHandlers();

            // set the old connection as this connection
            canvas.currentConnection = connection;

            // TODO:  zIndex
            e.stopPropagation();
        };
    };

    /**
     * Sets the handlers for each segment.
     * This method sets the handler for each segment, also sets a variable called hasMoveHandler on each segment to
     * either true or false (it'll be false if the current segment is either the first or the last segment of
     * the connection)
     * @chainable
     */
    Connection.prototype.setSegmentMoveHandlers = function () {
        var i,
            currentSegment,
            orientationOptions = [this.HORIZONTAL, this.VERTICAL],
            segmentOrientation = (this.destPort.direction === this.TOP ||
            this.destPort.direction === this.BOTTOM) ? 1 : 0;
        for (i = this.lineSegments.getSize() - 1; i >= 0; i -= 1) {
            currentSegment = this.lineSegments.get(i);
            currentSegment.orientation =
                orientationOptions[segmentOrientation];
            currentSegment.hasMoveHandler = false;

            // set prev and next segments
            if (i < this.lineSegments.getSize() - 1 && i > 0) {
                currentSegment.nextNeighbor = this.lineSegments.get(i + 1);
                currentSegment.previousNeighbor = this.lineSegments.get(i - 1);
                currentSegment.hasMoveHandler = true;
                currentSegment.addSegmentMoveHandler();
            }
            segmentOrientation = 1 - segmentOrientation;
        }
        return this;
    };

    /**
     * Remove all the segmentHandlers of this connection
     * (removing its html)
     * @chainable
     */
    Connection.prototype.removeAllSegmentHandlers = function () {
        var segment,
            i;
        for (i = 0; i < this.lineSegments.getSize(); i += 1) {
            segment = this.lineSegments.get(i);
            if (segment.hasMoveHandler) {
                $(segment.moveHandler.html).remove();
            }
        }
        return this;
    };

    /**
     * Show the moveHandlers of the connections
     * @chainable
     */
    Connection.prototype.showMoveHandlers = function () {
        var i,
            currentHandler;
        for (i = 0; i < this.lineSegments.getSize(); i += 1) {
            currentHandler = this.lineSegments.get(i).moveHandler;
            if (currentHandler) {
                currentHandler.setVisible(true);
            }
        }
        return this;
    };

    /**
     * Hide the moveHandlers of the connection
     * @chainable
     */
    Connection.prototype.hideMoveHandlers = function () {
        var i,
            currentHandler;
        for (i = 0; i < this.lineSegments.getSize(); i += 1) {
            currentHandler = this.lineSegments.get(i).moveHandler;
            if (currentHandler) {
                currentHandler.setVisible(false);
            }
        }
        return this;
    };

    /**
     * Hides the ports and handlers of this connection.
     * @chainable
     */
    Connection.prototype.hidePortsAndHandlers = function () {
        this.hidePorts();
        this.hideMoveHandlers();
        return this;
    };

    /**
     * Shows the ports and handlers of this connection.
     * @chainable
     */
    Connection.prototype.showPortsAndHandlers = function () {
        this.showPorts();
        this.showMoveHandlers();
        return this;
    };

    /**
     * Paints the connection according to the parameters given as config options, this method `paint()`
     * unlike other similar `paint()` does not append the HTML to the DOM, this is done with a call
     * to `canvas.addConnection(connection)`.
     * @param {Object} options Configuration options
     * @param {string} [options.algorithm="manhattan"] The algorithm used to draw the connection
     * @param {Array} [options.points=[]] Points to be used if the algorithm is "user"
     * @param {number} [options.dx=0] Move the points dx
     * @param {number} [options.dy=0] Move the points dy
     * @chainable
     */
    Connection.prototype.paint = function (options) {
        var defaults = {
            algorithm: 'manhattan',
            points: [],
            dx: 0,
            dy: 0
        };
        $.extend(true, defaults, options);
        try {
            if (this.html === null) {
                this.createHTML();
            }

            $(this.html).empty();
            this.oldPoint = null;

            switch (defaults.algorithm) {
                case 'manhattan':
                    this.createManhattanRoute();
                    break;
                case 'user':
                    this.createUserDefinedRoute(defaults.points,
                        defaults.dx, defaults.dy);
                    break;
                default:
                    throw new Error('Connection.paint():  the algorithm provided ' +
                        'is not correct');
            }
            if (options && options.updateZoomPoints !== undefined) {
                this.savePoints({updateZoomPoints: options.updateZoomPoints});
            } else {
                this.savePoints();
            }
            this.style.applyStyle();

            // the inline style might have changed in this.move()
            // so restore the style to the original setting
            this.style.addProperties({
                top: 0,
                left: 0
            });

            // paint the decorator if any exists
            if (this.destDecorator !== null) {
                this.destDecorator.paint();
                if (!this.canvas.readOnly) {
                    this.destDecorator.attachListeners();
                }
            }
            if (this.srcDecorator !== null) {
                this.srcDecorator.paint();
            }

            this.oldPoint = null;

        } catch (e) {
        }
        return this;
    };

    /**
     * Hides the connection and its intersections
     * @param {boolean} [savePoints] If set to true, the connection state will be saved in `this.points
     * (see the definition of {@link PMUI.draw.Connection#property-points} in the definition of the class).
     * @chainable
     */
    Connection.prototype.disconnect = function (savePoints) {
        this.clearAllIntersections();

        // hide the segment handlers
        this.hideMoveHandlers();

        // save the line segments and use them in the createCustomRoute method
        if (savePoints) {
            this.savePoints();
        }
        this.lineSegments.clear();

        // empty the contents
        $(this.html).empty();

        return this;
    };

    /**
     * Connects two elements using options as a parameter (alias for `this.paint`)
     * @param {Object} options Configuration options
     * @param {string} [options.algorithm="manhattan"] The algorithm used to draw the connection
     * @param {Array} [options.points=[]] Points to be used if the algorithm is "user"
     * @param {number} [options.dx=0] Move the points dx
     * @param {number} [options.dy=0] Move the points dy
     * @chainable
     */
    Connection.prototype.connect = function (options) {
        var i,
            max,
            srcPortPosition,
            destPortPosition,
            lineLength,
            lastPoint,
            currentOrientation,
            newPoint,
            referencePoints,
            x,
            y;

        if (options && options.algorithm === 'user' && options.points) {
            referencePoints = this.zoomPoints.length ? this.zoomPoints : options.points;
            srcPortPosition = {
                x: Math.round(this.srcPort.getAbsoluteX() + (this.srcPort.getWidth() / 2)),
                y: Math.round(this.srcPort.getAbsoluteY() + (this.srcPort.getHeight() / 2))
            };
            destPortPosition = {
                x: Math.round(this.destPort.getAbsoluteX() + (this.destPort.getWidth() / 2)),
                y: Math.round(this.destPort.getAbsoluteY() + (this.destPort.getHeight() / 2))
            };

            this.points = [];
            for (i = 0, max = options.points.length; i < max; i += 1) {
                if (i === 0) {
                    x = srcPortPosition.x;
                    y = srcPortPosition.y;
                    currentOrientation = (this.srcPort.getDirection() === this.srcPort.LEFT
                    || this.srcPort.getDirection() === this.srcPort.RIGHT) ? this.srcPort.HORIZONTAL
                        : this.srcPort.VERTICAL;
                } else if (i === max - 2 || i === max - 1) {
                    if (currentOrientation === this.srcPort.HORIZONTAL) {
                        x = destPortPosition.x;
                    } else {
                        y = destPortPosition.y;
                    }
                    currentOrientation = currentOrientation === this.srcPort.HORIZONTAL ? this.srcPort.VERTICAL
                        : this.srcPort.HORIZONTAL;
                } else {
                    if (currentOrientation === this.srcPort.HORIZONTAL) {
                        lineLength = (referencePoints[i].x - referencePoints[i - 1].x) * this.canvas.zoomFactor;
                        x = lastPoint.x + lineLength;
                    } else {
                        lineLength = (referencePoints[i].y - referencePoints[i - 1].y) * this.canvas.zoomFactor;
                        y = lastPoint.y + lineLength;
                    }
                    currentOrientation = currentOrientation === this.srcPort.HORIZONTAL ? this.srcPort.VERTICAL
                        : this.srcPort.HORIZONTAL;
                }
                newPoint = new PMUI.util.Point(x, y);
                this.points.push(newPoint);
                lastPoint = newPoint;
            }
            options.points = this.points;
        }

        this.paint(options);
        this.attachListeners();
        return this;
    };

    /**
     * Hides the ports of the connection
     * @chainable
     */
    Connection.prototype.hidePorts = function () {
        this.srcPort.hide();
        this.destPort.hide();
        return this;
    };

    /**
     * Shows the ports of the connection
     * @chainable
     */
    Connection.prototype.showPorts = function () {
        this.srcPort.show();
        this.destPort.show();
        return this;
    };

    /**
     * Saves the state of the connection.
     * @param {Object} options
     * @param {boolean} [options.saveToOldPoints=false] If set to true then it will save the state
     * to `this.oldPoints` array
     * @chainable
     */
    Connection.prototype.savePoints = function (options) {
        var i,
            segment,
            arrayChosen = 'points',
            defaults = {
                saveToOldPoints: false,
                updateZoomPoints: true
            };

        $.extend(true, defaults, options);

        if (defaults.saveToOldPoints) {
            arrayChosen = "oldPoints";
        }

        this[arrayChosen] = [];
        if (defaults.updateZoomPoints) {
            this.zoomPoints = [];
        }
        for (i = 0; i < this.lineSegments.getSize(); i += 1) {
            segment = this.lineSegments.get(i);
            if (i === 0) {
                // insert the startPoint only for the first segment
                this[arrayChosen].push(new PMUI.util.Point(
                    segment.startPoint.x,
                    segment.startPoint.y
                ));
                if (defaults.updateZoomPoints) {
                    this.zoomPoints.push(new PMUI.util.Point(
                        segment.startPoint.x / this.canvas.zoomFactor,
                        segment.startPoint.y / this.canvas.zoomFactor
                    ));
                }
            }
            this[arrayChosen].push(new PMUI.util.Point(
                segment.endPoint.x,
                segment.endPoint.y
            ));
            if (defaults.updateZoomPoints) {
                this.zoomPoints.push(new PMUI.util.Point(
                    segment.endPoint.x / this.canvas.zoomFactor,
                    segment.endPoint.y / this.canvas.zoomFactor
                ));
            }
        }
        return this;
    };

    /**
     * Creates the segments of the connection using points and moving the segments dx and dy
     * @param {Array} points
     * @param {number} dx
     * @param {number} dy
     * @chainable
     */
    Connection.prototype.createUserDefinedRoute = function (points, dx, dy) {
        var i,
            segment,
            diffPoint = new PMUI.util.Point(dx, dy);
        for (i = 1; i < points.length; i += 1) {
            segment = new PMUI.draw.Segment({
                startPoint: new PMUI.util.Point(
                    parseInt(points[i - 1].x, 10),
                    parseInt(points[i - 1].y, 10)
                ).add(diffPoint),
                endPoint: new PMUI.util.Point(
                    parseInt(points[i].x, 10),
                    parseInt(points[i].y, 10)
                ).add(diffPoint),
                parent: this,
                canvas: this.canvas,
                color: this.segmentColor
            });
            this.addSegment(segment);
        }
        return this;
    };

    /**
     * Create the segments of the connection using the points defined by the algorithm "ManhattanConnection"
     * @chainable
     */
    Connection.prototype.createManhattanRoute = function () {
        var points = this.router.createRoute(this),
            i,
            segment;
        // create the segments now that we have the points
        for (i = 1; i < points.length; i += 1) {
            segment = new PMUI.draw.Segment({
                startPoint: new PMUI.util.Point(
                    parseInt(points[i - 1].x - this.canvas.absoluteX, 10),
                    parseInt(points[i - 1].y - this.canvas.absoluteY, 10)
                ),
                endPoint: new PMUI.util.Point(
                    parseInt(points[i].x - this.canvas.absoluteX, 10),
                    parseInt(points[i].y - this.canvas.absoluteY, 10)
                ),
                parent: this,
                canvas: this.canvas,
                color: this.segmentColor
            });
            this.addSegment(segment);
        }
        return this;
    };

    /**
     * Add a segment to the line segments arrayList (painting it first)
     * @param {PMUI.draw.Segment} segment
     * @chainable
     */
    Connection.prototype.addSegment = function (segment) {
        segment.setStyle(this.segmentStyle);
        segment.paint();
        this.lineSegments.insert(segment);
        return this;
    };

    /**
     * Destroys the connection but saving its HTML first
     * @chainable
     */
    Connection.prototype.saveAndDestroy = function () {

        if (this.canvas.currentConnection) {
            this.hidePortsAndHandlers();
            this.canvas.currentConnection = null;
        }

        // remove this from the canvas connections arrayList
        this.canvas.removeConnection(this);

        //this.canvas.removeFromList(this);
        this.srcPort.saveAndDestroy(); //destroy srcPort
        this.destPort.saveAndDestroy(); //destroy destPort

        // save the html but detach it from the DOM
        this.html = $(this.html).detach()[0];

        return this;
    };

    /**
     * Fixes the zIndex of the connection based on the parents of the connection ports (which are
     * shapes), the zIndex is defined as the maximum zIndex the ports parents + 2
     * @chainable
     */
    Connection.prototype.fixZIndex = function () {
        var sourceShape = this.srcPort.parent,
            destShape = this.destPort.parent,
            sourceShapeParent,
            destShapeParent,
            sourceShapeParentZIndex,
            destShapeParentZIndex;

        if (sourceShape.parent) {
            sourceShapeParent = sourceShape.parent;
        } else {
            sourceShapeParent = sourceShape.canvas;
        }
        sourceShapeParentZIndex = Math.min(sourceShapeParent.getZOrder(),
            sourceShape.getZOrder() - 1);

        if (destShape.parent) {
            destShapeParent = destShape.parent;
        } else {
            destShapeParent = destShape.canvas;
        }
        destShapeParentZIndex = Math.min(destShapeParent.getZOrder(),
            destShape.getZOrder() - 1);

        this.setZOrder(Math.max(sourceShapeParentZIndex, destShapeParentZIndex) +
            2);
        return this;
    };
    /**
     * Checks and creates intersections of `this` connection with the `otherConnection`
     * @param {PMUI.draw.Connection} otherConnection
     * @return {boolean} True if there is at least one intersection
     */
    Connection.prototype.checkAndCreateIntersections = function (otherConnection) {
        // iterate over all the segments of this connection
        var i,
            j,
            segment,
            testingSegment,
            hasAtLeastOneIntersection = false,
            ip; // intersectionPoint

        for (i = 0; i < this.lineSegments.getSize(); i += 1) {
            segment = this.lineSegments.get(i);
            for (j = 0; j < otherConnection.lineSegments.getSize(); j += 1) {
                testingSegment = otherConnection.lineSegments.get(j);

                // create the intersection of the segments if possible
                ip = PMUI.draw.Geometry.perpendicularSegmentIntersection(segment.startPoint,
                    segment.endPoint, testingSegment.startPoint,
                    testingSegment.endPoint);
                if (ip) {
                    hasAtLeastOneIntersection = true;
                    segment.createIntersectionWith(testingSegment, ip);
                }
            }
        }
        if (hasAtLeastOneIntersection) {
            if (!this.intersectionWith.find('id', otherConnection.getID())) {
                this.intersectionWith.insert(otherConnection);
            }
            if (!otherConnection.intersectionWith.find('id', this.getID())) {
                otherConnection.intersectionWith.insert(this);
            }
        }
        return hasAtLeastOneIntersection;
    };

    /**
     * Checks and creates intersections with all the other connections found in this canvas.
     * This method also repaints the segments that have intersections.
     * @chainable
     */
    Connection.prototype.checkAndCreateIntersectionsWithAll = function () {
        var i,
            otherConnection,
            segment;
        // create the intersections of this connection
        // each segment of this connection saves the intersections it has with
        // other segments as an ArrayList of Intersections
        //    console.log(this.canvas.connections.getSize());
        for (i = 0; i < this.canvas.connections.getSize(); i += 1) {
            otherConnection = this.canvas.connections.get(i);
            if (otherConnection.getID() !== this.getID()) {
                this.checkAndCreateIntersections(otherConnection);
            }
        }

        // after we've got all the intersections
        // paint the segments with their intersections
        for (i = 0; i < this.lineSegments.getSize(); i += 1) {
            segment = this.lineSegments.get(i);
            if (segment.intersections.getSize()) {
                segment.paintWithIntersections();
            }
        }
        return this;
    };

    /**
     * Clears all the intersections with the otherConnection that exist in this connection
     * @param {PMUI.draw.Connection} otherConnection
     * @chainable
     */
    Connection.prototype.clearIntersectionsWith = function (otherConnection) {
        var i,
            segment,
            intersectionObject,
            intersectionWasErased;
        for (i = 0; i < this.lineSegments.getSize(); i += 1) {
            intersectionWasErased = false;
            segment = this.lineSegments.get(i);
            while (true) {
                intersectionObject = segment.
                intersections.find('idOtherConnection',
                    otherConnection.getID());
                if (intersectionObject) {
                    segment.intersections.remove(intersectionObject);
                    intersectionObject.destroy();
                } else {
                    break;
                }
                intersectionWasErased = true;
            }
            if (intersectionWasErased) {
                segment.paintWithIntersections();
            }
        }
        // remove other connection from this connection intersectionWith ArrayList
        this.intersectionWith.remove(otherConnection);
        otherConnection.intersectionWith.remove(this);
        return this;
    };

    /**
     * Clear all the intersections of this connection calling clearIntersectionsWith
     * many times (one for each connection that exists in the canvas)
     * @chainable
     */
    Connection.prototype.clearAllIntersections = function () {
        var otherIntersection;
        while (this.intersectionWith.getSize() > 0) {
            otherIntersection = this.intersectionWith.get(0);
            otherIntersection.clearIntersectionsWith(this);
        }
        return this;
    };

    /**
     * Moves the connection [dx, dy]
     * @param {number} dx
     * @param {number} dy
     * @chainable
     */
    Connection.prototype.move = function (dx, dy) {
        var top,
            left;

        // moving with inline style
        top = parseFloat(this.html.style.top);
        left = parseFloat(this.html.style.left);
        $(this.html).css({
            'top': top + dy,
            'left': left + dx
        });
        return this;
    };

    /**
     * Serializes this object (as a JavaScript object)
     * @return {Object}
     * @return {string} return.segmentStyle The style of each segment of this connection
     * @return {Object} return.srcPort The serialization of `this.srcPort`
     * @return {Object} return.destPort The serialization of `this.destPort`
     * @return {Array} return.state The array of points that represent this connection a.k.a. state
     * @return {string} return.srcDecoratorPrefix The source decorator prefix
     * @return {string} return.destDecoratorPrefix The destination decorator prefix
     */
    Connection.prototype.stringify = function () {
        return {
            segmentStyle: this.getSegmentStyle(),
            srcPort: this.getSrcPort().stringify(),
            destPort: this.getDestPort().stringify(),
            state: this.savePoints() && this.points,
            srcDecoratorPrefix: this.getSrcDecorator().getDecoratorPrefix(),
            destDecoratorPrefix: this.getDestDecorator().getDecoratorPrefix()
        };
    };

    /**
     * Sets the color of the segments of this connection
     * @param {PMUI.util.Color} newColor
     * @param {boolean} [repaint] True if the segment are to be painted immediately
     * @chainable
     */
    Connection.prototype.setSegmentColor = function (newColor, repaint) {
        var i,
            segment;
        this.segmentColor = newColor;
        if (this.html && repaint) {
            for (i = 0; i < this.lineSegments.getSize(); i += 1) {
                segment = this.lineSegments.get(i);
                segment.setColor(this.segmentColor);
                segment.paint();
            }
        }
        return this;
    };

    /**
     * Get the segment color of this connection
     * @return {PMUI.util.Color}
     */
    Connection.prototype.getSegmentColor = function () {
        return this.segmentColor;
    };

    /**
     * Sets the style of each segment of this connection
     * @param {string} newStyle
     * @param {boolean} [repaint] True if the segment are to be painted immediately
     * @chainable
     */
    Connection.prototype.setSegmentStyle = function (newStyle, repaint) {
        var i,
            segment;
        this.segmentStyle = newStyle;
        if (this.html && repaint) {
            for (i = 0; i < this.lineSegments.getSize(); i += 1) {
                segment = this.lineSegments.get(i);
                segment.setStyle(this.segmentStyle);
                segment.paint();
            }
        }
        return this;
    };

    /**
     * Get the segment style of this connection
     * @return {string}
     */
    Connection.prototype.getSegmentStyle = function () {
        return this.segmentStyle;
    };

    /**
     * Sets the source port
     * @param {PMUI.draw.Port} newSrcPort
     * @chainable
     */
    Connection.prototype.setSrcPort = function (newSrcPort) {
        this.srcPort = newSrcPort;
        return this;
    };

    /**
     * Gets the source port
     * @return {PMUI.draw.Port}
     */
    Connection.prototype.getSrcPort = function () {
        return this.srcPort;
    };

    /**
     * Sets the destination port
     * @param {PMUI.draw.Port} newDestPort
     * @chainable
     */
    Connection.prototype.setDestPort = function (newDestPort) {
        this.destPort = newDestPort;
        return this;
    };

    /**
     * Gets the destination port
     * @return {PMUI.draw.Port}
     */
    Connection.prototype.getDestPort = function () {
        return this.destPort;
    };

    /**
     * Returns the source decorator of the connection
     * @returns {PMUI.draw.ConnectionDecorator}
     */
    Connection.prototype.getSrcDecorator = function () {
        return this.srcDecorator;
    };
    /**
     * Returns the target decorator of the connection
     * @returns {PMUI.draw.ConnectionDecorator}
     */
    Connection.prototype.getDestDecorator = function () {
        return this.destDecorator;
    };
    /**
     * Returns a list of the lines associated with this connection
     * @returns {PMUI.util.ArrayList}
     */
    Connection.prototype.getLineSegments = function () {
        return this.lineSegments;
    };

    /**
     * Sets the source decorator of the connection
     * @param {PMUI.draw.ConnectionDecorator} newDecorator
     * @chainable
     */
    Connection.prototype.setSrcDecorator = function (newDecorator) {
        if (newDecorator.type === 'ConnectionDecorator') {
            this.srcDecorator = newDecorator;
        }
        return this;
    };

    /**
     * Sets the destination decorator of the connection
     * @param {PMUI.draw.ConnectionDecorator} newDecorator
     * @chainable
     */
    Connection.prototype.setDestDecorator = function (newDecorator) {
        if (newDecorator.type === 'ConnectionDecorator') {
            this.destDecorator = newDecorator;
        }
        return this;
    };

    /**
     * Gets the zOrder of the connection
     * @return {number}
     */
    Connection.prototype.getZOrder = function () {
        return PMUI.draw.Shape.prototype.getZOrder.call(this);
    };

    /**
     * Gets the oldPoints of the connection
     * @return {Array}
     */
    Connection.prototype.getOldPoints = function () {
        return this.oldPoints;
    };

    /**
     * Gets the points of the connection
     * @return {Array}
     */
    Connection.prototype.getPoints = function () {
        return this.points;
    };

    Connection.prototype.hitTest = function (point) {
        var count = this.getLineSegments().asArray().length,
            i,
            segment,
            response;

        for (i = 0; i < count; i += 1) {
            segment = this.getLineSegments().get(i);
            response = segment.hit(this.corona, point);
            if (response) {
                return true;
            }
        }
        return false;
    };

    Connection.prototype.applyZoom = function () {
        this.disconnect(false);
        this.connect({
            algorithm: 'user',
            points: this.points,
            updateZoomPoints: false
        });
        this.canvas.refreshArray.insert(this);
        this.setSegmentMoveHandlers();
        this.checkAndCreateIntersectionsWithAll();
    };

    Connection.prototype.reconnectUser = function (delta, inContainer) {
        var j;
        if (delta) {
            for (j = 0; j < this.points.length; j += 1) {
                this.points[j].x += delta.dx * this.canvas.zoomFactor;
                this.points[j].y += delta.dy * this.canvas.zoomFactor;
            }

            this.disconnect(inContainer);
            this.connect({
                algorithm: 'user',
                points: this.points
            });
        }
    };

    Connection.prototype.reconnectManhattah = function (inContainer) {
        this.disconnect(inContainer);
        this.connect();
    };
    /**
     * Increases z-index to bring to front a connection
     * @returns {PMUI.draw.Connection}
     */
    Connection.prototype.increaseZIndex = function () {
        this.setZOrder(PMUI.util.Style.MAX_ZINDEX);
        return this;
    };

    PMUI.extendNamespace('PMUI.draw.Connection', Connection);

    if (typeof exports !== 'undefined') {
        module.exports = Connection;
    }

}());

(function () {
    /**
     * @class PMUI.draw.ConnectionDecorator
     * Represents the decorator on each endpoint of a connection (represented as srcDecorator and destDecorator in
     * the class Connection).
     * The connection will be painted as follows:
     *
     * 1. Each connection decorator is painted with a CSS sprite
     * 2. The CSS class is built concatenating (with underscores) the following:
     *
     *      1. The prefix (passed as an argument in the config options)
     *      2. The zoom factor multiplied by 100
     *      3. The decorator type (passed as an argument in the config options)
     *      4. The direction of the decorator (which is the same as the direction of the port it corresponds to)
     *
     * Some examples:
     *
     *      // e.g.
     *      // let's assume that the zoom factor is 1
     *      // let's assume that connection is an instance of the class Connection
     *
     *      // To create a target decorator
     *      var connectionDecorator = new PMUI.draw.ConnectionDecorator({
     *          decoratorPrefix:  'con',
     *          decoratorType:  'target',
     *          style:  {
     *              cssClasses:  [],
     *              cssProperties:  {}
     *          },
     *          parent:  connection
     *      });
     *
     *      // assuming that the direction of the port is (1) TOP
     *      // paint() will build the class like this:
     *      // CSSClass = decoratorPrefix + "_" + zoomFactor * 100 + "_" + decoratorType + "_" + direction
     *      // CSSClass = "con_100_target_TOP"
     *
     *      // To create a source decorator
     *      var connectionDecorator = new PMUI.draw.ConnectionDecorator({
     *          decoratorPrefix:  'con',
     *          decoratorType:  'source',
     *          style:  {
     *              cssClasses:  [],
     *              cssProperties:  {}
     *          },
     *          parent:  connection
     *      });
     *
     *      // assuming that the direction of the port is (3) LEFT
     *      // paint() will build the class like this:
     *      // CSSClass = decoratorPrefix + "_" + zoomFactor * 100 + "_" + decoratorType + "_" + direction
     *      // CSSClass = "con_100_source_LEFT"
     *
     * @extends PMUI.draw.Core
     *
     * @constructor Creates an instance of the class ConnectionDecorator
     * @param {Object} options Initialization options
     * @cfg {PMUI.util.Point} [decoratorPrefix=''] Decorator prefix used to
     reconstruct the css class for the sprite
     * @cfg {PMUI.draw.Connection} [parent=null] The parent of this decorator
     (must be an instance of the class Connection)
     * @cfg {Object} [style={cssClasses: [], cssProperties: {}}] CSS classes and properties
     */
    var ConnectionDecorator = function (options) {

        ConnectionDecorator.superclass.call(this, options);

        /**
         * Parent of this decorator (must be a Connection)
         * @property {PMUI.draw.Connection}
         */
        this.parent = null;

        /**
         * The type of this decorator (either "source" or "target")
         * @property {"source" / "target"}
         */
        this.decoratorType = null;

        /**
         * Decorator prefix of this decorator to build the CSS class for the sprite
         * @property {String}
         */
        this.decoratorPrefix = null;

        /**
         * This parameter is an array to see if the end point
         * is UP, RIGHT, BOTTOM and LEFT
         * @property {Object} spriteDirection
         * @property {string} [spriteDirection.0="up"] Enum for "up"
         * @property {string} [spriteDirection.1="right"] Enum for "right"
         * @property {string} [spriteDirection.2="bottom"] Enum for "bottom"
         * @property {string} [spriteDirection.3="left"] Enum for "left"
         */
        this.spriteDirection = {
            '0': 'top', '1': 'right',
            '2': 'bottom', '3': 'left'
        };

        /**
         * Height of this decorator
         * @property {number} [height=11]
         */
        this.height = 11;

        /**
         * Width of this decorator
         * @property {number} [width=11]
         */
        this.width = 11;

        /**
         * Separator used to build the class
         * @type {String}
         */
        this.separator = null;

        /**
         * Sprite used to build the class
         * @type {String}
         */
        this.sprite = null;

        /**
         * The class that will be constructed using the parameters given in
         * the options object
         * @type {string}
         */
        this.cssClass = null;

        ConnectionDecorator.prototype.initObject.call(this, options);
    };

    PMUI.inheritFrom('PMUI.draw.Core', ConnectionDecorator);

    /**
     * Type of this connection decorator
     * @property {String}
     */
    ConnectionDecorator.prototype.type = "ConnectionDecorator";

    /**
     * Instance initializer which uses options to extend the config options to initialize the instance
     * @param {Object} options The object that contains the config
     * @private
     */
    ConnectionDecorator.prototype.initObject = function (options) {

        var defaults = {
            width: 11,
            height: 11,
            sprite: 'bpmn_zoom',
            decoratorPrefix: '',
            separator: '_',
            decoratorType: 'target',
            parent: null
        };

        // extend recursively the defaultOptions with the given options
        $.extend(true, defaults, options);

        // init
        this.setDecoratorType(defaults.decoratorType)
            .setDecoratorPrefix(defaults.decoratorPrefix)
            .setSeparator(defaults.separator)
            .setSprite(defaults.sprite)
            .setParent(defaults.parent)
            .setDimension(defaults.width, defaults.height)
            .setCssClass('');       // cssClass defaults to empty
    };

    /**
     * Paints the connectionDecorator according to the parameters saved in `this.initObject`.
     * The steps to paint the decorator are:
     *
     * 1. Determine if this decorator belongs to the source or destination port
     * 2. Determine the direction of the decorator
     * 3. Build the class using the direction, decorator prefix, decorator type and zoom
     * 4. Determine the position of this decorator
     *
     * @chainable
     */
    ConnectionDecorator.prototype.paint = function (options) {
        var linePoint,
            portPoint,
            canvas,
            direction,
            lineSegment,
            port,
            topStyle,
            leftStyle;

        if (this.decoratorType === "source") {
            port = this.parent.getSrcPort();
            lineSegment = this.parent.lineSegments.get(0);
            linePoint = lineSegment.startPoint;
        } else {
            port = this.parent.getDestPort();
            lineSegment = this.parent.lineSegments.get(this.parent.lineSegments.getSize() - 1);
            linePoint = lineSegment.endPoint;
        }

        portPoint = port.getPoint();
        portPoint.x = Math.round(portPoint.x - this.canvas.absoluteX);
        portPoint.y = Math.round(portPoint.y - this.canvas.absoluteY);

        direction = port.getDirection();
        canvas = port.canvas;

        switch (direction) {
            case 0:
            case 2:
                if (this.zoomWidth % 2 === 0) {
                    leftStyle = (this.zoomWidth / 2) - 1;
                } else {
                    leftStyle = Math.floor(this.zoomWidth / 2);
                }
                leftStyle = linePoint.x - leftStyle;
                topStyle = portPoint.y - (direction === 0 ? this.zoomHeight : 0);
                break;
            case 1:
            case 3:
                if (this.zoomHeight % 2 === 0) {
                    topStyle = (this.zoomHeight / 2) - 1;
                } else {
                    topStyle = Math.floor(this.zoomHeight / 2);
                }
                topStyle = linePoint.y - topStyle;
                leftStyle = portPoint.x - ((direction === 1) ? 0 : this.zoomWidth);
                break;
            default:
                throw new Error("paint(): invalid direction.");
        }

        if (this.getHTML() === null) {
            this.createHTML();
        }

        if (this.decoratorType === null) {
            this.html = null;
            return this;
        }

        // remove the last class if possible
        this.style.removeClasses([this.cssClass]);

        // construct the new class to be applied
        this.setCssClass([this.prefix, parseInt(canvas.zoomFactor * 100, 10),
            this.decoratorType, this.spriteDirection[direction]].join(this.separator));

        this.style.addClasses([
            this.sprite,
            this.getCssClass()
        ]);

        // top and left position
        this.style.addProperties({
            top: topStyle,
            left: leftStyle
        });

        this.parent.html.appendChild(this.html);
        return this;
    };

    /**
     * Creates the HTML Representation of the SourceSpriteConnectionDecorator
     * @returns {HTMLElement}
     */
    ConnectionDecorator.prototype.createHTML = function () {
        this.html = document.createElement('div');
        this.html.id = this.id;
        this.style.applyStyle();
        this.style.addProperties({
            position: "absolute",
            left: 0,
            top: 0,
            height: this.zoomHeight,
            width: this.zoomWidth,
            zIndex: PMUI.util.Style.MAX_ZINDEX
        });

        return this.html;
    };

    /**
     * Attaches listeners to the connectionDecorator (currently it has click and mouseDown events)
     * @chainable
     */
    ConnectionDecorator.prototype.attachListeners = function () {
        var $connectionDecorator;
        $connectionDecorator = $(this.getHTML()).click(this.onClick(this));
        $connectionDecorator.on("mousedown", this.onMouseDown(this));
        return this;
    };

    /**
     * Refresh the dimension and position of the decorator to apply the current
     * zoom scale
     * @chainable
     */
    ConnectionDecorator.prototype.applyZoom = function () {
        this.setDimension(this.width, this.height);
        return this;
    };
    /**
     * @event mousedown
     * ConnectionDecorator mouse down callback fired when the mouse is down on it.
     * @param {PMUI.draw.ConnectionDecorator} decorator
     */
    ConnectionDecorator.prototype.onMouseDown = function (decorator) {
        return function (e, ui) {
            e.preventDefault();
            if (e.which === 3) {
                decorator.parent.canvas.updatedElement = decorator.parent;
                $(decorator.parent.canvas.html).trigger("rightclick");
            }
            e.stopPropagation();
        };
    };
    /**
     * @event click
     * Click callback fired when the decorator is clicked on.
     * It hides the currentSelection if any and shows the ports and handlers of `decorator` parent
     * (which is a connection).
     * @param {PMUI.draw.ConnectionDecorator} decorator
     */
    ConnectionDecorator.prototype.onClick = function (decorator) {
        return function (e, ui) {
            var connection = decorator.parent,
                oldConnection = decorator.parent.canvas.currentConnection,
                canvas = connection.canvas;

            // SHOW
            // show the ports and the handlers of the new connection
            connection.showPortsAndHandlers();

            // HIDE
            // if there were some shapes in the current selection then
            // empty the current selection
            canvas.emptyCurrentSelection();

            // if there was a connection previously select hide its ports
            // and its handlers
            if (oldConnection) {
                oldConnection.hidePortsAndHandlers();
            }
            // set the old connection as this connection
            canvas.currentConnection = connection;

            // TODO:  zIndex
            e.stopPropagation();
        };
    };

    /**
     * Serializes this connection decorator.
     * @return {Object}
     * @return {"source" / "target"} return.decoratorType The decorator type to build the CSS class for the sprite
     * @return {string} return.decoratorPrefix The decorator prefix to build the CSS class for the sprite
     */
    ConnectionDecorator.prototype.stringify = function () {
        var inheritedJSON = {},
            thisJSON = {
                decoratorType: this.getDecoratorType(),
                decoratorPrefix: this.getDecoratorPrefix()
            };
        $.extend(true, inheritedJSON, thisJSON);
        return inheritedJSON;
    };

    /**
     * Returns the decorator type
     * @returns {String}
     */
    ConnectionDecorator.prototype.getDecoratorType = function () {
        return this.decoratorType;
    };

    /**
     * Sets the decoration type
     * @param {String} newType
     * @chainable
     */
    ConnectionDecorator.prototype.setDecoratorType = function (newType) {
        this.decoratorType = newType;
        return this;
    };

    /**
     * Returns the decorator type
     * @returns {String}
     */
    ConnectionDecorator.prototype.getDecoratorPrefix = function () {
        return this.prefix;
    };

    /**
     * Sets the decoration prefix
     * @param {String} newType
     * @chainable
     */
    ConnectionDecorator.prototype.setDecoratorPrefix = function (newType) {
        this.prefix = newType;
        return this;
    };

    /**
     * Sets the parent of this connectionDecorator
     * @param {PMUI.draw.Connection} newParent
     * @chainable
     */
    ConnectionDecorator.prototype.setParent = function (newParent) {
        this.parent = newParent;
        return this;
    };

    /**
     * Gets the parent of this connectionDecorator
     * @return {PMUI.draw.Connection}
     */
    ConnectionDecorator.prototype.getParent = function () {
        return this.parent;
    };

    /**
     * Sets the separator of this connectionDecorator
     * @param {String} newSeparator
     * @chainable
     */
    ConnectionDecorator.prototype.setSeparator = function (newSeparator) {
        this.separator = newSeparator;
        return this;
    };

    /**
     * Sets the sprite of this connectionDecorator
     * @param {String} newSprite
     * @chainable
     */
    ConnectionDecorator.prototype.setSprite = function (newSprite) {
        this.sprite = newSprite;
        return this;
    };

    /**
     * Sets the cssClass of this connectionDecorator
     * @param {string} newCssClass
     * @chainable
     */
    ConnectionDecorator.prototype.setCssClass = function (newCssClass) {
        this.cssClass = newCssClass;
        return this;
    };

    /**
     * Gets the cssClass of this connectionDecorator
     * @return {string}
     */
    ConnectionDecorator.prototype.getCssClass = function () {
        return this.cssClass;
    };

    PMUI.extendNamespace('PMUI.draw.ConnectionDecorator', ConnectionDecorator);

    if (typeof exports !== 'undefined') {
        module.exports = ConnectionDecorator;
    }

}());

(function () {
    /**
     *
     * @class PMUI.draw.Layer
     * Class that contains the properties of a layer for a shape we need
     * to have a shape already instantiated and added to the canvas in order for
     * this class to be effective
     *
     *          //e.g.
     *           var layer = new PMUI.draw.Layer({
     *                  //Determine the layer's parent
     *                  parent: customShape,
     *                  layerName: "first layer",
     *                  //the order in which the layers will be added in increasing
     *                  //order
     *                  priority: 0,
     *                  //determines if the layer will be hidden or visible
     *                  visible: true,
     *                  //sprites to be applied on the layers according to a zoom
     *                  //scale
     *                  zoomSprites : ["class50, class75, class100, class125,
     *                      class 150"]
     *           });
     * @extend PMUI.draw.Core
     *
     * @constructor
     * Initializes a layer, the constructor must be called with all its parameter
     * for the object to be meaningful, its is important to denote that the css
     * class must follow this structure
     * any word_zoomScale_anythingYouWantHere
     * @param {Object} options
     * @cfg {Object} parent, Parent of a corresponding layer, a layer may not exist
     * without a parent
     * @cfg {String} [layerName="defaultLayerName"] A name we want to label a layer
     * with
     * @cfg {number} [priority=0] The orders in which the layers will be added in
     * increasing order
     * @cfg {boolean} [visible=true] Determines whether a layer wll be visible or
     * hidden
     * @cfg {Array} [zoomSprites=["","","","",""]] Sprites to be applied to the
     * layer according to a zoom scale
     */
    var Layer = function (options) {

        // TODO:  check elementClass and bpmnClass removal impact on the layers
        //Layer = function (parent, name, elementClass, priority, bpmnClass, visible) {

        Layer.superclass.call(this, options);

        /**
         * The name of the layer
         * @property {String}
         */
        this.layerName = null;

        /**
         * The priority of the layer, determines which layer should be on top
         * @property {number}
         */
        this.priority = null;

        /**
         * The bpmnShape that this layer belongs too.
         * Extremely important since some data will be strictly drawn by its parent
         * @property {Object}
         */
        this.parent = null;

        /**
         * Determines when a layer is visible or not
         * @property boolean
         */
        this.visible = null;

        /**
         * Determines when a layer is resizable or not
         * @property boolean
         */
        this.resizable = null;

        /**
         * The current Sprite applied in the zoom scale
         * @property {String}
         */
        this.currentZoomClass = "";
        /**
         * Sprites for the layer in each zoom scale
         * @property {Array}
         */
        this.zoomSprites = [];

        Layer.prototype.initObject.call(this, options);
    };

    PMUI.inheritFrom('PMUI.draw.Core', Layer);

    /**
     * Type of an instance of this class
     * @property {String}
     */
    Layer.prototype.type = "Layer";

    /**
     * Object init method (internal)
     * @param {Object} options
     */
    Layer.prototype.initObject = function (options) {
        /**
         * Default options for the object
         * @property {Object}
         */
        var defaults = {
            x: 0,
            y: 0,
            parent: null,
            layerName: "defaultLayerName",
            resizable: true,
            priority: 0,
            visible: true,
            zoomSprites: ["", "", "", "", ""]
        };

        // extend recursively the defaultOptions with the given options
        $.extend(true, defaults, options);

        // call setters using the defaults object
        this.setParent(defaults.parent)
            .setPosition(defaults.x, defaults.y)
            .setLayerName(defaults.layerName)
            .setPriority(defaults.priority)
            .setVisible(defaults.visible)
            .setZoomSprites(defaults.zoomSprites)
            .setProperties()
            .setResizable(defaults.resizable);

    };
    /**
     * Updates the properties in order to change zoom scales
     */
    Layer.prototype.applyZoom = function () {
        this.setProperties();
    };

    /**
     * Updates the property resizable
     */
    Layer.prototype.setResizable = function (option) {
        this.resizable = option;
    };

    /**
     * Comparison function for ordering layers according to priority
     * @param {PMUI.draw.Layer} layer1
     * @param {PMUI.draw.Layer} layer2
     * @returns {boolean}
     */
    Layer.prototype.comparisonFunction = function (layer1, layer2) {
        if (layer1.priority > layer2.priority) {
            return -1;
        } else if (layer1.priority > layer2.priority) {
            return 1;
        }
        return 0;
    };
    /**
     * Creates the HTML representation of the layer
     * @returns {HTMLElement}
     */
    Layer.prototype.createHTML = function (modifying) {
        this.setProperties();
        Layer.superclass.prototype.createHTML.call(this, modifying);
        return this.html;
    };
    /**
     * Paints the corresponding layer, in this case adds the
     * corresponding css classes
     * @chainable
     */
    Layer.prototype.paint = function () {

        var $layer = $(this.html),
            newSprite;
        this.style.removeClasses([this.currentZoomClass]);
        newSprite = this.zoomSprites[this.canvas.zoomPropertiesIndex];
        this.style.addClasses([newSprite]);
        this.currentZoomClass = newSprite;
        this.style.applyStyle();
        return this;
    };

    /**
     * This method will set the parent necessary properties for the layer to work
     * @chainable
     */
    Layer.prototype.setProperties = function () {

        if (!this.parent) {
            return this;
        }
        //generates an id for the layer
        this.id = this.parent.getID() + "Layer-" + this.layerName;
        if ((typeof this.width !== "number") && (typeof this.height !== "number")) {
            this.setDimension(this.parent.getWidth(), this.parent.getHeight());
        } else {
            this.setDimension(this.width, this.height);
        }

        if (this.resizable)
            this.setDimension(this.parent.getWidth(), this.parent.getHeight());

        // DO NOT ASSUME THAT THE POSITION OF THE LAYER IS 0,0 BECAUSE OF THE
        // BORDERS IT MAY HAVE
        this.canvas = this.parent.canvas;
        return this;
    };
    /**
     * Returns the layer name
     * @returns {String}
     */
    Layer.prototype.getLayerName = function () {
        return this.layerName;
    };

    /**
     * Returns the priority of the layer
     * @returns {number}
     */
    Layer.prototype.getPriority = function () {
        return this.priority;
    };

    /**
     * Sets the layer name
     * @param {String} newLayerName
     * @chainable
     */
    Layer.prototype.setLayerName = function (newLayerName) {
        if (typeof newLayerName === "string" && newLayerName !== "") {
            this.layerName = newLayerName;
        }
        return this;
    };

    /**
     * Sets the priority of the layer
     * @param {number} newPriority
     * @chainable
     */
    Layer.prototype.setPriority = function (newPriority) {
        if (typeof newPriority === "number") {
            this.priority = newPriority;
        }
        return this;
    };

    /**
     * Sets the parent of this layer
     * @param {PMUI.draw.CustomShape} newParent
     * @chainable
     */
    Layer.prototype.setParent = function (newParent) {
        if (newParent) {
            this.parent = newParent;
        }
        return this;
    };

    /**
     * Gets the parent of this layer
     * @return {PMUI.draw.Shape}
     */
    Layer.prototype.getParent = function () {
        return this.parent;
    };

    /**
     * Sets the css classes for the zoom scales
     * @param {Array} zoomSprites
     * @chainable
     */
    Layer.prototype.setZoomSprites = function (zoomSprites) {
        var i;
        this.zoomSprites = ["", "", "", "", ""];
        for (i = 0; i < zoomSprites.length; i += 1) {
            this.zoomSprites[i] = zoomSprites[i];
        }
        return this;
    };
    /**
     * Serializes this object
     * @return {Object}
     */
    Layer.prototype.stringify = function () {
        /**
         * inheritedJSON = {
         *     id:  #
         *     x:  #,
         *     y:  #,
         *     width:  #,
         *     height:  #
         * }
         * @property {Object}
         */
        var inheritedJSON = {},
            thisJSON = {
                id: this.getID(),
                x: this.getX(),
                y: this.getY(),
                layerName: this.getLayerName(),
                priority: this.getPriority(),
                style: {
                    cssClasses: this.style.getClasses()
                },
                zoomSprites: this.zoomSprites
            };
        $.extend(true, inheritedJSON, thisJSON);
        return inheritedJSON;
    };

    PMUI.extendNamespace('PMUI.draw.Layer', Layer);

    if (typeof exports !== 'undefined') {
        module.exports = Layer;
    }

}());

(function () {
    /**
     * @abstract
     * @class PMUI.draw.Handler
     * Abstract class which provides methods to represent a handler.
     * @extends PMUI.draw.Core
     *
     * @constructor Creates an instance of the class Handler (for inheritance purposes only).
     * @param {Object} options Initialization options.
     */
    var Handler = function (options) {

        Handler.superclass.call(this, options);

        /**
         * Representation of this handler.
         * @property {Object}
         */
        this.representation = null;

        /**
         * The parent of this handler.
         * @property {PMUI.draw.Shape}
         */
        this.parent = null;

        /**
         * Color of this handler.
         * @property {PMUI.util.Color}
         */
        this.color = null;

        /**
         * The orientation of this handler.
         * @property {string}
         */
        this.orientation = null;
    };

    PMUI.inheritFrom('PMUI.draw.Core', Handler);

    /**
     * Sets the parent of this handler
     * @param newParent
     * @chainable
     */
    Handler.prototype.setParent = function (newParent) {
        this.parent = newParent;
        return this;
    };

    /**
     * Gets the parent of this handler
     * @return {PMUI.draw.Shape}
     */
    Handler.prototype.getParent = function () {
        return this.parent;
    };

    /**
     * Sets the representation of this handler
     * @param representation
     * @chainable
     */
    Handler.prototype.setRepresentation = function (representation) {
        this.representation = representation;
        return this;
    };

    /**
     * Gets the representation of this handler
     * @return {Object}
     */
    Handler.prototype.getRepresentation = function () {
        return this.representation;
    };

    /**
     * Sets the orientation of this handler
     * @param newOrientation
     * @chainable
     */
    Handler.prototype.setOrientation = function (newOrientation) {
        this.orientation = newOrientation;
        return this;
    };

    /**
     * Gets the orientation of this handler
     * @return {string}
     */
    Handler.prototype.getOrientation = function () {
        return this.orientation;
    };

    /**
     * Paint the handler method which will call `this.representation.paint()`
     * @chainable
     */
    Handler.prototype.paint = function () {
        this.representation.paint.call(this);
        this.style.applyStyle();
        return this;
    };

    /**
     * The color representation of this object
     * @param {PMUI.util.Color} newColor
     * @chainable
     */
    Handler.prototype.setColor = function (newColor) {
        this.color = newColor;
        return this;
    };

    /**
     * Get the color representation of this object
     * @return {PMUI.util.Color}
     */
    Handler.prototype.getColor = function () {
        return this.color;
    };

    PMUI.extendNamespace('PMUI.draw.Handler', Handler);

    if (typeof exports !== 'undefined') {
        module.exports = Handler;
    }

}());
(function () {
    /**
     * @class PMUI.draw.ResizeHandler
     * Defines a class resize handler to represent handlers used with jQueryUI' resizable plugin, currently
     * it has only support for rectangle resize handler (oval resize handlers were implemented but apparently
     * jQueryUI won't accept a child of the designated HTMLElement to be used as the resize handler).
     *
     * An example of use:
     *
     *      // e.g.
     *      // let's assume that shape is an instance of the class Shape
     *      // let's assume that rectangle is an instance of the class Rectangle
     *
     *      var resizableStyle = {
     *              cssProperties: {
     *                  'background-color': "rgb(0, 255, 0)",
     *                  'border': '1px solid black'
     *              }
     *          },
     *          nonResizableStyle = {
     *              cssProperties: {
     *                  'background-color': "white",
     *                  'border': '1px solid black'
     *              }
     *          },
     *          resizeHandler;
     *
     *      resizeHandler = new PMUI.draw.ResizeHandler({
     *          width: 8,
     *          height: 8,
     *          parent: shape,
     *          orientation: 'nw'                   // see jQueryUI's resizable plugin 'handles' option
     *          representation: rectangle,
     *          resizableStyle: resizableStyle,
     *          nonResizableStyle: nonResizableStyle,
     *          zOrder: 2
     *      });
     *
     * @extend PMUI.draw.Handler
     * @constructor Creates an instance of resize handler.
     * @param {Object} options
     * @cfg {number} [width=4] The width of this resize handler.
     * @cfg {number} [height=4] The height of this resize handler.
     * @cfg {PMUI.draw.Shape} [parent=null] The parent of this resize handler.
     * @cfg {string} [orientation=null] The orientation of this resize handler.
     * @cfg {string} [representation=null] The representation of this resize handler.
     * @cfg {Object} [resizableStyle={}] The parameters to create an instance of the class Style used
     * when the object is resizable.
     * @cfg {Object} [nonResizableStyle={}] The parameters to create an instance of the class Style used
     * when the object is not resizable.
     * @cfg {number} [zOrder=2] The z-index of this resize handler.
     */
    var ResizeHandler = function (options) {

        ResizeHandler.superclass.call(this, options);

        /**
         * Category of this resize handler
         * @type {"resizable"/"nonresizable"}
         */
        this.category = null;

        /**
         * Denotes whether the resize handle is visible or not.
         * @property boolean
         */
        this.visible = false;

        /**
         * JSON used to create an instance of the class Style used when the object is resizable.
         * @property {Object}
         */
        this.resizableStyle = null;

        /**
         * JSON used to create an instance of the class Style used when the object is not resizable.
         * @property {Object}
         */
        this.nonResizableStyle = null;

        // set defaults
        ResizeHandler.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.draw.Handler', ResizeHandler);

    /**
     * The type of each instance of this class.
     * @property {String}
     */
    ResizeHandler.prototype.type = "ResizeHandler";

    /**
     * Instance initializer which uses options to extend the config options to initialize the instance
     * @param {Object} options The object that contains the config
     * @private
     */
    ResizeHandler.prototype.init = function (options) {

        var defaults = {
            width: 4,
            height: 4,
            parent: null,
            orientation: null,
            representation: null,
            resizableStyle: {},
            nonResizableStyle: {},
            zOrder: 2
        };

        // extend recursively the defaultOptions with the given options
        $.extend(true, defaults, options);

        // add default zIndex to this handler
        if (defaults.resizableStyle.cssProperties) {
            defaults.resizableStyle.cssProperties.zIndex = defaults.zOrder;
        }
        if (defaults.nonResizableStyle.cssProperties) {
            defaults.nonResizableStyle.cssProperties.zIndex = defaults.zOrder;
        }

        // init
        this.setParent(defaults.parent)
            .setWidth(defaults.width)
            .setHeight(defaults.height)
            .setOrientation(defaults.orientation)
            .setRepresentation(defaults.representation)
            .setResizableStyle(defaults.resizableStyle)
            .setNonResizableStyle(defaults.nonResizableStyle);

        // create the id
        this.id = defaults.orientation + defaults.parent.id + "resizehandler";
    };

    /**
     * Sets the parent of this handler
     * @param {PMUI.draw.Shape} newParent
     * @chainable
     */
    ResizeHandler.prototype.setParent = function (newParent) {
        this.parent = newParent;
        return this;
    };

    /**
     * Gets the parent of this handler.
     * @return {PMUI.draw.Shape}
     */
    ResizeHandler.prototype.getParent = function () {
        return this.parent;
    };

    /**
     * Paints this resize handler by calling it's parent's `paint` and setting
     * the visibility of this resize handler
     * @chainable
     */
    ResizeHandler.prototype.paint = function () {
        if (!this.html) {
            throw new Error("paint():  This handler has no html");
        }

        // this line paints the representation (by default a rectangle)
        ResizeHandler.superclass.prototype.paint.call(this);

        this.setVisible(this.visible);
        return this;
    };

    /**
     * Sets the category of the resizeHandler (also adds the needed class to
     * make the element resizable)
     * @param newCategory
     * @chainable
     */
    ResizeHandler.prototype.setCategory = function (newCategory) {
        if (typeof newCategory === "string") {
            this.category = newCategory;
        }
        if (this.category === "resizable") {
            this.color = new PMUI.util.Color(0, 255, 0);
            this.style.addClasses([
                "ui-resizable-handle", "ui-resizable-" + this.orientation
            ]);
        } else {
            this.color = new PMUI.util.Color(255, 255, 255);
            this.style.removeClasses([
                "ui-resizable-handle", "ui-resizable-" + this.orientation
            ]);
        }
        return this;
    };


    /**
     * Sets the resizable style of this shape by creating an instance of the class Style
     * @param {Object} style
     * @chainable
     */
    ResizeHandler.prototype.setResizableStyle = function (style) {
        this.resizableStyle = new PMUI.util.Style({
            belongsTo: this,
            cssProperties: style.cssProperties,
            cssClasses: style.cssClasses
        });
        return this;
    };

    /**
     * Sets the non resizable style for this shape by creating an instance of the class Style
     * @param {Object} style
     * @chainable
     */
    ResizeHandler.prototype.setNonResizableStyle = function (style) {
        this.nonResizableStyle = new PMUI.util.Style({
            belongsTo: this,
            cssProperties: style.cssProperties,
            cssClasses: style.cssClasses
        });
        return this;
    };

    PMUI.extendNamespace('PMUI.draw.ResizeHandler', ResizeHandler);

    if (typeof exports !== 'undefined') {
        module.exports = ResizeHandler;
    }

}());

(function () {
    /**
     * @class PMUI.draw.SegmentMoveHandler
     * Represents the handler to move a segment (the handlers are visible when a decorator of the parent of this
     * segment is clicked on)
     *
     * An example of use:
     *
     *      // e.g.
     *      // let's assume that segment is an instance of the class Segment
     *      // let's assume that rectangle is an instance of the class Rectangle
     *
     *      segmentMoveHandler = new PMUI.draw.SegmentMoveHandler({
     *          width: 8,
     *          height: 8,
     *          parent: segment,
     *          orientation: 0                      // corresponds to a vertical segment
     *          representation: rectangle,
     *          color: new Color(255, 0, 0)         // red !!
     *      });
     * @extend PMUI.draw.Handler
     * @constructor Creates an instance of the class SegmentMoveHandler
     * @param {Object} options
     * @cfg {number} [width=4] The width of this segment move handler.
     * @cfg {number} [height=4] The height of this segment move handler.
     * @cfg {PMUI.draw.Shape} [parent=null] The parent of this segment move handler.
     * @cfg {number} [orientation=null] The orientation of this segment move handler.
     * @cfg {string} [representation=null] The representation of this segment move handler.
     * @cfg {number} [color=new Color(0, 255, 0)] The color of this segment move handler (green).
     */
    var SegmentMoveHandler = function (options) {

        SegmentMoveHandler.superclass.call(this, options);

        /**
         * Orientation of this segment move handler (useful to do the drag).
         * @property {number}
         */
        this.orientation = null;

        /**
         * Denotes whether the SegmentMove point is visible or not.
         * @property {boolean} [visible=false]
         */
        this.visible = false;

        /**
         * The default zOrder of this handler.
         * @property {number} [zOrder=2]
         */
        this.zOrder = 2;

        // set defaults
        SegmentMoveHandler.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.draw.Handler', SegmentMoveHandler);

    /**
     * Type of each instance of this class
     * @property {String}
     */
    SegmentMoveHandler.prototype.type = "SegmentMoveHandler";

    /**
     * Instance initializer which uses options to extend the config options to initialize the instance
     * @param {Object} options The object that contains the config
     * @private
     */
    SegmentMoveHandler.prototype.init = function (options) {

        var defaults = {
            width: 4,
            height: 4,
            parent: null,
            orientation: null,
            representation: new PMUI.draw.Rectangle(),
            color: new PMUI.util.Color(0, 255, 0)
        };

        // extend recursively the defaultOptions with the given options
        $.extend(true, defaults, options);

        // init
        this.setWidth(defaults.width)
            .setHeight(defaults.height)
            .setParent(defaults.parent)
            .setColor(defaults.color)
            .setOrientation(defaults.orientation)
            .setRepresentation(defaults.representation);
    };

    /**
     * Paints this resize handler by calling it's parent's `paint` and setting
     * the visibility of this resize handler.
     * @chainable
     */
    SegmentMoveHandler.prototype.paint = function () {
        // before it was:  Rectangle.prototype.paint.call(this);
        SegmentMoveHandler.superclass.prototype.paint.call(this);
        this.setVisible(this.visible);
        return this;
    };

    /**
     * Attaches listeners to the segmentMoveHandler, by default it creates the click,
     * mouseDown and draggable events.
     * @param {PMUI.draw.SegmentMoveHandler} handler
     * @chainable
     */
    SegmentMoveHandler.prototype.attachListeners = function (handler) {
        var $handler = $(handler.html);
        $handler.on('click', handler.onClick(handler));
        $handler.on('mousedown', handler.onMouseDown(handler));
        $handler.draggable({
            start: handler.onDragStart(handler),
            drag: handler.onDrag(handler),
            stop: handler.onDragEnd(handler),
            axis: (handler.orientation === handler.HORIZONTAL) ? "y" : "x"
            //containment:  handler.parent.parent.html
        });
        return this;
    };

    /**
     * @event mousedown
     * MouseDown callback fired when the user mouse downs on the `handler`
     * @param {PMUI.draw.SegmentMoveHandler} handler
     */
    SegmentMoveHandler.prototype.onMouseDown = function (handler) {
        return function (e, ui) {
            // This is done to avoid the start of a selection in the canvas
            // handler > segment > connection > canvas
            handler.parent.parent.canvas.draggingASegmentHandler = true;
        };
    };

    /**
     * @event click
     * Click callback fired when the user clicks on the handler
     * @param {PMUI.draw.SegmentMoveHandler} handler
     */
    SegmentMoveHandler.prototype.onClick = function (handler) {
        return function (e, ui) {
            e.stopPropagation();
        };
    };

    /**
     * @event dragStart
     * DragStart callback fired when the handler is dragged (it's executed only once).
     * It does the following:
     *
     * 1. Gather the connection by calling the handler's grandparent
     * 2. Save the state if the connection (for the undo-redo stack)
     * 3. Clear all the intersections of each segment of the connection
     *
     * @param {PMUI.draw.SegmentMoveHandler} handler
     */
    SegmentMoveHandler.prototype.onDragStart = function (handler) {
        return function (e, ui) {
            var parentSegment = handler.parent,
                segment,
                connection = parentSegment.getParent(),
                i;

            // TESTING: 
            // save values for the undo-redo stack
            connection.savePoints({
                saveToOldPoints: true
            });

            // clear all intersections that exists in
            // parentSegment.parent (connection)
            for (i = 0; i < parentSegment.parent.lineSegments.getSize(); i += 1) {
                segment = parentSegment.parent.lineSegments.get(i);
                segment.clearIntersections();
            }
            // clear all intersections that exists among other connections and
            // parentSegment (the ones that exists in the other connections)
            parentSegment.parent.clearAllIntersections();
            e.stopPropagation();
        };
    };

    /**
     * @event drag
     * Drag callback fired when the handler is being dragged.
     * It only moves the segment vertically or horizontally.
     *
     * @param {PMUI.draw.SegmentMoveHandler} handler
     */
    SegmentMoveHandler.prototype.onDrag = function (handler) {
        return function (e, ui) {
            var parentSegment = handler.parent;
            parentSegment.moveSegment(ui.position.left, ui.position.top);
        };
    };

    /**
     * @event dragEnd
     * DragEnd callback fired when the handler stops being dragged.
     * It does the following:
     *
     * 1. Gather the connection by calling the handler's grandparent
     * 2. Save the state if the connection (for the undo-redo stack)
     * 3. Create a command for the undo-redo stack
     *
     * @param {PMUI.draw.SegmentMoveHandler} handler
     */
    SegmentMoveHandler.prototype.onDragEnd = function (handler) {
        return function (e, ui) {
            var parentSegment = handler.parent,
                connection = parentSegment.getParent(),
                canvas = connection.canvas,
                command;

            canvas.draggingASegmentHandler = false;
            handler.onDrag(handler)(e, ui);

            // LOGIC:  connection.points is an array of points that is not updated
            // automatically when a connection is painted, it must be
            // explicitly called as connection.savePoints()
            connection.savePoints();
            command = new PMUI.command.CommandSegmentMove(connection, {
                oldPoints: connection.getOldPoints(),
                newPoints: connection.getPoints()
            });
            command.execute();
            canvas.commandStack.add(command);

        };
    };

    PMUI.extendNamespace('PMUI.draw.SegmentMoveHandler', SegmentMoveHandler);

    if (typeof exports !== 'undefined') {
        module.exports = SegmentMoveHandler;
    }

}());

(function () {
    /**
     * @class PMUI.draw.Port
     * Class Port represent a special point in a shape where each point is one end point of a connection
     * (a customShape has many ports and each port has a reference to the connection it belongs to).
     *
     * The relation of this class with customShape and connections are described below:
     *
     * - Each port is one end point of a connection (the connection has links to the start port and the end port,
     *      the port has a link to the connection)
     * - A custom shape might have *n* ports (the parent of the port is the custom shape)
     *      so the custom shape has the HTML of the port on it.
     *
     * Some examples of usage:
     *
     *      // let's assume the connection is an instance of the class Connection
     *      // let's assume the customShape is an instance of the class CustomShape
     *      var port = new PMUI.draw.Port({
     *          width: 8,
     *          height: 8,
     *          visible: true,
     *          parent: customShape
     *      })
     *
     *      // after a port is created, it need to be added to the customShape
     *      // let's add it at position [100, 100]
     *      customShape.addPort(port, 100, 100)
     *
     *      // finally when a connection is created it needs to have links to the ports
     *      // let's assume that another port is an instance of the class Port
     *      // i.e
     *      connection = new PMUI.draw.Connection({
     *          srcPort: port,
     *          destPort: anotherPort,
     *          segmentColor: new PMUI.util.Color(0, 200, 0),
     *          segmentStyle: "regular"
     *      });
     *
     * @extend PMUI.draw.Core
     *
     *
     * @param {Object} options Initialization options
     * @cfg {number} [width=4] The width of this port
     * @cfg {number} [height=4] The height of this port
     * @cfg {boolean} [visible=false] The visibility of this port
     * @cfg {PMUI.draw.CustomShape} [parent=null] The parent of this port
     *
     * @constructor Creates an instance of the class Port
     */
    var Port = function (options) {

        Port.superclass.call(this);

        /**
         * Connection to whom this port belongs to
         * @property {PMUI.draw.Connection}
         */
        this.connection = null;

        /**
         * Representation (Shape) of the port when it is connected (currently it's represented as an
         {@link PMUI.draw.Oval})
         * @property {PMUI.draw.Shape}
         */
        this.representation = null;

        /**
         * Parent of this port.
         * @property {PMUI.draw.CustomShape}
         */
        this.parent = null;

        /**
         * Old parent of this port.
         * @property {PMUI.draw.CustomShape}
         */
        this.oldParent = null;

        /**
         * Port direction respect to its parent (its parent is an instance of {@link PMUI.draw.CustomShape}).
         * @property {number}
         */
        this.direction = null;

        /**
         * The percentage relative to where the port is located regarding one of
         * the shape dimensions (useful to recalculate the ports position while resizing).
         * @property {number}
         */
        this.percentage = null;

        /**
         * Current zIndex of the port.
         * @property {number} [zOrder=1]
         */
        this.zOrder = 1;

        /**
         * Default zIndex of the ports.
         * @property {number} [defaultZOrder=1]
         */
        this.defaultZOrder = 1;

        /**
         * X coordinate sent to the database
         * @property {number} [realX=0]
         */
        this.realX = 0;
        /**
         * Y coordinate sent to the database
         * @property {number} [realY=0]
         */
        this.realY = 0;

        Port.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.draw.RegularShape', Port);

    /**
     * The distance moved when a connection is selected (when a connection is
     * selected the ports move towards the center of the shape so that it's
     * easier to drag the ports)
     * @property {number} [TOWARDS_CENTER=5]
     */
    Port.prototype.TOWARDS_CENTER = 5;

    /**
     * Type of each instance of this class
     * @property {String}
     */
    Port.prototype.type = "Port";

    /**
     * Instance initializer which uses options to extend the config options to initialize the instance.
     * @param {Object} options The object that contains the config
     * @private
     */
    Port.prototype.init = function (options) {
        /**
         * Default options for the object
         * @property {Object}
         */
        var defaults = {
            width: 4,
            height: 4,
            visible: false,
            parent: null
        };

        // extend recursively the defaultOptions with the given options
        $.extend(true, defaults, options);
        $.extend(true, defaults, {
            // oval is initialized with default values
            representation: new PMUI.draw.Oval({
                width: defaults.width,
                height: defaults.height,
                center: new PMUI.util.Point(0, 0),
                visible: true
            })
        });

        // call setters using the defaults object
        this.setVisible(defaults.visible)
            .setParent(defaults.parent)
            .setDimension(defaults.width, defaults.height)
            .setRepresentation(defaults.representation);
    };

    /**
     * Creates the HTML Representation of the Port
     * @returns {HTMLElement}
     */
    Port.prototype.createHTML = function () {
        Port.superclass.prototype.createHTML.call(this);
        this.style.addClasses(["port"]);
        this.setPosition(this.x, this.y);
        return this.html;
    };

    /**
     * Sets the x coordinate of this port
     * @param {number} newX
     * @chainable
     */
    Port.prototype.setX = function (newX) {
        if (this.canvas) {
            this.realX = newX / this.canvas.zoomFactor;
        } else {
            this.realX = newX;
        }
        this.x = this.zoomX = Math.round(newX);
        this.setAbsoluteX();
        if (this.html) {
            this.style.addProperties({left: this.zoomX});
        }
        return this;
    };

    /**
     * Sets the y coordinate of this port
     * @param {number} newY
     * @chainable
     */
    Port.prototype.setY = function (newY) {
        this.y = this.zoomY = Math.round(newY);
        this.setAbsoluteY();
        if (this.canvas) {
            this.realY = newY / this.canvas.zoomFactor;
        } else {
            this.realY = newY;
        }
        if (this.html) {
            this.style.addProperties({top: this.zoomY});
        }
        return this;
    };

    /**
     * Sets the width of this port
     * @param {number} newWidth
     * @chainable
     */
    Port.prototype.setWidth = function (newWidth) {
        this.width = newWidth;
        this.zoomWidth = this.width;
        if (this.html) {
            this.style.addProperties({width: this.zoomWidth});
        }
        return this;
    };

    /**
     * Sets the height of this port
     * @param {number} newHeight
     * @chainable
     */
    Port.prototype.setHeight = function (newHeight) {
        this.height = newHeight;
        this.zoomHeight = this.height;
        if (this.html) {
            this.style.addProperties({height: this.zoomHeight});
        }
        return this;
    };

    /**
     * Paint the port appending its `representation` HTML to `this` HTML.
     * @chainable
     */
    Port.prototype.paint = function () {

        this.html.appendChild(this.representation.getHTML());
        this.representation.paint();

        // sets the visibility of this port
        this.setVisible(this.visible);
        this.style.applyStyle();

        return this;
    };

    /**
     * Repaints the port re-applying its left and top position.
     * @param {PMUI.draw.Port} port
     * @chainable
     */
    Port.prototype.repaint = function (port) {

        port.style.addProperties({
            left: port.x,
            top: port.y
        });
        port.connection.connect();
        port.connection.setSegmentMoveHandlers();
        port.connection.checkAndCreateIntersectionsWithAll();
        return this;
    };

    /**
     * @event dragStart
     * DragStart callback fired when the port is dragged (it's executed only once).
     * It does the following:
     *
     * 1. Moves the port away from the center
     * 2. Moves the otherPort away from the center
     * 3. Disconnects the connection
     *
     * @param {PMUI.draw.Port} port
     * @param {PMUI.draw.Port} otherPort
     */
    Port.prototype.onDragStart = function (port, otherPort) {
        return function (e, ui) {

            // move the ports off the center, they'll be correctly repositioned
            // later (in "onDragEnd")
            otherPort.moveTowardsTheCenter(true);
            port.moveTowardsTheCenter(true);

            port.connection.disconnect();
            return true;
        };
    };

    /**
     * @event drag
     * Drag callback fired when the port is being dragged.
     * It makes a new segment from the other port to the current position of the mouse.
     *
     * @param {PMUI.draw.Port} port
     * @param {PMUI.util.Point} endPoint
     * @param {PMUI.draw.Port} otherPort
     * @param {PMUI.draw.Canvas} canvas
     */
    Port.prototype.onDrag = function (port, endPoint, otherPort, canvas) {
        return function (e, ui) {
            var startPoint;
            if (canvas.connectionSegment) {
                $(canvas.connectionSegment.getHTML()).remove();
            }

            endPoint.x = e.pageX - canvas.getX() + canvas.getLeftScroll() - canvas.getAbsoluteX();
            endPoint.y = e.pageY - canvas.getY() + canvas.getTopScroll() - canvas.getAbsoluteY();
            //make connection segment
            startPoint = otherPort.getPoint(false);
            startPoint.x = startPoint.x - canvas.getAbsoluteX();
            startPoint.y = startPoint.y - canvas.getAbsoluteY();
            canvas.connectionSegment = new PMUI.draw.Segment({
                startPoint: startPoint,
                endPoint: endPoint,
                parent: canvas
            });
            canvas.connectionSegment.pointsTo = port;
            canvas.connectionSegment.createHTML();
            canvas.connectionSegment.paint();
        };
    };

    /**
     * @event dragEnd
     * DragEnd callback fired when the port stops being dragged.
     * It does the following:
     *
     * 1. Repaints the port
     * 2. Moves otherPort towards the center of the shape
     * 3. Moves port towards the center of the shape
     * 4. Shows the handlers of the connection
     *
     * @param {PMUI.draw.Port} port
     * @param {PMUI.draw.Port} otherPort
     * @param {PMUI.draw.Canvas} canvas
     */
    Port.prototype.onDragEnd = function (port, otherPort, canvas) {
        return function (e, ui) {

            if (canvas.connectionSegment) {
                $(canvas.connectionSegment.getHTML()).remove();
            }
            port.repaint(port);

            // move the ports towards the center of its parent
            // (they were moved off the center in "onDragStart")
            otherPort.moveTowardsTheCenter();
            port.moveTowardsTheCenter();

            // show the segmentMoveHandlers
            port.connection.showMoveHandlers();
        };
    };

    /**
     * Determine the percentage relative to the shape where the port is located.
     * The range of `this.percentage` is from 0 to 1 (inclusive).
     * @return {boolean}
     */
    Port.prototype.determinePercentage = function () {
        //Shape and port dimension to consider, it can be either width or height
        var shapeDimension,
            portDimension;
        if (!this.parent) {
            return false;
        }
        if (this.direction === this.TOP || this.direction === this.BOTTOM) {
            shapeDimension = this.parent.getZoomWidth();
            portDimension = this.x + (5 * this.canvas.getZoomFactor());
        } else {
            shapeDimension = this.parent.getZoomHeight();
            portDimension = this.y + (5 * this.canvas.getZoomFactor());
        }

        this.percentage = Math.round((portDimension / shapeDimension) * 100.0);
        return true;
    };

    /**
     * Shows this port (moving it's HTML representation towards the center for easy dragging).
     * @chainable
     */
    Port.prototype.show = function () {
        this.visible = true;
        this.paint();
        this.html.style.zIndex = 3;
        // move the ports towards the center
        return this;
    };

    /**
     * Hides this port (moving it's HTML representation off the center of the shape).
     * @chainable
     */
    Port.prototype.hide = function () {
        this.visible = false;
        this.paint();

        return this;
    };

    /**
     * Detaches the HTML of the port from the DOM (saving it in `this.html`), it also removes the port
     * from its parent.
     * @chainable
     */
    Port.prototype.saveAndDestroy = function () {
        this.parent.removePort(this);  //remove from shape

        // save the html but detach it from the DOM
        this.html = $(this.html).detach()[0];
        return this;
    };

    /**
     * Attaches event listeners to this port, currently it has the draggable and mouse over events.
     * @param {PMUI.draw.Port} currPort
     * @return {PMUI.draw.Port}
     */
    Port.prototype.attachListeners = function (currPort) {
        var otherPort,
            portDragOptions;
        otherPort = currPort.connection.srcPort.getPoint(false)
            .equals(currPort.getPoint(false)) ? currPort.connection.destPort :
            currPort.connection.srcPort;

        portDragOptions = {
            //containment :  "parent"
            start: currPort.onDragStart(currPort, otherPort),
            drag: currPort.onDrag(currPort, currPort.getPoint(false),
                otherPort, currPort.parent.canvas),
            stop: currPort.onDragEnd(currPort, otherPort, currPort.parent.canvas)
        };
        $(currPort.html).draggable(portDragOptions);
        $(currPort.html).mouseover(
            function () {
                $(currPort.html).css('cursor', 'Move');
            }
        );
        return currPort;
    };

    /**
     * Moves a port towards or off the center (for easy dragging).
     * @param {boolean} reverse If it's set to true then it will move it off the center
     * @chainable
     */
    Port.prototype.moveTowardsTheCenter = function (reverse) {
        var towardsCenterDistance = Port.prototype.TOWARDS_CENTER,
            dx = [0, -towardsCenterDistance, 0, towardsCenterDistance],
            dy = [towardsCenterDistance, 0, -towardsCenterDistance, 0],
            multiplier = 1;

        if (reverse) {
            multiplier = -1;
        }
        this.setPosition(this.x + dx[this.direction] * multiplier,
            this.y + dy[this.direction] * multiplier);
        return this;
    };

    /**
     * Sets the Direction to the port.
     * @param {number} newDirection
     * @chainable
     */
    Port.prototype.setDirection = function (newDirection) {
        if (newDirection >= 0 && newDirection < 4) {
            this.direction = newDirection;
        } else {
            throw new Error("setDirection():  parameter '" + newDirection +
                "'is not valid");
        }
        return this;
    };

    /**
     * Get the direction to the port. (0 = TOP, 1 = RIGHT, 2 = BOTTOM, 3 = LEFT)
     * @returns {number}
     */
    Port.prototype.getDirection = function () {
        return this.direction;
    };

    /**
     * Sets the parent of the port.
     * @param {PMUI.draw.Shape} newParent
     * @param {boolean} triggerChange If set to true it'll fire {@link PMUI.draw.Canvas#event-changeelement}
     * @chainable
     */
    Port.prototype.setParent = function (newParent, triggerChange) {
        if (this.canvas && triggerChange) {
            this.canvas.updatedElement = {
                "id": this.id,
                "type": this.type,
                "fields": [
                    {
                        "field": "parent",
                        "oldVal": this.parent,
                        "newVal": newParent
                    }
                ]
            };
            $(this.canvas.html).trigger("changeelement");
        }
        this.parent = newParent;
        return this;
    };

    /**
     * Gets the parent of the port.
     * @return {PMUI.draw.Port}
     */
    Port.prototype.getParent = function () {
        return this.parent;
    };

    /**
     * Sets the old parent of this port
     * @param {PMUI.draw.CustomShape} parent
     * @chainable
     */
    Port.prototype.setOldParent = function (parent) {
        this.oldParent = parent;
        return this;
    };
    /**
     * Gets the old parent of this port.
     * @return {PMUI.draw.Port}
     */
    Port.prototype.getOldParent = function () {
        return this.oldParent;
    };

    /**
     * Sets the connection associated with this port.
     * @param {PMUI.draw.Connection} newConn
     * @chainable
     */
    Port.prototype.setConnection = function (newConn) {
        if (newConn && newConn.family === "Connection") {
            this.connection = newConn;
        } else {
            throw new Error("setConnection():  parameter is not valid");
        }
        return this;
    };

    /**
     * Gets the connection associated with this port
     * @returns {PMUI.draw.Connection}
     */
    Port.prototype.getConnection = function () {
        return this.connection;
    };

    /**
     * Returns the representation of the port (currently an instance of the class {@link PMUI.draw.Oval})
     * @returns {PMUI.draw.Oval}
     */
    Port.prototype.getRepresentation = function () {
        return this.representation;
    };

    /**
     * Sets the representation of this port (not supported yet)
     * @param {PMUI.draw.Shape} newRep
     * @chainable
     */
    Port.prototype.setRepresentation = function (newRep) {
        if (newRep instanceof PMUI.draw.RegularShape) {
            this.representation = newRep;
        } else {
            throw new Error("setRepresentation():  parameter must be an instance" +
                " of any regularShape");
        }
        return this;
    };

    /**
     * Gets the ports position (if `relativeToShape` is set to true it'll return the position
     * respect to the shape, otherwise it'll return its position respect to the canvas)
     * @param {boolean} relativeToShape
     * @returns {PMUI.util.Point}
     */
    Port.prototype.getPoint = function (relativeToShape) {
        var border = parseInt(this.parent.style.getProperty('border'), 10) || 0;
        if (relativeToShape) {
            return new PMUI.util.Point(this.getX() + Math.round(this.getWidth() / 2),
                this.getY() + Math.round(this.getHeight() / 2));
        }
        return new PMUI.util.Point(
            this.getAbsoluteX() + Math.round(this.getWidth() / 2),
            this.getAbsoluteY() + Math.round(this.getHeight() / 2)
        );

    };

    /**
     * Gets the percentage of this port relative to its parent.
     * @return {number}
     */
    Port.prototype.getPercentage = function () {
        return this.percentage;
    };

    /**
     * Serializes this port.
     * @return {Object}
     * @return {number} return.x
     * @return {number} return.y
     * @return {number} return.realX
     * @return {number} return.realY
     * @return {string} return.parent The ID of its parent.
     */
    Port.prototype.stringify = function () {
        var inheritedJSON = {},
            thisJSON = {
                x: this.getX(),
                y: this.getY(),
                realX: this.realX,
                realY: this.realY,
                parent: this.getParent().getID()
            };
        $.extend(true, inheritedJSON, thisJSON);
        return inheritedJSON;
    };

    PMUI.extendNamespace('PMUI.draw.Port', Port);

    if (typeof exports !== 'undefined') {
        module.exports = Port;
    }

}());

(function () {
    /**
     * @class PMUI.draw.Segment
     * A class that represents a segment, a segment is defined with two points
     (`startPoint` and `enPoint`).
     * In the PMDraw library a segment is used as a the key part of connections,
     it has also the following characteristics:
     *
     * - Since a segment is used as part of a connection, it has neighbors (`previousNeighbor` and `nextNeighbor`).
     * - A segment is parallel to an axis if it forms part of a connection.
     * - A segment has a move handler to move the segment.
     * - A segment has info of other connections it has intersections with
     *
     * Some examples of usage:
     *
     *      // let's assume that we want to connect two shapes, the shapes are connected
     *      // through the creation of a segment (the start point is the mouse position where
     *      // the user fired the mouse down event and the end point is the mouse position where the user
     *      // fired the mouse up event)
     *      // let's assume that canvas is an instance of the class Canvas and it's creating the segment
     *      var redSegment = new PMUI.draw.Segment({
     *          startPoint: new PMUI.util.Point(100, 100),        // a random point
     *          endPoint: new PMUI.util.Point(200, 200),          // a random point
     *          parent: canvas,
     *          color: new PMUI.util.Color(255, 0, 0)             // red !!
     *      });
     *
     * @extend PMUI.draw.Core
     *
     * @constructor Creates an instance of the class Segment
     * @param {Object} options Initialization options
     * @cfg {PMUI.util.Point} [startPoint=new Point(0, 0)] The start point of the segment
     * @cfg {PMUI.util.Point} [endPoint=new Point(0, 0)] The end point of the segment
     * @cfg {PMUI.draw.Canvas / PMUI.draw.Connection} [parent=null] The parent of the segment
     * @cfg {PMUI.util.Color} [color=new Color(0, 0, 0)] The color of this segment
     */
    var Segment = function (options) {
        Segment.superclass.call(this, options);

        /**
         * The parent of the segment.
         * @property {PMUI.draw.Canvas / PMUI.draw.Connection} [parent=null]
         */
        this.parent = null;
        /**
         * The start point of the segment.
         * @property {PMUI.util.Point} [startPoint=null]
         */
        this.startPoint = null;

        /**
         * The end point of the segment.
         * @property {PMUI.util.Point} [endPoint=null]
         */
        this.endPoint = null;

        /**
         * zOrder of the segment.
         * @property {number} [zOrder=Shape.prototype.MAX_ZINDEX]
         */
        this.zOrder = PMUI.draw.Shape.prototype.MAX_ZINDEX;

        /**
         * The segment to the left of this segment.
         * @property {PMUI.draw.Segment} [previousNeighbor=null]
         */
        this.previousNeighbor = null;

        /**
         * The segment to the right of this segment.
         * @property {PMUI.draw.Segment} [nextNeighbor=null]
         */
        this.nextNeighbor = null;

        /**
         * Orientation of the segment, the possible values are:
         *
         * - Vertical
         * - Horizontal
         *
         * @property {String} [orientation=""]
         */
        this.orientation = "";

        /**
         * The width of the segment.
         * @property {number} [width=1]
         */
        this.width = 1;

        /**
         * Graphics object
         * @property {PMUI.draw.Graphics} [graphics=null]
         */
        this.graphics = null;

        /**
         * This segment style, the possible values are:
         *
         * - "dotted"
         * - "segmented"
         * - "segmentdot"
         * @property {string} [segmentStyle=null]
         */
        this.segmentStyle = null;

        /**
         * This segment color.
         * @property {PMUI.util.Color} [segmentColor=null]
         */
        this.segmentColor = null;

        /**
         * The move handler is the segment move handler of this segment.
         * @property {PMUI.draw.SegmentMoveHandler} [moveHandler=null]
         */
        this.moveHandler = null;

        /**
         * Creates an ArrayList of the intersections with other connections.
         *
         *      // the structure is like:
         *      //intersections = [
         *      //    {
         *      //        center:  point of intersection,
         *      //        IdOtherConnection:  id of the other connection
         *      //    }
         *      //]
         * @property {PMUI.util.ArrayList} [intersections=new PMUI.ArrayList()]
         */
        this.intersections = new PMUI.util.ArrayList();

        /**
         * True if this segment has a move handler.
         * @property {boolean} [hasMoveHandler=false]
         */
        this.hasMoveHandler = false;

        // set defaults
        Segment.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.draw.Core', Segment);

    /**
     * The type of each instance of this class
     * @property {String}
     */
    Segment.prototype.type = "Segment";

    /**
     * Instance initializer which uses options to extend the config options to initialize the instance.
     * @param {Object} options The object that contains the config
     * @private
     */
    Segment.prototype.init = function (options) {
        /**
         * Default options for the constructor
         * @property {Object}
         */
        var defaults = {
            startPoint: new PMUI.util.Point(0, 0),
            endPoint: new PMUI.util.Point(0, 0),
            parent: null,
            color: new PMUI.util.Color(0, 0, 0),
            zOrder: 10
        };

        // extend recursively the defaultOptions with the given options
        $.extend(true, defaults, options);

        // init
        this.setStartPoint(defaults.startPoint)
            .setEndPoint(defaults.endPoint)
            .setColor(defaults.color)
            .setParent(defaults.parent)
            .setZOrder(defaults.zOrder);
    };

    /**
     * Creates the HTML Representation of the Segment.
     * @returns {HTMLElement}
     */
    Segment.prototype.createHTML = function () {
        this.html = document.createElement('div');
        this.html.id = this.id;
        this.html.style.position = "absolute";
        this.html.style.left = "0px";
        this.html.style.top = "0px";
        this.html.style.height = "0px";
        this.html.style.width = "0px";
        this.html.style.zIndex = this.zOrder;
        return this.html;
    };

    /**
     * Paints a segment by creating an instance of the class {@link PMUI.draw.Graphics} and
     * calling {@link PMUI.draw.Graphics#drawLine}, it also append it's HTML to its parent's HTML.
     * @chainable
     */
    Segment.prototype.paint = function () {
        if (this.getHTML() === null) {
            return this;
        }
        if (this.graphics === null) {
            this.graphics = new PMUI.draw.Graphics(this.html);
        }
        //dibujas linea llamar a drawLine de la clase graphics con los puntos
        this.graphics.drawLine(this.startPoint.x, this.startPoint.y,
            this.endPoint.x, this.endPoint.y, this.segmentStyle, this.segmentColor);
        this.parent.html.appendChild(this.html);
        return this;
    };

    /**
     * Removes its HTML from the DOM.
     * @chainable
     */
    Segment.prototype.destroy = function () {
        $(this.html).remove();
        return this;
    };

    /**
     * Paint this segment with the intersections it has stored (this method is called from
     * {@link PMUI.draw.Connection#checkAndCreateIntersectionsWithAll}), it also append it's HTML to its parent's HTML.
     * @chainable
     */
    Segment.prototype.paintWithIntersections = function () {

        // we have to paint the segment again so destroy the previous one
        this.destroy();

        var startPoint,
            endPoint,
            diff,
            i,
            reverse = false;

        if (this.getHTML() === null) {
            return this;
        }
        if (this.graphics === null) {
            this.graphics = new PMUI.draw.Graphics(this.html);
        }

        //console.log(this.hasMoveHandler);
        if (this.hasMoveHandler) {
            $(this.moveHandler.html).remove();
            this.addSegmentMoveHandler();
        }


        // default differentials to split the segment
        if (this.orientation === this.HORIZONTAL) {
            diff = new PMUI.util.Point(PMUI.draw.Shape.prototype.DEFAULT_RADIUS, 0);
            if (this.startPoint.x > this.endPoint.x) {
                reverse = true;
            }

            // for this to work we need to sort the intersections
            this.intersections.sort(function (i, j) {
                return i.center.x - j.center.x;
            });

        } else {
            diff = new PMUI.util.Point(0, PMUI.draw.Shape.prototype.DEFAULT_RADIUS);
            if (this.startPoint.y > this.endPoint.y) {
                reverse = true;
            }

            // for this to work we need to sort the intersections
            this.intersections.sort(function (i, j) {
                return i.center.y - j.center.y;
            });
        }
        this.graphics.graphics.clear();

        startPoint = this.startPoint.clone();
        for (i = 0; i < this.intersections.getSize(); i += 1) {
            // if the direction is reverse then we get the
            // inverse position for i in the array
            if (reverse) {
                endPoint = this.intersections
                    .get(this.intersections.getSize() - i - 1).center;
            } else {
                endPoint = this.intersections.get(i).center;
            }

            if (reverse) {
                endPoint = endPoint.add(diff);
            } else {
                endPoint = endPoint.subtract(diff);
            }
            this.graphics.drawLine(startPoint.x, startPoint.y,
                endPoint.x, endPoint.y, this.segmentStyle,
                this.segmentColor, 0, 0, true);
            if (reverse) {
                startPoint = endPoint.subtract(diff.multiply(2));
            } else {
                startPoint = endPoint.add(diff.multiply(2));
            }
        }

        // draw last segment
        endPoint = this.endPoint.clone();
        this.graphics.drawLine(startPoint.x, startPoint.y,
            endPoint.x, endPoint.y, this.segmentStyle, this.segmentColor,
            0, 0, true);
        this.parent.html.appendChild(this.html);
        return this;
    };

    /**
     * Adds a segmentMoveHandler to this segment, it also append the segmentMoveHandler instance HTML to this HTML
     * @chainable
     */
    Segment.prototype.addSegmentMoveHandler = function () {
        var midX = (this.startPoint.x + this.endPoint.x) / 2,
            midY = (this.startPoint.y + this.endPoint.y) / 2;
        this.moveHandler = new PMUI.draw.SegmentMoveHandler({
            parent: this,
            orientation: this.orientation,
            style: {
                cssProperties: {
                    border: "1px solid black"
                }
            }
        });
        midX -= this.moveHandler.width / 2;
        midY -= this.moveHandler.height / 2;
        this.moveHandler.setPosition(midX, midY);
        this.html.appendChild(this.moveHandler.getHTML());
        this.moveHandler.paint();
        this.moveHandler.attachListeners(this.moveHandler);
        return this;
    };

    /**
     * Returns the parent of the segment
     * @returns {PMUI.draw.Canvas / PMUI.draw.Connection}
     */
    Segment.prototype.getParent = function () {
        return this.parent;
    };

    /**
     * Returns the start point of the segment.
     * @returns {PMUI.util.Point}
     */
    Segment.prototype.getStartPoint = function () {
        return this.startPoint;
    };

    /**
     * Returns the end point of the segment.
     * @returns {PMUI.util.Point}
     */
    Segment.prototype.getEndPoint = function () {
        return this.endPoint;
    };

    /**
     * Sets the parent of the segment.
     * @param {Object} newParent
     * @chainable
     */
    Segment.prototype.setParent = function (newParent) {
        this.parent = newParent;
        return this;
    };

    /**
     * Sets the start point of the segment.
     * @param {PMUI.util.Point} newPoint
     * @chainable
     */
    Segment.prototype.setStartPoint = function (newPoint) {
        this.startPoint = newPoint;
        return this;
    };

    /**
     * Sets the end point of the segment.
     * @param {PMUI.util.Point} newPoint
     * @chainable
     */
    Segment.prototype.setEndPoint = function (newPoint) {
        this.endPoint = newPoint;
        return this;
    };

    /**
     * Sets the segmentStyle of this segment
     * @param {string} newStyle
     * @chainable
     *
     */
    Segment.prototype.setStyle = function (newStyle) {
        this.segmentStyle = newStyle;
        return this;
    };

    /**
     * Sets the color of this segment
     * @param {PMUI.util.Color} newColor
     * @chainable
     */
    Segment.prototype.setColor = function (newColor) {
        this.segmentColor = newColor;
        return this;
    };

    /**
     * Creates an intersection with `otherSegment` and saves it in `this.intersections`.
     * If it doesn't have an intersection point passed as a parameter it will determine the
     * intersection point and add it to `this.intersections` (`this.intersections` considers only unique points)
     * @param {PMUI.draw.Segment} otherSegment
     * @param {PMUI.util.Point} [ip] Intersection Point
     * @chainable
     */
    Segment.prototype.createIntersectionWith = function (otherSegment, ip) {
        var intersectionObject,
            intersectionPoint,
            i,
            goodToInsert = true;
        if (ip) {
            intersectionPoint = ip;
        } else {
            intersectionPoint = PMUI.draw.Geometry.segmentIntersectionPoint(this.startPoint,
                this.endPoint, otherSegment.startPoint, otherSegment.endPoint);
        }

        // let's consider the case when an intersection point is the same e.g. when a segment crosses two
        // other segments in the same point
        for (i = 0; i < this.intersections.getSize(); i += 1) {
            if (ip.equals(this.intersections.get(i).center)) {
                goodToInsert = false;
            }
        }

        if (goodToInsert) {
            intersectionObject = new PMUI.draw.Intersection(intersectionPoint,
                otherSegment.parent.getID(), this);
            this.html.appendChild(intersectionObject.getHTML());
            intersectionObject.paint();
            this.intersections.insert(intersectionObject);
        }

        //console.log(intersectionObject);
        //console.log(this.intersections);
        return this;
    };

    /**
     * Clear all the intersections in this segment.
     * @chainable
     */
    Segment.prototype.clearIntersections = function () {
        var i,
            intersection,
            size = this.intersections.getSize();
        while (size > 0) {
            intersection = this.intersections.get(size - 1);
            $(intersection.html).remove();
            this.intersections.popLast();
            size -= 1;
        }
        return this;
    };

    /**
     * Moves the segment either to x or y but not both
     (this method is called from {@link PMUI.draw.SegmentMoveHandler#event-drag}).
     * @param {number} x new x coordinate of the segment in the canvas
     * @param {number} y new y coordinate of the segment in the canvas
     */
    Segment.prototype.moveSegment = function (x, y) {
        var handler = this.moveHandler,
            prevNeighbor = this.previousNeighbor,
            nextNeighbor = this.nextNeighbor,
            midX,
            midY;

        if (handler.orientation === handler.VERTICAL) {
            this.startPoint.x = x
                + handler.width / 2;
            this.endPoint.x = x
                + handler.width / 2;
            prevNeighbor.endPoint.x =
                this.startPoint.x;
            nextNeighbor.startPoint.x =
                this.endPoint.x;
        } else {
            this.startPoint.y = y
                + handler.height / 2;
            this.endPoint.y = y
                + handler.height / 2;
            prevNeighbor.endPoint.y =
                this.startPoint.y;
            nextNeighbor.startPoint.y =
                this.endPoint.y;
        }

        // fix handler for the this segment
        if (this.moveHandler) {     // of course yes!
            midX = (this.startPoint.x + this.endPoint.x) / 2
                - this.moveHandler.width / 2;
            midY = (this.startPoint.y + this.endPoint.y) / 2
                - this.moveHandler.height / 2;
            this.moveHandler.setPosition(midX, midY);
        }

        // paint the previous segment
        prevNeighbor.paint();
        // fix handler for the prev segment if possible
        if (prevNeighbor.moveHandler) {
            midX = (prevNeighbor.startPoint.x + prevNeighbor.endPoint.x) / 2
                - prevNeighbor.moveHandler.width / 2;
            midY = (prevNeighbor.startPoint.y + prevNeighbor.endPoint.y) / 2
                - prevNeighbor.moveHandler.height / 2;
            prevNeighbor.moveHandler.setPosition(midX, midY);
        }

        // paint the next segment
        nextNeighbor.paint();
        // fix moveHandler for the next segment if possible
        if (nextNeighbor.moveHandler) {
            midX = (nextNeighbor.startPoint.x + nextNeighbor.endPoint.x) / 2
                - nextNeighbor.moveHandler.width / 2;
            midY = (nextNeighbor.startPoint.y + nextNeighbor.endPoint.y) / 2
                - nextNeighbor.moveHandler.height / 2;
            nextNeighbor.moveHandler.setPosition(midX, midY);
        }

        this.paint();
        return this;
    };

    Segment.prototype.hit = function (coronaWidth, point) {
        var dotprod, projlenSq;
        // Adjust vectors relative to X1,Y1
        // X2,Y2 becomes relative vector from X1,Y1 to end of segment
        X1 = this.startPoint.x;
        Y1 = this.startPoint.y;
        X2 = this.endPoint.x;
        Y2 = this.endPoint.y;
        px = point.x;
        py = point.y

        X2 -= X1;
        Y2 -= Y1;

        px -= X1;
        py -= Y1;

        dotprod = px * X2 + py * Y2;
        if (dotprod <= 0.0) {
            projlenSq = 0.0;
        } else {
            px = X2 - px;
            py = Y2 - py;
            dotprod = px * X2 + py * Y2;
            if (dotprod <= 0.0) {
                projlenSq = 0.0;
            } else {
                projlenSq = dotprod * dotprod / (X2 * X2 + Y2 * Y2);
            }
        }

        var lenSq = px * px + py * py - projlenSq;
        if (lenSq < 0) {
            lenSq = 0;
        }
        return Math.sqrt(lenSq) < coronaWidth;
    };

    PMUI.extendNamespace('PMUI.draw.Segment', Segment);

    if (typeof exports !== 'undefined') {
        module.exports = Segment;
    }

}());

(function () {
    /**
     * @class PMUI.draw.ReadOnlyLayer
     * Layer used to give the canvas a readonly state so that the user can just look
     * at the diagram and not be able to perform any modification,
     * the canvas is in charge of instantiating this object when its property
     * readOnly is set to true, there is no need to instance this object
     * independently
     * @extends PMUI.draw.Core
     *
     * @constructor
     * Creates an instance of this class
     * @param {Object} options configuration options inherited from Core
     */

    var ReadOnlyLayer = function (options) {
        ReadOnlyLayer.superclass.call(this, options);
        ReadOnlyLayer.prototype.initObject.call(this, options);
    };

    PMUI.inheritFrom('PMUI.draw.Core', ReadOnlyLayer);

    /**
     * Creates the HTML and attach the event listeners
     * @param options
     */
    ReadOnlyLayer.prototype.initObject = function (options) {
        this.createHTML();
        this.attachListeners();
    };
    /**
     * Attach the event listeners necessary for blocking interactions
     */
    ReadOnlyLayer.prototype.attachListeners = function () {
        var $layer = $(this.html);
        $layer.on('mousedown', this.onMouseDown(this))
            .on('mouseup', this.onMouseUp(this))
            .on('mousemove', this.onMouseMove(this))
            .on('click', this.onClick(this))
            .droppable({
                accept: "*",
                greedy: true,
                onDrop: function () {
                    return false;
                }
            });
    };
    /**
     * Stops the propagation of the mousedown event
     * @param {PMUI.draw.ReadOnlyLayer} layer
     * @return {Function}
     */
    ReadOnlyLayer.prototype.onMouseDown = function (layer) {
        return function (e, ui) {
            e.stopPropagation();
        };
    };
    /**
     * Stops the propagation of the mouseup event
     * @param {PMUI.draw.ReadOnlyLayer} layer
     * @return {Function}
     */
    ReadOnlyLayer.prototype.onMouseUp = function (layer) {
        return function (e, ui) {
            e.stopPropagation();
        };
    };
    /**
     * Stops the propagation of the click event
     * @param {PMUI.draw.ReadOnlyLayer}layer
     * @return {Function}
     */
    ReadOnlyLayer.prototype.onClick = function (layer) {
        return function (e, ui) {
            e.stopPropagation();
        };
    };
    /**
     * Stops the propagation of the mousemove event
     * @param {PMUI.draw.ReadOnlyLayer} layer
     * @return {Function}
     */
    ReadOnlyLayer.prototype.onMouseMove = function (layer) {
        return function (e, ui) {
            e.stopPropagation();
        };
    };

    PMUI.extendNamespace('PMUI.draw.ReadOnlyLayer', ReadOnlyLayer);

    if (typeof exports !== 'undefined') {
        module.exports = ReadOnlyLayer;
    }

}());


(function () {
    var DataItem = function (key, value) {
        this.id = PMUI.generateUniqueId();
        this.val = value;
        this.key = key;
    };

    DataItem.prototype.type = 'DataItem';

    DataItem.prototype.family = 'DataItem';

    DataItem.prototype.getKey = function () {
        return this.key;
    };

    DataItem.prototype.getValue = function () {
        return this.val;
    };

    DataItem.prototype.setKey = function (key) {
        this.key = key;
        return this;
    };

    DataItem.prototype.setValue = function (value) {
        this.val = value;
        return this;
    };

    DataItem.prototype.set = function (key, value) {
        this.key = key;
        this.val = value;
        return this;
    };

    DataItem.prototype.get = function () {
        var aux = {};
        aux[this.key] = this.val;
        return aux;
    };

    DataItem.prototype.getRecord = function () {
        return {
            key: this.key,
            value: this.val
        };
    };

    DataItem.prototype.clear = function () {
        this.val = null;
        this.key = null;

        return this;
    };

    PMUI.extendNamespace('PMUI.data.DataItem', DataItem);

    if (typeof exports !== 'undefined') {
        module.exports = DataItem;
    }

}());
(function () {
    var DataField = function (options) {
        options = options || {};
        DataField.superclass.call(this, options.key, options.value);
        this.typeField = null;
        this.custom = null;
        this.customKeys = {};
        DataField.prototype.init.call(this, options);
    };

    PMUI.inheritFrom('PMUI.data.DataItem', DataField);

    DataField.prototype.type = "DataField";

    DataField.prototype.dataTypes = {
        "string": "native",
        "number": "native",
        "date": "native",
        "boolean": "native",
        "object": "native"
    };

    DataField.prototype.basicKeys = {
        "key": "native",
        "value": "native",
        "type": "class",
        "val": "private",
        "custom": "native",
        "customKeys": "native",
        "typeField": "native",
        "family": "class"
    };

    DataField.prototype.defaultType = "string";

    DataField.prototype.init = function (options) {
        var that = this;
        if (options) {
            this.setKey(options.key || null);
            this.setValue(options.value || null);
            this.setType(options.type || null);
        }
        jQuery.each(options, function (k, v) {
            if (!that.basicKeys[k]) {
                if (typeof v !== 'object' && typeof v !== 'function') {
                    that[k] = v;
                    that.customKeys[k] = 'custom';
                }
            }
        });
    };

    DataField.prototype.setType = function (type) {
        if (type) {
            if (this.dataTypes[type]) {
                this.typeField = type;
                this.custom = false;
            } else {
                this.typeField = type;
                this.custom = true;
            }
        } else {
            this.custom = false;
            this.typeField = this.defaultType;
        }
        return this;
    };

    DataField.prototype.addAttribute = function (key, value) {
        this.customKeys[key] = value;
        return this;
    };

    DataField.prototype.getAttribute = function (key) {
        if (key === 'val' || key === 'key') {
            return this[key];
        }
        return this.customKeys[key];
    };

    DataField.prototype.existsKey = function (key) {
        if (key === "val" || key === "key") {
            return true;
        }
        return this.customKeys.hasOwnProperty(key);
    };

    DataField.prototype.getRecord = function (format) {
        var castType = format || this.typeField,
            out = {
                key: this.key,
                type: this.typeField,
                value: PMUI.castValue(this.val, castType)
            };
        jQuery.each(this.customKeys, function (key, value) {
            out[key] = value;
        });
        return out;
    };

    DataField.prototype.clear = function () {
        var key;
        DataField.superclass.prototype.clear.call(this);
        for (key in this.customKeys) {
            if (this.customKeys.hasOwnProperty(key)) {
                delete this.customKeys[key];
            }
        }
        return this;
    };

    PMUI.extendNamespace('PMUI.data.DataField', DataField);

    if (typeof exports !== 'undefined') {
        module.exports = DataField;
    }

}());
(function () {
    var DataSet = function (options) {
        this.items = null;
        this.factory = null;
        DataSet.prototype.init.call(this, options);
    };

    DataSet.prototype.type = "DataSet";

    DataSet.prototype.family = "DataSet";

    DataSet.prototype.init = function (options) {
        var defaults = {
            factory: {
                products: {
                    "datafield": PMUI.data.DataField,
                    "dataitem": PMUI.data.DataItem
                },
                defaultProduct: "dataitem"
            },
            items: []
        };
        jQuery.extend(true, defaults, options);
        this.items = new PMUI.util.ArrayList();
        this.factory = new PMUI.util.Factory(defaults.factory);
        this.setItems(defaults.items);
    };

    DataSet.prototype.setItems = function (items) {
        var that = this;
        jQuery.each(items, function (k, item) {
            that.items.insert(that.factory.make(item));
        });
        return this;
    };

    DataSet.prototype.addItem = function (item) {
        if (item) {
            this.items.insert(this.factory.make(item));
        }
        return this;
    };

    DataSet.prototype.getData = function () {
        var items,
            that = this,
            out = [];
        items = this.items.asArray();
        jQuery.each(items, function (k, item) {
            out.push(item.getRecord());
        });
        return out;
    };

    DataSet.prototype.getJSON = function () {
        return JSON.stringify(this.getData());
    };

    DataSet.prototype.getXML = function () {
        return PMUI.json2xml(this.getJSON());
    };

    DataSet.prototype.removeItem = function (item) {
        this.items.remove(item);
    };

    DataSet.prototype.getValue = function (key) {
        var item = this.items.find('key', key);
        return item.getValue();
    };

    DataSet.prototype.setValue = function (key, value) {
        var item = this.items.find('key', key);
        item.setValue(value);
        return this;
    };

    DataSet.prototype.keys = function () {
        var items,
            out = [];
        items = this.items.asArray();
        jQuery.each(items, function (k, item) {
            out.push(item.getKey());
        });
        return out;
    };

    DataSet.prototype.values = function () {
        var items,
            out = [];
        items = this.items.asArray();
        jQuery.each(items, function (k, item) {
            out.push(item.getValue());
        });
        return out;
    };

    PMUI.extendNamespace('PMUI.data.DataSet', DataSet);

    if (typeof exports !== 'undefined') {
        module.exports = DataSet;
    }
}());
(function () {
    /**
     * @class PMUI.control.OptionsSelectorItemControl
     * @extends PMUI.control.HTMLControl
     *
     * The class is the basic element for {@link PMUI.control.OptionsSelectorControl OptionsSelectorControl} class.
     *
     *  For instance the class, the sentences below show how do it.
     *
     *      @example
     *      new PMUI.control.OptionsSelectorItemControl({
     *          text: ....
     *      });
     *
     * @cfg {Boolean} selected Sets an item as selected inside of the {@link PMUI.control.OptionsSelectorControl component}
     * @cfg {String} text Defines the label for the component, the text that appear inside of the component
     *
     */
    var OptionsSelectorItemControl = function (settings) {
        OptionsSelectorItemControl.superclass.call(this, settings);
        /**
         * @property {Boolean} [selected=false]
         * Sets an item as selected inside of the {@link PMUI.control.OptionsSelectorControl component}
         */
        this.selected = null;
        /**
         * @property {String} [cls='pmui-switch-buttonitem']
         * Sets the class name for the component. It is related to the styles for the element
         */
        this.cls = null;
        /**
         * @property {String} [iconCls]
         * Sets the class name for the icon of the component.
         */
        this.iconCls = null;
        /**
         * @property {String} [clsSelected= 'pmui-switch-buttonitem-selected']
         * Sets the class name when an item is selected
         */
        this.clsSelected = null;
        /**
         * @property {Object} [dom=null]
         * Represents the DOM of the component
         */
        this.dom = null;
        /**
         * @property {Object} [disabled=false]
         * Defines whether the item is disabled or enabled
         */
        this.disabled = null;
        /**
         * @property {String} [text=null]
         * Defines the label for the component, the text that appear inside of the component
         */
        this.text = null;

        OptionsSelectorItemControl.prototype.init.call(this, settings);
    };
    PMUI.inheritFrom("PMUI.control.HTMLControl", OptionsSelectorItemControl);
    /**
     *
     *
     */
    OptionsSelectorItemControl.prototype.type = "button";
    /**
     *
     *
     */
    OptionsSelectorItemControl.prototype.family = "Item";

    OptionsSelectorItemControl.prototype.init = function (settings) {
        var defaults = {
            selected: false,
            disabled: false,
            cls: 'pmui-switch-buttonitem',
            iconCls: 'pmui-switch-buttonitem-icon',
            clsSelected: 'pmui-switch-buttonitem-selected',
            items: [],
            text: '',
            value: ''
        };
        jQuery.extend(true, defaults, settings);
        this.setSelected(defaults.selected)
            .setCls(defaults.cls)
            .setClsSelected(defaults.clsSelected)
            .setIconClass(defaults.iconCls)
            .setText(defaults.text)
            .setDisable(defaults.disabled)
            .setValue(defaults.value);
    };
    /**
     *
     *
     */
    OptionsSelectorItemControl.prototype.setSelected = function (status) {
        var otherElements,
            i,
            that = this;
        this.selected = (typeof status === 'boolean') ? status : false;
        return this;
    };
    /**
     *
     *
     */
    OptionsSelectorItemControl.prototype.setCls = function (className) {
        this.cls = (typeof className === 'string') ? className : '';
        return this;
    };
    /**
     *
     *
     */
    OptionsSelectorItemControl.prototype.setClsSelected = function (className) {
        this.clsSelected = (typeof className === 'string') ? className : '';
        return this;
    };
    /**
     *
     *
     */
    OptionsSelectorItemControl.prototype.setIconClass = function (className) {
        this.iconCls = (typeof className === 'string') ? className : '';
        return this;
    };
    /**
     *
     *
     */
    OptionsSelectorItemControl.prototype.setText = function (text) {
        this.text = (typeof text === 'string') ? text : '';
        return this;
    };
    OptionsSelectorItemControl.prototype.setValue = function (value) {
        value = value.toString();
        if (typeof value === 'string') {
            if (value.length === 0) {
                this.value = this.text;
            } else {
                this.value = value;
            }
        }
        return this;
    };
    /**
     *
     *
     */
    OptionsSelectorItemControl.prototype.activeElement = function (that) {
        var i;

        if (typeof that === "undefined") {
            that = this;
        }
        if (!that.disabled) {
            if (!that.parent.itemsSelected.contains(that)) {
                that.parent.itemsSelected.insert(that);
            }
            that.setSelected(true);
            if (this.dom) {
                that.dom.container.className = that.clsSelected;
                for (i = 0; i < that.style.getClasses().length; i += 1) {
                    that.dom.container.className += " " + that.style.getClasses()[i];
                }
            }
        }

        return this;
    };
    /**
     *
     *
     */
    OptionsSelectorItemControl.prototype.desactiveElement = function (that) {
        var i;
        if (typeof that === "undefined") {
            that = this;
        }
        if (!that.disabled) {
            that.parent.itemsSelected.remove(that);
            that.setSelected(false);
            if (this.dom) {
                that.dom.container.className = that.cls;
                for (i = 0; i < that.style.getClasses().length; i += 1) {
                    that.dom.container.className += " " + that.style.getClasses()[i];
                }
            }
        }

        return this;
    };

    /**
     * Disables/enables the control
     * @param {Boolean} setDisable If the value is evaluated as true then the control is disabled,
     * otherwise the control is enabled.
     * @chainable
     */
    OptionsSelectorItemControl.prototype.setDisable = function (disable) {
        if (typeof disable === 'boolean') {
            this.disabled = disable;
            if (this.html) {
                if (disable) {
                    this.disable();
                } else {
                    this.enable();
                }
            }
        }

        return this;
    };
    /**
     * [disable description]
     * @return {[type]} [description]
     */
    OptionsSelectorItemControl.prototype.disable = function () {
        if (arguments[0] === undefined) {
            this.disabled = true;
            if (this.html) {
                jQuery(this.html).unbind("click");
                this.html.setAttribute("class", "pmui-switch-buttonitem-disabled");
            }
        } else {
            if (arguments[0]) {
                this.disable();
            } else {
                this.enable();
            }
        }
        return this;
    };
    /**
     * [enable description]
     * @return {[type]} [description]
     */
    OptionsSelectorItemControl.prototype.enable = function () {
        if (arguments[0] === undefined) {
            this.disabled = false;
            if (this.html) {
                this.html.setAttribute("class", this.cls);
                this.defineEvents();
            }
        } else {
            if (arguments[0]) {
                this.disable();
            } else {
                this.enable();
            }
        }
        return this;
    };
    /**
     *
     *
     */
    OptionsSelectorItemControl.prototype.selectItem = function () {
        var that = this,
            otherElements;

        if (this.selected) {
            that.desactiveElement(that);
        } else {
            that.activeElement(that);
            if (this.parent.multipleSelection === false) {
                otherElements = this.parent.items.asArray();
                for (i = 0; i < otherElements.length; i += 1) {
                    if (that.id !== otherElements[i].id) {
                        that.desactiveElement(otherElements[i]);
                    }
                }
            }
        }
        return this;
    };
    /**
     *
     *
     */
    OptionsSelectorItemControl.prototype.createHTML = function () {
        var li,
            display,
            button,
            icon,
            text,
            i;

        OptionsSelectorItemControl.superclass.prototype.setElementTag.call(this, "a");
        OptionsSelectorItemControl.superclass.prototype.createHTML.call(this);

        //button = PMUI.createHTMLElement("a");
        //button.id = PMUI.generateUniqueId()
        if (this.disabled) {
            this.html.className = "pmui-switch-buttonitem-disabled";
        } else {
            this.html.className = this.cls;
            for (i = 0; i < this.style.getClasses().length; i += 1) {
                this.html.className += " " + this.style.getClasses()[i];
            }
        }

        this.html.href = '#';
        if (this.parent.orientation === 'vertical') {
            this.html.setAttribute("style", "display: block;");
        }
        icon = PMUI.createHTMLElement("span");
        icon.className = this.iconCls;
        text = PMUI.createHTMLElement("span");
        text.innerHTML = this.text;
        this.html.appendChild(icon);
        this.html.appendChild(text);

        this.dom = {
            container: this.html,
            items: [
                {
                    icon: icon
                },
                {
                    text: text
                }
            ]
        };
        if (this.selected) {
            this.activeElement(this);
        }
        this.html.id = this.id;
        return this.html;
    };
    /**
     * Defines events related to the object.
     */
    OptionsSelectorItemControl.prototype.defineEvents = function () {
        var j,
            fnSelect,
            children,
            that = this;
        this.removeEvents().eventsDefined = true;
        if (!this.disabled) {
            that.addEvent('click').listen(this.html, function (e) {
                e.preventDefault();
                that.selectItem();
                fnSelect = that.parent.listeners.select;
                fnSelect(that, e);
                e.stopPropagation();
            });
            if (e.which === PMUI.keyCodeF5) {
                this.blur();
                e.preventDefault();
                window.location.reload(true);
            }
        }
    };
    PMUI.extendNamespace("PMUI.control.OptionsSelectorItemControl", OptionsSelectorItemControl);
    if (typeof exports !== "undefined") {
        module.exports = OptionsSelectorItemControl;
    }
}());

(function () {
    /**
     * @class PMUI.control.OptionsSelectorControl
     * @extends PMUI.control.HTMLControl
     * Class to handle the Select HTML form element.
     *
     * Example usage:
     *
     *      @example
     *               OptionsSelectorControl = new PMUI.control.OptionsSelectorControl({
     *                   items: [
     *                       {
     *                           text: "First option"
     *                       },
     *                       {
     *                           text: "Second option",
     *                           selected: true
     *                       },
     *                       {
     *                           text: "Third option"
     *                       }
     *                   ],
     *                   listeners: {
     *                       select: function (item, event) {
     *                           console.log("selected", item);
     *                       }
     *                  }
     *               });
     *               document.body.appendChild(OptionsSelectorControl.getHTML());
     *               OptionsSelectorControl.defineEvents();
     *
     * @cfg {Array} items Defines the data items as an array objects.
     *
     *              items: [
     *                  {
     *                      ...
     *                  },
     *                  {
     *                      text: "Item1",
     *                      selected: true
     *                  },
     *                  {
     *                      ...
     *                  }
     *              ]
     *
     * @cfg {Boolean} multipleSelection Defines wether the component will support the behavior of the check group.
     *
     *
     */
    var OptionsSelectorControl = function (settings) {
        OptionsSelectorControl.superclass.call(this, settings);
        /**
         * @property {Boolean} [multipleSelection=false] If the property is enabled, the items can be
         * selected like checkGroup otherwise is like to radioGroup
         */
        this.multipleSelection = null;
        /**
         * @property {String} [orientation=null]
         *
         */
        this.orientation = null;
        /**
         * @property {Object} [container=null]
         * @private
         */
        this.container = null;
        /**
         * @property {Object} [listeners] A config object containing one or more handlers to be added
         * to this object during initialization. By default the 'select' handler is enabled
         * @readOnly
         */
        this.listeners = {
            /**
             * @event select
             * Fires when the component has been selected. Returning the current object and the event.
             * @return {Object} {@link PMUI.control.OptionsSelectorItemControl OptionsSelectorItemControl}
             * @return {Object} event
             */
            select: function () {
            }
        };
        /**
         * @property {@link PMUI.util.ArrayList ArrayList}
         *
         */
        this.itemsSelected = null;

        this.items = new PMUI.util.ArrayList();
        OptionsSelectorControl.prototype.init.call(this, settings);
    };
    PMUI.inheritFrom('PMUI.control.HTMLControl', OptionsSelectorControl);
    OptionsSelectorControl.prototype.type = "control";
    OptionsSelectorControl.prototype.family = "control";

    OptionsSelectorControl.prototype.init = function (settings) {
        var defaults = {
            multipleSelection: false,
            orientation: 'horizontal',
            items: [],
            iconClass: '',
            listeners: {
                select: function () {
                }
            }
        };
        jQuery.extend(true, defaults, settings);
        this.setMultipleSelection(defaults.multipleSelection)
            .setOrientation(defaults.orientation)
            .setChildren(defaults.items)
            .setIconClass(defaults.iconClass)
            .setListeners(defaults.listeners);

        this.itemsSelected = new PMUI.util.ArrayList();

    };
    /**
     * Sets the value whether the {@link PMUI.control.OptionsSelectorItemControl items} can be selected
     * at the same time
     * @param {Boolean} status
     */
    OptionsSelectorControl.prototype.setMultipleSelection = function (status) {
        this.multipleSelection = (typeof status === 'boolean') ? status : false;
        return this;
    };
    /**
     * Sets the orientation for every {@link PMUI.control.OptionsSelectorItemControl element}
     * There are two possibilities:
     * - horizontal, the elements will appear horizontally
     * - vertical, the elements will appear vertically
     * @param {String} orientation
     */
    OptionsSelectorControl.prototype.setOrientation = function (orientation) {
        var i,
            items,
            options = {
                horizontal: true,
                vertical: true
            },
            layout;
        this.orientation = (options[orientation]) ? orientation : 'horizontal';
        if (this.html) {
            items = this.items.asArray();
            for (i = 0; i < items.length; i += 1) {
                if (this.orientation === "vertical") {
                    items[i].html.setAttribute("style", "display:block;");
                    this.html.setAttribute("style", "display:inline-table;");
                } else {
                    items[i].html.removeAttribute("style");
                    this.html.removeAttribute("style");
                }
            }
        }
        return this;
    };
    /**
     * Sets the {@link PMUI.control.OptionsSelectorItemControl elements} child inside of
     * {@link PMUI.control.OptionsSelectorControl container}
     * @param {Array} items Represents to children
     */
    OptionsSelectorControl.prototype.setChildren = function (items) {
        var i;
        if (items instanceof Array) {
            this.items.clear();
            for (i = 0; i < items.length; i += 1) {
                this.addItem(items[i]);
            }
        }
        return this;
    };
    /**
     * SetValue method
     * @param {String} values Defines the current values of the items
     */
    OptionsSelectorControl.prototype.setValue = function (values) {
        var options,
            items,
            i,
            separator;

        if (values) {
            if (values.length > 0) {
                //options = JSON.parse(values);
                separator = this.field.separator;
                options = values.split(separator);
                items = this.items.asArray();
                for (i = 0; i < items.length; i += 1) {
                    if (jQuery.inArray(items[i].getValue(), options) !== -1) {
                        items[i].selected = false;
                        items[i].selectItem();
                    }
                }

                if (this.multipleSelection === false && options.length > 1) {
                    throw  new Error("Is not possible sets more than one when the multipleSelection property is false.")
                }
            }
        }

        return this;
    };
    /**
     * Sets the class name for the icon that appears in the header
     * @param {String} class name
     */
    OptionsSelectorControl.prototype.setIconClass = function (className) {
        this.iconClass = (typeof className === 'string') ? className : '';
        return this;
    };
    /**
     * Gets items selected
     * @return {Object} this
     */
    OptionsSelectorControl.prototype.getItemsSelected = function () {
        return this.itemsSelected.asArray()
    };
    /**
     * Sets listeners to object
     * @param {Object} listeners
     */
    OptionsSelectorControl.prototype.setListeners = function (listeners) {
        if (typeof listeners === 'object') {
            this.listeners = listeners;
        }
        return this;
    };
    /**
     * Adds new items to the component
     * @param {Object|{@link PMUI.control.OptionsSelectorItemControl OptionsSelectorItemControl}} item Represents the item
     * for the parent container.
     * @return {PMUI.control.OptionsSelectorControl} this
     */
    OptionsSelectorControl.prototype.addItem = function (item) {
        var factory,
            itemToBeAdded;
        factory = new PMUI.behavior.BehaviorFactory({
            products: {
                "optionsSelectorItem": PMUI.control.OptionsSelectorItemControl
            },
            defaultProduct: "optionsSelectorItem"
        });
        if (typeof item === "object") {
            itemToBeAdded = factory.make(item);
        }

        itemToBeAdded.parent = this;
        this.items.insert(itemToBeAdded);
        if (this.html) {
            this.html.appendChild(itemToBeAdded.getHTML());
            itemToBeAdded.defineEvents();
        }
        return this;
    };
    /**
     * Removes and item
     * @param  {[type]} item [description]
     * @return {[type]}      [description]
     */
    OptionsSelectorControl.prototype.removeOption = function (position) {
        var items = this.items.asArray(),
            item;
        if (items[position]) {
            item = items[position];
            this.items.remove(item);
            this.html.removeChild(item.html);
        }
        return item;
    };
    /**
     * [disableOption description]
     * @param  {[type]} position [description]
     * @param  {[type]} disabled [description]
     * @return {[type]}          [description]
     */
    OptionsSelectorControl.prototype.disableOption = function (position, disable) {
        var items = this.items.asArray(),
            item;

        if (items[position]) {
            item = items[position];
            item.setDisable(disable)
        }

        return item;
    };
    OptionsSelectorControl.prototype.getOptions = function () {
        return this.items.asArray();
    };
    OptionsSelectorControl.prototype.clearOptions = function () {
        var items = this.items.asArray(),
            i;
        for (i = 0; i < items.length; i += 1) {
            this.html.removeChild(items[i].html);
        }

        this.items.clear();
        return this;
    };

    /**
     * Creates the HTML elements
     * @return {HTMLElement} Returns the HTMLElement of the current component
     */
    OptionsSelectorControl.prototype.createHTML = function () {
        var container,
            items,
            i;
        container = PMUI.createHTMLElement("span");
        container.className = "pmui-field-control-container";
        if (this.orientation === "vertical") {
            container.setAttribute("style", "display:inline-table;");
        }

        items = this.items.asArray();
        if (typeof items.length === 'number') {
            for (i = 0; i < items.length; i += 1) {
                container.appendChild(items[i].getHTML());
            }
        }
        container.id = this.id;
        this.html = container;
        return this.html;
    };
    /**
     * Defines and active all the events related to the object
     */
    OptionsSelectorControl.prototype.defineEvents = function () {
        var that = this,
            items,
            i;

        this.eventsDefined = true;
        if (that.items.getSize() > 0) {
            items = that.items.asArray();
            for (i = 0; i < items.length; i += 1) {
                items[i].defineEvents();
            }
        }
        return this;
    };


    PMUI.extendNamespace("PMUI.control.OptionsSelectorControl", OptionsSelectorControl);
    if (typeof exports !== "undefined") {
        module.exports = OptionsSelectorControl;
    }

}());
(function () {
    /**
     * @class PMUI.field.OptionsSelectorField
     * @extends PMUI.form.Field
     *
     * How use the class.
     * The component represents basically two types of known controls, those are the radio group and check group fields.
     * By default the component has the functionality to the radio group field with a better look and feel.
     *
     * The example below shows the parameters (config options) for build an field of options selector (radio group).
     *
     *     @example
     *               OptionsSelectorControl = new PMUI.field.OptionsSelectorField({
     *                   label: "radiogroup",
     *                   items: [
     *                       {
     *                           text: "First option"
     *                       },
     *                       {
     *                           text: "Second option",
     *                           selected: true
     *                       },
     *                       {
     *                           text: "Third option"
     *                       }
     *                   ],
     *                   listeners: {
     *                       select: function (item, event) {
     *                           console.log("selected", item);
     *                       }
     *                  }
     *               });
     *               document.body.appendChild(OptionsSelectorControl.getHTML());
     *               OptionsSelectorControl.defineEvents();
     *
     *
     * The otherwise is posible create a check group field applying the following sentence on the JSON config:
     *
     *          {   ...
     *              multipleSelection: true
     *              ...
     *          }
     *
     * With the above parameter the component will have the behavior of the check group field.
     *
     *     @example
     *               checkboxGroup = new PMUI.field.OptionsSelectorField({
     *                   label: "radiogroup",
     *                   multipleSelection: true,
     *                   items: [
     *                       {
     *                           text: "First option"
     *                       },
     *                       {
     *                           text: "Second option",
     *                           selected: true
     *                       },
     *                       {
     *                           text: "Third option"
     *                       }
     *                   ],
     *                   listeners: {
     *                       select: function (item, event) {
     *                           console.log("selected", item);
     *                       }
     *                  }
     *               });
     *               document.body.appendChild(checkboxGroup.getHTML());
     *               checkboxGroup.defineEvents();
     *
     *
     * @cfg {Array} items Defines the data items as an array objects.
     *
     *              items: [
     *                  {   
     *                      ... 
     *                  },
     *                  {
     *                      text: "Item1",
     *                      selected: true
     *                  },
     *                  {   
     *                      ... 
     *                  }
     *              ]
     *
     * @cfg {Boolean} [multipleSelection=false] Defines wether the component will support the behavior of the check group.
     * @cfg {String} [orientation="horizontal"] Defines the orientation of the component, it means if the orientation is vertical or
     * horizontal
     *
     *
     */
    var OptionsSelectorField = function (settings) {
        OptionsSelectorField.superclass.call(this, settings);
        /**
         * @property {Boolean} [disabled=false] If the field is disabled or not.
         * @readonly
         */

        this.listWidth = null;
        /**
         * @property {String} controlPositioning The direction for the options to be added in the field.
         * @readonly
         */
        this.controlPositioning = null;
        /**
         * @property {String} [separator=","]
         * Defines the separator between every item selected on the field
         */
        this.separator = null;
        OptionsSelectorField.prototype.init.call(this, settings);

    };

    PMUI.inheritFrom("PMUI.form.Field", OptionsSelectorField);

    OptionsSelectorField.prototype.type = "OptionsSelectorField";

    OptionsSelectorField.prototype.init = function (settings) {
        var defaults = {
            options: [],
            listWidth: 'auto',
            separator: ",",
            controlPositioning: "vertical"
        };

        jQuery.extend(true, defaults, settings);

        this.setConfigControl(defaults);
        this.setSeparator(defaults.separator);
        this.setListWidth(defaults.listWidth);
        this.setControlPositioning(defaults.controlPositioning);
    };

    /**
     * Set if the HTML element is visible or not.
     * @param {Boolean} visible
     * @chainable
     */
    OptionsSelectorField.prototype.setVisible = function (visible) {
        var j,
            k;

        visible = !!visible;
        this.visible = visible;

        if (this.controls) {
            for (j = 0; j < this.controls.length; j += 1) {
                this.controls[j].visible = visible;
            }
        }

        if (this.html) {
            this.style.addProperties({"display": this.visible ? this.display : "none"});
            if (this.controls) {
                for (k = 0; k < this.controls.length; k += 1) {
                    this.controls[k].style.addProperties({"display": this.visible ? this.display : "none"});
                }
            }

        }

        return this;
    };
    /**
     * Sets the separator for the item when use the {@link PMUI.field.OptionsSelectorField#setValue setvalue} method
     * or the {@link PMUI.field.OptionsSelectorField#getValue getvalue} method
     * @param {String} type
     * It is a string that represent the separator. For example: '#', '|', ',', ...
     *
     */
    OptionsSelectorField.prototype.setSeparator = function (type) {
        if (typeof type === "string") {
            this.separator = type;
        }
        return this;
    };

    /**
     * Sets the options/option groups for the control.
     * @param {Array} options An array with the same structure that the
     * {@link PMUI.field.OptionsSelectorField#cfg-options "options"} property in the
     * Config settings section.
     */
    OptionsSelectorField.prototype.setOptions = function (options) {
        var i = 0,
            c,
            j,
            that = this,
            controls,
            k;

        if (jQuery.isArray(options)) {
            for (j = 0; j < that.controls.length; j += 1) {
                that.dom.controlContainer.removeChild(that.controls[j].html);
            }
            ;
            that.setConfigControl({
                items: options
            });
            controls = this.controls.slice();
            for (k = 0; k < controls.length; k += 1) {
                if (controls[k] !== null) {
                    if (that.dom.controlContainer) {
                        that.dom.controlContainer.appendChild(controls[k].getHTML());
                        controls[k].defineEvents();
                        controls[k] = null;
                    }
                }
            }
        } else {
            throw new Error("setOptions(): the supplied argument must be an array.");
        }

        return this;
    };
    OptionsSelectorField.prototype.setConfigControl = function (options) {
        var newControl;
        newControl = new PMUI.control.OptionsSelectorControl(options);
        newControl.setField(this);
        this.controls = [];
        this.controls.push(newControl);
        return this;
    };
    OptionsSelectorField.prototype.setControlPositioning = function (positioning) {
        var that = this,
            k;
        if (typeof positioning === 'string') {
            this.controlPositioning = positioning;
            controls = this.controls.slice();
            for (k = 0; k < controls.length; k += 1) {
                if (controls[k] !== null) {
                    if (that.dom.controlContainer) {
                        that.dom.controlContainer.appendChild(controls[k].getHTML());
                        controls[k] = null;
                    }
                }
            }
        }

        return this;
    };
    OptionsSelectorField.prototype.setOnChangeHandler = function () {
        return this;
    };
    /**
     * Sets the value for the field
     * If the parameter is simple string like to
     *
     *     "myoption" || "1"
     *
     * the value will be assigned to all the items.
     *
     * Otherwise the parameter must have the follow structure
     *
     *      "1,2,3" || "first,second,third, ...]"
     *
     * @param {String} value
     */
    OptionsSelectorField.prototype.setValue = function (value) {
        value = value.toString();
        if (typeof value === 'string') {
            controls = this.controls[0];

            this.value = value;
            this.data.setValue(value);
            this.setValueToControls(value);
        } else {
            throw new Error("The setValue() method only accepts string values!");
        }
        return this;
    };
    /**
     * Gets the value for the item
     * @return {[type]} [description]
     */
    OptionsSelectorField.prototype.getValue = function () {
        var controls,
            values,
            i;

        controls = this.controls[0];
        items = this.getItemsSelected();
        if (controls.multipleSelection) {
            values = [];
            for (i = 0; i < items.length; i += 1) {
                values.push(items[i].getValue());
            }
            values.sort();
            values = values.join(this.separator);
        } else {
            values = items[0].getValue();
        }
        this.value = values
        return this.value;
    };
    /**
     * Adds a new item to current options of the OptionsSelector field
     * @param {Boolean} option This is a basic {@link PMUI.control.OptionsSelectorItemControl element} for the field.
     */
    OptionsSelectorField.prototype.addOption = function (option) {
        var newOption,
            control;

        control = this.controls[0];
        if (control.html) {
            control.addItem(jQuery.extend(true, option, {name: this.controls.length + 1}));
        }

        return this;
    };
    /**
     * Gets the options or items of the field
     * @return {Array} options Items of the field
     */
    OptionsSelectorField.prototype.getOptions = function (includeGroups) {
        return this.controls[0].getOptions(includeGroups);
    };
    /**
     * Clear all the options from the control.
     * @chainable
     */
    OptionsSelectorField.prototype.clearOptions = function () {
        this.controls[0].clearOptions();
        return this;
    };
    /**
     * [disableOption description]
     * @param  {[type]} position [description]
     * @return {[type]}          [description]
     */
    OptionsSelectorField.prototype.disableOption = function (position) {
        this.controls[0].disableOption(position, true);
        return this;
    };
    /**
     * [enableOption description]
     * @param  {[type]} position [description]
     * @return {[type]}          [description]
     */
    OptionsSelectorField.prototype.enableOption = function (position) {
        this.controls[0].disableOption(position, false);
        return this;
    };
    /**
     * Removes an option of the field
     * @param  {Number} position Represent the index of the options array
     *
     *  For example if the options array is:
     *
     *      options = [a, b, c, d]
     *
     * And the option 'b' has to be removed, the method will be:
     *
     *      obj.removeOption(2);
     *
     * @return {Object} this
     */
    OptionsSelectorField.prototype.removeOption = function (position) {
        this.controls[0].removeOption(position);
        return this;
    };
    OptionsSelectorField.prototype.getSelectedLabel = function () {
        this.controls[0].getSelectedLabel();
        return this;
    };
    /**
     * Disables the field, the label and the control of the field are disabled
     * @return {Object} this
     */
    OptionsSelectorField.prototype.disable = function () {
        var items,
            i;
        this.disabled = true;
        OptionsSelectorField.superclass.prototype.disable.call(this);
        if (jQuery.isArray(this.controls)) {
            if (this.controls[0]) {
                control = this.controls[0];
                for (i = 0; i < control.items.getSize(); i += 1) {
                    control.disableOption(i, true);
                }
            }
        }
        return this;
    };
    /**
     * This method enable the field if the label and the control of the item are disabled.
     * @return {Object} this
     */
    OptionsSelectorField.prototype.enable = function () {
        var control,
            i;
        this.disabled = false;
        OptionsSelectorField.superclass.prototype.enable.call(this);
        if (jQuery.isArray(this.controls)) {
            if (this.controls[0]) {
                control = this.controls[0];
                for (i = 0; i < control.items.getSize(); i += 1) {
                    control.disableOption(i, false);
                }
            }

        }
        return this;
    };

    OptionsSelectorField.prototype.setListWidth = function (width) {
        var items,
            i;

        if (typeof this.width === "number") {
            this.listWidth = width;
            if (jQuery.isArray(this.controls)) {
                items = this.controls;
                for (i = 0; i < items.length; i += 1) {
                    items[i].setWidth(this.listWidth);
                }
            }
        }
        return this;
    };
    OptionsSelectorField.prototype.getListWidth = function (width) {
        return this.listWidth;
    };
    /**
     * Gets all items selected on the OptionsSelector
     * @return {Array} items Represent all the items selected on the Field
     */
    OptionsSelectorField.prototype.getItemsSelected = function () {
        var items,
            i,
            selected = [];

        if (jQuery.isArray(this.controls)) {
            items = this.controls;
            for (i = 0; i < items.length; i += 1) {
                selected = jQuery.merge(selected, items[i].getItemsSelected());
            }
        }
        return selected;
    };
    /**
     * Sets the orientation for every {@link PMUI.control.OptionsSelectorItemControl element}
     * There are two possibilities:
     *
     * - horizontal, the elements will be aligned horizontally
     * - vertical, the elements will be aligned vertically
     *
     * @param {String} orientation
     * @return {Object} this
     */
    OptionsSelectorField.prototype.setOrientation = function (orientation) {
        var items,
            i;

        if (typeof orientation === "string" && jQuery.isArray(this.controls) === true) {
            items = this.controls;
            for (i = 0; i < items.length; i += 1) {
                items[i].setOrientation(orientation);
            }
        }
        return this;
    };
    OptionsSelectorField.prototype.setLabel = function (label) {

        if (typeof label === 'string') {
            this.label = label;
        } else {
            throw new Error("The setLabel() method only accepts string values!");
        }
        if (this.dom.labelTextContainer) {
            if (label === '[field]' || label === '') {
                this.label = '';
                this.dom.labelTextContainer.textContent = this.label;

            } else {
                this.dom.labelTextContainer.textContent = this.label + (this.colonVisible ? ':' : '');
            }
        }
        return this;
    };

    PMUI.extendNamespace('PMUI.field.OptionsSelectorField', OptionsSelectorField);

    if (typeof exports !== "undefined") {
        module.exports = OptionsSelectorField;
    }

}());
(function () {
    var UploadControl = function (settings) {
        UploadControl.superclass.call(this, settings);
        this.accept = null;
        this.multiple = null;
        this.files = null;
        UploadControl.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.control.HTMLControl', UploadControl);

    UploadControl.prototype.type = 'UploadControl';
    UploadControl.prototype.family = 'control';

    UploadControl.prototype.init = function (settings) {
        var defaults;
        defaults = {
            accept: "",
            multiple: false,
            height: '100%'
        };

        jQuery.extend(true, defaults, settings);

        this.setAcceptedFiles(defaults.accept)
            .setMultiple(defaults.multiple)
            .setHeight(defaults.height);
    };

    /**
     * @method setAccepted
     * here we can define the  type  files our send, the valid types are MIME, separation with ","
     * for more information go to http://reference.sitepoint.com/html/mime-types-full
     * @param {String} accept MIME types are valid for our upload
     */
    UploadControl.prototype.setAcceptedFiles = function (accept) {
        if (!(typeof accept === "string")) {
            throw new Error('setAcceptedFiles(): The parameter type is invalid, should be type string');
        }
        this.accept = '';
        this.accept = accept;
        if (this.html) {
            this.html.accept = accept;
        }
        return this;
    };

    UploadControl.prototype.defineEvents = function () {
        var that = this;
        UploadControl.superclass.prototype.defineEvents.call(this);
        this.html.addEventListener("click", function (e) {
            e.stopPropagation();
        });
        return this;
    };

    UploadControl.prototype.setMultiple = function (value) {
        if (!(typeof value === 'boolean')) {
            throw new Error('setMultiple(): The parameter type is invalid, should be type string');
        }
        this.multiple = value;
        if (this.html) {
            this.html.multiple = value;
        }
        return this;
    };

    UploadControl.prototype.createHTML = function () {
        if (this.html) {
            return this.html;
        }
        UploadControl.superclass.prototype.createHTML.call(this);
        this.html.type = 'file';
        this.setMultiple(this.multiple);
        this.setAcceptedFiles(this.accept);

        return this.html;
    };

    PMUI.extendNamespace('PMUI.control.UploadControl', UploadControl);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = UploadControl;
    }
}());
(function () {
    /**
     * @class PMUI.control.ButtonControl
     * @extends PMUI.control.Control
     *
     *  this button is a control, the config options is not equal to button of UI
     *  the use of this control is for a field
     */
    var ButtonControl = function (settings) {
        ButtonControl.superclass.call(this, settings);
        /**
         * this a PMUI.ui.Button object
         * @type {PMUI.ui.Button}
         */
        this.button = null;
        ButtonControl.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.control.Control', ButtonControl);

    ButtonControl.prototype.type = "ButtonControl";

    ButtonControl.prototype.init = function (settings) {
        var defaults = {
            handler: null
        };
        jQuery.extend(true, defaults, settings);
        this.button = new PMUI.ui.Button();
        this.setHandler(defaults.handler);
    };
    /**
     * diabled the buttonControl
     * @param {Boolean} value if true disable button, default value false
     * @chainable
     */
    ButtonControl.prototype.setDisabled = function (value) {
        if (this.button) {
            this.button.setDisabled(value);
        }

        return this;
    }
    /**
     * disable Button
     * @chainable
     */
    ButtonControl.prototype.disable = function () {
        if (this.button) {
            this.button.disable();
        }

        return this;
    };
    /**
     * ebable button
     * @chainable
     */
    ButtonControl.prototype.enable = function () {
        this.button.enable();
        return this;
    };
    /**
     * set bordet for Button
     * @chainable
     */
    ButtonControl.prototype.setBorder = function (border) {
        this.button.setBorder(border);
        return this;
    };
    /**
     * this click is established with jQuery, simulate the behavior click
     * @chainable
     */
    ButtonControl.prototype.click = function () {
        this.button.click();
        return this;
    };
    /**
     * set handler for button
     * @param {Function} handle the event to trigger
     * @chainable
     */
    ButtonControl.prototype.setHandler = function (handler) {
        this.button.setHandler(handler);
        return this;
    };
    /**
     * remove  one event of the events
     * @param  {String} type It is an event that is stored in an object event
     * @chainable
     */
    ButtonControl.prototype.removeEvent = function (type) {
        this.button.removeEvent(type);
        return this;
    };
    /**
     * removes all events
     * @return {[type]}
     */
    ButtonControl.prototype.removeEvents = function () {
        this.button.removeEvents();
        return this;
    };
    /**
     * fix the text for the button
     * @param {String} text
     */
    ButtonControl.prototype.setText = function (text) {
        this.button.setText(text);
        return this;
    };
    ButtonControl.prototype.setIcon = function (iconClass) {
        this.button.setIcon(iconClass);
        return this;
    };
    ButtonControl.prototype.setAliasButton = function (alias) {
        this.button.setAliasButton(alias);
        return this;
    };
    ButtonControl.prototype.setParent = function (parent) {
        this.button.setParent(parent);
        return this;
    };
    ButtonControl.prototype.createHTML = function () {
        if (this.html) {
            return this.html;
        }

        this.html = this.button.getHTML();

        return this.html;
    };
    ButtonControl.prototype.defineEvents = function () {
        this.button.defineEvents();
        return this;
    };
    ButtonControl.prototype.setHeight = function (height) {
        if (this.button) {
            this.button.setHeight(height);
        }
        return this;
    };
    ButtonControl.prototype.setIconPosition = function (position) {
        this.setIconPosition(position);
        return this;
    };
    /**
     * fix a message for  button
     * @param {String} messageTooltip
     */
    ButtonControl.prototype.setTooltipMessage = function (messageTooltip) {
        this.button.setTooltipMessage(messageTooltip);
        return this;
    };

    PMUI.extendNamespace('PMUI.control.ButtonControl', ButtonControl);

    if (typeof exports !== "undefined") {
        module.exports = ButtonControl;
    }
}());
(function () {

    var NumberControl = function (settings) {
        NumberControl.superclass.call(this, settings);
        /**
         * @event onKeyUp
         * Fired when a pressed key is released.
         * @param {Object} event The event object.
         */
        this.onKeyUp = null;
        this.mask = null;
        this.percentage = null;
        this.filterKey = null;
        this.maskList = [];
        this.expRegList = [];
        this.expRegMask = [];
        this.optionDecimal = null;
        this.decimals = null;
        this.valueInit = null;
        this.onlyNumber = null;

        this.onHandlerMaskKeyDown = null;
        this.onHandlerMaskKeyPress = null;
        NumberControl.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.control.TextControl', NumberControl);

    NumberControl.prototype.type = "TextControl";
    /**
     * Initializes the object.
     * @param  {Object} [settings=null] A JSON object with the config options.
     * @private
     */
    NumberControl.prototype.init = function (settings) {
        var defaults = {
            placeholder: "",
            maxLength: 524288,
            onKeyUp: null,
            optionDecimal: null,
            valueInit: "",
            mask: "###,###,###,###.###########"
        };

        $.extend(true, defaults, settings);
        this.setMaskDefault();
        this.setPlaceholder(defaults.placeholder)
            .setMaxLength(defaults.maxLength)
            .setOnKeyUpHandler(defaults.onKeyUp)
            .setOptionDecimal(defaults.optionDecimal)
            .setMask(defaults.mask);

        this.mask = defaults.mask;

    };

    /**
     * Sets the callback function to be called when the keyup event occurs.
     * @chainable
     */

    NumberControl.prototype.evalMask = function (textvalue) {
        var i;

        for (i = 0; i < this.expRegList.length; i += 1) {
            if (this.expRegList[i].test(textvalue))
                return true;
        }
        return false;
    };

    NumberControl.prototype.setOptionDecimal = function (option) {
        if (typeof option === "string")
            this.optionDecimal = option;
        return this;
    };


    NumberControl.prototype.evalMaskNumeric = function (textvalue) {
        var sw = true,
            i;

        if (this.expRegMask === undefined) {
            return true;
        }
        else {
            for (i = 0; i < this.expRegMask.length; i += 1) {
                if (this.expRegMask[i][2] != null) {
                    if (!this.expRegMask[i][2].test(textvalue))
                        sw = false;
                }

            }
            return sw;
        }
    };

    NumberControl.prototype.truncate = function (number, digits) {
        var multiplier = Math.pow(10, digits),
            adjustedNum = number * multiplier,
            truncatedNum = Math[adjustedNum < 0 ? 'ceil' : 'floor'](adjustedNum);
        return truncatedNum / multiplier;
    };


    NumberControl.prototype.setValue = function (strControl) {
        var strAux;
        this.valueInit = strControl;
        if (this.isNumber(strControl)) {
            if (strControl != null && strControl != "") {
                if (this.evalMaskNumeric(strControl) || strControl == "") {
                    NumberControl.superclass.prototype.setValue.call(this, strControl);
                    //this.setValue(strControl);
                }
                else {
                    if (this.optionDecimal == "truncate") {

                    }

                    strControl = this.removeNumberCommas(strControl);

                    if (this.optionDecimal == "truncate")
                        strAux = this.truncate(parseFloat(strControl), this.decimals);
                    if (this.optionDecimal == "round")
                        strAux = Math.ceil(strControl * ('1e' + this.decimals)) / ('1e' + this.decimals);


                    strAux = this.setNumberCommas(strAux == undefined ? "" : strAux.toString());
                    if (this.evalMaskNumeric(strAux)) {
                        NumberControl.superclass.prototype.setValue.call(this, strAux);
                        // this.setValue(strAux);
                    }
                }
            }
        }
        return this;

    };

    NumberControl.prototype.getValue = function (control) {
        var value,
            array,
            result,
            i;

        if (this.valueInit != null) {
            if (control)
                return this.removeNumberCommas(this.valueInit);
            else {
                if (this.html)
                    return this.html.value;
                else
                    return "";
            }
        }
        if (control) {
            value = this.getValue();
            array = value.split(",");
            result = "";
            for (i = 0; i < array.length; i += 1) {
                result = result + array[i];
            }
            return result;
        }

        return NumberControl.superclass.prototype.getValue.call(this);
    };

    NumberControl.prototype.isNumber = function (strControl) {
        var validator = /^[\d\,\.]*$/;
        if (validator.test(strControl))
            return true;
        else
            return false;
    };

    NumberControl.prototype.removeNumberCommas = function (strControl) {
        var pint = this.getParseInt(strControl),
            pdec = this.getParseDec(strControl);
        if (pdec != undefined)
            strControl = pint + "." + pdec;
        else
            strControl = pint;
        return strControl;
    };

    NumberControl.prototype.setNumberCommas = function (strControl) {
        var pint = this.getParseInt(strControl),
            pdec = this.getParseDec(strControl),
            array,
            arraypos,
            a1,
            a2,
            strFinal = "",
            i;
        array = pint.split(",");
        for (i = 0; i <= array.length - 1; i += 1) {
            strFinal += array[i];
        }
        arraypos = strFinal.length;
        while (arraypos > 3) {
            a1 = strFinal.substr(0, arraypos - 3);
            a2 = strFinal.substr(arraypos - 3, strFinal.length);
            strFinal = a1 + "," + a2;
            arraypos++;
            arraypos = arraypos - 4;
        }
        if (pdec != undefined)
            return strFinal + "." + pdec;
        else
            return strFinal;
    };

    NumberControl.prototype.setMaskDefault = function () {
        this.expRegMask.push([/^\#{0,3}(\,\#{3})*\%{0,1}$/, '^\\d{0,3}(\\,\\d{3}){@n}\%{0,1}$', null, false]);   // captura de numeros sin decimal con comas
        this.expRegMask.push([/^[\#\,0]+(?:(\.)([\#0]*)){1}(\%){0,1}$/, '^[\\d\\,]+(\\.){0,1}(\\d{@n}){0,1}(\%){0,1}$', null, false]);   // decimales
        this.expRegMask.push([/^[\#0]{0,3}(\,[\#0]{3})*(?:(\.)([\#0]*))(\%){0,1}$/, '^\\d{@x}(\\,\\d{3}){@n}((\\.)(\\d*)){0,1}(\%){0,1}$', null, false]);   // parte entera
        this.expRegMask.push([/^[\#\,]*(0*)\.(0*)(\%){0,1}$/, '^[\\d\\,]*(0{@x})\\.([\\d]{@y})(\%){0,1}$', null, false]);   // zeros fijos
        this.expRegMask.push([/^[\#\,\.]*\%{1}$/, '^[\\d\\,\\.]*\\%{1}$', null, false]);   // percentage
        this.expRegMask.push([/^\#*\%{0,1}$/, '^\\d{@n}\%{0,1}$', null, false]);   // captura de numeros sin decimal con comas

    };

    NumberControl.prototype.setMaskNumeric = function (mask) {
        var array,
            ngroups,
            ini,
            nreg,
            nx,
            ny,
            a;

        if (this.expRegMask[0][0].test(mask)) {
            array = this.expRegMask[0][0].exec(mask);
            ngroups = array[0].split(",").length - 1;
            this.expRegMask[0][2] = new RegExp(this.expRegMask[0][1].replace(/@n/g, '0,' + ngroups.toString()));
            this.expRegMask[0][3] = true;
        }
        if (this.expRegMask[1][0].test(mask)) {
            array = this.expRegMask[1][0].exec(mask);
            ngroups = array[2].length;
            this.decimals = ngroups;
            this.expRegMask[1][2] = new RegExp(this.expRegMask[1][1].replace(/@n/g, '0,' + ngroups.toString()));
            this.expRegMask[1][3] = true;
        }
        if (this.expRegMask[2][0].test(mask)) {
            array = this.expRegMask[2][0].exec(mask);
            ngroups = array[0].split(",").length - 1;
            ini = this.expRegMask[2][0].exec("##,###.##")[0].split(",")[0].length
            nreg = this.expRegMask[2][1].replace(/@n/g, '0,' + ngroups.toString());
            this.expRegMask[2][2] = new RegExp(nreg.replace(/@x/g, '0,3'));
            this.expRegMask[2][3] = true;
        }
        if (this.expRegMask[3][0].test(mask)) {
            array = this.expRegMask[3][0].exec(mask);
            nx = array[1].length;
            ny = array[2].length;
            a = this.expRegMask[3][1].replace(/@x/g, '0,' + nx.toString());
            a = a.replace(/@y/g, '0,' + ny.toString());
            this.expRegMask[3][2] = new RegExp(a);
            this.expRegMask[3][3] = true;
        }
        //percentage
        if (this.expRegMask[4][0].test(mask)) {
            this.expRegMask[4][2] = new RegExp(this.expRegMask[4][1]);
            this.expRegMask[4][3] = true;
            this.percentage = true;
        }
        //number not comas
        if (this.expRegMask[5][0].test(mask)) {
            array = this.expRegMask[5][0].exec(mask);
            nx;
            if (this.percentage)
                nx = array[0].length - 1;
            else
                nx = array[0].length;


            this.expRegMask[5][2] = new RegExp(this.expRegMask[5][1].replace(/@n/g, '0,' + nx.toString()));
            this.expRegMask[5][3] = true;
            this.onlyNumber = true;
        }


    };

    NumberControl.prototype.setMaskHandlerNumeric = function (mask) {
        //handler numeric
        var that = this,
            thatMask = mask;
        handlerKeyDown = function (e) {
            var pressKey,
                textValue,
                regResult,
                selStart,
                selEnd,
                strStart,
                strEnd,
                strControl = "",
                textLength,
                strFinal,
                pressKey = (window.event) ? window.event.keyCode : e.which,
                onlynumbers,
                dash,
                flagequal,
                flagZeros,
                pint,
                pdec;

            textValue = that.html.value;
            selStart = that.html.selectionStart;
            selEnd = that.html.selectionEnd;
            textLength = that.html.value.length;
            onlynumbers = that.expRegMask[0][3];
            dash = that.expRegMask[1][3];
            flagequal = false;
            flagZeros = that.expRegMask[3][3];
            if (selStart == selEnd) {
                flagequal = true;
            }

            if ((pressKey > 46 && pressKey <= 57) || (pressKey >= 96 && pressKey <= 105) || pressKey == 8 || pressKey == 190 || pressKey == 37 || pressKey == 39) {
                this.valueInit = null;
                //quitar porcentaje
                if (this.percentage)
                    textValue = textValue.substr(0, textLength - 1);


                if (pressKey >= 96 && pressKey <= 105)
                    pressKey = pressKey - 48;
                if (pressKey > 45 && pressKey <= 57) {
                    if (selStart == textLength) {
                        strControl += textValue + String.fromCharCode(pressKey);
                        selStart = selStart + 1;
                    }
                    else {
                        strStart = textValue.substr(0, selStart);
                        strEnd = textValue.substr(selStart, textLength);
                        strControl = strStart + String.fromCharCode(pressKey) + strEnd;
                        selStart++;
                    }
                }
                if (pressKey == 8) {
                    if (flagequal && selStart == textLength) {
                        strControl = textValue.substr(0, selStart - 1);
                    }
                    else {
                        strStart = textValue.substr(0, selStart - 1);
                        strEnd = textValue.substr(selStart, textLength);
                        strControl = strStart + strEnd;
                        selStart--;
                    }
                }
                if (onlynumbers && pressKey == 190) {
                    strControl = textValue;
                }
                if (pressKey == 37) {
                    selStart = selStart - 1;
                    strControl = textValue;

                }
                if (pressKey == 39) {
                    if (selStart == selEnd) {
                        selEnd++;
                    }
                    selStart = selStart + 1;
                    strControl = textValue;
                }
                if (flagZeros) {
                    strControl = that.setZeros(strControl, thatMask);
                }

                //convierto con comas
                if (this.onlyNumber) {
                    if (pressKey == 190) {
                        strFinal = textValue;
                    }
                    if (pressKey != 190) {
                        strFinal = strControl;
                    }
                }
                else {
                    pint = that.getParseInt(strControl);
                    pdec = that.getParseDec(strControl);
                    selStart = selStart - (strControl.split(",").length - 1);
                    strFinal = that.setComas(pint);
                    selStart = selStart + (strFinal.split(",").length - 1);
                    if (pdec != undefined) {
                        strFinal = strFinal + "." + pdec;
                        if (flagequal && selStart > selEnd)
                            selEnd = selStart;
                        else {
                            selEnd = selStart;
                        }
                    }
                }
                if (dash && pressKey == 190) {
                    strControl += textValue + ".";
                    strFinal = strControl;
                    selStart = selStart + 1;
                }
            }
            else {
                strFinal = this.html.value;
            }

            //agregar signo de porcentaje
            if (this.percentage) {
                strFinal += "%";
            }

            if (this.evalMaskNumeric(strFinal) || strFinal == "") {
                that.html.value = strFinal;
                that.html.selectionStart = selStart;
                if (flagequal) {
                    that.html.selectionEnd = selStart;
                }
            }
            e.preventDefault();
        }
        this.onHandlerMaskKeyDown = handlerKeyDown;
    };


    NumberControl.prototype.setComas = function (strControl) {
        var array,
            arraypos,
            a1,
            a2,
            i,
            strFinal = "";

        array = strControl.split(",");
        for (i = 0; i <= array.length - 1; i += 1) {
            strFinal += array[i];
        }
        arraypos = strFinal.length;
        while (arraypos > 3) {
            a1 = strFinal.substr(0, arraypos - 3);
            a2 = strFinal.substr(arraypos - 3, strFinal.length);
            strFinal = a1 + "," + a2;
            arraypos++;
            arraypos = arraypos - 4;
        }
        return strFinal;
    };

    NumberControl.prototype.getParseInt = function (strControl) {
        var aux1,
            aux2,
            array,
            strFinal = "",
            i;

        aux1 = strControl.split(".")[0];
        array = aux1.split(",");
        for (i = 0; i <= array.length - 1; i += 1) {
            strFinal += array[i];
        }
        return strFinal;
    };

    NumberControl.prototype.getParseDec = function (strControl) {
        var aux1,
            aux2,
            array,
            strFinal = "";
        aux1 = strControl.split(".")[1];
        return aux1;
    }

    NumberControl.prototype.setZeros = function (strControl, mask) {
        var i,
            aux1,
            aux2,
            array,
            strFinal = "",
            nZerosLeft,
            nZerosRight,
            nRightStr;
        array = this.expRegMask[3][0].exec(mask);
        nZerosLeft = array[1].length;
        nZerosRight = array[2].length;

        if (strControl.split(".").length != 1) {
            nRightStr = strControl.split(".")[1].length;
            if (nRightStr < nZerosRight) {
                for (i = 0; i < nZerosRight - nRightStr; i += 1) {
                    strControl += "0";
                }
                return strControl;
            }
            if (nRightStr > nZerosRight) {
                strControl = strControl.substr(0, strControl.length - 1);
            }
            return strControl;
        }
        else {
            if (strControl == "")
                return array[1] + "." + array[2];
            else {
                return strControl + "." + array[2];
            }
        }
        return aux1;
    }

    NumberControl.prototype.setMask = function (mask) {
        var expMaskPercentage = /^(\#+)(?:(\.)(\#*))?\%$/i,
            expMaskNumber1 = /^(\#+)(?:(\.)(\#*))?$/i,
            expMaskNumber2 = /^(\#{0,3})(?:(\,)(\#{3}))*(?:(\.)(\#*))?$/i;
        this.onHandlerMaskKeyDown = null;
        this.onHandlerMaskKeyPress = null;
        this.maskList = [];
        this.expRegMask[0][3] = false;
        this.expRegMask[1][3] = false;
        this.expRegMask[2][3] = false;
        this.expRegMask[3][3] = false;
        this.expRegMask[4][3] = false;
        this.expRegMask[0][2] = null;
        this.expRegMask[1][2] = null;
        this.expRegMask[2][2] = null;
        this.expRegMask[3][2] = null;
        this.expRegMask[4][2] = null;
        // Save the list of mask
        if (typeof mask === "string") {
            this.percentage = null;
            this.onlyNumber = null;
            this.maskList.push(mask);
            this.setMaskNumeric(mask);
            this.setMaskHandlerNumeric(mask);
        }
    };
    /**
     * Set the events for the object.
     * @chainable
     */
    NumberControl.prototype.defineEvents = function () {
        var that = this;

        if (this.html && !this.eventsDefined) {
            NumberControl.superclass.prototype.defineEvents.call(this);
            this.addEvent('keyup').listen(this.html, function (e) {
                if (typeof that.onKeyUp === 'function') {
                    that.onKeyUp(e);
                }
            });
            this.addEvent('keydown').listen(this.html, function (e) {
                if (e.which === PMUI.keyCodeF5) {
                    this.blur();
                    e.preventDefault();
                    window.location.reload(true);
                }
                if (typeof that.onHandlerMaskKeyDown === 'function') {
                    that.onHandlerMaskKeyDown(e);
                }
            });
            this.addEvent('keypress').listen(this.html, function (e) {
                if (typeof that.onHandlerMaskKeyPress === 'function') {
                    that.onHandlerMaskKeyPress(e);
                }
            });
            this.eventsDefined = true;
        }
        return this;
    };
    /**
     * Creates the HTML element for the object
     * @return {HTMLElement}
     */
    NumberControl.prototype.createHTML = function () {
        NumberControl.superclass.prototype.createHTML.call(this);
        this.html.type = "text";
        this.setPlaceholder(this.placeholder)
            .setMaxLength(this.maxLength)
            .setReadOnly(this.readonly);

        //this.defineEvents();
        this.setMask(this.maskList[0]);

        return this.html;
    };
    /**
     * Sets if the control will be enabled for read only.
     * @param {Boolean}
     * @chainable
     */

    PMUI.extendNamespace('PMUI.control.NumberControl', NumberControl);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = NumberControl;
    }
}());
(function () {
    var NumberField = function (settings) {
        NumberField.superclass.call(this, settings);
        /**
         * @property {Boolean} [trimOnBlur=true] If the field's value must be trimmed every time it loses focus.
         */
        this.trimOnBlur = null;

        NumberField.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.form.Field', NumberField);

    NumberField.prototype.type = "NumberField";

    NumberField.prototype.init = function (settings) {
        var defaults = {
            placeholder: "",
            maxLength: 0,
            trimOnBlur: true,
            readOnly: false,
            mask: "",
            value: "",
            optionDecimal: null
        };

        $.extend(true, defaults, settings);

        this.setPlaceholder(defaults.placeholder)
            .setMaxLength(defaults.maxLength)
            .setTrimOnBlur(defaults.trimOnBlur)
            .setReadOnly(defaults.readOnly)
            .setOptionDecimal(defaults.optionDecimal)
            .setMask(defaults.mask);

    };
    /**
     * Sets the value for the field.
     * @param {String} value
     * @chainable
     */
    NumberField.prototype.setValue = function (value) {
        if (this.trimOnBlur) {
            value = jQuery.trim(value);
        }
        NumberField.superclass.prototype.setValue.call(this, value);
        //this.controls[0].setValue(value);
        return this;
    };

    /**
     * Sets the value for the field.
     * @param {String} value
     * @chainable
     */
    NumberField.prototype.setOptionDecimal = function (value) {
        if (this.trimOnBlur) {
            value = jQuery.trim(value);
        }
        this.controls[0].setOptionDecimal(value);
        return this;
    };

    /**
     * Sets the value for the field.
     * @param {String} value
     * @chainable
     */
    NumberField.prototype.setMask = function (value) {
        if (this.trimOnBlur) {
            value = jQuery.trim(value);
        }
        this.controls[0].setMask(value);
        return this;
    };

    /**
     * Sets the placeholder for the control. Note that this feature is only supported
     * by browsers which support the "placeholder" for input elements.
     * @param {String} placeholder
     * @chainable
     */
    NumberField.prototype.setReadOnly = function (readonly) {
        this.controls[0].setReadOnly(readonly);
        return this;
    };
    /**
     * [setPlaceholder description]
     * @param {[type]} placeholder [description]
     */
    NumberField.prototype.setPlaceholder = function (placeholder) {
        this.controls[0].setPlaceholder(placeholder);
        return this;
    };
    /**
     * [getPlaceholder description]
     * @return {[type]} [description]
     */
    NumberField.prototype.getPlaceholder = function () {
        return this.controls[0].getPlaceholder();
    };
    /**
     * [setMaxLength description]
     * @param {[type]} maxLength [description]
     */
    NumberField.prototype.setMaxLength = function (maxLength) {
        this.controls[0].setMaxLength(maxLength);
        return this;
    };
    /**
     * [getMaxLength description]
     * @return {[type]} [description]
     */
    NumberField.prototype.getMaxLength = function () {
        return this.controls[0].getMaxLength();
    };
    /**
     * [isReadOnly description]
     * @return {Boolean} [description]
     */
    NumberField.prototype.isReadOnly = function () {
        return this.controls[0].isReadOnly();
    };
    /**
     * Switches on/off the value trimming for the field's value when it loses focus.
     * @param {Boolean} trimOnBlur Send true for switch on or false to switch off.
     * @chainable
     */
    NumberField.prototype.setTrimOnBlur = function (trimOnBlur) {
        this.trimOnBlur = !!trimOnBlur;
        return this;
    };
    /**
     * Returns a boolean value that indicates if the trimming function is enabled/disabled
     * @return {Boolean}
     */
    NumberField.prototype.getTrimOnBlur = function () {
        return this.trimOnBlur;
    };
    /**
     * Sets the control for the NumberField
     * @chainable
     * @private
     */
    NumberField.prototype.setControls = function () {
        if (this.controls.length) {
            return this;
        }
        this.controls.push(new PMUI.control.NumberControl());

        return this;
    };
    /**
     * Sets the control for the NumberField
     * @chainable
     * @private
     */
    NumberField.prototype.getValue = function (control) {

        return this.controls[0].getValue(control);
    };
    /**
     * Establish the handler function for the internal onChange event
     * @return {Function}
     * @private
     */
    NumberField.prototype.onChangeHandler = function () {
        var that = this;

        return function () {
            var previousValue = that.value,
                value,
                i;

            if (that.trimOnBlur) {
                value = that.controls[0].getValue();
                value = jQuery.trim(value);
                that.controls[0].setValue(value);
            }
            that.updateValueFromControls();
            if (that.validAtChange) {
                that.isValid();
            }
            if (typeof that.onChange === 'function') {
                that.onChange(that.getValue(), previousValue);
            }
            if (that.form) {
                (that.form.onChangeHandler())(that, that.getValue(), previousValue);
            }
            if (previousValue !== value) {
                for (i = 0; i < that.dependentFields.getSize(); i += 1) {
                    that.dependentFields.get(i).dependencyHandler(that.dependentFields.get(i).dependencyFields.asArray().slice(0));
                }
            }
        };
    };

    PMUI.extendNamespace('PMUI.field.NumberField', NumberField);

    if (typeof exports !== "undefined") {
        module.exports = NumberField;
    }
}());
(function () {
    var UploadField = function (settings) {
        UploadField.superclass.call(this, settings);
        UploadField.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.form.Field', UploadField);

    UploadField.prototype.type = 'UploadField';
    UploadField.prototype.family = 'field';

    UploadField.prototype.init = function (settings) {
        var defaults;
        defaults = {
            accept: "",
            multiple: false
        };

        jQuery.extend(true, defaults, settings);

        this.setAcceptedFiles(defaults.accept)
            .setMultipleFiles(defaults.multiple);
    };

    /**
     * @method setAccepted
     * here we can define the  type  files our send, the valid types are MIME, separation with ","
     * for more information go to http://reference.sitepoint.com/html/mime-types-full
     * @param {String} accept MIME types are valid for our upload
     */
    UploadField.prototype.setAcceptedFiles = function (accept) {
        this.controls[0].setAcceptedFiles(accept);
        return this;
    };

    UploadField.prototype.setMultipleFiles = function (value) {
        this.controls[0].setMultiple(value);
        return this;
    };

    UploadField.prototype.setControls = function () {
        if (this.controls.length) {
            return this;
        }
        this.controls.push(new PMUI.control.UploadControl());
        return this;
    };

    PMUI.extendNamespace('PMUI.field.UploadField', UploadField);

    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== "undefined") {
        module.exports = UploadField;
    }
}());
(function () {
    /**
     * @class PMUI.field.ButtonField
     * @extends PMUI.form.Field
     * is a special field not return a value unlike other fields it is not
     * equal intanciacion a button PMUI.ui.Button, new properties tine see examples:
     *
     *      @example
     *      var b1,b2,b3;
     *      $(function() {
     *                 b1 = new PMUI.field.ButtonField({
     *                     value : "button1",
     *                     handler : function () {
     *                         alert("hi! I 'm Button 1")
     *                     },
     *                     name : 'button1'
     *                 });
     *      
     *                 document.body.appendChild(b1.getHTML());
     *                 b1.defineEvents();
     *      
     *      
     *                 b2 = new PMUI.field.ButtonField({
     *                     value : "button2",
     *                     handler : function () {
     *                         alert("hi! I 'm Button 2")
     *                     },
     *                     name : 'button2',
     *                     labelVisible : false,
     *                     style : {
     *                         cssClasses : ['customButton']
     *                     },
     *                     buttonAlign : 'center'
     *                 });
     *      
     *                 document.body.appendChild(b2.getHTML());
     *                 b2.defineEvents();
     *                 
     *                 b3 = new PMUI.field.ButtonField({
     *                     value : "button3",
     *                     handler : function () {
     *                         alert("hi! I 'm Button 3")
     *                     },
     *                     name : 'button3',
     *                     labelVisible : false,
     *                     buttonAlign : 'center'
     *                 });
     *      
     *                 document.body.appendChild(b3.getHTML());
     *                 b3.defineEvents();
     *           });
     *
     *
     * @cfg {Boolean}  [disableButtonControl = [false]] button will be disabled if
     * @cfg {String}  [value = "[button-field]"] be the value with the button as text, this value does not reflect data on a form
     * @cfg {String}  [tooltipMessage = [""]] message will have the button
     * @cfg {String}  [controlIconClass = [""]] icon wearing this button is a class
     * @cfg {Function}  [handler = function(){}] the handler of button in the field
     * @cfg {String}  [buttonAlign = [""]] the position will have the pin is reflected when the label of the field is hidden
     */
    var ButtonField = function (settings) {
        ButtonField.superclass.call(this, settings);
        this.buttonAlign = null;
        this.handler = null;
        ButtonField.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.form.Field', ButtonField);

    ButtonField.prototype.type = "ButtonField";

    ButtonField.prototype.init = function (settings) {
        var defaults = {
            disableButtonControl: false,
            value: '[button-field]',
            tooltipMessage: "",
            controlIconClass: '',
            handler: function () {
            },
            buttonAlign: '',
            handler: null
        }

        jQuery.extend(true, defaults, settings);

        this.setHandler(defaults.handler);
        this.setControls();
        this.setDisabled(defaults.disableButtonControl);
        this.setValue(defaults.value);
        this.setTooltipMessage(defaults.tooltipMessage);
        this.setButtonAlign(defaults.buttonAlign);
    };
    /**
     * The callback function to be executed when the the field's button is clicked.
     * @return {Function} The callback function.
     * @private
     */
    ButtonField.prototype.onButtonClickHandler = function () {
        var that = this;
        return function (button) {
            if (typeof that.handler === 'function') {
                that.handler(that, button);
            }
        };
    };
    /**
     * fix the control PMUI.control.ButtonControl
     * @chainable
     */
    ButtonField.prototype.setControls = function () {
        var that = this;
        if (this.handler === undefined || this.controls.length) {
            return this;
        }
        this.controls.push(new PMUI.control.ButtonControl({
            handler: that.onButtonClickHandler()
        }));
        return this;
    };
    /**
     * diabled the Buttonfield
     * @param {Boolean} value if true disable button, default value false
     * @chainable
     */
    ButtonField.prototype.setDisabled = function (value) {
        this.controls[0].setDisabled(value);
        return this;
    }
    /**
     * disable Button
     * @chainable
     */
    ButtonField.prototype.disable = function () {
        if (this.controls[0]) {
            this.controls[0].disable();
        }
        return this;
    };
    /**
     * ebable button
     * @chainable
     */
    ButtonField.prototype.enable = function () {
        if (this.controls[0]) {
            this.controls[0].enable();
        }
        return this;
    };

    ButtonField.prototype.click = function () {
        if (this.controls[0].click) {
            this.controls[0].click();
        }
        return this;
    };
    /**
     * this click is established with jQuery, simulate the behavior click
     * @chainable
     */
    ButtonField.prototype.setHandler = function (handler) {
        if (!(handler === null || typeof handler === 'function')) {
            throw new Error("setHandler(): The parameter must be a function or null.");
        }
        this.handler = handler;
        return this;
    };
    /**
     * remove  one event of the events
     * @param  {String} type It is an event that is stored in an object event
     * @chainable
     */
    ButtonField.prototype.removeEvent = function (type) {
        this.controls[0].removeEvent(type);
        return this;
    };
    /**
     * removes all events
     * @return {[type]}
     */
    ButtonField.prototype.removeEvents = function () {
        this.controls[0].removeEvents();
        return this;
    };
    /**
     * fix the text for the button
     * @param {String} value
     */
    ButtonField.prototype.setValue = function (value) {
        this.value = value;
        if (this.controls[0]) {
            this.controls[0].setText(value);
        }
        return this;
    };

    ButtonField.prototype.setIcon = function (iconClass) {
        this.controls[0].setIcon(iconClass);
        return this;
    };
    ButtonField.prototype.setAliasButton = function (alias) {
        this.controls[0].setAliasButton(alias);
        return this;
    };
    ButtonField.prototype.setParent = function (parent) {
        this.controls[0].setParent(parent);
        return this;
    };
    ButtonField.prototype.defineEvents = function () {
        ButtonField.superclass.prototype.defineEvents.call(this);
        this.controls[0].defineEvents();
        return this;
    };
    /**
     * fix a message for  button
     * @param {String} messageTooltip
     */
    ButtonField.prototype.setTooltipMessage = function (messageTooltip) {
        this.controls[0].setTooltipMessage(messageTooltip);
        return this;
    };
    /**
     * the value return is forever null
     * @return {object}
     */
    ButtonField.prototype.getValue = function () {
        return null;
    }
    /**
     * hide label of the field, display none
     * @chainable
     */
    ButtonField.prototype.hideLabel = function () {
        jQuery(this.dom.labelTextContainer).hide();
        this.labelVisible = false;
        return this;
    }
    /**
     * hide label of the field, display inline-block
     * @chainable
     */
    ButtonField.prototype.showLabel = function () {
        jQuery(this.dom.labelTextContainer).show();
        this.labelVisible = true;
        return this;
    }

    ButtonField.prototype.setButtonAlign = function (position) {
        if (position == "left" || position == 'center' || position == "right" || position == '') {
            this.buttonAlign = position;
        }
        if (this.html) {
            this.style.addProperties({'textAlign': position});
        }
        return this;
    };

    ButtonField.prototype.createHTML = function () {
        ButtonField.superclass.prototype.createHTML.call(this);
        this.setButtonAlign(this.buttonAlign);
        return this.html;
    };

    PMUI.extendNamespace('PMUI.field.ButtonField', ButtonField);

    if (typeof exports !== "undefined") {
        module.exports = ButtonField;
    }
}());
(function () {
    var ToolbarItem = function (settings) {
        ToolbarItem.superclass.call(this, settings);
        this.text = null;
        this.dom = {};
        ToolbarItem.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Item', ToolbarItem);

    ToolbarItem.prototype.type = 'ToolbarItem';

    ToolbarItem.prototype.init = function (settings) {
        var defaults = {
            text: ''
        };
        jQuery.extend(true, defaults, settings);
        this.setText(defaults.text);
    };

    ToolbarItem.prototype.setText = function (text) {
        if (typeof text !== 'string') {
            throw new Error('setText(): The parameter must be a string.');
        }
        this.text = text;
        if (this.dom.textContainer) {
            this.dom.textContainer = text;
        }
        return this;
    };

    ToolbarItem.prototype.createHTML = function () {
        if (this.html) {
            return this.html;
        }
        ToolbarItem.superclass.prototype.createHTML.call(this);
        this.dom.textContainer = this.html;
        return this.html;
    };

    PMUI.extendNamespace('PMUI.toolbar.ToolbarItem', ToolbarItem);
}());
(function () {
    var ToolbarAction = function (settings) {
        ToolbarAction.superclass.call(this, settings);
        this.action = null;
        this.visibleIcon = null;
        this.iconClass = null;
        ToolbarAction.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.toolbar.ToolbarItem', ToolbarAction);

    ToolbarAction.prototype.init = function (settings) {
        var defaults = {
            action: null,
            visibleIcon: true,
            iconClass: null,
            elementTag: 'li'
        };

        jQuery.extend(true, defaults, settings);
        this.setElementTag(defaults.elementTag)
            .setAction(defaults.action)
            .setIconClass(defaults.iconClass);
        if (defaults.visibleIcon) {
            this.showIcon();
        } else {
            this.hideIcon();
        }
    };

    ToolbarAction.prototype.setIconClass = function (iconClass) {
        if (!(iconClass === null || typeof iconClass === 'string')) {
            throw new Error("setIconClass(): the parameter must be a string or null.");
        }
        this.iconClass = iconClass;
        if (this.dom.iconContainer) {
            this.dom.iconContainer.className = 'pmui-toolbaraction-icon ' + (iconClass || "");
        }
        return this;
    };

    ToolbarAction.prototype.setText = function (text) {
        if (typeof text !== 'string') {
            throw new Error('setText(): The parameter must be a string.');
        }
        this.text = text;
        if (this.dom.textContainer) {
            this.dom.textContainer.textContent = text;
        }
        return this;
    };

    ToolbarAction.prototype.setAction = function (action) {
        if (!(action === null || typeof action === 'function')) {
            throw new Error("setAction(): the parameter must be a function or null.");
        }
        this.action = action;
        return this;
    };

    ToolbarAction.prototype.showIcon = function () {
        this.visibleIcon = true;
        if (this.dom.iconContainer) {
            this.dom.iconContainer.style.display = '';
        }
        return this;
    };

    ToolbarAction.prototype.hideIcon = function () {
        this.visibleIcon = false;
        if (this.dom.iconContainer) {
            this.dom.iconContainer.style.display = 'none';
        }
        return this;
    };

    ToolbarAction.prototype.attachEventListeners = function () {
        var that = this;

        if (this.html) {
            this.removeEvents();
            this.addEvent('click').listen(this.dom.link, function (e) {
                e.preventDefault();
                e.stopPropagation();
                if (typeof that.action === 'function') {
                    that.action(that);
                }
            });
        }
        return this;
    };

    ToolbarAction.prototype.createHTML = function () {
        var link,
            iconContainer,
            textContainer;

        if (this.html) {
            return this.html;
        }

        ToolbarAction.superclass.prototype.createHTML.call(this);
        link = PMUI.createHTMLElement('a');
        link.className = 'pmui-toolbaraction-link';
        link.href = "#";
        iconContainer = PMUI.createHTMLElement('i');
        iconContainer.className = 'pmui-toolbaraction-icon';
        textContainer = PMUI.createHTMLElement('span');
        textContainer.className = 'pmui-toolbaraction-text';

        link.appendChild(iconContainer);
        link.appendChild(textContainer);

        this.dom.iconContainer = iconContainer;
        this.dom.link = link;
        this.dom.textContainer = textContainer;

        this.html.appendChild(link);

        this.setText(this.text)
            .setIconClass(this.iconClass);

        this.attachEventListeners();

        return this.html;
    };

    PMUI.extendNamespace('PMUI.toolbar.ToolbarAction', ToolbarAction);
}());
(function () {
    var Toolbar = function (settings) {
        Toolbar.superclass.call(this, settings);
        Toolbar.prototype.init.call(this, settings);
    };

    PMUI.inheritFrom('PMUI.core.Container', Toolbar);

    Toolbar.prototype.type = "Toolbar";

    Toolbar.prototype.init = function (settings) {
        var defaults = {
            elementTag: 'ul'
        };

        jQuery(true, defaults, settings);

        this.setElementTag(defaults.elementTag);
    };

    Toolbar.prototype.setFactory = function () {
        this.factory = new PMUI.util.Factory({
            products: {
                'toolbaraction': PMUI.toolbar.ToolbarAction
            },
            defaultProduct: 'toolbaraction'
        });
        return this;
    };

    PMUI.extendNamespace('PMUI.toolbar.Toolbar', Toolbar);
}());
(function () {

    var I18N = {
        indexes: {},
        repository: {},
        observers: [],
        defaultLanguage: 'en',
        currentLanguage: 'en',
        contextIndex: {},
        setDefaultLanguage: function (lang) {
            this.defaultLanguage = lang;
        },
        MD5: function (string) {

            function RotateLeft(lValue, iShiftBits) {
                return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
            }

            function AddUnsigned(lX, lY) {
                var lX4,
                    lY4,
                    lX8,
                    lY8,
                    lResult;

                lX8 = (lX & 0x80000000);
                lY8 = (lY & 0x80000000);
                lX4 = (lX & 0x40000000);
                lY4 = (lY & 0x40000000);
                lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
                if (lX4 & lY4) {
                    return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
                }
                if (lX4 | lY4) {
                    if (lResult & 0x40000000) {
                        return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
                    } else {
                        return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
                    }
                } else {
                    return (lResult ^ lX8 ^ lY8);
                }
            }

            function F(x, y, z) {
                return (x & y) | ((~x) & z);
            }

            function G(x, y, z) {
                return (x & z) | (y & (~z));
            }

            function H(x, y, z) {
                return (x ^ y ^ z);
            }

            function I(x, y, z) {
                return (y ^ (x | (~z)));
            }

            function FF(a, b, c, d, x, s, ac) {
                a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
                return AddUnsigned(RotateLeft(a, s), b);
            };

            function GG(a, b, c, d, x, s, ac) {
                a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
                return AddUnsigned(RotateLeft(a, s), b);
            };

            function HH(a, b, c, d, x, s, ac) {
                a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
                return AddUnsigned(RotateLeft(a, s), b);
            };

            function II(a, b, c, d, x, s, ac) {
                a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
                return AddUnsigned(RotateLeft(a, s), b);
            };

            function ConvertToWordArray(string) {
                var lWordCount,
                    lMessageLength = string.length,
                    lNumberOfWords_temp1 = lMessageLength + 8,
                    lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64,
                    lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16,
                    lWordArray = Array(lNumberOfWords - 1),
                    lBytePosition = 0,
                    lByteCount = 0;

                while (lByteCount < lMessageLength) {
                    lWordCount = (lByteCount - (lByteCount % 4)) / 4;
                    lBytePosition = (lByteCount % 4) * 8;
                    lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition));
                    lByteCount++;
                }
                lWordCount = (lByteCount - (lByteCount % 4)) / 4;
                lBytePosition = (lByteCount % 4) * 8;
                lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
                lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
                lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;

                return lWordArray;
            };

            function WordToHex(lValue) {
                var WordToHexValue = "",
                    WordToHexValue_temp = "",
                    lByte,
                    lCount;

                for (lCount = 0; lCount <= 3; lCount += 1) {
                    lByte = (lValue >>> (lCount * 8)) & 255;
                    WordToHexValue_temp = "0" + lByte.toString(16);
                    WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2);
                }
                return WordToHexValue;
            };

            function Utf8Encode(string) {
                var utftext = "",
                    n,
                    c;

                string = string.replace(/\r\n/g, "\n");
                for (n = 0; n < string.length; n += 1) {
                    c = string.charCodeAt(n);
                    if (c < 128) {
                        utftext += String.fromCharCode(c);
                    }
                    else if ((c > 127) && (c < 2048)) {
                        utftext += String.fromCharCode((c >> 6) | 192);
                        utftext += String.fromCharCode((c & 63) | 128);
                    }
                    else {
                        utftext += String.fromCharCode((c >> 12) | 224);
                        utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                        utftext += String.fromCharCode((c & 63) | 128);
                    }
                }

                return utftext;
            };

            var x,
                k,
                AA,
                BB,
                CC,
                DD,
                a,
                b,
                c,
                d,
                S11 = 7,
                S12 = 12,
                S13 = 17,
                S14 = 22,
                S21 = 5,
                S22 = 9,
                S23 = 14,
                S24 = 20,
                S31 = 4,
                S32 = 11,
                S33 = 16,
                S34 = 23,
                S41 = 6,
                S42 = 10,
                S43 = 15,
                S44 = 21,
                temp;

            string = Utf8Encode(string);

            x = ConvertToWordArray(string);

            a = 0x67452301;
            b = 0xEFCDAB89;
            c = 0x98BADCFE;
            d = 0x10325476;

            for (k = 0; k < x.length; k += 16) {
                AA = a;
                BB = b;
                CC = c;
                DD = d;
                a = FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
                d = FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
                c = FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
                b = FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
                a = FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
                d = FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
                c = FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
                b = FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
                a = FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
                d = FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
                c = FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
                b = FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
                a = FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
                d = FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
                c = FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
                b = FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
                a = GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
                d = GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
                c = GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
                b = GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
                a = GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
                d = GG(d, a, b, c, x[k + 10], S22, 0x2441453);
                c = GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
                b = GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
                a = GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
                d = GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
                c = GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
                b = GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
                a = GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
                d = GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
                c = GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
                b = GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
                a = HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
                d = HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
                c = HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
                b = HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
                a = HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
                d = HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
                c = HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
                b = HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
                a = HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
                d = HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
                c = HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
                b = HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
                a = HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
                d = HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
                c = HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
                b = HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
                a = II(a, b, c, d, x[k + 0], S41, 0xF4292244);
                d = II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
                c = II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
                b = II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
                a = II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
                d = II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
                c = II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
                b = II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
                a = II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
                d = II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
                c = II(c, d, a, b, x[k + 6], S43, 0xA3014314);
                b = II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
                a = II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
                d = II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
                c = II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
                b = II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
                a = AddUnsigned(a, AA);
                b = AddUnsigned(b, BB);
                c = AddUnsigned(c, CC);
                d = AddUnsigned(d, DD);
            }

            temp = WordToHex(a) + WordToHex(b) + WordToHex(c) + WordToHex(d);

            return temp.toLowerCase();
        }
    };

    I18N.setDefaultLanguage = function (lang) {
        this.defaultLanguage = lang;
        if (this.repository[lang]) {
            this.loadDefaultLanguege(this.repository[lang]);
        }
        return this;
    };

    I18N.loadLanguage = function (data, lang, loaded) {
        if (typeof data != 'object') {
            throw new Error("loadLanguage(): the first parameter is not valid, should be 'object'");
        }
        if (typeof lang != 'string') {
            throw new Error("loadLanguage(): the second parameter is not valid, should be 'string'");
        }
        if (this.defaultLanguage == lang) {
            this.loadDefaultLanguege(data);
        }
        if (!loaded) {
            this.repository[lang] = data;
        }
        return this;
    };

    I18N.loadDefaultLanguege = function (data) {
        var label,
            value;

        this.indexes = {};
        this.contextIndex = {};
        for (label in data) {
            if (!this.indexes[data[label]]) {
                this.indexes[data[label]] = label;
            } else {
                if (!this.contextIndex[data[label]]) {
                    value = this.indexes[data[label]];
                    this.contextIndex[data[label]] = {};
                    this.contextIndex[data[label]]['0'] = value;
                    this.contextIndex[data[label]]['1'] = label;
                } else {
                    var n = this.getSizeJson(this.contextIndex[data[label]]);
                    this.contextIndex[data[label]][n.toString()] = label;
                }
            }
        }
        return this;
    };

    I18N.getSizeJson = function (json) {
        var size = 0,
            i;

        if (typeof json == 'object') {
            for (i in json) {
                size += 1;
            }
            return size;
        } else {
            throw new Error('the parameter is not a JSON');
        }
        return this;
    };

    I18N.setCurrentLanguage = function (lang) {
        this.currentLanguage = lang;
        if (this.repository[lang]) {
            this.loadLanguage(this.repository[lang], lang, true);
        }
        return this;
    };

    I18N.translate = function (variablesLabels) {
        var translation = String(this),
            index,
            i;

        index = I18N.indexes[this];
        if (index && I18N.repository[I18N.currentLanguage][index]) {
            translation = I18N.repository[I18N.currentLanguage][index];
        }
        if (variablesLabels) {
            for (i = 0; i < variablesLabels.length; i += 1) {
                translation = translation.replace("{" + i + "}", String(variablesLabels[i]));
            }
        }
        return translation;
    };

    I18N.translateContext = function (value) {
        var translation,
            index,
            label;

        if (typeof value != 'number') {
            for (label in I18N.contextIndex[this]) {
                if (I18N.contextIndex[this][label] == value) {
                    index = I18N.contextIndex[this][label];
                }
            }
        } else {
            index = I18N.contextIndex[this][value.toString()];
        }

        if (index) {
            translation = I18N.repository[I18N.currentLanguage][index];
            return translation;
        }

        return String(this);
    };
    // Declarations created to instantiate in NodeJS environment
    if (typeof exports !== 'undefined') {
        module.exports = I18N;
    }

    PMUI.extendNamespace('PMUI.lang.I18N', I18N);

}());
PMUI.init();


/**
 * configure and extend classes and prototypes similar to inheritance in
 * another languages. The method detects if we trying to extend a method of an object
 * or a function inserted into a prototype.
 * @param  {String|Object} classObj contains the value of the class that we want to take an method for modifying
 * @param  {Sting} method the name of the function that we will be modified or overwritten
 * @param  {function} additionalFunc contains a function with the new code or the additional code
 *                    for modified the original function.
 *
 * @return {[type]}    [returns the value of the function modified, with the new functionality in this context.]
 */

var PMExtend = function (classObj, method, additionalFunc) {
    var originalFunc;
    if (classObj.prototype !== undefined) {
        originalFunc = classObj.prototype[method];
        if (originalFunc !== undefined && typeof originalFunc === 'function') {
            return function () {
                var returnVal = originalFunc.apply(this, arguments);
                if (returnVal) {
                    returnVal = additionalFunc.apply(this, [returnVal].concat(arguments));
                } else {
                    additionalFunc.apply(this, arguments);
                }
                return returnVal;
            };

        } else {
            //You need to implement a catch on a higher level or at the plugin
            throw new Error("Cannot extend method " + method + " in Class " + classObj.name);
        }
    } else {
        originalFunc = classObj[method];
        if (originalFunc !== undefined && typeof originalFunc === 'function') {
            return function () {
                var res;
                res = originalFunc.apply(this, arguments);
                res = additionalFunc.apply(this, [res].concat(arguments));
                return res;
            };
        } else {
            //You need to implement a catch on a higher level or at the plugin
            throw new Error("Cannot extend method " + method + " in Class " + classObj.name);
        }
    }
};
/**
 * @license
 * Lo-Dash 2.4.1 (Custom Build) <http://lodash.com/>
 * Build: `lodash modern -o ./dist/lodash.js`
 * Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
 * Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
 * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
 * Available under MIT license <http://lodash.com/license>
 */
;(function() {

  /** Used as a safe reference for `undefined` in pre ES5 environments */
  var undefined;

  /** Used to pool arrays and objects used internally */
  var arrayPool = [],
      objectPool = [];

  /** Used to generate unique IDs */
  var idCounter = 0;

  /** Used to prefix keys to avoid issues with `__proto__` and properties on `Object.prototype` */
  var keyPrefix = +new Date + '';

  /** Used as the size when optimizations are enabled for large arrays */
  var largeArraySize = 75;

  /** Used as the max size of the `arrayPool` and `objectPool` */
  var maxPoolSize = 40;

  /** Used to detect and test whitespace */
  var whitespace = (
    // whitespace
    ' \t\x0B\f\xA0\ufeff' +

    // line terminators
    '\n\r\u2028\u2029' +

    // unicode category "Zs" space separators
    '\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000'
  );

  /** Used to match empty string literals in compiled template source */
  var reEmptyStringLeading = /\b__p \+= '';/g,
      reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
      reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;

  /**
   * Used to match ES6 template delimiters
   * http://people.mozilla.org/~jorendorff/es6-draft.html#sec-literals-string-literals
   */
  var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;

  /** Used to match regexp flags from their coerced string values */
  var reFlags = /\w*$/;

  /** Used to detected named functions */
  var reFuncName = /^\s*function[ \n\r\t]+\w/;

  /** Used to match "interpolate" template delimiters */
  var reInterpolate = /<%=([\s\S]+?)%>/g;

  /** Used to match leading whitespace and zeros to be removed */
  var reLeadingSpacesAndZeros = RegExp('^[' + whitespace + ']*0+(?=.$)');

  /** Used to ensure capturing order of template delimiters */
  var reNoMatch = /($^)/;

  /** Used to detect functions containing a `this` reference */
  var reThis = /\bthis\b/;

  /** Used to match unescaped characters in compiled string literals */
  var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g;

  /** Used to assign default `context` object properties */
  var contextProps = [
    'Array', 'Boolean', 'Date', 'Function', 'Math', 'Number', 'Object',
    'RegExp', 'String', '_', 'attachEvent', 'clearTimeout', 'isFinite', 'isNaN',
    'parseInt', 'setTimeout'
  ];

  /** Used to make template sourceURLs easier to identify */
  var templateCounter = 0;

  /** `Object#toString` result shortcuts */
  var argsClass = '[object Arguments]',
      arrayClass = '[object Array]',
      boolClass = '[object Boolean]',
      dateClass = '[object Date]',
      funcClass = '[object Function]',
      numberClass = '[object Number]',
      objectClass = '[object Object]',
      regexpClass = '[object RegExp]',
      stringClass = '[object String]';

  /** Used to identify object classifications that `_.clone` supports */
  var cloneableClasses = {};
  cloneableClasses[funcClass] = false;
  cloneableClasses[argsClass] = cloneableClasses[arrayClass] =
  cloneableClasses[boolClass] = cloneableClasses[dateClass] =
  cloneableClasses[numberClass] = cloneableClasses[objectClass] =
  cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true;

  /** Used as an internal `_.debounce` options object */
  var debounceOptions = {
    'leading': false,
    'maxWait': 0,
    'trailing': false
  };

  /** Used as the property descriptor for `__bindData__` */
  var descriptor = {
    'configurable': false,
    'enumerable': false,
    'value': null,
    'writable': false
  };

  /** Used to determine if values are of the language type Object */
  var objectTypes = {
    'boolean': false,
    'function': true,
    'object': true,
    'number': false,
    'string': false,
    'undefined': false
  };

  /** Used to escape characters for inclusion in compiled string literals */
  var stringEscapes = {
    '\\': '\\',
    "'": "'",
    '\n': 'n',
    '\r': 'r',
    '\t': 't',
    '\u2028': 'u2028',
    '\u2029': 'u2029'
  };

  /** Used as a reference to the global object */
  var root = (objectTypes[typeof window] && window) || this;

  /** Detect free variable `exports` */
  var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;

  /** Detect free variable `module` */
  var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;

  /** Detect the popular CommonJS extension `module.exports` */
  var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;

  /** Detect free variable `global` from Node.js or Browserified code and use it as `root` */
  var freeGlobal = objectTypes[typeof global] && global;
  if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
    root = freeGlobal;
  }

  /*--------------------------------------------------------------------------*/

  /**
   * The base implementation of `_.indexOf` without support for binary searches
   * or `fromIndex` constraints.
   *
   * @private
   * @param {Array} array The array to search.
   * @param {*} value The value to search for.
   * @param {number} [fromIndex=0] The index to search from.
   * @returns {number} Returns the index of the matched value or `-1`.
   */
  function baseIndexOf(array, value, fromIndex) {
    var index = (fromIndex || 0) - 1,
        length = array ? array.length : 0;

    while (++index < length) {
      if (array[index] === value) {
        return index;
      }
    }
    return -1;
  }

  /**
   * An implementation of `_.contains` for cache objects that mimics the return
   * signature of `_.indexOf` by returning `0` if the value is found, else `-1`.
   *
   * @private
   * @param {Object} cache The cache object to inspect.
   * @param {*} value The value to search for.
   * @returns {number} Returns `0` if `value` is found, else `-1`.
   */
  function cacheIndexOf(cache, value) {
    var type = typeof value;
    cache = cache.cache;

    if (type == 'boolean' || value == null) {
      return cache[value] ? 0 : -1;
    }
    if (type != 'number' && type != 'string') {
      type = 'object';
    }
    var key = type == 'number' ? value : keyPrefix + value;
    cache = (cache = cache[type]) && cache[key];

    return type == 'object'
      ? (cache && baseIndexOf(cache, value) > -1 ? 0 : -1)
      : (cache ? 0 : -1);
  }

  /**
   * Adds a given value to the corresponding cache object.
   *
   * @private
   * @param {*} value The value to add to the cache.
   */
  function cachePush(value) {
    var cache = this.cache,
        type = typeof value;

    if (type == 'boolean' || value == null) {
      cache[value] = true;
    } else {
      if (type != 'number' && type != 'string') {
        type = 'object';
      }
      var key = type == 'number' ? value : keyPrefix + value,
          typeCache = cache[type] || (cache[type] = {});

      if (type == 'object') {
        (typeCache[key] || (typeCache[key] = [])).push(value);
      } else {
        typeCache[key] = true;
      }
    }
  }

  /**
   * Used by `_.max` and `_.min` as the default callback when a given
   * collection is a string value.
   *
   * @private
   * @param {string} value The character to inspect.
   * @returns {number} Returns the code unit of given character.
   */
  function charAtCallback(value) {
    return value.charCodeAt(0);
  }

  /**
   * Used by `sortBy` to compare transformed `collection` elements, stable sorting
   * them in ascending order.
   *
   * @private
   * @param {Object} a The object to compare to `b`.
   * @param {Object} b The object to compare to `a`.
   * @returns {number} Returns the sort order indicator of `1` or `-1`.
   */
  function compareAscending(a, b) {
    var ac = a.criteria,
        bc = b.criteria,
        index = -1,
        length = ac.length;

    while (++index < length) {
      var value = ac[index],
          other = bc[index];

      if (value !== other) {
        if (value > other || typeof value == 'undefined') {
          return 1;
        }
        if (value < other || typeof other == 'undefined') {
          return -1;
        }
      }
    }
    // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
    // that causes it, under certain circumstances, to return the same value for
    // `a` and `b`. See https://github.com/jashkenas/underscore/pull/1247
    //
    // This also ensures a stable sort in V8 and other engines.
    // See http://code.google.com/p/v8/issues/detail?id=90
    return a.index - b.index;
  }

  /**
   * Creates a cache object to optimize linear searches of large arrays.
   *
   * @private
   * @param {Array} [array=[]] The array to search.
   * @returns {null|Object} Returns the cache object or `null` if caching should not be used.
   */
  function createCache(array) {
    var index = -1,
        length = array.length,
        first = array[0],
        mid = array[(length / 2) | 0],
        last = array[length - 1];

    if (first && typeof first == 'object' &&
        mid && typeof mid == 'object' && last && typeof last == 'object') {
      return false;
    }
    var cache = getObject();
    cache['false'] = cache['null'] = cache['true'] = cache['undefined'] = false;

    var result = getObject();
    result.array = array;
    result.cache = cache;
    result.push = cachePush;

    while (++index < length) {
      result.push(array[index]);
    }
    return result;
  }

  /**
   * Used by `template` to escape characters for inclusion in compiled
   * string literals.
   *
   * @private
   * @param {string} match The matched character to escape.
   * @returns {string} Returns the escaped character.
   */
  function escapeStringChar(match) {
    return '\\' + stringEscapes[match];
  }

  /**
   * Gets an array from the array pool or creates a new one if the pool is empty.
   *
   * @private
   * @returns {Array} The array from the pool.
   */
  function getArray() {
    return arrayPool.pop() || [];
  }

  /**
   * Gets an object from the object pool or creates a new one if the pool is empty.
   *
   * @private
   * @returns {Object} The object from the pool.
   */
  function getObject() {
    return objectPool.pop() || {
      'array': null,
      'cache': null,
      'criteria': null,
      'false': false,
      'index': 0,
      'null': false,
      'number': null,
      'object': null,
      'push': null,
      'string': null,
      'true': false,
      'undefined': false,
      'value': null
    };
  }

  /**
   * Releases the given array back to the array pool.
   *
   * @private
   * @param {Array} [array] The array to release.
   */
  function releaseArray(array) {
    array.length = 0;
    if (arrayPool.length < maxPoolSize) {
      arrayPool.push(array);
    }
  }

  /**
   * Releases the given object back to the object pool.
   *
   * @private
   * @param {Object} [object] The object to release.
   */
  function releaseObject(object) {
    var cache = object.cache;
    if (cache) {
      releaseObject(cache);
    }
    object.array = object.cache = object.criteria = object.object = object.number = object.string = object.value = null;
    if (objectPool.length < maxPoolSize) {
      objectPool.push(object);
    }
  }

  /**
   * Slices the `collection` from the `start` index up to, but not including,
   * the `end` index.
   *
   * Note: This function is used instead of `Array#slice` to support node lists
   * in IE < 9 and to ensure dense arrays are returned.
   *
   * @private
   * @param {Array|Object|string} collection The collection to slice.
   * @param {number} start The start index.
   * @param {number} end The end index.
   * @returns {Array} Returns the new array.
   */
  function slice(array, start, end) {
    start || (start = 0);
    if (typeof end == 'undefined') {
      end = array ? array.length : 0;
    }
    var index = -1,
        length = end - start || 0,
        result = Array(length < 0 ? 0 : length);

    while (++index < length) {
      result[index] = array[start + index];
    }
    return result;
  }

  /*--------------------------------------------------------------------------*/

  /**
   * Create a new `lodash` function using the given context object.
   *
   * @static
   * @memberOf _
   * @category Utilities
   * @param {Object} [context=root] The context object.
   * @returns {Function} Returns the `lodash` function.
   */
  function runInContext(context) {
    // Avoid issues with some ES3 environments that attempt to use values, named
    // after built-in constructors like `Object`, for the creation of literals.
    // ES5 clears this up by stating that literals must use built-in constructors.
    // See http://es5.github.io/#x11.1.5.
    context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root;

    /** Native constructor references */
    var Array = context.Array,
        Boolean = context.Boolean,
        Date = context.Date,
        Function = context.Function,
        Math = context.Math,
        Number = context.Number,
        Object = context.Object,
        RegExp = context.RegExp,
        String = context.String,
        TypeError = context.TypeError;

    /**
     * Used for `Array` method references.
     *
     * Normally `Array.prototype` would suffice, however, using an array literal
     * avoids issues in Narwhal.
     */
    var arrayRef = [];

    /** Used for native method references */
    var objectProto = Object.prototype;

    /** Used to restore the original `_` reference in `noConflict` */
    var oldDash = context._;

    /** Used to resolve the internal [[Class]] of values */
    var toString = objectProto.toString;

    /** Used to detect if a method is native */
    var reNative = RegExp('^' +
      String(toString)
        .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
        .replace(/toString| for [^\]]+/g, '.*?') + '$'
    );

    /** Native method shortcuts */
    var ceil = Math.ceil,
        clearTimeout = context.clearTimeout,
        floor = Math.floor,
        fnToString = Function.prototype.toString,
        getPrototypeOf = isNative(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf,
        hasOwnProperty = objectProto.hasOwnProperty,
        push = arrayRef.push,
        setTimeout = context.setTimeout,
        splice = arrayRef.splice,
        unshift = arrayRef.unshift;

    /** Used to set meta data on functions */
    var defineProperty = (function() {
      // IE 8 only accepts DOM elements
      try {
        var o = {},
            func = isNative(func = Object.defineProperty) && func,
            result = func(o, o, o) && func;
      } catch(e) { }
      return result;
    }());

    /* Native method shortcuts for methods with the same name as other `lodash` methods */
    var nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate,
        nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray,
        nativeIsFinite = context.isFinite,
        nativeIsNaN = context.isNaN,
        nativeKeys = isNative(nativeKeys = Object.keys) && nativeKeys,
        nativeMax = Math.max,
        nativeMin = Math.min,
        nativeParseInt = context.parseInt,
        nativeRandom = Math.random;

    /** Used to lookup a built-in constructor by [[Class]] */
    var ctorByClass = {};
    ctorByClass[arrayClass] = Array;
    ctorByClass[boolClass] = Boolean;
    ctorByClass[dateClass] = Date;
    ctorByClass[funcClass] = Function;
    ctorByClass[objectClass] = Object;
    ctorByClass[numberClass] = Number;
    ctorByClass[regexpClass] = RegExp;
    ctorByClass[stringClass] = String;

    /*--------------------------------------------------------------------------*/

    /**
     * Creates a `lodash` object which wraps the given value to enable intuitive
     * method chaining.
     *
     * In addition to Lo-Dash methods, wrappers also have the following `Array` methods:
     * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`,
     * and `unshift`
     *
     * Chaining is supported in custom builds as long as the `value` method is
     * implicitly or explicitly included in the build.
     *
     * The chainable wrapper functions are:
     * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`,
     * `compose`, `concat`, `countBy`, `create`, `createCallback`, `curry`,
     * `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`,
     * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`,
     * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`,
     * `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`,
     * `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`,
     * `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`,
     * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`,
     * `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`,
     * and `zip`
     *
     * The non-chainable wrapper functions are:
     * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `findIndex`,
     * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `has`, `identity`,
     * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`,
     * `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`,
     * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`,
     * `lastIndexOf`, `mixin`, `noConflict`, `parseInt`, `pop`, `random`, `reduce`,
     * `reduceRight`, `result`, `shift`, `size`, `some`, `sortedIndex`, `runInContext`,
     * `template`, `unescape`, `uniqueId`, and `value`
     *
     * The wrapper functions `first` and `last` return wrapped values when `n` is
     * provided, otherwise they return unwrapped values.
     *
     * Explicit chaining can be enabled by using the `_.chain` method.
     *
     * @name _
     * @constructor
     * @category Chaining
     * @param {*} value The value to wrap in a `lodash` instance.
     * @returns {Object} Returns a `lodash` instance.
     * @example
     *
     * var wrapped = _([1, 2, 3]);
     *
     * // returns an unwrapped value
     * wrapped.reduce(function(sum, num) {
     *   return sum + num;
     * });
     * // => 6
     *
     * // returns a wrapped value
     * var squares = wrapped.map(function(num) {
     *   return num * num;
     * });
     *
     * _.isArray(squares);
     * // => false
     *
     * _.isArray(squares.value());
     * // => true
     */
    function lodash(value) {
      // don't wrap if already wrapped, even if wrapped by a different `lodash` constructor
      return (value && typeof value == 'object' && !isArray(value) && hasOwnProperty.call(value, '__wrapped__'))
       ? value
       : new lodashWrapper(value);
    }

    /**
     * A fast path for creating `lodash` wrapper objects.
     *
     * @private
     * @param {*} value The value to wrap in a `lodash` instance.
     * @param {boolean} chainAll A flag to enable chaining for all methods
     * @returns {Object} Returns a `lodash` instance.
     */
    function lodashWrapper(value, chainAll) {
      this.__chain__ = !!chainAll;
      this.__wrapped__ = value;
    }
    // ensure `new lodashWrapper` is an instance of `lodash`
    lodashWrapper.prototype = lodash.prototype;

    /**
     * An object used to flag environments features.
     *
     * @static
     * @memberOf _
     * @type Object
     */
    var support = lodash.support = {};

    /**
     * Detect if functions can be decompiled by `Function#toString`
     * (all but PS3 and older Opera mobile browsers & avoided in Windows 8 apps).
     *
     * @memberOf _.support
     * @type boolean
     */
    support.funcDecomp = !isNative(context.WinRTError) && reThis.test(runInContext);

    /**
     * Detect if `Function#name` is supported (all but IE).
     *
     * @memberOf _.support
     * @type boolean
     */
    support.funcNames = typeof Function.name == 'string';

    /**
     * By default, the template delimiters used by Lo-Dash are similar to those in
     * embedded Ruby (ERB). Change the following template settings to use alternative
     * delimiters.
     *
     * @static
     * @memberOf _
     * @type Object
     */
    lodash.templateSettings = {

      /**
       * Used to detect `data` property values to be HTML-escaped.
       *
       * @memberOf _.templateSettings
       * @type RegExp
       */
      'escape': /<%-([\s\S]+?)%>/g,

      /**
       * Used to detect code to be evaluated.
       *
       * @memberOf _.templateSettings
       * @type RegExp
       */
      'evaluate': /<%([\s\S]+?)%>/g,

      /**
       * Used to detect `data` property values to inject.
       *
       * @memberOf _.templateSettings
       * @type RegExp
       */
      'interpolate': reInterpolate,

      /**
       * Used to reference the data object in the template text.
       *
       * @memberOf _.templateSettings
       * @type string
       */
      'variable': '',

      /**
       * Used to import variables into the compiled template.
       *
       * @memberOf _.templateSettings
       * @type Object
       */
      'imports': {

        /**
         * A reference to the `lodash` function.
         *
         * @memberOf _.templateSettings.imports
         * @type Function
         */
        '_': lodash
      }
    };

    /*--------------------------------------------------------------------------*/

    /**
     * The base implementation of `_.bind` that creates the bound function and
     * sets its meta data.
     *
     * @private
     * @param {Array} bindData The bind data array.
     * @returns {Function} Returns the new bound function.
     */
    function baseBind(bindData) {
      var func = bindData[0],
          partialArgs = bindData[2],
          thisArg = bindData[4];

      function bound() {
        // `Function#bind` spec
        // http://es5.github.io/#x15.3.4.5
        if (partialArgs) {
          // avoid `arguments` object deoptimizations by using `slice` instead
          // of `Array.prototype.slice.call` and not assigning `arguments` to a
          // variable as a ternary expression
          var args = slice(partialArgs);
          push.apply(args, arguments);
        }
        // mimic the constructor's `return` behavior
        // http://es5.github.io/#x13.2.2
        if (this instanceof bound) {
          // ensure `new bound` is an instance of `func`
          var thisBinding = baseCreate(func.prototype),
              result = func.apply(thisBinding, args || arguments);
          return isObject(result) ? result : thisBinding;
        }
        return func.apply(thisArg, args || arguments);
      }
      setBindData(bound, bindData);
      return bound;
    }

    /**
     * The base implementation of `_.clone` without argument juggling or support
     * for `thisArg` binding.
     *
     * @private
     * @param {*} value The value to clone.
     * @param {boolean} [isDeep=false] Specify a deep clone.
     * @param {Function} [callback] The function to customize cloning values.
     * @param {Array} [stackA=[]] Tracks traversed source objects.
     * @param {Array} [stackB=[]] Associates clones with source counterparts.
     * @returns {*} Returns the cloned value.
     */
    function baseClone(value, isDeep, callback, stackA, stackB) {
      if (callback) {
        var result = callback(value);
        if (typeof result != 'undefined') {
          return result;
        }
      }
      // inspect [[Class]]
      var isObj = isObject(value);
      if (isObj) {
        var className = toString.call(value);
        if (!cloneableClasses[className]) {
          return value;
        }
        var ctor = ctorByClass[className];
        switch (className) {
          case boolClass:
          case dateClass:
            return new ctor(+value);

          case numberClass:
          case stringClass:
            return new ctor(value);

          case regexpClass:
            result = ctor(value.source, reFlags.exec(value));
            result.lastIndex = value.lastIndex;
            return result;
        }
      } else {
        return value;
      }
      var isArr = isArray(value);
      if (isDeep) {
        // check for circular references and return corresponding clone
        var initedStack = !stackA;
        stackA || (stackA = getArray());
        stackB || (stackB = getArray());

        var length = stackA.length;
        while (length--) {
          if (stackA[length] == value) {
            return stackB[length];
          }
        }
        result = isArr ? ctor(value.length) : {};
      }
      else {
        result = isArr ? slice(value) : assign({}, value);
      }
      // add array properties assigned by `RegExp#exec`
      if (isArr) {
        if (hasOwnProperty.call(value, 'index')) {
          result.index = value.index;
        }
        if (hasOwnProperty.call(value, 'input')) {
          result.input = value.input;
        }
      }
      // exit for shallow clone
      if (!isDeep) {
        return result;
      }
      // add the source value to the stack of traversed objects
      // and associate it with its clone
      stackA.push(value);
      stackB.push(result);

      // recursively populate clone (susceptible to call stack limits)
      (isArr ? forEach : forOwn)(value, function(objValue, key) {
        result[key] = baseClone(objValue, isDeep, callback, stackA, stackB);
      });

      if (initedStack) {
        releaseArray(stackA);
        releaseArray(stackB);
      }
      return result;
    }

    /**
     * The base implementation of `_.create` without support for assigning
     * properties to the created object.
     *
     * @private
     * @param {Object} prototype The object to inherit from.
     * @returns {Object} Returns the new object.
     */
    function baseCreate(prototype, properties) {
      return isObject(prototype) ? nativeCreate(prototype) : {};
    }
    // fallback for browsers without `Object.create`
    if (!nativeCreate) {
      baseCreate = (function() {
        function Object() {}
        return function(prototype) {
          if (isObject(prototype)) {
            Object.prototype = prototype;
            var result = new Object;
            Object.prototype = null;
          }
          return result || context.Object();
        };
      }());
    }

    /**
     * The base implementation of `_.createCallback` without support for creating
     * "_.pluck" or "_.where" style callbacks.
     *
     * @private
     * @param {*} [func=identity] The value to convert to a callback.
     * @param {*} [thisArg] The `this` binding of the created callback.
     * @param {number} [argCount] The number of arguments the callback accepts.
     * @returns {Function} Returns a callback function.
     */
    function baseCreateCallback(func, thisArg, argCount) {
      if (typeof func != 'function') {
        return identity;
      }
      // exit early for no `thisArg` or already bound by `Function#bind`
      if (typeof thisArg == 'undefined' || !('prototype' in func)) {
        return func;
      }
      var bindData = func.__bindData__;
      if (typeof bindData == 'undefined') {
        if (support.funcNames) {
          bindData = !func.name;
        }
        bindData = bindData || !support.funcDecomp;
        if (!bindData) {
          var source = fnToString.call(func);
          if (!support.funcNames) {
            bindData = !reFuncName.test(source);
          }
          if (!bindData) {
            // checks if `func` references the `this` keyword and stores the result
            bindData = reThis.test(source);
            setBindData(func, bindData);
          }
        }
      }
      // exit early if there are no `this` references or `func` is bound
      if (bindData === false || (bindData !== true && bindData[1] & 1)) {
        return func;
      }
      switch (argCount) {
        case 1: return function(value) {
          return func.call(thisArg, value);
        };
        case 2: return function(a, b) {
          return func.call(thisArg, a, b);
        };
        case 3: return function(value, index, collection) {
          return func.call(thisArg, value, index, collection);
        };
        case 4: return function(accumulator, value, index, collection) {
          return func.call(thisArg, accumulator, value, index, collection);
        };
      }
      return bind(func, thisArg);
    }

    /**
     * The base implementation of `createWrapper` that creates the wrapper and
     * sets its meta data.
     *
     * @private
     * @param {Array} bindData The bind data array.
     * @returns {Function} Returns the new function.
     */
    function baseCreateWrapper(bindData) {
      var func = bindData[0],
          bitmask = bindData[1],
          partialArgs = bindData[2],
          partialRightArgs = bindData[3],
          thisArg = bindData[4],
          arity = bindData[5];

      var isBind = bitmask & 1,
          isBindKey = bitmask & 2,
          isCurry = bitmask & 4,
          isCurryBound = bitmask & 8,
          key = func;

      function bound() {
        var thisBinding = isBind ? thisArg : this;
        if (partialArgs) {
          var args = slice(partialArgs);
          push.apply(args, arguments);
        }
        if (partialRightArgs || isCurry) {
          args || (args = slice(arguments));
          if (partialRightArgs) {
            push.apply(args, partialRightArgs);
          }
          if (isCurry && args.length < arity) {
            bitmask |= 16 & ~32;
            return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]);
          }
        }
        args || (args = arguments);
        if (isBindKey) {
          func = thisBinding[key];
        }
        if (this instanceof bound) {
          thisBinding = baseCreate(func.prototype);
          var result = func.apply(thisBinding, args);
          return isObject(result) ? result : thisBinding;
        }
        return func.apply(thisBinding, args);
      }
      setBindData(bound, bindData);
      return bound;
    }

    /**
     * The base implementation of `_.difference` that accepts a single array
     * of values to exclude.
     *
     * @private
     * @param {Array} array The array to process.
     * @param {Array} [values] The array of values to exclude.
     * @returns {Array} Returns a new array of filtered values.
     */
    function baseDifference(array, values) {
      var index = -1,
          indexOf = getIndexOf(),
          length = array ? array.length : 0,
          isLarge = length >= largeArraySize && indexOf === baseIndexOf,
          result = [];

      if (isLarge) {
        var cache = createCache(values);
        if (cache) {
          indexOf = cacheIndexOf;
          values = cache;
        } else {
          isLarge = false;
        }
      }
      while (++index < length) {
        var value = array[index];
        if (indexOf(values, value) < 0) {
          result.push(value);
        }
      }
      if (isLarge) {
        releaseObject(values);
      }
      return result;
    }

    /**
     * The base implementation of `_.flatten` without support for callback
     * shorthands or `thisArg` binding.
     *
     * @private
     * @param {Array} array The array to flatten.
     * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
     * @param {boolean} [isStrict=false] A flag to restrict flattening to arrays and `arguments` objects.
     * @param {number} [fromIndex=0] The index to start from.
     * @returns {Array} Returns a new flattened array.
     */
    function baseFlatten(array, isShallow, isStrict, fromIndex) {
      var index = (fromIndex || 0) - 1,
          length = array ? array.length : 0,
          result = [];

      while (++index < length) {
        var value = array[index];

        if (value && typeof value == 'object' && typeof value.length == 'number'
            && (isArray(value) || isArguments(value))) {
          // recursively flatten arrays (susceptible to call stack limits)
          if (!isShallow) {
            value = baseFlatten(value, isShallow, isStrict);
          }
          var valIndex = -1,
              valLength = value.length,
              resIndex = result.length;

          result.length += valLength;
          while (++valIndex < valLength) {
            result[resIndex++] = value[valIndex];
          }
        } else if (!isStrict) {
          result.push(value);
        }
      }
      return result;
    }

    /**
     * The base implementation of `_.isEqual`, without support for `thisArg` binding,
     * that allows partial "_.where" style comparisons.
     *
     * @private
     * @param {*} a The value to compare.
     * @param {*} b The other value to compare.
     * @param {Function} [callback] The function to customize comparing values.
     * @param {Function} [isWhere=false] A flag to indicate performing partial comparisons.
     * @param {Array} [stackA=[]] Tracks traversed `a` objects.
     * @param {Array} [stackB=[]] Tracks traversed `b` objects.
     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
     */
    function baseIsEqual(a, b, callback, isWhere, stackA, stackB) {
      // used to indicate that when comparing objects, `a` has at least the properties of `b`
      if (callback) {
        var result = callback(a, b);
        if (typeof result != 'undefined') {
          return !!result;
        }
      }
      // exit early for identical values
      if (a === b) {
        // treat `+0` vs. `-0` as not equal
        return a !== 0 || (1 / a == 1 / b);
      }
      var type = typeof a,
          otherType = typeof b;

      // exit early for unlike primitive values
      if (a === a &&
          !(a && objectTypes[type]) &&
          !(b && objectTypes[otherType])) {
        return false;
      }
      // exit early for `null` and `undefined` avoiding ES3's Function#call behavior
      // http://es5.github.io/#x15.3.4.4
      if (a == null || b == null) {
        return a === b;
      }
      // compare [[Class]] names
      var className = toString.call(a),
          otherClass = toString.call(b);

      if (className == argsClass) {
        className = objectClass;
      }
      if (otherClass == argsClass) {
        otherClass = objectClass;
      }
      if (className != otherClass) {
        return false;
      }
      switch (className) {
        case boolClass:
        case dateClass:
          // coerce dates and booleans to numbers, dates to milliseconds and booleans
          // to `1` or `0` treating invalid dates coerced to `NaN` as not equal
          return +a == +b;

        case numberClass:
          // treat `NaN` vs. `NaN` as equal
          return (a != +a)
            ? b != +b
            // but treat `+0` vs. `-0` as not equal
            : (a == 0 ? (1 / a == 1 / b) : a == +b);

        case regexpClass:
        case stringClass:
          // coerce regexes to strings (http://es5.github.io/#x15.10.6.4)
          // treat string primitives and their corresponding object instances as equal
          return a == String(b);
      }
      var isArr = className == arrayClass;
      if (!isArr) {
        // unwrap any `lodash` wrapped values
        var aWrapped = hasOwnProperty.call(a, '__wrapped__'),
            bWrapped = hasOwnProperty.call(b, '__wrapped__');

        if (aWrapped || bWrapped) {
          return baseIsEqual(aWrapped ? a.__wrapped__ : a, bWrapped ? b.__wrapped__ : b, callback, isWhere, stackA, stackB);
        }
        // exit for functions and DOM nodes
        if (className != objectClass) {
          return false;
        }
        // in older versions of Opera, `arguments` objects have `Array` constructors
        var ctorA = a.constructor,
            ctorB = b.constructor;

        // non `Object` object instances with different constructors are not equal
        if (ctorA != ctorB &&
              !(isFunction(ctorA) && ctorA instanceof ctorA && isFunction(ctorB) && ctorB instanceof ctorB) &&
              ('constructor' in a && 'constructor' in b)
            ) {
          return false;
        }
      }
      // assume cyclic structures are equal
      // the algorithm for detecting cyclic structures is adapted from ES 5.1
      // section 15.12.3, abstract operation `JO` (http://es5.github.io/#x15.12.3)
      var initedStack = !stackA;
      stackA || (stackA = getArray());
      stackB || (stackB = getArray());

      var length = stackA.length;
      while (length--) {
        if (stackA[length] == a) {
          return stackB[length] == b;
        }
      }
      var size = 0;
      result = true;

      // add `a` and `b` to the stack of traversed objects
      stackA.push(a);
      stackB.push(b);

      // recursively compare objects and arrays (susceptible to call stack limits)
      if (isArr) {
        // compare lengths to determine if a deep comparison is necessary
        length = a.length;
        size = b.length;
        result = size == length;

        if (result || isWhere) {
          // deep compare the contents, ignoring non-numeric properties
          while (size--) {
            var index = length,
                value = b[size];

            if (isWhere) {
              while (index--) {
                if ((result = baseIsEqual(a[index], value, callback, isWhere, stackA, stackB))) {
                  break;
                }
              }
            } else if (!(result = baseIsEqual(a[size], value, callback, isWhere, stackA, stackB))) {
              break;
            }
          }
        }
      }
      else {
        // deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys`
        // which, in this case, is more costly
        forIn(b, function(value, key, b) {
          if (hasOwnProperty.call(b, key)) {
            // count the number of properties.
            size++;
            // deep compare each property value.
            return (result = hasOwnProperty.call(a, key) && baseIsEqual(a[key], value, callback, isWhere, stackA, stackB));
          }
        });

        if (result && !isWhere) {
          // ensure both objects have the same number of properties
          forIn(a, function(value, key, a) {
            if (hasOwnProperty.call(a, key)) {
              // `size` will be `-1` if `a` has more properties than `b`
              return (result = --size > -1);
            }
          });
        }
      }
      stackA.pop();
      stackB.pop();

      if (initedStack) {
        releaseArray(stackA);
        releaseArray(stackB);
      }
      return result;
    }

    /**
     * The base implementation of `_.merge` without argument juggling or support
     * for `thisArg` binding.
     *
     * @private
     * @param {Object} object The destination object.
     * @param {Object} source The source object.
     * @param {Function} [callback] The function to customize merging properties.
     * @param {Array} [stackA=[]] Tracks traversed source objects.
     * @param {Array} [stackB=[]] Associates values with source counterparts.
     */
    function baseMerge(object, source, callback, stackA, stackB) {
      (isArray(source) ? forEach : forOwn)(source, function(source, key) {
        var found,
            isArr,
            result = source,
            value = object[key];

        if (source && ((isArr = isArray(source)) || isPlainObject(source))) {
          // avoid merging previously merged cyclic sources
          var stackLength = stackA.length;
          while (stackLength--) {
            if ((found = stackA[stackLength] == source)) {
              value = stackB[stackLength];
              break;
            }
          }
          if (!found) {
            var isShallow;
            if (callback) {
              result = callback(value, source);
              if ((isShallow = typeof result != 'undefined')) {
                value = result;
              }
            }
            if (!isShallow) {
              value = isArr
                ? (isArray(value) ? value : [])
                : (isPlainObject(value) ? value : {});
            }
            // add `source` and associated `value` to the stack of traversed objects
            stackA.push(source);
            stackB.push(value);

            // recursively merge objects and arrays (susceptible to call stack limits)
            if (!isShallow) {
              baseMerge(value, source, callback, stackA, stackB);
            }
          }
        }
        else {
          if (callback) {
            result = callback(value, source);
            if (typeof result == 'undefined') {
              result = source;
            }
          }
          if (typeof result != 'undefined') {
            value = result;
          }
        }
        object[key] = value;
      });
    }

    /**
     * The base implementation of `_.random` without argument juggling or support
     * for returning floating-point numbers.
     *
     * @private
     * @param {number} min The minimum possible value.
     * @param {number} max The maximum possible value.
     * @returns {number} Returns a random number.
     */
    function baseRandom(min, max) {
      return min + floor(nativeRandom() * (max - min + 1));
    }

    /**
     * The base implementation of `_.uniq` without support for callback shorthands
     * or `thisArg` binding.
     *
     * @private
     * @param {Array} array The array to process.
     * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
     * @param {Function} [callback] The function called per iteration.
     * @returns {Array} Returns a duplicate-value-free array.
     */
    function baseUniq(array, isSorted, callback) {
      var index = -1,
          indexOf = getIndexOf(),
          length = array ? array.length : 0,
          result = [];

      var isLarge = !isSorted && length >= largeArraySize && indexOf === baseIndexOf,
          seen = (callback || isLarge) ? getArray() : result;

      if (isLarge) {
        var cache = createCache(seen);
        indexOf = cacheIndexOf;
        seen = cache;
      }
      while (++index < length) {
        var value = array[index],
            computed = callback ? callback(value, index, array) : value;

        if (isSorted
              ? !index || seen[seen.length - 1] !== computed
              : indexOf(seen, computed) < 0
            ) {
          if (callback || isLarge) {
            seen.push(computed);
          }
          result.push(value);
        }
      }
      if (isLarge) {
        releaseArray(seen.array);
        releaseObject(seen);
      } else if (callback) {
        releaseArray(seen);
      }
      return result;
    }

    /**
     * Creates a function that aggregates a collection, creating an object composed
     * of keys generated from the results of running each element of the collection
     * through a callback. The given `setter` function sets the keys and values
     * of the composed object.
     *
     * @private
     * @param {Function} setter The setter function.
     * @returns {Function} Returns the new aggregator function.
     */
    function createAggregator(setter) {
      return function(collection, callback, thisArg) {
        var result = {};
        callback = lodash.createCallback(callback, thisArg, 3);

        var index = -1,
            length = collection ? collection.length : 0;

        if (typeof length == 'number') {
          while (++index < length) {
            var value = collection[index];
            setter(result, value, callback(value, index, collection), collection);
          }
        } else {
          forOwn(collection, function(value, key, collection) {
            setter(result, value, callback(value, key, collection), collection);
          });
        }
        return result;
      };
    }

    /**
     * Creates a function that, when called, either curries or invokes `func`
     * with an optional `this` binding and partially applied arguments.
     *
     * @private
     * @param {Function|string} func The function or method name to reference.
     * @param {number} bitmask The bitmask of method flags to compose.
     *  The bitmask may be composed of the following flags:
     *  1 - `_.bind`
     *  2 - `_.bindKey`
     *  4 - `_.curry`
     *  8 - `_.curry` (bound)
     *  16 - `_.partial`
     *  32 - `_.partialRight`
     * @param {Array} [partialArgs] An array of arguments to prepend to those
     *  provided to the new function.
     * @param {Array} [partialRightArgs] An array of arguments to append to those
     *  provided to the new function.
     * @param {*} [thisArg] The `this` binding of `func`.
     * @param {number} [arity] The arity of `func`.
     * @returns {Function} Returns the new function.
     */
    function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) {
      var isBind = bitmask & 1,
          isBindKey = bitmask & 2,
          isCurry = bitmask & 4,
          isCurryBound = bitmask & 8,
          isPartial = bitmask & 16,
          isPartialRight = bitmask & 32;

      if (!isBindKey && !isFunction(func)) {
        throw new TypeError;
      }
      if (isPartial && !partialArgs.length) {
        bitmask &= ~16;
        isPartial = partialArgs = false;
      }
      if (isPartialRight && !partialRightArgs.length) {
        bitmask &= ~32;
        isPartialRight = partialRightArgs = false;
      }
      var bindData = func && func.__bindData__;
      if (bindData && bindData !== true) {
        // clone `bindData`
        bindData = slice(bindData);
        if (bindData[2]) {
          bindData[2] = slice(bindData[2]);
        }
        if (bindData[3]) {
          bindData[3] = slice(bindData[3]);
        }
        // set `thisBinding` is not previously bound
        if (isBind && !(bindData[1] & 1)) {
          bindData[4] = thisArg;
        }
        // set if previously bound but not currently (subsequent curried functions)
        if (!isBind && bindData[1] & 1) {
          bitmask |= 8;
        }
        // set curried arity if not yet set
        if (isCurry && !(bindData[1] & 4)) {
          bindData[5] = arity;
        }
        // append partial left arguments
        if (isPartial) {
          push.apply(bindData[2] || (bindData[2] = []), partialArgs);
        }
        // append partial right arguments
        if (isPartialRight) {
          unshift.apply(bindData[3] || (bindData[3] = []), partialRightArgs);
        }
        // merge flags
        bindData[1] |= bitmask;
        return createWrapper.apply(null, bindData);
      }
      // fast path for `_.bind`
      var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper;
      return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]);
    }

    /**
     * Used by `escape` to convert characters to HTML entities.
     *
     * @private
     * @param {string} match The matched character to escape.
     * @returns {string} Returns the escaped character.
     */
    function escapeHtmlChar(match) {
      return htmlEscapes[match];
    }

    /**
     * Gets the appropriate "indexOf" function. If the `_.indexOf` method is
     * customized, this method returns the custom method, otherwise it returns
     * the `baseIndexOf` function.
     *
     * @private
     * @returns {Function} Returns the "indexOf" function.
     */
    function getIndexOf() {
      var result = (result = lodash.indexOf) === indexOf ? baseIndexOf : result;
      return result;
    }

    /**
     * Checks if `value` is a native function.
     *
     * @private
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if the `value` is a native function, else `false`.
     */
    function isNative(value) {
      return typeof value == 'function' && reNative.test(value);
    }

    /**
     * Sets `this` binding data on a given function.
     *
     * @private
     * @param {Function} func The function to set data on.
     * @param {Array} value The data array to set.
     */
    var setBindData = !defineProperty ? noop : function(func, value) {
      descriptor.value = value;
      defineProperty(func, '__bindData__', descriptor);
    };

    /**
     * A fallback implementation of `isPlainObject` which checks if a given value
     * is an object created by the `Object` constructor, assuming objects created
     * by the `Object` constructor have no inherited enumerable properties and that
     * there are no `Object.prototype` extensions.
     *
     * @private
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
     */
    function shimIsPlainObject(value) {
      var ctor,
          result;

      // avoid non Object objects, `arguments` objects, and DOM elements
      if (!(value && toString.call(value) == objectClass) ||
          (ctor = value.constructor, isFunction(ctor) && !(ctor instanceof ctor))) {
        return false;
      }
      // In most environments an object's own properties are iterated before
      // its inherited properties. If the last iterated property is an object's
      // own property then there are no inherited enumerable properties.
      forIn(value, function(value, key) {
        result = key;
      });
      return typeof result == 'undefined' || hasOwnProperty.call(value, result);
    }

    /**
     * Used by `unescape` to convert HTML entities to characters.
     *
     * @private
     * @param {string} match The matched character to unescape.
     * @returns {string} Returns the unescaped character.
     */
    function unescapeHtmlChar(match) {
      return htmlUnescapes[match];
    }

    /*--------------------------------------------------------------------------*/

    /**
     * Checks if `value` is an `arguments` object.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if the `value` is an `arguments` object, else `false`.
     * @example
     *
     * (function() { return _.isArguments(arguments); })(1, 2, 3);
     * // => true
     *
     * _.isArguments([1, 2, 3]);
     * // => false
     */
    function isArguments(value) {
      return value && typeof value == 'object' && typeof value.length == 'number' &&
        toString.call(value) == argsClass || false;
    }

    /**
     * Checks if `value` is an array.
     *
     * @static
     * @memberOf _
     * @type Function
     * @category Objects
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if the `value` is an array, else `false`.
     * @example
     *
     * (function() { return _.isArray(arguments); })();
     * // => false
     *
     * _.isArray([1, 2, 3]);
     * // => true
     */
    var isArray = nativeIsArray || function(value) {
      return value && typeof value == 'object' && typeof value.length == 'number' &&
        toString.call(value) == arrayClass || false;
    };

    /**
     * A fallback implementation of `Object.keys` which produces an array of the
     * given object's own enumerable property names.
     *
     * @private
     * @type Function
     * @param {Object} object The object to inspect.
     * @returns {Array} Returns an array of property names.
     */
    var shimKeys = function(object) {
      var index, iterable = object, result = [];
      if (!iterable) return result;
      if (!(objectTypes[typeof object])) return result;
        for (index in iterable) {
          if (hasOwnProperty.call(iterable, index)) {
            result.push(index);
          }
        }
      return result
    };

    /**
     * Creates an array composed of the own enumerable property names of an object.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {Object} object The object to inspect.
     * @returns {Array} Returns an array of property names.
     * @example
     *
     * _.keys({ 'one': 1, 'two': 2, 'three': 3 });
     * // => ['one', 'two', 'three'] (property order is not guaranteed across environments)
     */
    var keys = !nativeKeys ? shimKeys : function(object) {
      if (!isObject(object)) {
        return [];
      }
      return nativeKeys(object);
    };

    /**
     * Used to convert characters to HTML entities:
     *
     * Though the `>` character is escaped for symmetry, characters like `>` and `/`
     * don't require escaping in HTML and have no special meaning unless they're part
     * of a tag or an unquoted attribute value.
     * http://mathiasbynens.be/notes/ambiguous-ampersands (under "semi-related fun fact")
     */
    var htmlEscapes = {
      '&': '&amp;',
      '<': '&lt;',
      '>': '&gt;',
      '"': '&quot;',
      "'": '&#39;'
    };

    /** Used to convert HTML entities to characters */
    var htmlUnescapes = invert(htmlEscapes);

    /** Used to match HTML entities and HTML characters */
    var reEscapedHtml = RegExp('(' + keys(htmlUnescapes).join('|') + ')', 'g'),
        reUnescapedHtml = RegExp('[' + keys(htmlEscapes).join('') + ']', 'g');

    /*--------------------------------------------------------------------------*/

    /**
     * Assigns own enumerable properties of source object(s) to the destination
     * object. Subsequent sources will overwrite property assignments of previous
     * sources. If a callback is provided it will be executed to produce the
     * assigned values. The callback is bound to `thisArg` and invoked with two
     * arguments; (objectValue, sourceValue).
     *
     * @static
     * @memberOf _
     * @type Function
     * @alias extend
     * @category Objects
     * @param {Object} object The destination object.
     * @param {...Object} [source] The source objects.
     * @param {Function} [callback] The function to customize assigning values.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Object} Returns the destination object.
     * @example
     *
     * _.assign({ 'name': 'fred' }, { 'employer': 'slate' });
     * // => { 'name': 'fred', 'employer': 'slate' }
     *
     * var defaults = _.partialRight(_.assign, function(a, b) {
     *   return typeof a == 'undefined' ? b : a;
     * });
     *
     * var object = { 'name': 'barney' };
     * defaults(object, { 'name': 'fred', 'employer': 'slate' });
     * // => { 'name': 'barney', 'employer': 'slate' }
     */
    var assign = function(object, source, guard) {
      var index, iterable = object, result = iterable;
      if (!iterable) return result;
      var args = arguments,
          argsIndex = 0,
          argsLength = typeof guard == 'number' ? 2 : args.length;
      if (argsLength > 3 && typeof args[argsLength - 2] == 'function') {
        var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);
      } else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {
        callback = args[--argsLength];
      }
      while (++argsIndex < argsLength) {
        iterable = args[argsIndex];
        if (iterable && objectTypes[typeof iterable]) {
        var ownIndex = -1,
            ownProps = objectTypes[typeof iterable] && keys(iterable),
            length = ownProps ? ownProps.length : 0;

        while (++ownIndex < length) {
          index = ownProps[ownIndex];
          result[index] = callback ? callback(result[index], iterable[index]) : iterable[index];
        }
        }
      }
      return result
    };

    /**
     * Creates a clone of `value`. If `isDeep` is `true` nested objects will also
     * be cloned, otherwise they will be assigned by reference. If a callback
     * is provided it will be executed to produce the cloned values. If the
     * callback returns `undefined` cloning will be handled by the method instead.
     * The callback is bound to `thisArg` and invoked with one argument; (value).
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {*} value The value to clone.
     * @param {boolean} [isDeep=false] Specify a deep clone.
     * @param {Function} [callback] The function to customize cloning values.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {*} Returns the cloned value.
     * @example
     *
     * var characters = [
     *   { 'name': 'barney', 'age': 36 },
     *   { 'name': 'fred',   'age': 40 }
     * ];
     *
     * var shallow = _.clone(characters);
     * shallow[0] === characters[0];
     * // => true
     *
     * var deep = _.clone(characters, true);
     * deep[0] === characters[0];
     * // => false
     *
     * _.mixin({
     *   'clone': _.partialRight(_.clone, function(value) {
     *     return _.isElement(value) ? value.cloneNode(false) : undefined;
     *   })
     * });
     *
     * var clone = _.clone(document.body);
     * clone.childNodes.length;
     * // => 0
     */
    function clone(value, isDeep, callback, thisArg) {
      // allows working with "Collections" methods without using their `index`
      // and `collection` arguments for `isDeep` and `callback`
      if (typeof isDeep != 'boolean' && isDeep != null) {
        thisArg = callback;
        callback = isDeep;
        isDeep = false;
      }
      return baseClone(value, isDeep, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
    }

    /**
     * Creates a deep clone of `value`. If a callback is provided it will be
     * executed to produce the cloned values. If the callback returns `undefined`
     * cloning will be handled by the method instead. The callback is bound to
     * `thisArg` and invoked with one argument; (value).
     *
     * Note: This method is loosely based on the structured clone algorithm. Functions
     * and DOM nodes are **not** cloned. The enumerable properties of `arguments` objects and
     * objects created by constructors other than `Object` are cloned to plain `Object` objects.
     * See http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {*} value The value to deep clone.
     * @param {Function} [callback] The function to customize cloning values.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {*} Returns the deep cloned value.
     * @example
     *
     * var characters = [
     *   { 'name': 'barney', 'age': 36 },
     *   { 'name': 'fred',   'age': 40 }
     * ];
     *
     * var deep = _.cloneDeep(characters);
     * deep[0] === characters[0];
     * // => false
     *
     * var view = {
     *   'label': 'docs',
     *   'node': element
     * };
     *
     * var clone = _.cloneDeep(view, function(value) {
     *   return _.isElement(value) ? value.cloneNode(true) : undefined;
     * });
     *
     * clone.node == view.node;
     * // => false
     */
    function cloneDeep(value, callback, thisArg) {
      return baseClone(value, true, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
    }

    /**
     * Creates an object that inherits from the given `prototype` object. If a
     * `properties` object is provided its own enumerable properties are assigned
     * to the created object.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {Object} prototype The object to inherit from.
     * @param {Object} [properties] The properties to assign to the object.
     * @returns {Object} Returns the new object.
     * @example
     *
     * function Shape() {
     *   this.x = 0;
     *   this.y = 0;
     * }
     *
     * function Circle() {
     *   Shape.call(this);
     * }
     *
     * Circle.prototype = _.create(Shape.prototype, { 'constructor': Circle });
     *
     * var circle = new Circle;
     * circle instanceof Circle;
     * // => true
     *
     * circle instanceof Shape;
     * // => true
     */
    function create(prototype, properties) {
      var result = baseCreate(prototype);
      return properties ? assign(result, properties) : result;
    }

    /**
     * Assigns own enumerable properties of source object(s) to the destination
     * object for all destination properties that resolve to `undefined`. Once a
     * property is set, additional defaults of the same property will be ignored.
     *
     * @static
     * @memberOf _
     * @type Function
     * @category Objects
     * @param {Object} object The destination object.
     * @param {...Object} [source] The source objects.
     * @param- {Object} [guard] Allows working with `_.reduce` without using its
     *  `key` and `object` arguments as sources.
     * @returns {Object} Returns the destination object.
     * @example
     *
     * var object = { 'name': 'barney' };
     * _.defaults(object, { 'name': 'fred', 'employer': 'slate' });
     * // => { 'name': 'barney', 'employer': 'slate' }
     */
    var defaults = function(object, source, guard) {
      var index, iterable = object, result = iterable;
      if (!iterable) return result;
      var args = arguments,
          argsIndex = 0,
          argsLength = typeof guard == 'number' ? 2 : args.length;
      while (++argsIndex < argsLength) {
        iterable = args[argsIndex];
        if (iterable && objectTypes[typeof iterable]) {
        var ownIndex = -1,
            ownProps = objectTypes[typeof iterable] && keys(iterable),
            length = ownProps ? ownProps.length : 0;

        while (++ownIndex < length) {
          index = ownProps[ownIndex];
          if (typeof result[index] == 'undefined') result[index] = iterable[index];
        }
        }
      }
      return result
    };

    /**
     * This method is like `_.findIndex` except that it returns the key of the
     * first element that passes the callback check, instead of the element itself.
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {Object} object The object to search.
     * @param {Function|Object|string} [callback=identity] The function called per
     *  iteration. If a property name or object is provided it will be used to
     *  create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {string|undefined} Returns the key of the found element, else `undefined`.
     * @example
     *
     * var characters = {
     *   'barney': {  'age': 36, 'blocked': false },
     *   'fred': {    'age': 40, 'blocked': true },
     *   'pebbles': { 'age': 1,  'blocked': false }
     * };
     *
     * _.findKey(characters, function(chr) {
     *   return chr.age < 40;
     * });
     * // => 'barney' (property order is not guaranteed across environments)
     *
     * // using "_.where" callback shorthand
     * _.findKey(characters, { 'age': 1 });
     * // => 'pebbles'
     *
     * // using "_.pluck" callback shorthand
     * _.findKey(characters, 'blocked');
     * // => 'fred'
     */
    function findKey(object, callback, thisArg) {
      var result;
      callback = lodash.createCallback(callback, thisArg, 3);
      forOwn(object, function(value, key, object) {
        if (callback(value, key, object)) {
          result = key;
          return false;
        }
      });
      return result;
    }

    /**
     * This method is like `_.findKey` except that it iterates over elements
     * of a `collection` in the opposite order.
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {Object} object The object to search.
     * @param {Function|Object|string} [callback=identity] The function called per
     *  iteration. If a property name or object is provided it will be used to
     *  create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {string|undefined} Returns the key of the found element, else `undefined`.
     * @example
     *
     * var characters = {
     *   'barney': {  'age': 36, 'blocked': true },
     *   'fred': {    'age': 40, 'blocked': false },
     *   'pebbles': { 'age': 1,  'blocked': true }
     * };
     *
     * _.findLastKey(characters, function(chr) {
     *   return chr.age < 40;
     * });
     * // => returns `pebbles`, assuming `_.findKey` returns `barney`
     *
     * // using "_.where" callback shorthand
     * _.findLastKey(characters, { 'age': 40 });
     * // => 'fred'
     *
     * // using "_.pluck" callback shorthand
     * _.findLastKey(characters, 'blocked');
     * // => 'pebbles'
     */
    function findLastKey(object, callback, thisArg) {
      var result;
      callback = lodash.createCallback(callback, thisArg, 3);
      forOwnRight(object, function(value, key, object) {
        if (callback(value, key, object)) {
          result = key;
          return false;
        }
      });
      return result;
    }

    /**
     * Iterates over own and inherited enumerable properties of an object,
     * executing the callback for each property. The callback is bound to `thisArg`
     * and invoked with three arguments; (value, key, object). Callbacks may exit
     * iteration early by explicitly returning `false`.
     *
     * @static
     * @memberOf _
     * @type Function
     * @category Objects
     * @param {Object} object The object to iterate over.
     * @param {Function} [callback=identity] The function called per iteration.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Object} Returns `object`.
     * @example
     *
     * function Shape() {
     *   this.x = 0;
     *   this.y = 0;
     * }
     *
     * Shape.prototype.move = function(x, y) {
     *   this.x += x;
     *   this.y += y;
     * };
     *
     * _.forIn(new Shape, function(value, key) {
     *   console.log(key);
     * });
     * // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments)
     */
    var forIn = function(collection, callback, thisArg) {
      var index, iterable = collection, result = iterable;
      if (!iterable) return result;
      if (!objectTypes[typeof iterable]) return result;
      callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
        for (index in iterable) {
          if (callback(iterable[index], index, collection) === false) return result;
        }
      return result
    };

    /**
     * This method is like `_.forIn` except that it iterates over elements
     * of a `collection` in the opposite order.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {Object} object The object to iterate over.
     * @param {Function} [callback=identity] The function called per iteration.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Object} Returns `object`.
     * @example
     *
     * function Shape() {
     *   this.x = 0;
     *   this.y = 0;
     * }
     *
     * Shape.prototype.move = function(x, y) {
     *   this.x += x;
     *   this.y += y;
     * };
     *
     * _.forInRight(new Shape, function(value, key) {
     *   console.log(key);
     * });
     * // => logs 'move', 'y', and 'x' assuming `_.forIn ` logs 'x', 'y', and 'move'
     */
    function forInRight(object, callback, thisArg) {
      var pairs = [];

      forIn(object, function(value, key) {
        pairs.push(key, value);
      });

      var length = pairs.length;
      callback = baseCreateCallback(callback, thisArg, 3);
      while (length--) {
        if (callback(pairs[length--], pairs[length], object) === false) {
          break;
        }
      }
      return object;
    }

    /**
     * Iterates over own enumerable properties of an object, executing the callback
     * for each property. The callback is bound to `thisArg` and invoked with three
     * arguments; (value, key, object). Callbacks may exit iteration early by
     * explicitly returning `false`.
     *
     * @static
     * @memberOf _
     * @type Function
     * @category Objects
     * @param {Object} object The object to iterate over.
     * @param {Function} [callback=identity] The function called per iteration.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Object} Returns `object`.
     * @example
     *
     * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
     *   console.log(key);
     * });
     * // => logs '0', '1', and 'length' (property order is not guaranteed across environments)
     */
    var forOwn = function(collection, callback, thisArg) {
      var index, iterable = collection, result = iterable;
      if (!iterable) return result;
      if (!objectTypes[typeof iterable]) return result;
      callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
        var ownIndex = -1,
            ownProps = objectTypes[typeof iterable] && keys(iterable),
            length = ownProps ? ownProps.length : 0;

        while (++ownIndex < length) {
          index = ownProps[ownIndex];
          if (callback(iterable[index], index, collection) === false) return result;
        }
      return result
    };

    /**
     * This method is like `_.forOwn` except that it iterates over elements
     * of a `collection` in the opposite order.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {Object} object The object to iterate over.
     * @param {Function} [callback=identity] The function called per iteration.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Object} Returns `object`.
     * @example
     *
     * _.forOwnRight({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
     *   console.log(key);
     * });
     * // => logs 'length', '1', and '0' assuming `_.forOwn` logs '0', '1', and 'length'
     */
    function forOwnRight(object, callback, thisArg) {
      var props = keys(object),
          length = props.length;

      callback = baseCreateCallback(callback, thisArg, 3);
      while (length--) {
        var key = props[length];
        if (callback(object[key], key, object) === false) {
          break;
        }
      }
      return object;
    }

    /**
     * Creates a sorted array of property names of all enumerable properties,
     * own and inherited, of `object` that have function values.
     *
     * @static
     * @memberOf _
     * @alias methods
     * @category Objects
     * @param {Object} object The object to inspect.
     * @returns {Array} Returns an array of property names that have function values.
     * @example
     *
     * _.functions(_);
     * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]
     */
    function functions(object) {
      var result = [];
      forIn(object, function(value, key) {
        if (isFunction(value)) {
          result.push(key);
        }
      });
      return result.sort();
    }

    /**
     * Checks if the specified property name exists as a direct property of `object`,
     * instead of an inherited property.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {Object} object The object to inspect.
     * @param {string} key The name of the property to check.
     * @returns {boolean} Returns `true` if key is a direct property, else `false`.
     * @example
     *
     * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b');
     * // => true
     */
    function has(object, key) {
      return object ? hasOwnProperty.call(object, key) : false;
    }

    /**
     * Creates an object composed of the inverted keys and values of the given object.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {Object} object The object to invert.
     * @returns {Object} Returns the created inverted object.
     * @example
     *
     * _.invert({ 'first': 'fred', 'second': 'barney' });
     * // => { 'fred': 'first', 'barney': 'second' }
     */
    function invert(object) {
      var index = -1,
          props = keys(object),
          length = props.length,
          result = {};

      while (++index < length) {
        var key = props[index];
        result[object[key]] = key;
      }
      return result;
    }

    /**
     * Checks if `value` is a boolean value.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if the `value` is a boolean value, else `false`.
     * @example
     *
     * _.isBoolean(null);
     * // => false
     */
    function isBoolean(value) {
      return value === true || value === false ||
        value && typeof value == 'object' && toString.call(value) == boolClass || false;
    }

    /**
     * Checks if `value` is a date.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if the `value` is a date, else `false`.
     * @example
     *
     * _.isDate(new Date);
     * // => true
     */
    function isDate(value) {
      return value && typeof value == 'object' && toString.call(value) == dateClass || false;
    }

    /**
     * Checks if `value` is a DOM element.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if the `value` is a DOM element, else `false`.
     * @example
     *
     * _.isElement(document.body);
     * // => true
     */
    function isElement(value) {
      return value && value.nodeType === 1 || false;
    }

    /**
     * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a
     * length of `0` and objects with no own enumerable properties are considered
     * "empty".
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {Array|Object|string} value The value to inspect.
     * @returns {boolean} Returns `true` if the `value` is empty, else `false`.
     * @example
     *
     * _.isEmpty([1, 2, 3]);
     * // => false
     *
     * _.isEmpty({});
     * // => true
     *
     * _.isEmpty('');
     * // => true
     */
    function isEmpty(value) {
      var result = true;
      if (!value) {
        return result;
      }
      var className = toString.call(value),
          length = value.length;

      if ((className == arrayClass || className == stringClass || className == argsClass ) ||
          (className == objectClass && typeof length == 'number' && isFunction(value.splice))) {
        return !length;
      }
      forOwn(value, function() {
        return (result = false);
      });
      return result;
    }

    /**
     * Performs a deep comparison between two values to determine if they are
     * equivalent to each other. If a callback is provided it will be executed
     * to compare values. If the callback returns `undefined` comparisons will
     * be handled by the method instead. The callback is bound to `thisArg` and
     * invoked with two arguments; (a, b).
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {*} a The value to compare.
     * @param {*} b The other value to compare.
     * @param {Function} [callback] The function to customize comparing values.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
     * @example
     *
     * var object = { 'name': 'fred' };
     * var copy = { 'name': 'fred' };
     *
     * object == copy;
     * // => false
     *
     * _.isEqual(object, copy);
     * // => true
     *
     * var words = ['hello', 'goodbye'];
     * var otherWords = ['hi', 'goodbye'];
     *
     * _.isEqual(words, otherWords, function(a, b) {
     *   var reGreet = /^(?:hello|hi)$/i,
     *       aGreet = _.isString(a) && reGreet.test(a),
     *       bGreet = _.isString(b) && reGreet.test(b);
     *
     *   return (aGreet || bGreet) ? (aGreet == bGreet) : undefined;
     * });
     * // => true
     */
    function isEqual(a, b, callback, thisArg) {
      return baseIsEqual(a, b, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 2));
    }

    /**
     * Checks if `value` is, or can be coerced to, a finite number.
     *
     * Note: This is not the same as native `isFinite` which will return true for
     * booleans and empty strings. See http://es5.github.io/#x15.1.2.5.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if the `value` is finite, else `false`.
     * @example
     *
     * _.isFinite(-101);
     * // => true
     *
     * _.isFinite('10');
     * // => true
     *
     * _.isFinite(true);
     * // => false
     *
     * _.isFinite('');
     * // => false
     *
     * _.isFinite(Infinity);
     * // => false
     */
    function isFinite(value) {
      return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value));
    }

    /**
     * Checks if `value` is a function.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if the `value` is a function, else `false`.
     * @example
     *
     * _.isFunction(_);
     * // => true
     */
    function isFunction(value) {
      return typeof value == 'function';
    }

    /**
     * Checks if `value` is the language type of Object.
     * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if the `value` is an object, else `false`.
     * @example
     *
     * _.isObject({});
     * // => true
     *
     * _.isObject([1, 2, 3]);
     * // => true
     *
     * _.isObject(1);
     * // => false
     */
    function isObject(value) {
      // check if the value is the ECMAScript language type of Object
      // http://es5.github.io/#x8
      // and avoid a V8 bug
      // http://code.google.com/p/v8/issues/detail?id=2291
      return !!(value && objectTypes[typeof value]);
    }

    /**
     * Checks if `value` is `NaN`.
     *
     * Note: This is not the same as native `isNaN` which will return `true` for
     * `undefined` and other non-numeric values. See http://es5.github.io/#x15.1.2.4.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if the `value` is `NaN`, else `false`.
     * @example
     *
     * _.isNaN(NaN);
     * // => true
     *
     * _.isNaN(new Number(NaN));
     * // => true
     *
     * isNaN(undefined);
     * // => true
     *
     * _.isNaN(undefined);
     * // => false
     */
    function isNaN(value) {
      // `NaN` as a primitive is the only value that is not equal to itself
      // (perform the [[Class]] check first to avoid errors with some host objects in IE)
      return isNumber(value) && value != +value;
    }

    /**
     * Checks if `value` is `null`.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if the `value` is `null`, else `false`.
     * @example
     *
     * _.isNull(null);
     * // => true
     *
     * _.isNull(undefined);
     * // => false
     */
    function isNull(value) {
      return value === null;
    }

    /**
     * Checks if `value` is a number.
     *
     * Note: `NaN` is considered a number. See http://es5.github.io/#x8.5.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if the `value` is a number, else `false`.
     * @example
     *
     * _.isNumber(8.4 * 5);
     * // => true
     */
    function isNumber(value) {
      return typeof value == 'number' ||
        value && typeof value == 'object' && toString.call(value) == numberClass || false;
    }

    /**
     * Checks if `value` is an object created by the `Object` constructor.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
     * @example
     *
     * function Shape() {
     *   this.x = 0;
     *   this.y = 0;
     * }
     *
     * _.isPlainObject(new Shape);
     * // => false
     *
     * _.isPlainObject([1, 2, 3]);
     * // => false
     *
     * _.isPlainObject({ 'x': 0, 'y': 0 });
     * // => true
     */
    var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
      if (!(value && toString.call(value) == objectClass)) {
        return false;
      }
      var valueOf = value.valueOf,
          objProto = isNative(valueOf) && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);

      return objProto
        ? (value == objProto || getPrototypeOf(value) == objProto)
        : shimIsPlainObject(value);
    };

    /**
     * Checks if `value` is a regular expression.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if the `value` is a regular expression, else `false`.
     * @example
     *
     * _.isRegExp(/fred/);
     * // => true
     */
    function isRegExp(value) {
      return value && typeof value == 'object' && toString.call(value) == regexpClass || false;
    }

    /**
     * Checks if `value` is a string.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if the `value` is a string, else `false`.
     * @example
     *
     * _.isString('fred');
     * // => true
     */
    function isString(value) {
      return typeof value == 'string' ||
        value && typeof value == 'object' && toString.call(value) == stringClass || false;
    }

    /**
     * Checks if `value` is `undefined`.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if the `value` is `undefined`, else `false`.
     * @example
     *
     * _.isUndefined(void 0);
     * // => true
     */
    function isUndefined(value) {
      return typeof value == 'undefined';
    }

    /**
     * Creates an object with the same keys as `object` and values generated by
     * running each own enumerable property of `object` through the callback.
     * The callback is bound to `thisArg` and invoked with three arguments;
     * (value, key, object).
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {Object} object The object to iterate over.
     * @param {Function|Object|string} [callback=identity] The function called
     *  per iteration. If a property name or object is provided it will be used
     *  to create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Array} Returns a new object with values of the results of each `callback` execution.
     * @example
     *
     * _.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
     * // => { 'a': 3, 'b': 6, 'c': 9 }
     *
     * var characters = {
     *   'fred': { 'name': 'fred', 'age': 40 },
     *   'pebbles': { 'name': 'pebbles', 'age': 1 }
     * };
     *
     * // using "_.pluck" callback shorthand
     * _.mapValues(characters, 'age');
     * // => { 'fred': 40, 'pebbles': 1 }
     */
    function mapValues(object, callback, thisArg) {
      var result = {};
      callback = lodash.createCallback(callback, thisArg, 3);

      forOwn(object, function(value, key, object) {
        result[key] = callback(value, key, object);
      });
      return result;
    }

    /**
     * Recursively merges own enumerable properties of the source object(s), that
     * don't resolve to `undefined` into the destination object. Subsequent sources
     * will overwrite property assignments of previous sources. If a callback is
     * provided it will be executed to produce the merged values of the destination
     * and source properties. If the callback returns `undefined` merging will
     * be handled by the method instead. The callback is bound to `thisArg` and
     * invoked with two arguments; (objectValue, sourceValue).
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {Object} object The destination object.
     * @param {...Object} [source] The source objects.
     * @param {Function} [callback] The function to customize merging properties.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Object} Returns the destination object.
     * @example
     *
     * var names = {
     *   'characters': [
     *     { 'name': 'barney' },
     *     { 'name': 'fred' }
     *   ]
     * };
     *
     * var ages = {
     *   'characters': [
     *     { 'age': 36 },
     *     { 'age': 40 }
     *   ]
     * };
     *
     * _.merge(names, ages);
     * // => { 'characters': [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] }
     *
     * var food = {
     *   'fruits': ['apple'],
     *   'vegetables': ['beet']
     * };
     *
     * var otherFood = {
     *   'fruits': ['banana'],
     *   'vegetables': ['carrot']
     * };
     *
     * _.merge(food, otherFood, function(a, b) {
     *   return _.isArray(a) ? a.concat(b) : undefined;
     * });
     * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] }
     */
    function merge(object) {
      var args = arguments,
          length = 2;

      if (!isObject(object)) {
        return object;
      }
      // allows working with `_.reduce` and `_.reduceRight` without using
      // their `index` and `collection` arguments
      if (typeof args[2] != 'number') {
        length = args.length;
      }
      if (length > 3 && typeof args[length - 2] == 'function') {
        var callback = baseCreateCallback(args[--length - 1], args[length--], 2);
      } else if (length > 2 && typeof args[length - 1] == 'function') {
        callback = args[--length];
      }
      var sources = slice(arguments, 1, length),
          index = -1,
          stackA = getArray(),
          stackB = getArray();

      while (++index < length) {
        baseMerge(object, sources[index], callback, stackA, stackB);
      }
      releaseArray(stackA);
      releaseArray(stackB);
      return object;
    }

    /**
     * Creates a shallow clone of `object` excluding the specified properties.
     * Property names may be specified as individual arguments or as arrays of
     * property names. If a callback is provided it will be executed for each
     * property of `object` omitting the properties the callback returns truey
     * for. The callback is bound to `thisArg` and invoked with three arguments;
     * (value, key, object).
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {Object} object The source object.
     * @param {Function|...string|string[]} [callback] The properties to omit or the
     *  function called per iteration.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Object} Returns an object without the omitted properties.
     * @example
     *
     * _.omit({ 'name': 'fred', 'age': 40 }, 'age');
     * // => { 'name': 'fred' }
     *
     * _.omit({ 'name': 'fred', 'age': 40 }, function(value) {
     *   return typeof value == 'number';
     * });
     * // => { 'name': 'fred' }
     */
    function omit(object, callback, thisArg) {
      var result = {};
      if (typeof callback != 'function') {
        var props = [];
        forIn(object, function(value, key) {
          props.push(key);
        });
        props = baseDifference(props, baseFlatten(arguments, true, false, 1));

        var index = -1,
            length = props.length;

        while (++index < length) {
          var key = props[index];
          result[key] = object[key];
        }
      } else {
        callback = lodash.createCallback(callback, thisArg, 3);
        forIn(object, function(value, key, object) {
          if (!callback(value, key, object)) {
            result[key] = value;
          }
        });
      }
      return result;
    }

    /**
     * Creates a two dimensional array of an object's key-value pairs,
     * i.e. `[[key1, value1], [key2, value2]]`.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {Object} object The object to inspect.
     * @returns {Array} Returns new array of key-value pairs.
     * @example
     *
     * _.pairs({ 'barney': 36, 'fred': 40 });
     * // => [['barney', 36], ['fred', 40]] (property order is not guaranteed across environments)
     */
    function pairs(object) {
      var index = -1,
          props = keys(object),
          length = props.length,
          result = Array(length);

      while (++index < length) {
        var key = props[index];
        result[index] = [key, object[key]];
      }
      return result;
    }

    /**
     * Creates a shallow clone of `object` composed of the specified properties.
     * Property names may be specified as individual arguments or as arrays of
     * property names. If a callback is provided it will be executed for each
     * property of `object` picking the properties the callback returns truey
     * for. The callback is bound to `thisArg` and invoked with three arguments;
     * (value, key, object).
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {Object} object The source object.
     * @param {Function|...string|string[]} [callback] The function called per
     *  iteration or property names to pick, specified as individual property
     *  names or arrays of property names.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Object} Returns an object composed of the picked properties.
     * @example
     *
     * _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name');
     * // => { 'name': 'fred' }
     *
     * _.pick({ 'name': 'fred', '_userid': 'fred1' }, function(value, key) {
     *   return key.charAt(0) != '_';
     * });
     * // => { 'name': 'fred' }
     */
    function pick(object, callback, thisArg) {
      var result = {};
      if (typeof callback != 'function') {
        var index = -1,
            props = baseFlatten(arguments, true, false, 1),
            length = isObject(object) ? props.length : 0;

        while (++index < length) {
          var key = props[index];
          if (key in object) {
            result[key] = object[key];
          }
        }
      } else {
        callback = lodash.createCallback(callback, thisArg, 3);
        forIn(object, function(value, key, object) {
          if (callback(value, key, object)) {
            result[key] = value;
          }
        });
      }
      return result;
    }

    /**
     * An alternative to `_.reduce` this method transforms `object` to a new
     * `accumulator` object which is the result of running each of its own
     * enumerable properties through a callback, with each callback execution
     * potentially mutating the `accumulator` object. The callback is bound to
     * `thisArg` and invoked with four arguments; (accumulator, value, key, object).
     * Callbacks may exit iteration early by explicitly returning `false`.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {Array|Object} object The object to iterate over.
     * @param {Function} [callback=identity] The function called per iteration.
     * @param {*} [accumulator] The custom accumulator value.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {*} Returns the accumulated value.
     * @example
     *
     * var squares = _.transform([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(result, num) {
     *   num *= num;
     *   if (num % 2) {
     *     return result.push(num) < 3;
     *   }
     * });
     * // => [1, 9, 25]
     *
     * var mapped = _.transform({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
     *   result[key] = num * 3;
     * });
     * // => { 'a': 3, 'b': 6, 'c': 9 }
     */
    function transform(object, callback, accumulator, thisArg) {
      var isArr = isArray(object);
      if (accumulator == null) {
        if (isArr) {
          accumulator = [];
        } else {
          var ctor = object && object.constructor,
              proto = ctor && ctor.prototype;

          accumulator = baseCreate(proto);
        }
      }
      if (callback) {
        callback = lodash.createCallback(callback, thisArg, 4);
        (isArr ? forEach : forOwn)(object, function(value, index, object) {
          return callback(accumulator, value, index, object);
        });
      }
      return accumulator;
    }

    /**
     * Creates an array composed of the own enumerable property values of `object`.
     *
     * @static
     * @memberOf _
     * @category Objects
     * @param {Object} object The object to inspect.
     * @returns {Array} Returns an array of property values.
     * @example
     *
     * _.values({ 'one': 1, 'two': 2, 'three': 3 });
     * // => [1, 2, 3] (property order is not guaranteed across environments)
     */
    function values(object) {
      var index = -1,
          props = keys(object),
          length = props.length,
          result = Array(length);

      while (++index < length) {
        result[index] = object[props[index]];
      }
      return result;
    }

    /*--------------------------------------------------------------------------*/

    /**
     * Creates an array of elements from the specified indexes, or keys, of the
     * `collection`. Indexes may be specified as individual arguments or as arrays
     * of indexes.
     *
     * @static
     * @memberOf _
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {...(number|number[]|string|string[])} [index] The indexes of `collection`
     *   to retrieve, specified as individual indexes or arrays of indexes.
     * @returns {Array} Returns a new array of elements corresponding to the
     *  provided indexes.
     * @example
     *
     * _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]);
     * // => ['a', 'c', 'e']
     *
     * _.at(['fred', 'barney', 'pebbles'], 0, 2);
     * // => ['fred', 'pebbles']
     */
    function at(collection) {
      var args = arguments,
          index = -1,
          props = baseFlatten(args, true, false, 1),
          length = (args[2] && args[2][args[1]] === collection) ? 1 : props.length,
          result = Array(length);

      while(++index < length) {
        result[index] = collection[props[index]];
      }
      return result;
    }

    /**
     * Checks if a given value is present in a collection using strict equality
     * for comparisons, i.e. `===`. If `fromIndex` is negative, it is used as the
     * offset from the end of the collection.
     *
     * @static
     * @memberOf _
     * @alias include
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {*} target The value to check for.
     * @param {number} [fromIndex=0] The index to search from.
     * @returns {boolean} Returns `true` if the `target` element is found, else `false`.
     * @example
     *
     * _.contains([1, 2, 3], 1);
     * // => true
     *
     * _.contains([1, 2, 3], 1, 2);
     * // => false
     *
     * _.contains({ 'name': 'fred', 'age': 40 }, 'fred');
     * // => true
     *
     * _.contains('pebbles', 'eb');
     * // => true
     */
    function contains(collection, target, fromIndex) {
      var index = -1,
          indexOf = getIndexOf(),
          length = collection ? collection.length : 0,
          result = false;

      fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) || 0;
      if (isArray(collection)) {
        result = indexOf(collection, target, fromIndex) > -1;
      } else if (typeof length == 'number') {
        result = (isString(collection) ? collection.indexOf(target, fromIndex) : indexOf(collection, target, fromIndex)) > -1;
      } else {
        forOwn(collection, function(value) {
          if (++index >= fromIndex) {
            return !(result = value === target);
          }
        });
      }
      return result;
    }

    /**
     * Creates an object composed of keys generated from the results of running
     * each element of `collection` through the callback. The corresponding value
     * of each key is the number of times the key was returned by the callback.
     * The callback is bound to `thisArg` and invoked with three arguments;
     * (value, index|key, collection).
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {Function|Object|string} [callback=identity] The function called
     *  per iteration. If a property name or object is provided it will be used
     *  to create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Object} Returns the composed aggregate object.
     * @example
     *
     * _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); });
     * // => { '4': 1, '6': 2 }
     *
     * _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
     * // => { '4': 1, '6': 2 }
     *
     * _.countBy(['one', 'two', 'three'], 'length');
     * // => { '3': 2, '5': 1 }
     */
    var countBy = createAggregator(function(result, value, key) {
      (hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1);
    });

    /**
     * Checks if the given callback returns truey value for **all** elements of
     * a collection. The callback is bound to `thisArg` and invoked with three
     * arguments; (value, index|key, collection).
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @alias all
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {Function|Object|string} [callback=identity] The function called
     *  per iteration. If a property name or object is provided it will be used
     *  to create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {boolean} Returns `true` if all elements passed the callback check,
     *  else `false`.
     * @example
     *
     * _.every([true, 1, null, 'yes']);
     * // => false
     *
     * var characters = [
     *   { 'name': 'barney', 'age': 36 },
     *   { 'name': 'fred',   'age': 40 }
     * ];
     *
     * // using "_.pluck" callback shorthand
     * _.every(characters, 'age');
     * // => true
     *
     * // using "_.where" callback shorthand
     * _.every(characters, { 'age': 36 });
     * // => false
     */
    function every(collection, callback, thisArg) {
      var result = true;
      callback = lodash.createCallback(callback, thisArg, 3);

      var index = -1,
          length = collection ? collection.length : 0;

      if (typeof length == 'number') {
        while (++index < length) {
          if (!(result = !!callback(collection[index], index, collection))) {
            break;
          }
        }
      } else {
        forOwn(collection, function(value, index, collection) {
          return (result = !!callback(value, index, collection));
        });
      }
      return result;
    }

    /**
     * Iterates over elements of a collection, returning an array of all elements
     * the callback returns truey for. The callback is bound to `thisArg` and
     * invoked with three arguments; (value, index|key, collection).
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @alias select
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {Function|Object|string} [callback=identity] The function called
     *  per iteration. If a property name or object is provided it will be used
     *  to create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Array} Returns a new array of elements that passed the callback check.
     * @example
     *
     * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
     * // => [2, 4, 6]
     *
     * var characters = [
     *   { 'name': 'barney', 'age': 36, 'blocked': false },
     *   { 'name': 'fred',   'age': 40, 'blocked': true }
     * ];
     *
     * // using "_.pluck" callback shorthand
     * _.filter(characters, 'blocked');
     * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
     *
     * // using "_.where" callback shorthand
     * _.filter(characters, { 'age': 36 });
     * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
     */
    function filter(collection, callback, thisArg) {
      var result = [];
      callback = lodash.createCallback(callback, thisArg, 3);

      var index = -1,
          length = collection ? collection.length : 0;

      if (typeof length == 'number') {
        while (++index < length) {
          var value = collection[index];
          if (callback(value, index, collection)) {
            result.push(value);
          }
        }
      } else {
        forOwn(collection, function(value, index, collection) {
          if (callback(value, index, collection)) {
            result.push(value);
          }
        });
      }
      return result;
    }

    /**
     * Iterates over elements of a collection, returning the first element that
     * the callback returns truey for. The callback is bound to `thisArg` and
     * invoked with three arguments; (value, index|key, collection).
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @alias detect, findWhere
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {Function|Object|string} [callback=identity] The function called
     *  per iteration. If a property name or object is provided it will be used
     *  to create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {*} Returns the found element, else `undefined`.
     * @example
     *
     * var characters = [
     *   { 'name': 'barney',  'age': 36, 'blocked': false },
     *   { 'name': 'fred',    'age': 40, 'blocked': true },
     *   { 'name': 'pebbles', 'age': 1,  'blocked': false }
     * ];
     *
     * _.find(characters, function(chr) {
     *   return chr.age < 40;
     * });
     * // => { 'name': 'barney', 'age': 36, 'blocked': false }
     *
     * // using "_.where" callback shorthand
     * _.find(characters, { 'age': 1 });
     * // =>  { 'name': 'pebbles', 'age': 1, 'blocked': false }
     *
     * // using "_.pluck" callback shorthand
     * _.find(characters, 'blocked');
     * // => { 'name': 'fred', 'age': 40, 'blocked': true }
     */
    function find(collection, callback, thisArg) {
      callback = lodash.createCallback(callback, thisArg, 3);

      var index = -1,
          length = collection ? collection.length : 0;

      if (typeof length == 'number') {
        while (++index < length) {
          var value = collection[index];
          if (callback(value, index, collection)) {
            return value;
          }
        }
      } else {
        var result;
        forOwn(collection, function(value, index, collection) {
          if (callback(value, index, collection)) {
            result = value;
            return false;
          }
        });
        return result;
      }
    }

    /**
     * This method is like `_.find` except that it iterates over elements
     * of a `collection` from right to left.
     *
     * @static
     * @memberOf _
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {Function|Object|string} [callback=identity] The function called
     *  per iteration. If a property name or object is provided it will be used
     *  to create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {*} Returns the found element, else `undefined`.
     * @example
     *
     * _.findLast([1, 2, 3, 4], function(num) {
     *   return num % 2 == 1;
     * });
     * // => 3
     */
    function findLast(collection, callback, thisArg) {
      var result;
      callback = lodash.createCallback(callback, thisArg, 3);
      forEachRight(collection, function(value, index, collection) {
        if (callback(value, index, collection)) {
          result = value;
          return false;
        }
      });
      return result;
    }

    /**
     * Iterates over elements of a collection, executing the callback for each
     * element. The callback is bound to `thisArg` and invoked with three arguments;
     * (value, index|key, collection). Callbacks may exit iteration early by
     * explicitly returning `false`.
     *
     * Note: As with other "Collections" methods, objects with a `length` property
     * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
     * may be used for object iteration.
     *
     * @static
     * @memberOf _
     * @alias each
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {Function} [callback=identity] The function called per iteration.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Array|Object|string} Returns `collection`.
     * @example
     *
     * _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(',');
     * // => logs each number and returns '1,2,3'
     *
     * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); });
     * // => logs each number and returns the object (property order is not guaranteed across environments)
     */
    function forEach(collection, callback, thisArg) {
      var index = -1,
          length = collection ? collection.length : 0;

      callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
      if (typeof length == 'number') {
        while (++index < length) {
          if (callback(collection[index], index, collection) === false) {
            break;
          }
        }
      } else {
        forOwn(collection, callback);
      }
      return collection;
    }

    /**
     * This method is like `_.forEach` except that it iterates over elements
     * of a `collection` from right to left.
     *
     * @static
     * @memberOf _
     * @alias eachRight
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {Function} [callback=identity] The function called per iteration.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Array|Object|string} Returns `collection`.
     * @example
     *
     * _([1, 2, 3]).forEachRight(function(num) { console.log(num); }).join(',');
     * // => logs each number from right to left and returns '3,2,1'
     */
    function forEachRight(collection, callback, thisArg) {
      var length = collection ? collection.length : 0;
      callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
      if (typeof length == 'number') {
        while (length--) {
          if (callback(collection[length], length, collection) === false) {
            break;
          }
        }
      } else {
        var props = keys(collection);
        length = props.length;
        forOwn(collection, function(value, key, collection) {
          key = props ? props[--length] : --length;
          return callback(collection[key], key, collection);
        });
      }
      return collection;
    }

    /**
     * Creates an object composed of keys generated from the results of running
     * each element of a collection through the callback. The corresponding value
     * of each key is an array of the elements responsible for generating the key.
     * The callback is bound to `thisArg` and invoked with three arguments;
     * (value, index|key, collection).
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`
     *
     * @static
     * @memberOf _
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {Function|Object|string} [callback=identity] The function called
     *  per iteration. If a property name or object is provided it will be used
     *  to create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Object} Returns the composed aggregate object.
     * @example
     *
     * _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); });
     * // => { '4': [4.2], '6': [6.1, 6.4] }
     *
     * _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
     * // => { '4': [4.2], '6': [6.1, 6.4] }
     *
     * // using "_.pluck" callback shorthand
     * _.groupBy(['one', 'two', 'three'], 'length');
     * // => { '3': ['one', 'two'], '5': ['three'] }
     */
    var groupBy = createAggregator(function(result, value, key) {
      (hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value);
    });

    /**
     * Creates an object composed of keys generated from the results of running
     * each element of the collection through the given callback. The corresponding
     * value of each key is the last element responsible for generating the key.
     * The callback is bound to `thisArg` and invoked with three arguments;
     * (value, index|key, collection).
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {Function|Object|string} [callback=identity] The function called
     *  per iteration. If a property name or object is provided it will be used
     *  to create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Object} Returns the composed aggregate object.
     * @example
     *
     * var keys = [
     *   { 'dir': 'left', 'code': 97 },
     *   { 'dir': 'right', 'code': 100 }
     * ];
     *
     * _.indexBy(keys, 'dir');
     * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
     *
     * _.indexBy(keys, function(key) { return String.fromCharCode(key.code); });
     * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
     *
     * _.indexBy(characters, function(key) { this.fromCharCode(key.code); }, String);
     * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
     */
    var indexBy = createAggregator(function(result, value, key) {
      result[key] = value;
    });

    /**
     * Invokes the method named by `methodName` on each element in the `collection`
     * returning an array of the results of each invoked method. Additional arguments
     * will be provided to each invoked method. If `methodName` is a function it
     * will be invoked for, and `this` bound to, each element in the `collection`.
     *
     * @static
     * @memberOf _
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {Function|string} methodName The name of the method to invoke or
     *  the function invoked per iteration.
     * @param {...*} [arg] Arguments to invoke the method with.
     * @returns {Array} Returns a new array of the results of each invoked method.
     * @example
     *
     * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
     * // => [[1, 5, 7], [1, 2, 3]]
     *
     * _.invoke([123, 456], String.prototype.split, '');
     * // => [['1', '2', '3'], ['4', '5', '6']]
     */
    function invoke(collection, methodName) {
      var args = slice(arguments, 2),
          index = -1,
          isFunc = typeof methodName == 'function',
          length = collection ? collection.length : 0,
          result = Array(typeof length == 'number' ? length : 0);

      forEach(collection, function(value) {
        result[++index] = (isFunc ? methodName : value[methodName]).apply(value, args);
      });
      return result;
    }

    /**
     * Creates an array of values by running each element in the collection
     * through the callback. The callback is bound to `thisArg` and invoked with
     * three arguments; (value, index|key, collection).
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @alias collect
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {Function|Object|string} [callback=identity] The function called
     *  per iteration. If a property name or object is provided it will be used
     *  to create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Array} Returns a new array of the results of each `callback` execution.
     * @example
     *
     * _.map([1, 2, 3], function(num) { return num * 3; });
     * // => [3, 6, 9]
     *
     * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; });
     * // => [3, 6, 9] (property order is not guaranteed across environments)
     *
     * var characters = [
     *   { 'name': 'barney', 'age': 36 },
     *   { 'name': 'fred',   'age': 40 }
     * ];
     *
     * // using "_.pluck" callback shorthand
     * _.map(characters, 'name');
     * // => ['barney', 'fred']
     */
    function map(collection, callback, thisArg) {
      var index = -1,
          length = collection ? collection.length : 0;

      callback = lodash.createCallback(callback, thisArg, 3);
      if (typeof length == 'number') {
        var result = Array(length);
        while (++index < length) {
          result[index] = callback(collection[index], index, collection);
        }
      } else {
        result = [];
        forOwn(collection, function(value, key, collection) {
          result[++index] = callback(value, key, collection);
        });
      }
      return result;
    }

    /**
     * Retrieves the maximum value of a collection. If the collection is empty or
     * falsey `-Infinity` is returned. If a callback is provided it will be executed
     * for each value in the collection to generate the criterion by which the value
     * is ranked. The callback is bound to `thisArg` and invoked with three
     * arguments; (value, index, collection).
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {Function|Object|string} [callback=identity] The function called
     *  per iteration. If a property name or object is provided it will be used
     *  to create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {*} Returns the maximum value.
     * @example
     *
     * _.max([4, 2, 8, 6]);
     * // => 8
     *
     * var characters = [
     *   { 'name': 'barney', 'age': 36 },
     *   { 'name': 'fred',   'age': 40 }
     * ];
     *
     * _.max(characters, function(chr) { return chr.age; });
     * // => { 'name': 'fred', 'age': 40 };
     *
     * // using "_.pluck" callback shorthand
     * _.max(characters, 'age');
     * // => { 'name': 'fred', 'age': 40 };
     */
    function max(collection, callback, thisArg) {
      var computed = -Infinity,
          result = computed;

      // allows working with functions like `_.map` without using
      // their `index` argument as a callback
      if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
        callback = null;
      }
      if (callback == null && isArray(collection)) {
        var index = -1,
            length = collection.length;

        while (++index < length) {
          var value = collection[index];
          if (value > result) {
            result = value;
          }
        }
      } else {
        callback = (callback == null && isString(collection))
          ? charAtCallback
          : lodash.createCallback(callback, thisArg, 3);

        forEach(collection, function(value, index, collection) {
          var current = callback(value, index, collection);
          if (current > computed) {
            computed = current;
            result = value;
          }
        });
      }
      return result;
    }

    /**
     * Retrieves the minimum value of a collection. If the collection is empty or
     * falsey `Infinity` is returned. If a callback is provided it will be executed
     * for each value in the collection to generate the criterion by which the value
     * is ranked. The callback is bound to `thisArg` and invoked with three
     * arguments; (value, index, collection).
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {Function|Object|string} [callback=identity] The function called
     *  per iteration. If a property name or object is provided it will be used
     *  to create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {*} Returns the minimum value.
     * @example
     *
     * _.min([4, 2, 8, 6]);
     * // => 2
     *
     * var characters = [
     *   { 'name': 'barney', 'age': 36 },
     *   { 'name': 'fred',   'age': 40 }
     * ];
     *
     * _.min(characters, function(chr) { return chr.age; });
     * // => { 'name': 'barney', 'age': 36 };
     *
     * // using "_.pluck" callback shorthand
     * _.min(characters, 'age');
     * // => { 'name': 'barney', 'age': 36 };
     */
    function min(collection, callback, thisArg) {
      var computed = Infinity,
          result = computed;

      // allows working with functions like `_.map` without using
      // their `index` argument as a callback
      if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
        callback = null;
      }
      if (callback == null && isArray(collection)) {
        var index = -1,
            length = collection.length;

        while (++index < length) {
          var value = collection[index];
          if (value < result) {
            result = value;
          }
        }
      } else {
        callback = (callback == null && isString(collection))
          ? charAtCallback
          : lodash.createCallback(callback, thisArg, 3);

        forEach(collection, function(value, index, collection) {
          var current = callback(value, index, collection);
          if (current < computed) {
            computed = current;
            result = value;
          }
        });
      }
      return result;
    }

    /**
     * Retrieves the value of a specified property from all elements in the collection.
     *
     * @static
     * @memberOf _
     * @type Function
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {string} property The name of the property to pluck.
     * @returns {Array} Returns a new array of property values.
     * @example
     *
     * var characters = [
     *   { 'name': 'barney', 'age': 36 },
     *   { 'name': 'fred',   'age': 40 }
     * ];
     *
     * _.pluck(characters, 'name');
     * // => ['barney', 'fred']
     */
    var pluck = map;

    /**
     * Reduces a collection to a value which is the accumulated result of running
     * each element in the collection through the callback, where each successive
     * callback execution consumes the return value of the previous execution. If
     * `accumulator` is not provided the first element of the collection will be
     * used as the initial `accumulator` value. The callback is bound to `thisArg`
     * and invoked with four arguments; (accumulator, value, index|key, collection).
     *
     * @static
     * @memberOf _
     * @alias foldl, inject
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {Function} [callback=identity] The function called per iteration.
     * @param {*} [accumulator] Initial value of the accumulator.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {*} Returns the accumulated value.
     * @example
     *
     * var sum = _.reduce([1, 2, 3], function(sum, num) {
     *   return sum + num;
     * });
     * // => 6
     *
     * var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
     *   result[key] = num * 3;
     *   return result;
     * }, {});
     * // => { 'a': 3, 'b': 6, 'c': 9 }
     */
    function reduce(collection, callback, accumulator, thisArg) {
      if (!collection) return accumulator;
      var noaccum = arguments.length < 3;
      callback = lodash.createCallback(callback, thisArg, 4);

      var index = -1,
          length = collection.length;

      if (typeof length == 'number') {
        if (noaccum) {
          accumulator = collection[++index];
        }
        while (++index < length) {
          accumulator = callback(accumulator, collection[index], index, collection);
        }
      } else {
        forOwn(collection, function(value, index, collection) {
          accumulator = noaccum
            ? (noaccum = false, value)
            : callback(accumulator, value, index, collection)
        });
      }
      return accumulator;
    }

    /**
     * This method is like `_.reduce` except that it iterates over elements
     * of a `collection` from right to left.
     *
     * @static
     * @memberOf _
     * @alias foldr
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {Function} [callback=identity] The function called per iteration.
     * @param {*} [accumulator] Initial value of the accumulator.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {*} Returns the accumulated value.
     * @example
     *
     * var list = [[0, 1], [2, 3], [4, 5]];
     * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []);
     * // => [4, 5, 2, 3, 0, 1]
     */
    function reduceRight(collection, callback, accumulator, thisArg) {
      var noaccum = arguments.length < 3;
      callback = lodash.createCallback(callback, thisArg, 4);
      forEachRight(collection, function(value, index, collection) {
        accumulator = noaccum
          ? (noaccum = false, value)
          : callback(accumulator, value, index, collection);
      });
      return accumulator;
    }

    /**
     * The opposite of `_.filter` this method returns the elements of a
     * collection that the callback does **not** return truey for.
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {Function|Object|string} [callback=identity] The function called
     *  per iteration. If a property name or object is provided it will be used
     *  to create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Array} Returns a new array of elements that failed the callback check.
     * @example
     *
     * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
     * // => [1, 3, 5]
     *
     * var characters = [
     *   { 'name': 'barney', 'age': 36, 'blocked': false },
     *   { 'name': 'fred',   'age': 40, 'blocked': true }
     * ];
     *
     * // using "_.pluck" callback shorthand
     * _.reject(characters, 'blocked');
     * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
     *
     * // using "_.where" callback shorthand
     * _.reject(characters, { 'age': 36 });
     * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
     */
    function reject(collection, callback, thisArg) {
      callback = lodash.createCallback(callback, thisArg, 3);
      return filter(collection, function(value, index, collection) {
        return !callback(value, index, collection);
      });
    }

    /**
     * Retrieves a random element or `n` random elements from a collection.
     *
     * @static
     * @memberOf _
     * @category Collections
     * @param {Array|Object|string} collection The collection to sample.
     * @param {number} [n] The number of elements to sample.
     * @param- {Object} [guard] Allows working with functions like `_.map`
     *  without using their `index` arguments as `n`.
     * @returns {Array} Returns the random sample(s) of `collection`.
     * @example
     *
     * _.sample([1, 2, 3, 4]);
     * // => 2
     *
     * _.sample([1, 2, 3, 4], 2);
     * // => [3, 1]
     */
    function sample(collection, n, guard) {
      if (collection && typeof collection.length != 'number') {
        collection = values(collection);
      }
      if (n == null || guard) {
        return collection ? collection[baseRandom(0, collection.length - 1)] : undefined;
      }
      var result = shuffle(collection);
      result.length = nativeMin(nativeMax(0, n), result.length);
      return result;
    }

    /**
     * Creates an array of shuffled values, using a version of the Fisher-Yates
     * shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle.
     *
     * @static
     * @memberOf _
     * @category Collections
     * @param {Array|Object|string} collection The collection to shuffle.
     * @returns {Array} Returns a new shuffled collection.
     * @example
     *
     * _.shuffle([1, 2, 3, 4, 5, 6]);
     * // => [4, 1, 6, 3, 5, 2]
     */
    function shuffle(collection) {
      var index = -1,
          length = collection ? collection.length : 0,
          result = Array(typeof length == 'number' ? length : 0);

      forEach(collection, function(value) {
        var rand = baseRandom(0, ++index);
        result[index] = result[rand];
        result[rand] = value;
      });
      return result;
    }

    /**
     * Gets the size of the `collection` by returning `collection.length` for arrays
     * and array-like objects or the number of own enumerable properties for objects.
     *
     * @static
     * @memberOf _
     * @category Collections
     * @param {Array|Object|string} collection The collection to inspect.
     * @returns {number} Returns `collection.length` or number of own enumerable properties.
     * @example
     *
     * _.size([1, 2]);
     * // => 2
     *
     * _.size({ 'one': 1, 'two': 2, 'three': 3 });
     * // => 3
     *
     * _.size('pebbles');
     * // => 7
     */
    function size(collection) {
      var length = collection ? collection.length : 0;
      return typeof length == 'number' ? length : keys(collection).length;
    }

    /**
     * Checks if the callback returns a truey value for **any** element of a
     * collection. The function returns as soon as it finds a passing value and
     * does not iterate over the entire collection. The callback is bound to
     * `thisArg` and invoked with three arguments; (value, index|key, collection).
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @alias any
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {Function|Object|string} [callback=identity] The function called
     *  per iteration. If a property name or object is provided it will be used
     *  to create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {boolean} Returns `true` if any element passed the callback check,
     *  else `false`.
     * @example
     *
     * _.some([null, 0, 'yes', false], Boolean);
     * // => true
     *
     * var characters = [
     *   { 'name': 'barney', 'age': 36, 'blocked': false },
     *   { 'name': 'fred',   'age': 40, 'blocked': true }
     * ];
     *
     * // using "_.pluck" callback shorthand
     * _.some(characters, 'blocked');
     * // => true
     *
     * // using "_.where" callback shorthand
     * _.some(characters, { 'age': 1 });
     * // => false
     */
    function some(collection, callback, thisArg) {
      var result;
      callback = lodash.createCallback(callback, thisArg, 3);

      var index = -1,
          length = collection ? collection.length : 0;

      if (typeof length == 'number') {
        while (++index < length) {
          if ((result = callback(collection[index], index, collection))) {
            break;
          }
        }
      } else {
        forOwn(collection, function(value, index, collection) {
          return !(result = callback(value, index, collection));
        });
      }
      return !!result;
    }

    /**
     * Creates an array of elements, sorted in ascending order by the results of
     * running each element in a collection through the callback. This method
     * performs a stable sort, that is, it will preserve the original sort order
     * of equal elements. The callback is bound to `thisArg` and invoked with
     * three arguments; (value, index|key, collection).
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an array of property names is provided for `callback` the collection
     * will be sorted by each property value.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {Array|Function|Object|string} [callback=identity] The function called
     *  per iteration. If a property name or object is provided it will be used
     *  to create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Array} Returns a new array of sorted elements.
     * @example
     *
     * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); });
     * // => [3, 1, 2]
     *
     * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math);
     * // => [3, 1, 2]
     *
     * var characters = [
     *   { 'name': 'barney',  'age': 36 },
     *   { 'name': 'fred',    'age': 40 },
     *   { 'name': 'barney',  'age': 26 },
     *   { 'name': 'fred',    'age': 30 }
     * ];
     *
     * // using "_.pluck" callback shorthand
     * _.map(_.sortBy(characters, 'age'), _.values);
     * // => [['barney', 26], ['fred', 30], ['barney', 36], ['fred', 40]]
     *
     * // sorting by multiple properties
     * _.map(_.sortBy(characters, ['name', 'age']), _.values);
     * // = > [['barney', 26], ['barney', 36], ['fred', 30], ['fred', 40]]
     */
    function sortBy(collection, callback, thisArg) {
      var index = -1,
          isArr = isArray(callback),
          length = collection ? collection.length : 0,
          result = Array(typeof length == 'number' ? length : 0);

      if (!isArr) {
        callback = lodash.createCallback(callback, thisArg, 3);
      }
      forEach(collection, function(value, key, collection) {
        var object = result[++index] = getObject();
        if (isArr) {
          object.criteria = map(callback, function(key) { return value[key]; });
        } else {
          (object.criteria = getArray())[0] = callback(value, key, collection);
        }
        object.index = index;
        object.value = value;
      });

      length = result.length;
      result.sort(compareAscending);
      while (length--) {
        var object = result[length];
        result[length] = object.value;
        if (!isArr) {
          releaseArray(object.criteria);
        }
        releaseObject(object);
      }
      return result;
    }

    /**
     * Converts the `collection` to an array.
     *
     * @static
     * @memberOf _
     * @category Collections
     * @param {Array|Object|string} collection The collection to convert.
     * @returns {Array} Returns the new converted array.
     * @example
     *
     * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4);
     * // => [2, 3, 4]
     */
    function toArray(collection) {
      if (collection && typeof collection.length == 'number') {
        return slice(collection);
      }
      return values(collection);
    }

    /**
     * Performs a deep comparison of each element in a `collection` to the given
     * `properties` object, returning an array of all elements that have equivalent
     * property values.
     *
     * @static
     * @memberOf _
     * @type Function
     * @category Collections
     * @param {Array|Object|string} collection The collection to iterate over.
     * @param {Object} props The object of property values to filter by.
     * @returns {Array} Returns a new array of elements that have the given properties.
     * @example
     *
     * var characters = [
     *   { 'name': 'barney', 'age': 36, 'pets': ['hoppy'] },
     *   { 'name': 'fred',   'age': 40, 'pets': ['baby puss', 'dino'] }
     * ];
     *
     * _.where(characters, { 'age': 36 });
     * // => [{ 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }]
     *
     * _.where(characters, { 'pets': ['dino'] });
     * // => [{ 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }]
     */
    var where = filter;

    /*--------------------------------------------------------------------------*/

    /**
     * Creates an array with all falsey values removed. The values `false`, `null`,
     * `0`, `""`, `undefined`, and `NaN` are all falsey.
     *
     * @static
     * @memberOf _
     * @category Arrays
     * @param {Array} array The array to compact.
     * @returns {Array} Returns a new array of filtered values.
     * @example
     *
     * _.compact([0, 1, false, 2, '', 3]);
     * // => [1, 2, 3]
     */
    function compact(array) {
      var index = -1,
          length = array ? array.length : 0,
          result = [];

      while (++index < length) {
        var value = array[index];
        if (value) {
          result.push(value);
        }
      }
      return result;
    }

    /**
     * Creates an array excluding all values of the provided arrays using strict
     * equality for comparisons, i.e. `===`.
     *
     * @static
     * @memberOf _
     * @category Arrays
     * @param {Array} array The array to process.
     * @param {...Array} [values] The arrays of values to exclude.
     * @returns {Array} Returns a new array of filtered values.
     * @example
     *
     * _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
     * // => [1, 3, 4]
     */
    function difference(array) {
      return baseDifference(array, baseFlatten(arguments, true, true, 1));
    }

    /**
     * This method is like `_.find` except that it returns the index of the first
     * element that passes the callback check, instead of the element itself.
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @category Arrays
     * @param {Array} array The array to search.
     * @param {Function|Object|string} [callback=identity] The function called
     *  per iteration. If a property name or object is provided it will be used
     *  to create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {number} Returns the index of the found element, else `-1`.
     * @example
     *
     * var characters = [
     *   { 'name': 'barney',  'age': 36, 'blocked': false },
     *   { 'name': 'fred',    'age': 40, 'blocked': true },
     *   { 'name': 'pebbles', 'age': 1,  'blocked': false }
     * ];
     *
     * _.findIndex(characters, function(chr) {
     *   return chr.age < 20;
     * });
     * // => 2
     *
     * // using "_.where" callback shorthand
     * _.findIndex(characters, { 'age': 36 });
     * // => 0
     *
     * // using "_.pluck" callback shorthand
     * _.findIndex(characters, 'blocked');
     * // => 1
     */
    function findIndex(array, callback, thisArg) {
      var index = -1,
          length = array ? array.length : 0;

      callback = lodash.createCallback(callback, thisArg, 3);
      while (++index < length) {
        if (callback(array[index], index, array)) {
          return index;
        }
      }
      return -1;
    }

    /**
     * This method is like `_.findIndex` except that it iterates over elements
     * of a `collection` from right to left.
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @category Arrays
     * @param {Array} array The array to search.
     * @param {Function|Object|string} [callback=identity] The function called
     *  per iteration. If a property name or object is provided it will be used
     *  to create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {number} Returns the index of the found element, else `-1`.
     * @example
     *
     * var characters = [
     *   { 'name': 'barney',  'age': 36, 'blocked': true },
     *   { 'name': 'fred',    'age': 40, 'blocked': false },
     *   { 'name': 'pebbles', 'age': 1,  'blocked': true }
     * ];
     *
     * _.findLastIndex(characters, function(chr) {
     *   return chr.age > 30;
     * });
     * // => 1
     *
     * // using "_.where" callback shorthand
     * _.findLastIndex(characters, { 'age': 36 });
     * // => 0
     *
     * // using "_.pluck" callback shorthand
     * _.findLastIndex(characters, 'blocked');
     * // => 2
     */
    function findLastIndex(array, callback, thisArg) {
      var length = array ? array.length : 0;
      callback = lodash.createCallback(callback, thisArg, 3);
      while (length--) {
        if (callback(array[length], length, array)) {
          return length;
        }
      }
      return -1;
    }

    /**
     * Gets the first element or first `n` elements of an array. If a callback
     * is provided elements at the beginning of the array are returned as long
     * as the callback returns truey. The callback is bound to `thisArg` and
     * invoked with three arguments; (value, index, array).
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @alias head, take
     * @category Arrays
     * @param {Array} array The array to query.
     * @param {Function|Object|number|string} [callback] The function called
     *  per element or the number of elements to return. If a property name or
     *  object is provided it will be used to create a "_.pluck" or "_.where"
     *  style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {*} Returns the first element(s) of `array`.
     * @example
     *
     * _.first([1, 2, 3]);
     * // => 1
     *
     * _.first([1, 2, 3], 2);
     * // => [1, 2]
     *
     * _.first([1, 2, 3], function(num) {
     *   return num < 3;
     * });
     * // => [1, 2]
     *
     * var characters = [
     *   { 'name': 'barney',  'blocked': true,  'employer': 'slate' },
     *   { 'name': 'fred',    'blocked': false, 'employer': 'slate' },
     *   { 'name': 'pebbles', 'blocked': true,  'employer': 'na' }
     * ];
     *
     * // using "_.pluck" callback shorthand
     * _.first(characters, 'blocked');
     * // => [{ 'name': 'barney', 'blocked': true, 'employer': 'slate' }]
     *
     * // using "_.where" callback shorthand
     * _.pluck(_.first(characters, { 'employer': 'slate' }), 'name');
     * // => ['barney', 'fred']
     */
    function first(array, callback, thisArg) {
      var n = 0,
          length = array ? array.length : 0;

      if (typeof callback != 'number' && callback != null) {
        var index = -1;
        callback = lodash.createCallback(callback, thisArg, 3);
        while (++index < length && callback(array[index], index, array)) {
          n++;
        }
      } else {
        n = callback;
        if (n == null || thisArg) {
          return array ? array[0] : undefined;
        }
      }
      return slice(array, 0, nativeMin(nativeMax(0, n), length));
    }

    /**
     * Flattens a nested array (the nesting can be to any depth). If `isShallow`
     * is truey, the array will only be flattened a single level. If a callback
     * is provided each element of the array is passed through the callback before
     * flattening. The callback is bound to `thisArg` and invoked with three
     * arguments; (value, index, array).
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @category Arrays
     * @param {Array} array The array to flatten.
     * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
     * @param {Function|Object|string} [callback=identity] The function called
     *  per iteration. If a property name or object is provided it will be used
     *  to create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Array} Returns a new flattened array.
     * @example
     *
     * _.flatten([1, [2], [3, [[4]]]]);
     * // => [1, 2, 3, 4];
     *
     * _.flatten([1, [2], [3, [[4]]]], true);
     * // => [1, 2, 3, [[4]]];
     *
     * var characters = [
     *   { 'name': 'barney', 'age': 30, 'pets': ['hoppy'] },
     *   { 'name': 'fred',   'age': 40, 'pets': ['baby puss', 'dino'] }
     * ];
     *
     * // using "_.pluck" callback shorthand
     * _.flatten(characters, 'pets');
     * // => ['hoppy', 'baby puss', 'dino']
     */
    function flatten(array, isShallow, callback, thisArg) {
      // juggle arguments
      if (typeof isShallow != 'boolean' && isShallow != null) {
        thisArg = callback;
        callback = (typeof isShallow != 'function' && thisArg && thisArg[isShallow] === array) ? null : isShallow;
        isShallow = false;
      }
      if (callback != null) {
        array = map(array, callback, thisArg);
      }
      return baseFlatten(array, isShallow);
    }

    /**
     * Gets the index at which the first occurrence of `value` is found using
     * strict equality for comparisons, i.e. `===`. If the array is already sorted
     * providing `true` for `fromIndex` will run a faster binary search.
     *
     * @static
     * @memberOf _
     * @category Arrays
     * @param {Array} array The array to search.
     * @param {*} value The value to search for.
     * @param {boolean|number} [fromIndex=0] The index to search from or `true`
     *  to perform a binary search on a sorted array.
     * @returns {number} Returns the index of the matched value or `-1`.
     * @example
     *
     * _.indexOf([1, 2, 3, 1, 2, 3], 2);
     * // => 1
     *
     * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3);
     * // => 4
     *
     * _.indexOf([1, 1, 2, 2, 3, 3], 2, true);
     * // => 2
     */
    function indexOf(array, value, fromIndex) {
      if (typeof fromIndex == 'number') {
        var length = array ? array.length : 0;
        fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0);
      } else if (fromIndex) {
        var index = sortedIndex(array, value);
        return array[index] === value ? index : -1;
      }
      return baseIndexOf(array, value, fromIndex);
    }

    /**
     * Gets all but the last element or last `n` elements of an array. If a
     * callback is provided elements at the end of the array are excluded from
     * the result as long as the callback returns truey. The callback is bound
     * to `thisArg` and invoked with three arguments; (value, index, array).
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @category Arrays
     * @param {Array} array The array to query.
     * @param {Function|Object|number|string} [callback=1] The function called
     *  per element or the number of elements to exclude. If a property name or
     *  object is provided it will be used to create a "_.pluck" or "_.where"
     *  style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Array} Returns a slice of `array`.
     * @example
     *
     * _.initial([1, 2, 3]);
     * // => [1, 2]
     *
     * _.initial([1, 2, 3], 2);
     * // => [1]
     *
     * _.initial([1, 2, 3], function(num) {
     *   return num > 1;
     * });
     * // => [1]
     *
     * var characters = [
     *   { 'name': 'barney',  'blocked': false, 'employer': 'slate' },
     *   { 'name': 'fred',    'blocked': true,  'employer': 'slate' },
     *   { 'name': 'pebbles', 'blocked': true,  'employer': 'na' }
     * ];
     *
     * // using "_.pluck" callback shorthand
     * _.initial(characters, 'blocked');
     * // => [{ 'name': 'barney',  'blocked': false, 'employer': 'slate' }]
     *
     * // using "_.where" callback shorthand
     * _.pluck(_.initial(characters, { 'employer': 'na' }), 'name');
     * // => ['barney', 'fred']
     */
    function initial(array, callback, thisArg) {
      var n = 0,
          length = array ? array.length : 0;

      if (typeof callback != 'number' && callback != null) {
        var index = length;
        callback = lodash.createCallback(callback, thisArg, 3);
        while (index-- && callback(array[index], index, array)) {
          n++;
        }
      } else {
        n = (callback == null || thisArg) ? 1 : callback || n;
      }
      return slice(array, 0, nativeMin(nativeMax(0, length - n), length));
    }

    /**
     * Creates an array of unique values present in all provided arrays using
     * strict equality for comparisons, i.e. `===`.
     *
     * @static
     * @memberOf _
     * @category Arrays
     * @param {...Array} [array] The arrays to inspect.
     * @returns {Array} Returns an array of shared values.
     * @example
     *
     * _.intersection([1, 2, 3], [5, 2, 1, 4], [2, 1]);
     * // => [1, 2]
     */
    function intersection() {
      var args = [],
          argsIndex = -1,
          argsLength = arguments.length,
          caches = getArray(),
          indexOf = getIndexOf(),
          trustIndexOf = indexOf === baseIndexOf,
          seen = getArray();

      while (++argsIndex < argsLength) {
        var value = arguments[argsIndex];
        if (isArray(value) || isArguments(value)) {
          args.push(value);
          caches.push(trustIndexOf && value.length >= largeArraySize &&
            createCache(argsIndex ? args[argsIndex] : seen));
        }
      }
      var array = args[0],
          index = -1,
          length = array ? array.length : 0,
          result = [];

      outer:
      while (++index < length) {
        var cache = caches[0];
        value = array[index];

        if ((cache ? cacheIndexOf(cache, value) : indexOf(seen, value)) < 0) {
          argsIndex = argsLength;
          (cache || seen).push(value);
          while (--argsIndex) {
            cache = caches[argsIndex];
            if ((cache ? cacheIndexOf(cache, value) : indexOf(args[argsIndex], value)) < 0) {
              continue outer;
            }
          }
          result.push(value);
        }
      }
      while (argsLength--) {
        cache = caches[argsLength];
        if (cache) {
          releaseObject(cache);
        }
      }
      releaseArray(caches);
      releaseArray(seen);
      return result;
    }

    /**
     * Gets the last element or last `n` elements of an array. If a callback is
     * provided elements at the end of the array are returned as long as the
     * callback returns truey. The callback is bound to `thisArg` and invoked
     * with three arguments; (value, index, array).
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @category Arrays
     * @param {Array} array The array to query.
     * @param {Function|Object|number|string} [callback] The function called
     *  per element or the number of elements to return. If a property name or
     *  object is provided it will be used to create a "_.pluck" or "_.where"
     *  style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {*} Returns the last element(s) of `array`.
     * @example
     *
     * _.last([1, 2, 3]);
     * // => 3
     *
     * _.last([1, 2, 3], 2);
     * // => [2, 3]
     *
     * _.last([1, 2, 3], function(num) {
     *   return num > 1;
     * });
     * // => [2, 3]
     *
     * var characters = [
     *   { 'name': 'barney',  'blocked': false, 'employer': 'slate' },
     *   { 'name': 'fred',    'blocked': true,  'employer': 'slate' },
     *   { 'name': 'pebbles', 'blocked': true,  'employer': 'na' }
     * ];
     *
     * // using "_.pluck" callback shorthand
     * _.pluck(_.last(characters, 'blocked'), 'name');
     * // => ['fred', 'pebbles']
     *
     * // using "_.where" callback shorthand
     * _.last(characters, { 'employer': 'na' });
     * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
     */
    function last(array, callback, thisArg) {
      var n = 0,
          length = array ? array.length : 0;

      if (typeof callback != 'number' && callback != null) {
        var index = length;
        callback = lodash.createCallback(callback, thisArg, 3);
        while (index-- && callback(array[index], index, array)) {
          n++;
        }
      } else {
        n = callback;
        if (n == null || thisArg) {
          return array ? array[length - 1] : undefined;
        }
      }
      return slice(array, nativeMax(0, length - n));
    }

    /**
     * Gets the index at which the last occurrence of `value` is found using strict
     * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used
     * as the offset from the end of the collection.
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @category Arrays
     * @param {Array} array The array to search.
     * @param {*} value The value to search for.
     * @param {number} [fromIndex=array.length-1] The index to search from.
     * @returns {number} Returns the index of the matched value or `-1`.
     * @example
     *
     * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
     * // => 4
     *
     * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3);
     * // => 1
     */
    function lastIndexOf(array, value, fromIndex) {
      var index = array ? array.length : 0;
      if (typeof fromIndex == 'number') {
        index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1;
      }
      while (index--) {
        if (array[index] === value) {
          return index;
        }
      }
      return -1;
    }

    /**
     * Removes all provided values from the given array using strict equality for
     * comparisons, i.e. `===`.
     *
     * @static
     * @memberOf _
     * @category Arrays
     * @param {Array} array The array to modify.
     * @param {...*} [value] The values to remove.
     * @returns {Array} Returns `array`.
     * @example
     *
     * var array = [1, 2, 3, 1, 2, 3];
     * _.pull(array, 2, 3);
     * console.log(array);
     * // => [1, 1]
     */
    function pull(array) {
      var args = arguments,
          argsIndex = 0,
          argsLength = args.length,
          length = array ? array.length : 0;

      while (++argsIndex < argsLength) {
        var index = -1,
            value = args[argsIndex];
        while (++index < length) {
          if (array[index] === value) {
            splice.call(array, index--, 1);
            length--;
          }
        }
      }
      return array;
    }

    /**
     * Creates an array of numbers (positive and/or negative) progressing from
     * `start` up to but not including `end`. If `start` is less than `stop` a
     * zero-length range is created unless a negative `step` is specified.
     *
     * @static
     * @memberOf _
     * @category Arrays
     * @param {number} [start=0] The start of the range.
     * @param {number} end The end of the range.
     * @param {number} [step=1] The value to increment or decrement by.
     * @returns {Array} Returns a new range array.
     * @example
     *
     * _.range(4);
     * // => [0, 1, 2, 3]
     *
     * _.range(1, 5);
     * // => [1, 2, 3, 4]
     *
     * _.range(0, 20, 5);
     * // => [0, 5, 10, 15]
     *
     * _.range(0, -4, -1);
     * // => [0, -1, -2, -3]
     *
     * _.range(1, 4, 0);
     * // => [1, 1, 1]
     *
     * _.range(0);
     * // => []
     */
    function range(start, end, step) {
      start = +start || 0;
      step = typeof step == 'number' ? step : (+step || 1);

      if (end == null) {
        end = start;
        start = 0;
      }
      // use `Array(length)` so engines like Chakra and V8 avoid slower modes
      // http://youtu.be/XAqIpGU8ZZk#t=17m25s
      var index = -1,
          length = nativeMax(0, ceil((end - start) / (step || 1))),
          result = Array(length);

      while (++index < length) {
        result[index] = start;
        start += step;
      }
      return result;
    }

    /**
     * Removes all elements from an array that the callback returns truey for
     * and returns an array of removed elements. The callback is bound to `thisArg`
     * and invoked with three arguments; (value, index, array).
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @category Arrays
     * @param {Array} array The array to modify.
     * @param {Function|Object|string} [callback=identity] The function called
     *  per iteration. If a property name or object is provided it will be used
     *  to create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Array} Returns a new array of removed elements.
     * @example
     *
     * var array = [1, 2, 3, 4, 5, 6];
     * var evens = _.remove(array, function(num) { return num % 2 == 0; });
     *
     * console.log(array);
     * // => [1, 3, 5]
     *
     * console.log(evens);
     * // => [2, 4, 6]
     */
    function remove(array, callback, thisArg) {
      var index = -1,
          length = array ? array.length : 0,
          result = [];

      callback = lodash.createCallback(callback, thisArg, 3);
      while (++index < length) {
        var value = array[index];
        if (callback(value, index, array)) {
          result.push(value);
          splice.call(array, index--, 1);
          length--;
        }
      }
      return result;
    }

    /**
     * The opposite of `_.initial` this method gets all but the first element or
     * first `n` elements of an array. If a callback function is provided elements
     * at the beginning of the array are excluded from the result as long as the
     * callback returns truey. The callback is bound to `thisArg` and invoked
     * with three arguments; (value, index, array).
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @alias drop, tail
     * @category Arrays
     * @param {Array} array The array to query.
     * @param {Function|Object|number|string} [callback=1] The function called
     *  per element or the number of elements to exclude. If a property name or
     *  object is provided it will be used to create a "_.pluck" or "_.where"
     *  style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Array} Returns a slice of `array`.
     * @example
     *
     * _.rest([1, 2, 3]);
     * // => [2, 3]
     *
     * _.rest([1, 2, 3], 2);
     * // => [3]
     *
     * _.rest([1, 2, 3], function(num) {
     *   return num < 3;
     * });
     * // => [3]
     *
     * var characters = [
     *   { 'name': 'barney',  'blocked': true,  'employer': 'slate' },
     *   { 'name': 'fred',    'blocked': false,  'employer': 'slate' },
     *   { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
     * ];
     *
     * // using "_.pluck" callback shorthand
     * _.pluck(_.rest(characters, 'blocked'), 'name');
     * // => ['fred', 'pebbles']
     *
     * // using "_.where" callback shorthand
     * _.rest(characters, { 'employer': 'slate' });
     * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
     */
    function rest(array, callback, thisArg) {
      if (typeof callback != 'number' && callback != null) {
        var n = 0,
            index = -1,
            length = array ? array.length : 0;

        callback = lodash.createCallback(callback, thisArg, 3);
        while (++index < length && callback(array[index], index, array)) {
          n++;
        }
      } else {
        n = (callback == null || thisArg) ? 1 : nativeMax(0, callback);
      }
      return slice(array, n);
    }

    /**
     * Uses a binary search to determine the smallest index at which a value
     * should be inserted into a given sorted array in order to maintain the sort
     * order of the array. If a callback is provided it will be executed for
     * `value` and each element of `array` to compute their sort ranking. The
     * callback is bound to `thisArg` and invoked with one argument; (value).
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @category Arrays
     * @param {Array} array The array to inspect.
     * @param {*} value The value to evaluate.
     * @param {Function|Object|string} [callback=identity] The function called
     *  per iteration. If a property name or object is provided it will be used
     *  to create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {number} Returns the index at which `value` should be inserted
     *  into `array`.
     * @example
     *
     * _.sortedIndex([20, 30, 50], 40);
     * // => 2
     *
     * // using "_.pluck" callback shorthand
     * _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x');
     * // => 2
     *
     * var dict = {
     *   'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 }
     * };
     *
     * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
     *   return dict.wordToNumber[word];
     * });
     * // => 2
     *
     * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
     *   return this.wordToNumber[word];
     * }, dict);
     * // => 2
     */
    function sortedIndex(array, value, callback, thisArg) {
      var low = 0,
          high = array ? array.length : low;

      // explicitly reference `identity` for better inlining in Firefox
      callback = callback ? lodash.createCallback(callback, thisArg, 1) : identity;
      value = callback(value);

      while (low < high) {
        var mid = (low + high) >>> 1;
        (callback(array[mid]) < value)
          ? low = mid + 1
          : high = mid;
      }
      return low;
    }

    /**
     * Creates an array of unique values, in order, of the provided arrays using
     * strict equality for comparisons, i.e. `===`.
     *
     * @static
     * @memberOf _
     * @category Arrays
     * @param {...Array} [array] The arrays to inspect.
     * @returns {Array} Returns an array of combined values.
     * @example
     *
     * _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]);
     * // => [1, 2, 3, 5, 4]
     */
    function union() {
      return baseUniq(baseFlatten(arguments, true, true));
    }

    /**
     * Creates a duplicate-value-free version of an array using strict equality
     * for comparisons, i.e. `===`. If the array is sorted, providing
     * `true` for `isSorted` will use a faster algorithm. If a callback is provided
     * each element of `array` is passed through the callback before uniqueness
     * is computed. The callback is bound to `thisArg` and invoked with three
     * arguments; (value, index, array).
     *
     * If a property name is provided for `callback` the created "_.pluck" style
     * callback will return the property value of the given element.
     *
     * If an object is provided for `callback` the created "_.where" style callback
     * will return `true` for elements that have the properties of the given object,
     * else `false`.
     *
     * @static
     * @memberOf _
     * @alias unique
     * @category Arrays
     * @param {Array} array The array to process.
     * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
     * @param {Function|Object|string} [callback=identity] The function called
     *  per iteration. If a property name or object is provided it will be used
     *  to create a "_.pluck" or "_.where" style callback, respectively.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Array} Returns a duplicate-value-free array.
     * @example
     *
     * _.uniq([1, 2, 1, 3, 1]);
     * // => [1, 2, 3]
     *
     * _.uniq([1, 1, 2, 2, 3], true);
     * // => [1, 2, 3]
     *
     * _.uniq(['A', 'b', 'C', 'a', 'B', 'c'], function(letter) { return letter.toLowerCase(); });
     * // => ['A', 'b', 'C']
     *
     * _.uniq([1, 2.5, 3, 1.5, 2, 3.5], function(num) { return this.floor(num); }, Math);
     * // => [1, 2.5, 3]
     *
     * // using "_.pluck" callback shorthand
     * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
     * // => [{ 'x': 1 }, { 'x': 2 }]
     */
    function uniq(array, isSorted, callback, thisArg) {
      // juggle arguments
      if (typeof isSorted != 'boolean' && isSorted != null) {
        thisArg = callback;
        callback = (typeof isSorted != 'function' && thisArg && thisArg[isSorted] === array) ? null : isSorted;
        isSorted = false;
      }
      if (callback != null) {
        callback = lodash.createCallback(callback, thisArg, 3);
      }
      return baseUniq(array, isSorted, callback);
    }

    /**
     * Creates an array excluding all provided values using strict equality for
     * comparisons, i.e. `===`.
     *
     * @static
     * @memberOf _
     * @category Arrays
     * @param {Array} array The array to filter.
     * @param {...*} [value] The values to exclude.
     * @returns {Array} Returns a new array of filtered values.
     * @example
     *
     * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
     * // => [2, 3, 4]
     */
    function without(array) {
      return baseDifference(array, slice(arguments, 1));
    }

    /**
     * Creates an array that is the symmetric difference of the provided arrays.
     * See http://en.wikipedia.org/wiki/Symmetric_difference.
     *
     * @static
     * @memberOf _
     * @category Arrays
     * @param {...Array} [array] The arrays to inspect.
     * @returns {Array} Returns an array of values.
     * @example
     *
     * _.xor([1, 2, 3], [5, 2, 1, 4]);
     * // => [3, 5, 4]
     *
     * _.xor([1, 2, 5], [2, 3, 5], [3, 4, 5]);
     * // => [1, 4, 5]
     */
    function xor() {
      var index = -1,
          length = arguments.length;

      while (++index < length) {
        var array = arguments[index];
        if (isArray(array) || isArguments(array)) {
          var result = result
            ? baseUniq(baseDifference(result, array).concat(baseDifference(array, result)))
            : array;
        }
      }
      return result || [];
    }

    /**
     * Creates an array of grouped elements, the first of which contains the first
     * elements of the given arrays, the second of which contains the second
     * elements of the given arrays, and so on.
     *
     * @static
     * @memberOf _
     * @alias unzip
     * @category Arrays
     * @param {...Array} [array] Arrays to process.
     * @returns {Array} Returns a new array of grouped elements.
     * @example
     *
     * _.zip(['fred', 'barney'], [30, 40], [true, false]);
     * // => [['fred', 30, true], ['barney', 40, false]]
     */
    function zip() {
      var array = arguments.length > 1 ? arguments : arguments[0],
          index = -1,
          length = array ? max(pluck(array, 'length')) : 0,
          result = Array(length < 0 ? 0 : length);

      while (++index < length) {
        result[index] = pluck(array, index);
      }
      return result;
    }

    /**
     * Creates an object composed from arrays of `keys` and `values`. Provide
     * either a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`
     * or two arrays, one of `keys` and one of corresponding `values`.
     *
     * @static
     * @memberOf _
     * @alias object
     * @category Arrays
     * @param {Array} keys The array of keys.
     * @param {Array} [values=[]] The array of values.
     * @returns {Object} Returns an object composed of the given keys and
     *  corresponding values.
     * @example
     *
     * _.zipObject(['fred', 'barney'], [30, 40]);
     * // => { 'fred': 30, 'barney': 40 }
     */
    function zipObject(keys, values) {
      var index = -1,
          length = keys ? keys.length : 0,
          result = {};

      if (!values && length && !isArray(keys[0])) {
        values = [];
      }
      while (++index < length) {
        var key = keys[index];
        if (values) {
          result[key] = values[index];
        } else if (key) {
          result[key[0]] = key[1];
        }
      }
      return result;
    }

    /*--------------------------------------------------------------------------*/

    /**
     * Creates a function that executes `func`, with  the `this` binding and
     * arguments of the created function, only after being called `n` times.
     *
     * @static
     * @memberOf _
     * @category Functions
     * @param {number} n The number of times the function must be called before
     *  `func` is executed.
     * @param {Function} func The function to restrict.
     * @returns {Function} Returns the new restricted function.
     * @example
     *
     * var saves = ['profile', 'settings'];
     *
     * var done = _.after(saves.length, function() {
     *   console.log('Done saving!');
     * });
     *
     * _.forEach(saves, function(type) {
     *   asyncSave({ 'type': type, 'complete': done });
     * });
     * // => logs 'Done saving!', after all saves have completed
     */
    function after(n, func) {
      if (!isFunction(func)) {
        throw new TypeError;
      }
      return function() {
        if (--n < 1) {
          return func.apply(this, arguments);
        }
      };
    }

    /**
     * Creates a function that, when called, invokes `func` with the `this`
     * binding of `thisArg` and prepends any additional `bind` arguments to those
     * provided to the bound function.
     *
     * @static
     * @memberOf _
     * @category Functions
     * @param {Function} func The function to bind.
     * @param {*} [thisArg] The `this` binding of `func`.
     * @param {...*} [arg] Arguments to be partially applied.
     * @returns {Function} Returns the new bound function.
     * @example
     *
     * var func = function(greeting) {
     *   return greeting + ' ' + this.name;
     * };
     *
     * func = _.bind(func, { 'name': 'fred' }, 'hi');
     * func();
     * // => 'hi fred'
     */
    function bind(func, thisArg) {
      return arguments.length > 2
        ? createWrapper(func, 17, slice(arguments, 2), null, thisArg)
        : createWrapper(func, 1, null, null, thisArg);
    }

    /**
     * Binds methods of an object to the object itself, overwriting the existing
     * method. Method names may be specified as individual arguments or as arrays
     * of method names. If no method names are provided all the function properties
     * of `object` will be bound.
     *
     * @static
     * @memberOf _
     * @category Functions
     * @param {Object} object The object to bind and assign the bound methods to.
     * @param {...string} [methodName] The object method names to
     *  bind, specified as individual method names or arrays of method names.
     * @returns {Object} Returns `object`.
     * @example
     *
     * var view = {
     *   'label': 'docs',
     *   'onClick': function() { console.log('clicked ' + this.label); }
     * };
     *
     * _.bindAll(view);
     * jQuery('#docs').on('click', view.onClick);
     * // => logs 'clicked docs', when the button is clicked
     */
    function bindAll(object) {
      var funcs = arguments.length > 1 ? baseFlatten(arguments, true, false, 1) : functions(object),
          index = -1,
          length = funcs.length;

      while (++index < length) {
        var key = funcs[index];
        object[key] = createWrapper(object[key], 1, null, null, object);
      }
      return object;
    }

    /**
     * Creates a function that, when called, invokes the method at `object[key]`
     * and prepends any additional `bindKey` arguments to those provided to the bound
     * function. This method differs from `_.bind` by allowing bound functions to
     * reference methods that will be redefined or don't yet exist.
     * See http://michaux.ca/articles/lazy-function-definition-pattern.
     *
     * @static
     * @memberOf _
     * @category Functions
     * @param {Object} object The object the method belongs to.
     * @param {string} key The key of the method.
     * @param {...*} [arg] Arguments to be partially applied.
     * @returns {Function} Returns the new bound function.
     * @example
     *
     * var object = {
     *   'name': 'fred',
     *   'greet': function(greeting) {
     *     return greeting + ' ' + this.name;
     *   }
     * };
     *
     * var func = _.bindKey(object, 'greet', 'hi');
     * func();
     * // => 'hi fred'
     *
     * object.greet = function(greeting) {
     *   return greeting + 'ya ' + this.name + '!';
     * };
     *
     * func();
     * // => 'hiya fred!'
     */
    function bindKey(object, key) {
      return arguments.length > 2
        ? createWrapper(key, 19, slice(arguments, 2), null, object)
        : createWrapper(key, 3, null, null, object);
    }

    /**
     * Creates a function that is the composition of the provided functions,
     * where each function consumes the return value of the function that follows.
     * For example, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`.
     * Each function is executed with the `this` binding of the composed function.
     *
     * @static
     * @memberOf _
     * @category Functions
     * @param {...Function} [func] Functions to compose.
     * @returns {Function} Returns the new composed function.
     * @example
     *
     * var realNameMap = {
     *   'pebbles': 'penelope'
     * };
     *
     * var format = function(name) {
     *   name = realNameMap[name.toLowerCase()] || name;
     *   return name.charAt(0).toUpperCase() + name.slice(1).toLowerCase();
     * };
     *
     * var greet = function(formatted) {
     *   return 'Hiya ' + formatted + '!';
     * };
     *
     * var welcome = _.compose(greet, format);
     * welcome('pebbles');
     * // => 'Hiya Penelope!'
     */
    function compose() {
      var funcs = arguments,
          length = funcs.length;

      while (length--) {
        if (!isFunction(funcs[length])) {
          throw new TypeError;
        }
      }
      return function() {
        var args = arguments,
            length = funcs.length;

        while (length--) {
          args = [funcs[length].apply(this, args)];
        }
        return args[0];
      };
    }

    /**
     * Creates a function which accepts one or more arguments of `func` that when
     * invoked either executes `func` returning its result, if all `func` arguments
     * have been provided, or returns a function that accepts one or more of the
     * remaining `func` arguments, and so on. The arity of `func` can be specified
     * if `func.length` is not sufficient.
     *
     * @static
     * @memberOf _
     * @category Functions
     * @param {Function} func The function to curry.
     * @param {number} [arity=func.length] The arity of `func`.
     * @returns {Function} Returns the new curried function.
     * @example
     *
     * var curried = _.curry(function(a, b, c) {
     *   console.log(a + b + c);
     * });
     *
     * curried(1)(2)(3);
     * // => 6
     *
     * curried(1, 2)(3);
     * // => 6
     *
     * curried(1, 2, 3);
     * // => 6
     */
    function curry(func, arity) {
      arity = typeof arity == 'number' ? arity : (+arity || func.length);
      return createWrapper(func, 4, null, null, null, arity);
    }

    /**
     * Creates a function that will delay the execution of `func` until after
     * `wait` milliseconds have elapsed since the last time it was invoked.
     * Provide an options object to indicate that `func` should be invoked on
     * the leading and/or trailing edge of the `wait` timeout. Subsequent calls
     * to the debounced function will return the result of the last `func` call.
     *
     * Note: If `leading` and `trailing` options are `true` `func` will be called
     * on the trailing edge of the timeout only if the the debounced function is
     * invoked more than once during the `wait` timeout.
     *
     * @static
     * @memberOf _
     * @category Functions
     * @param {Function} func The function to debounce.
     * @param {number} wait The number of milliseconds to delay.
     * @param {Object} [options] The options object.
     * @param {boolean} [options.leading=false] Specify execution on the leading edge of the timeout.
     * @param {number} [options.maxWait] The maximum time `func` is allowed to be delayed before it's called.
     * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
     * @returns {Function} Returns the new debounced function.
     * @example
     *
     * // avoid costly calculations while the window size is in flux
     * var lazyLayout = _.debounce(calculateLayout, 150);
     * jQuery(window).on('resize', lazyLayout);
     *
     * // execute `sendMail` when the click event is fired, debouncing subsequent calls
     * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
     *   'leading': true,
     *   'trailing': false
     * });
     *
     * // ensure `batchLog` is executed once after 1 second of debounced calls
     * var source = new EventSource('/stream');
     * source.addEventListener('message', _.debounce(batchLog, 250, {
     *   'maxWait': 1000
     * }, false);
     */
    function debounce(func, wait, options) {
      var args,
          maxTimeoutId,
          result,
          stamp,
          thisArg,
          timeoutId,
          trailingCall,
          lastCalled = 0,
          maxWait = false,
          trailing = true;

      if (!isFunction(func)) {
        throw new TypeError;
      }
      wait = nativeMax(0, wait) || 0;
      if (options === true) {
        var leading = true;
        trailing = false;
      } else if (isObject(options)) {
        leading = options.leading;
        maxWait = 'maxWait' in options && (nativeMax(wait, options.maxWait) || 0);
        trailing = 'trailing' in options ? options.trailing : trailing;
      }
      var delayed = function() {
        var remaining = wait - (now() - stamp);
        if (remaining <= 0) {
          if (maxTimeoutId) {
            clearTimeout(maxTimeoutId);
          }
          var isCalled = trailingCall;
          maxTimeoutId = timeoutId = trailingCall = undefined;
          if (isCalled) {
            lastCalled = now();
            result = func.apply(thisArg, args);
            if (!timeoutId && !maxTimeoutId) {
              args = thisArg = null;
            }
          }
        } else {
          timeoutId = setTimeout(delayed, remaining);
        }
      };

      var maxDelayed = function() {
        if (timeoutId) {
          clearTimeout(timeoutId);
        }
        maxTimeoutId = timeoutId = trailingCall = undefined;
        if (trailing || (maxWait !== wait)) {
          lastCalled = now();
          result = func.apply(thisArg, args);
          if (!timeoutId && !maxTimeoutId) {
            args = thisArg = null;
          }
        }
      };

      return function() {
        args = arguments;
        stamp = now();
        thisArg = this;
        trailingCall = trailing && (timeoutId || !leading);

        if (maxWait === false) {
          var leadingCall = leading && !timeoutId;
        } else {
          if (!maxTimeoutId && !leading) {
            lastCalled = stamp;
          }
          var remaining = maxWait - (stamp - lastCalled),
              isCalled = remaining <= 0;

          if (isCalled) {
            if (maxTimeoutId) {
              maxTimeoutId = clearTimeout(maxTimeoutId);
            }
            lastCalled = stamp;
            result = func.apply(thisArg, args);
          }
          else if (!maxTimeoutId) {
            maxTimeoutId = setTimeout(maxDelayed, remaining);
          }
        }
        if (isCalled && timeoutId) {
          timeoutId = clearTimeout(timeoutId);
        }
        else if (!timeoutId && wait !== maxWait) {
          timeoutId = setTimeout(delayed, wait);
        }
        if (leadingCall) {
          isCalled = true;
          result = func.apply(thisArg, args);
        }
        if (isCalled && !timeoutId && !maxTimeoutId) {
          args = thisArg = null;
        }
        return result;
      };
    }

    /**
     * Defers executing the `func` function until the current call stack has cleared.
     * Additional arguments will be provided to `func` when it is invoked.
     *
     * @static
     * @memberOf _
     * @category Functions
     * @param {Function} func The function to defer.
     * @param {...*} [arg] Arguments to invoke the function with.
     * @returns {number} Returns the timer id.
     * @example
     *
     * _.defer(function(text) { console.log(text); }, 'deferred');
     * // logs 'deferred' after one or more milliseconds
     */
    function defer(func) {
      if (!isFunction(func)) {
        throw new TypeError;
      }
      var args = slice(arguments, 1);
      return setTimeout(function() { func.apply(undefined, args); }, 1);
    }

    /**
     * Executes the `func` function after `wait` milliseconds. Additional arguments
     * will be provided to `func` when it is invoked.
     *
     * @static
     * @memberOf _
     * @category Functions
     * @param {Function} func The function to delay.
     * @param {number} wait The number of milliseconds to delay execution.
     * @param {...*} [arg] Arguments to invoke the function with.
     * @returns {number} Returns the timer id.
     * @example
     *
     * _.delay(function(text) { console.log(text); }, 1000, 'later');
     * // => logs 'later' after one second
     */
    function delay(func, wait) {
      if (!isFunction(func)) {
        throw new TypeError;
      }
      var args = slice(arguments, 2);
      return setTimeout(function() { func.apply(undefined, args); }, wait);
    }

    /**
     * Creates a function that memoizes the result of `func`. If `resolver` is
     * provided it will be used to determine the cache key for storing the result
     * based on the arguments provided to the memoized function. By default, the
     * first argument provided to the memoized function is used as the cache key.
     * The `func` is executed with the `this` binding of the memoized function.
     * The result cache is exposed as the `cache` property on the memoized function.
     *
     * @static
     * @memberOf _
     * @category Functions
     * @param {Function} func The function to have its output memoized.
     * @param {Function} [resolver] A function used to resolve the cache key.
     * @returns {Function} Returns the new memoizing function.
     * @example
     *
     * var fibonacci = _.memoize(function(n) {
     *   return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
     * });
     *
     * fibonacci(9)
     * // => 34
     *
     * var data = {
     *   'fred': { 'name': 'fred', 'age': 40 },
     *   'pebbles': { 'name': 'pebbles', 'age': 1 }
     * };
     *
     * // modifying the result cache
     * var get = _.memoize(function(name) { return data[name]; }, _.identity);
     * get('pebbles');
     * // => { 'name': 'pebbles', 'age': 1 }
     *
     * get.cache.pebbles.name = 'penelope';
     * get('pebbles');
     * // => { 'name': 'penelope', 'age': 1 }
     */
    function memoize(func, resolver) {
      if (!isFunction(func)) {
        throw new TypeError;
      }
      var memoized = function() {
        var cache = memoized.cache,
            key = resolver ? resolver.apply(this, arguments) : keyPrefix + arguments[0];

        return hasOwnProperty.call(cache, key)
          ? cache[key]
          : (cache[key] = func.apply(this, arguments));
      }
      memoized.cache = {};
      return memoized;
    }

    /**
     * Creates a function that is restricted to execute `func` once. Repeat calls to
     * the function will return the value of the first call. The `func` is executed
     * with the `this` binding of the created function.
     *
     * @static
     * @memberOf _
     * @category Functions
     * @param {Function} func The function to restrict.
     * @returns {Function} Returns the new restricted function.
     * @example
     *
     * var initialize = _.once(createApplication);
     * initialize();
     * initialize();
     * // `initialize` executes `createApplication` once
     */
    function once(func) {
      var ran,
          result;

      if (!isFunction(func)) {
        throw new TypeError;
      }
      return function() {
        if (ran) {
          return result;
        }
        ran = true;
        result = func.apply(this, arguments);

        // clear the `func` variable so the function may be garbage collected
        func = null;
        return result;
      };
    }

    /**
     * Creates a function that, when called, invokes `func` with any additional
     * `partial` arguments prepended to those provided to the new function. This
     * method is similar to `_.bind` except it does **not** alter the `this` binding.
     *
     * @static
     * @memberOf _
     * @category Functions
     * @param {Function} func The function to partially apply arguments to.
     * @param {...*} [arg] Arguments to be partially applied.
     * @returns {Function} Returns the new partially applied function.
     * @example
     *
     * var greet = function(greeting, name) { return greeting + ' ' + name; };
     * var hi = _.partial(greet, 'hi');
     * hi('fred');
     * // => 'hi fred'
     */
    function partial(func) {
      return createWrapper(func, 16, slice(arguments, 1));
    }

    /**
     * This method is like `_.partial` except that `partial` arguments are
     * appended to those provided to the new function.
     *
     * @static
     * @memberOf _
     * @category Functions
     * @param {Function} func The function to partially apply arguments to.
     * @param {...*} [arg] Arguments to be partially applied.
     * @returns {Function} Returns the new partially applied function.
     * @example
     *
     * var defaultsDeep = _.partialRight(_.merge, _.defaults);
     *
     * var options = {
     *   'variable': 'data',
     *   'imports': { 'jq': $ }
     * };
     *
     * defaultsDeep(options, _.templateSettings);
     *
     * options.variable
     * // => 'data'
     *
     * options.imports
     * // => { '_': _, 'jq': $ }
     */
    function partialRight(func) {
      return createWrapper(func, 32, null, slice(arguments, 1));
    }

    /**
     * Creates a function that, when executed, will only call the `func` function
     * at most once per every `wait` milliseconds. Provide an options object to
     * indicate that `func` should be invoked on the leading and/or trailing edge
     * of the `wait` timeout. Subsequent calls to the throttled function will
     * return the result of the last `func` call.
     *
     * Note: If `leading` and `trailing` options are `true` `func` will be called
     * on the trailing edge of the timeout only if the the throttled function is
     * invoked more than once during the `wait` timeout.
     *
     * @static
     * @memberOf _
     * @category Functions
     * @param {Function} func The function to throttle.
     * @param {number} wait The number of milliseconds to throttle executions to.
     * @param {Object} [options] The options object.
     * @param {boolean} [options.leading=true] Specify execution on the leading edge of the timeout.
     * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
     * @returns {Function} Returns the new throttled function.
     * @example
     *
     * // avoid excessively updating the position while scrolling
     * var throttled = _.throttle(updatePosition, 100);
     * jQuery(window).on('scroll', throttled);
     *
     * // execute `renewToken` when the click event is fired, but not more than once every 5 minutes
     * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
     *   'trailing': false
     * }));
     */
    function throttle(func, wait, options) {
      var leading = true,
          trailing = true;

      if (!isFunction(func)) {
        throw new TypeError;
      }
      if (options === false) {
        leading = false;
      } else if (isObject(options)) {
        leading = 'leading' in options ? options.leading : leading;
        trailing = 'trailing' in options ? options.trailing : trailing;
      }
      debounceOptions.leading = leading;
      debounceOptions.maxWait = wait;
      debounceOptions.trailing = trailing;

      return debounce(func, wait, debounceOptions);
    }

    /**
     * Creates a function that provides `value` to the wrapper function as its
     * first argument. Additional arguments provided to the function are appended
     * to those provided to the wrapper function. The wrapper is executed with
     * the `this` binding of the created function.
     *
     * @static
     * @memberOf _
     * @category Functions
     * @param {*} value The value to wrap.
     * @param {Function} wrapper The wrapper function.
     * @returns {Function} Returns the new function.
     * @example
     *
     * var p = _.wrap(_.escape, function(func, text) {
     *   return '<p>' + func(text) + '</p>';
     * });
     *
     * p('Fred, Wilma, & Pebbles');
     * // => '<p>Fred, Wilma, &amp; Pebbles</p>'
     */
    function wrap(value, wrapper) {
      return createWrapper(wrapper, 16, [value]);
    }

    /*--------------------------------------------------------------------------*/

    /**
     * Creates a function that returns `value`.
     *
     * @static
     * @memberOf _
     * @category Utilities
     * @param {*} value The value to return from the new function.
     * @returns {Function} Returns the new function.
     * @example
     *
     * var object = { 'name': 'fred' };
     * var getter = _.constant(object);
     * getter() === object;
     * // => true
     */
    function constant(value) {
      return function() {
        return value;
      };
    }

    /**
     * Produces a callback bound to an optional `thisArg`. If `func` is a property
     * name the created callback will return the property value for a given element.
     * If `func` is an object the created callback will return `true` for elements
     * that contain the equivalent object properties, otherwise it will return `false`.
     *
     * @static
     * @memberOf _
     * @category Utilities
     * @param {*} [func=identity] The value to convert to a callback.
     * @param {*} [thisArg] The `this` binding of the created callback.
     * @param {number} [argCount] The number of arguments the callback accepts.
     * @returns {Function} Returns a callback function.
     * @example
     *
     * var characters = [
     *   { 'name': 'barney', 'age': 36 },
     *   { 'name': 'fred',   'age': 40 }
     * ];
     *
     * // wrap to create custom callback shorthands
     * _.createCallback = _.wrap(_.createCallback, function(func, callback, thisArg) {
     *   var match = /^(.+?)__([gl]t)(.+)$/.exec(callback);
     *   return !match ? func(callback, thisArg) : function(object) {
     *     return match[2] == 'gt' ? object[match[1]] > match[3] : object[match[1]] < match[3];
     *   };
     * });
     *
     * _.filter(characters, 'age__gt38');
     * // => [{ 'name': 'fred', 'age': 40 }]
     */
    function createCallback(func, thisArg, argCount) {
      var type = typeof func;
      if (func == null || type == 'function') {
        return baseCreateCallback(func, thisArg, argCount);
      }
      // handle "_.pluck" style callback shorthands
      if (type != 'object') {
        return property(func);
      }
      var props = keys(func),
          key = props[0],
          a = func[key];

      // handle "_.where" style callback shorthands
      if (props.length == 1 && a === a && !isObject(a)) {
        // fast path the common case of providing an object with a single
        // property containing a primitive value
        return function(object) {
          var b = object[key];
          return a === b && (a !== 0 || (1 / a == 1 / b));
        };
      }
      return function(object) {
        var length = props.length,
            result = false;

        while (length--) {
          if (!(result = baseIsEqual(object[props[length]], func[props[length]], null, true))) {
            break;
          }
        }
        return result;
      };
    }

    /**
     * Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their
     * corresponding HTML entities.
     *
     * @static
     * @memberOf _
     * @category Utilities
     * @param {string} string The string to escape.
     * @returns {string} Returns the escaped string.
     * @example
     *
     * _.escape('Fred, Wilma, & Pebbles');
     * // => 'Fred, Wilma, &amp; Pebbles'
     */
    function escape(string) {
      return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar);
    }

    /**
     * This method returns the first argument provided to it.
     *
     * @static
     * @memberOf _
     * @category Utilities
     * @param {*} value Any value.
     * @returns {*} Returns `value`.
     * @example
     *
     * var object = { 'name': 'fred' };
     * _.identity(object) === object;
     * // => true
     */
    function identity(value) {
      return value;
    }

    /**
     * Adds function properties of a source object to the destination object.
     * If `object` is a function methods will be added to its prototype as well.
     *
     * @static
     * @memberOf _
     * @category Utilities
     * @param {Function|Object} [object=lodash] object The destination object.
     * @param {Object} source The object of functions to add.
     * @param {Object} [options] The options object.
     * @param {boolean} [options.chain=true] Specify whether the functions added are chainable.
     * @example
     *
     * function capitalize(string) {
     *   return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
     * }
     *
     * _.mixin({ 'capitalize': capitalize });
     * _.capitalize('fred');
     * // => 'Fred'
     *
     * _('fred').capitalize().value();
     * // => 'Fred'
     *
     * _.mixin({ 'capitalize': capitalize }, { 'chain': false });
     * _('fred').capitalize();
     * // => 'Fred'
     */
    function mixin(object, source, options) {
      var chain = true,
          methodNames = source && functions(source);

      if (!source || (!options && !methodNames.length)) {
        if (options == null) {
          options = source;
        }
        ctor = lodashWrapper;
        source = object;
        object = lodash;
        methodNames = functions(source);
      }
      if (options === false) {
        chain = false;
      } else if (isObject(options) && 'chain' in options) {
        chain = options.chain;
      }
      var ctor = object,
          isFunc = isFunction(ctor);

      forEach(methodNames, function(methodName) {
        var func = object[methodName] = source[methodName];
        if (isFunc) {
          ctor.prototype[methodName] = function() {
            var chainAll = this.__chain__,
                value = this.__wrapped__,
                args = [value];

            push.apply(args, arguments);
            var result = func.apply(object, args);
            if (chain || chainAll) {
              if (value === result && isObject(result)) {
                return this;
              }
              result = new ctor(result);
              result.__chain__ = chainAll;
            }
            return result;
          };
        }
      });
    }

    /**
     * Reverts the '_' variable to its previous value and returns a reference to
     * the `lodash` function.
     *
     * @static
     * @memberOf _
     * @category Utilities
     * @returns {Function} Returns the `lodash` function.
     * @example
     *
     * var lodash = _.noConflict();
     */
    function noConflict() {
      context._ = oldDash;
      return this;
    }

    /**
     * A no-operation function.
     *
     * @static
     * @memberOf _
     * @category Utilities
     * @example
     *
     * var object = { 'name': 'fred' };
     * _.noop(object) === undefined;
     * // => true
     */
    function noop() {
      // no operation performed
    }

    /**
     * Gets the number of milliseconds that have elapsed since the Unix epoch
     * (1 January 1970 00:00:00 UTC).
     *
     * @static
     * @memberOf _
     * @category Utilities
     * @example
     *
     * var stamp = _.now();
     * _.defer(function() { console.log(_.now() - stamp); });
     * // => logs the number of milliseconds it took for the deferred function to be called
     */
    var now = isNative(now = Date.now) && now || function() {
      return new Date().getTime();
    };

    /**
     * Converts the given value into an integer of the specified radix.
     * If `radix` is `undefined` or `0` a `radix` of `10` is used unless the
     * `value` is a hexadecimal, in which case a `radix` of `16` is used.
     *
     * Note: This method avoids differences in native ES3 and ES5 `parseInt`
     * implementations. See http://es5.github.io/#E.
     *
     * @static
     * @memberOf _
     * @category Utilities
     * @param {string} value The value to parse.
     * @param {number} [radix] The radix used to interpret the value to parse.
     * @returns {number} Returns the new integer value.
     * @example
     *
     * _.parseInt('08');
     * // => 8
     */
    var parseInt = nativeParseInt(whitespace + '08') == 8 ? nativeParseInt : function(value, radix) {
      // Firefox < 21 and Opera < 15 follow the ES3 specified implementation of `parseInt`
      return nativeParseInt(isString(value) ? value.replace(reLeadingSpacesAndZeros, '') : value, radix || 0);
    };

    /**
     * Creates a "_.pluck" style function, which returns the `key` value of a
     * given object.
     *
     * @static
     * @memberOf _
     * @category Utilities
     * @param {string} key The name of the property to retrieve.
     * @returns {Function} Returns the new function.
     * @example
     *
     * var characters = [
     *   { 'name': 'fred',   'age': 40 },
     *   { 'name': 'barney', 'age': 36 }
     * ];
     *
     * var getName = _.property('name');
     *
     * _.map(characters, getName);
     * // => ['barney', 'fred']
     *
     * _.sortBy(characters, getName);
     * // => [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred',   'age': 40 }]
     */
    function property(key) {
      return function(object) {
        return object[key];
      };
    }

    /**
     * Produces a random number between `min` and `max` (inclusive). If only one
     * argument is provided a number between `0` and the given number will be
     * returned. If `floating` is truey or either `min` or `max` are floats a
     * floating-point number will be returned instead of an integer.
     *
     * @static
     * @memberOf _
     * @category Utilities
     * @param {number} [min=0] The minimum possible value.
     * @param {number} [max=1] The maximum possible value.
     * @param {boolean} [floating=false] Specify returning a floating-point number.
     * @returns {number} Returns a random number.
     * @example
     *
     * _.random(0, 5);
     * // => an integer between 0 and 5
     *
     * _.random(5);
     * // => also an integer between 0 and 5
     *
     * _.random(5, true);
     * // => a floating-point number between 0 and 5
     *
     * _.random(1.2, 5.2);
     * // => a floating-point number between 1.2 and 5.2
     */
    function random(min, max, floating) {
      var noMin = min == null,
          noMax = max == null;

      if (floating == null) {
        if (typeof min == 'boolean' && noMax) {
          floating = min;
          min = 1;
        }
        else if (!noMax && typeof max == 'boolean') {
          floating = max;
          noMax = true;
        }
      }
      if (noMin && noMax) {
        max = 1;
      }
      min = +min || 0;
      if (noMax) {
        max = min;
        min = 0;
      } else {
        max = +max || 0;
      }
      if (floating || min % 1 || max % 1) {
        var rand = nativeRandom();
        return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand +'').length - 1)))), max);
      }
      return baseRandom(min, max);
    }

    /**
     * Resolves the value of property `key` on `object`. If `key` is a function
     * it will be invoked with the `this` binding of `object` and its result returned,
     * else the property value is returned. If `object` is falsey then `undefined`
     * is returned.
     *
     * @static
     * @memberOf _
     * @category Utilities
     * @param {Object} object The object to inspect.
     * @param {string} key The name of the property to resolve.
     * @returns {*} Returns the resolved value.
     * @example
     *
     * var object = {
     *   'cheese': 'crumpets',
     *   'stuff': function() {
     *     return 'nonsense';
     *   }
     * };
     *
     * _.result(object, 'cheese');
     * // => 'crumpets'
     *
     * _.result(object, 'stuff');
     * // => 'nonsense'
     */
    function result(object, key) {
      if (object) {
        var value = object[key];
        return isFunction(value) ? object[key]() : value;
      }
    }

    /**
     * A micro-templating method that handles arbitrary delimiters, preserves
     * whitespace, and correctly escapes quotes within interpolated code.
     *
     * Note: In the development build, `_.template` utilizes sourceURLs for easier
     * debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
     *
     * For more information on precompiling templates see:
     * http://lodash.com/custom-builds
     *
     * For more information on Chrome extension sandboxes see:
     * http://developer.chrome.com/stable/extensions/sandboxingEval.html
     *
     * @static
     * @memberOf _
     * @category Utilities
     * @param {string} text The template text.
     * @param {Object} data The data object used to populate the text.
     * @param {Object} [options] The options object.
     * @param {RegExp} [options.escape] The "escape" delimiter.
     * @param {RegExp} [options.evaluate] The "evaluate" delimiter.
     * @param {Object} [options.imports] An object to import into the template as local variables.
     * @param {RegExp} [options.interpolate] The "interpolate" delimiter.
     * @param {string} [sourceURL] The sourceURL of the template's compiled source.
     * @param {string} [variable] The data object variable name.
     * @returns {Function|string} Returns a compiled function when no `data` object
     *  is given, else it returns the interpolated text.
     * @example
     *
     * // using the "interpolate" delimiter to create a compiled template
     * var compiled = _.template('hello <%= name %>');
     * compiled({ 'name': 'fred' });
     * // => 'hello fred'
     *
     * // using the "escape" delimiter to escape HTML in data property values
     * _.template('<b><%- value %></b>', { 'value': '<script>' });
     * // => '<b>&lt;script&gt;</b>'
     *
     * // using the "evaluate" delimiter to generate HTML
     * var list = '<% _.forEach(people, function(name) { %><li><%- name %></li><% }); %>';
     * _.template(list, { 'people': ['fred', 'barney'] });
     * // => '<li>fred</li><li>barney</li>'
     *
     * // using the ES6 delimiter as an alternative to the default "interpolate" delimiter
     * _.template('hello ${ name }', { 'name': 'pebbles' });
     * // => 'hello pebbles'
     *
     * // using the internal `print` function in "evaluate" delimiters
     * _.template('<% print("hello " + name); %>!', { 'name': 'barney' });
     * // => 'hello barney!'
     *
     * // using a custom template delimiters
     * _.templateSettings = {
     *   'interpolate': /{{([\s\S]+?)}}/g
     * };
     *
     * _.template('hello {{ name }}!', { 'name': 'mustache' });
     * // => 'hello mustache!'
     *
     * // using the `imports` option to import jQuery
     * var list = '<% jq.each(people, function(name) { %><li><%- name %></li><% }); %>';
     * _.template(list, { 'people': ['fred', 'barney'] }, { 'imports': { 'jq': jQuery } });
     * // => '<li>fred</li><li>barney</li>'
     *
     * // using the `sourceURL` option to specify a custom sourceURL for the template
     * var compiled = _.template('hello <%= name %>', null, { 'sourceURL': '/basic/greeting.jst' });
     * compiled(data);
     * // => find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector
     *
     * // using the `variable` option to ensure a with-statement isn't used in the compiled template
     * var compiled = _.template('hi <%= data.name %>!', null, { 'variable': 'data' });
     * compiled.source;
     * // => function(data) {
     *   var __t, __p = '', __e = _.escape;
     *   __p += 'hi ' + ((__t = ( data.name )) == null ? '' : __t) + '!';
     *   return __p;
     * }
     *
     * // using the `source` property to inline compiled templates for meaningful
     * // line numbers in error messages and a stack trace
     * fs.writeFileSync(path.join(cwd, 'jst.js'), '\
     *   var JST = {\
     *     "main": ' + _.template(mainText).source + '\
     *   };\
     * ');
     */
    function template(text, data, options) {
      // based on John Resig's `tmpl` implementation
      // http://ejohn.org/blog/javascript-micro-templating/
      // and Laura Doktorova's doT.js
      // https://github.com/olado/doT
      var settings = lodash.templateSettings;
      text = String(text || '');

      // avoid missing dependencies when `iteratorTemplate` is not defined
      options = defaults({}, options, settings);

      var imports = defaults({}, options.imports, settings.imports),
          importsKeys = keys(imports),
          importsValues = values(imports);

      var isEvaluating,
          index = 0,
          interpolate = options.interpolate || reNoMatch,
          source = "__p += '";

      // compile the regexp to match each delimiter
      var reDelimiters = RegExp(
        (options.escape || reNoMatch).source + '|' +
        interpolate.source + '|' +
        (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' +
        (options.evaluate || reNoMatch).source + '|$'
      , 'g');

      text.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {
        interpolateValue || (interpolateValue = esTemplateValue);

        // escape characters that cannot be included in string literals
        source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar);

        // replace delimiters with snippets
        if (escapeValue) {
          source += "' +\n__e(" + escapeValue + ") +\n'";
        }
        if (evaluateValue) {
          isEvaluating = true;
          source += "';\n" + evaluateValue + ";\n__p += '";
        }
        if (interpolateValue) {
          source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'";
        }
        index = offset + match.length;

        // the JS engine embedded in Adobe products requires returning the `match`
        // string in order to produce the correct `offset` value
        return match;
      });

      source += "';\n";

      // if `variable` is not specified, wrap a with-statement around the generated
      // code to add the data object to the top of the scope chain
      var variable = options.variable,
          hasVariable = variable;

      if (!hasVariable) {
        variable = 'obj';
        source = 'with (' + variable + ') {\n' + source + '\n}\n';
      }
      // cleanup code by stripping empty strings
      source = (isEvaluating ? source.replace(reEmptyStringLeading, '') : source)
        .replace(reEmptyStringMiddle, '$1')
        .replace(reEmptyStringTrailing, '$1;');

      // frame code as the function body
      source = 'function(' + variable + ') {\n' +
        (hasVariable ? '' : variable + ' || (' + variable + ' = {});\n') +
        "var __t, __p = '', __e = _.escape" +
        (isEvaluating
          ? ', __j = Array.prototype.join;\n' +
            "function print() { __p += __j.call(arguments, '') }\n"
          : ';\n'
        ) +
        source +
        'return __p\n}';

      // Use a sourceURL for easier debugging.
      // http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
      var sourceURL = '\n/*\n//# sourceURL=' + (options.sourceURL || '/lodash/template/source[' + (templateCounter++) + ']') + '\n*/';

      try {
        var result = Function(importsKeys, 'return ' + source + sourceURL).apply(undefined, importsValues);
      } catch(e) {
        e.source = source;
        throw e;
      }
      if (data) {
        return result(data);
      }
      // provide the compiled function's source by its `toString` method, in
      // supported environments, or the `source` property as a convenience for
      // inlining compiled templates during the build process
      result.source = source;
      return result;
    }

    /**
     * Executes the callback `n` times, returning an array of the results
     * of each callback execution. The callback is bound to `thisArg` and invoked
     * with one argument; (index).
     *
     * @static
     * @memberOf _
     * @category Utilities
     * @param {number} n The number of times to execute the callback.
     * @param {Function} callback The function called per iteration.
     * @param {*} [thisArg] The `this` binding of `callback`.
     * @returns {Array} Returns an array of the results of each `callback` execution.
     * @example
     *
     * var diceRolls = _.times(3, _.partial(_.random, 1, 6));
     * // => [3, 6, 4]
     *
     * _.times(3, function(n) { mage.castSpell(n); });
     * // => calls `mage.castSpell(n)` three times, passing `n` of `0`, `1`, and `2` respectively
     *
     * _.times(3, function(n) { this.cast(n); }, mage);
     * // => also calls `mage.castSpell(n)` three times
     */
    function times(n, callback, thisArg) {
      n = (n = +n) > -1 ? n : 0;
      var index = -1,
          result = Array(n);

      callback = baseCreateCallback(callback, thisArg, 1);
      while (++index < n) {
        result[index] = callback(index);
      }
      return result;
    }

    /**
     * The inverse of `_.escape` this method converts the HTML entities
     * `&amp;`, `&lt;`, `&gt;`, `&quot;`, and `&#39;` in `string` to their
     * corresponding characters.
     *
     * @static
     * @memberOf _
     * @category Utilities
     * @param {string} string The string to unescape.
     * @returns {string} Returns the unescaped string.
     * @example
     *
     * _.unescape('Fred, Barney &amp; Pebbles');
     * // => 'Fred, Barney & Pebbles'
     */
    function unescape(string) {
      return string == null ? '' : String(string).replace(reEscapedHtml, unescapeHtmlChar);
    }

    /**
     * Generates a unique ID. If `prefix` is provided the ID will be appended to it.
     *
     * @static
     * @memberOf _
     * @category Utilities
     * @param {string} [prefix] The value to prefix the ID with.
     * @returns {string} Returns the unique ID.
     * @example
     *
     * _.uniqueId('contact_');
     * // => 'contact_104'
     *
     * _.uniqueId();
     * // => '105'
     */
    function uniqueId(prefix) {
      var id = ++idCounter;
      return String(prefix == null ? '' : prefix) + id;
    }

    /*--------------------------------------------------------------------------*/

    /**
     * Creates a `lodash` object that wraps the given value with explicit
     * method chaining enabled.
     *
     * @static
     * @memberOf _
     * @category Chaining
     * @param {*} value The value to wrap.
     * @returns {Object} Returns the wrapper object.
     * @example
     *
     * var characters = [
     *   { 'name': 'barney',  'age': 36 },
     *   { 'name': 'fred',    'age': 40 },
     *   { 'name': 'pebbles', 'age': 1 }
     * ];
     *
     * var youngest = _.chain(characters)
     *     .sortBy('age')
     *     .map(function(chr) { return chr.name + ' is ' + chr.age; })
     *     .first()
     *     .value();
     * // => 'pebbles is 1'
     */
    function chain(value) {
      value = new lodashWrapper(value);
      value.__chain__ = true;
      return value;
    }

    /**
     * Invokes `interceptor` with the `value` as the first argument and then
     * returns `value`. The purpose of this method is to "tap into" a method
     * chain in order to perform operations on intermediate results within
     * the chain.
     *
     * @static
     * @memberOf _
     * @category Chaining
     * @param {*} value The value to provide to `interceptor`.
     * @param {Function} interceptor The function to invoke.
     * @returns {*} Returns `value`.
     * @example
     *
     * _([1, 2, 3, 4])
     *  .tap(function(array) { array.pop(); })
     *  .reverse()
     *  .value();
     * // => [3, 2, 1]
     */
    function tap(value, interceptor) {
      interceptor(value);
      return value;
    }

    /**
     * Enables explicit method chaining on the wrapper object.
     *
     * @name chain
     * @memberOf _
     * @category Chaining
     * @returns {*} Returns the wrapper object.
     * @example
     *
     * var characters = [
     *   { 'name': 'barney', 'age': 36 },
     *   { 'name': 'fred',   'age': 40 }
     * ];
     *
     * // without explicit chaining
     * _(characters).first();
     * // => { 'name': 'barney', 'age': 36 }
     *
     * // with explicit chaining
     * _(characters).chain()
     *   .first()
     *   .pick('age')
     *   .value();
     * // => { 'age': 36 }
     */
    function wrapperChain() {
      this.__chain__ = true;
      return this;
    }

    /**
     * Produces the `toString` result of the wrapped value.
     *
     * @name toString
     * @memberOf _
     * @category Chaining
     * @returns {string} Returns the string result.
     * @example
     *
     * _([1, 2, 3]).toString();
     * // => '1,2,3'
     */
    function wrapperToString() {
      return String(this.__wrapped__);
    }

    /**
     * Extracts the wrapped value.
     *
     * @name valueOf
     * @memberOf _
     * @alias value
     * @category Chaining
     * @returns {*} Returns the wrapped value.
     * @example
     *
     * _([1, 2, 3]).valueOf();
     * // => [1, 2, 3]
     */
    function wrapperValueOf() {
      return this.__wrapped__;
    }

    /*--------------------------------------------------------------------------*/

    // add functions that return wrapped values when chaining
    lodash.after = after;
    lodash.assign = assign;
    lodash.at = at;
    lodash.bind = bind;
    lodash.bindAll = bindAll;
    lodash.bindKey = bindKey;
    lodash.chain = chain;
    lodash.compact = compact;
    lodash.compose = compose;
    lodash.constant = constant;
    lodash.countBy = countBy;
    lodash.create = create;
    lodash.createCallback = createCallback;
    lodash.curry = curry;
    lodash.debounce = debounce;
    lodash.defaults = defaults;
    lodash.defer = defer;
    lodash.delay = delay;
    lodash.difference = difference;
    lodash.filter = filter;
    lodash.flatten = flatten;
    lodash.forEach = forEach;
    lodash.forEachRight = forEachRight;
    lodash.forIn = forIn;
    lodash.forInRight = forInRight;
    lodash.forOwn = forOwn;
    lodash.forOwnRight = forOwnRight;
    lodash.functions = functions;
    lodash.groupBy = groupBy;
    lodash.indexBy = indexBy;
    lodash.initial = initial;
    lodash.intersection = intersection;
    lodash.invert = invert;
    lodash.invoke = invoke;
    lodash.keys = keys;
    lodash.map = map;
    lodash.mapValues = mapValues;
    lodash.max = max;
    lodash.memoize = memoize;
    lodash.merge = merge;
    lodash.min = min;
    lodash.omit = omit;
    lodash.once = once;
    lodash.pairs = pairs;
    lodash.partial = partial;
    lodash.partialRight = partialRight;
    lodash.pick = pick;
    lodash.pluck = pluck;
    lodash.property = property;
    lodash.pull = pull;
    lodash.range = range;
    lodash.reject = reject;
    lodash.remove = remove;
    lodash.rest = rest;
    lodash.shuffle = shuffle;
    lodash.sortBy = sortBy;
    lodash.tap = tap;
    lodash.throttle = throttle;
    lodash.times = times;
    lodash.toArray = toArray;
    lodash.transform = transform;
    lodash.union = union;
    lodash.uniq = uniq;
    lodash.values = values;
    lodash.where = where;
    lodash.without = without;
    lodash.wrap = wrap;
    lodash.xor = xor;
    lodash.zip = zip;
    lodash.zipObject = zipObject;

    // add aliases
    lodash.collect = map;
    lodash.drop = rest;
    lodash.each = forEach;
    lodash.eachRight = forEachRight;
    lodash.extend = assign;
    lodash.methods = functions;
    lodash.object = zipObject;
    lodash.select = filter;
    lodash.tail = rest;
    lodash.unique = uniq;
    lodash.unzip = zip;

    // add functions to `lodash.prototype`
    mixin(lodash);

    /*--------------------------------------------------------------------------*/

    // add functions that return unwrapped values when chaining
    lodash.clone = clone;
    lodash.cloneDeep = cloneDeep;
    lodash.contains = contains;
    lodash.escape = escape;
    lodash.every = every;
    lodash.find = find;
    lodash.findIndex = findIndex;
    lodash.findKey = findKey;
    lodash.findLast = findLast;
    lodash.findLastIndex = findLastIndex;
    lodash.findLastKey = findLastKey;
    lodash.has = has;
    lodash.identity = identity;
    lodash.indexOf = indexOf;
    lodash.isArguments = isArguments;
    lodash.isArray = isArray;
    lodash.isBoolean = isBoolean;
    lodash.isDate = isDate;
    lodash.isElement = isElement;
    lodash.isEmpty = isEmpty;
    lodash.isEqual = isEqual;
    lodash.isFinite = isFinite;
    lodash.isFunction = isFunction;
    lodash.isNaN = isNaN;
    lodash.isNull = isNull;
    lodash.isNumber = isNumber;
    lodash.isObject = isObject;
    lodash.isPlainObject = isPlainObject;
    lodash.isRegExp = isRegExp;
    lodash.isString = isString;
    lodash.isUndefined = isUndefined;
    lodash.lastIndexOf = lastIndexOf;
    lodash.mixin = mixin;
    lodash.noConflict = noConflict;
    lodash.noop = noop;
    lodash.now = now;
    lodash.parseInt = parseInt;
    lodash.random = random;
    lodash.reduce = reduce;
    lodash.reduceRight = reduceRight;
    lodash.result = result;
    lodash.runInContext = runInContext;
    lodash.size = size;
    lodash.some = some;
    lodash.sortedIndex = sortedIndex;
    lodash.template = template;
    lodash.unescape = unescape;
    lodash.uniqueId = uniqueId;

    // add aliases
    lodash.all = every;
    lodash.any = some;
    lodash.detect = find;
    lodash.findWhere = find;
    lodash.foldl = reduce;
    lodash.foldr = reduceRight;
    lodash.include = contains;
    lodash.inject = reduce;

    mixin(function() {
      var source = {}
      forOwn(lodash, function(func, methodName) {
        if (!lodash.prototype[methodName]) {
          source[methodName] = func;
        }
      });
      return source;
    }(), false);

    /*--------------------------------------------------------------------------*/

    // add functions capable of returning wrapped and unwrapped values when chaining
    lodash.first = first;
    lodash.last = last;
    lodash.sample = sample;

    // add aliases
    lodash.take = first;
    lodash.head = first;

    forOwn(lodash, function(func, methodName) {
      var callbackable = methodName !== 'sample';
      if (!lodash.prototype[methodName]) {
        lodash.prototype[methodName]= function(n, guard) {
          var chainAll = this.__chain__,
              result = func(this.__wrapped__, n, guard);

          return !chainAll && (n == null || (guard && !(callbackable && typeof n == 'function')))
            ? result
            : new lodashWrapper(result, chainAll);
        };
      }
    });

    /*--------------------------------------------------------------------------*/

    /**
     * The semantic version number.
     *
     * @static
     * @memberOf _
     * @type string
     */
    lodash.VERSION = '2.4.1';

    // add "Chaining" functions to the wrapper
    lodash.prototype.chain = wrapperChain;
    lodash.prototype.toString = wrapperToString;
    lodash.prototype.value = wrapperValueOf;
    lodash.prototype.valueOf = wrapperValueOf;

    // add `Array` functions that return unwrapped values
    forEach(['join', 'pop', 'shift'], function(methodName) {
      var func = arrayRef[methodName];
      lodash.prototype[methodName] = function() {
        var chainAll = this.__chain__,
            result = func.apply(this.__wrapped__, arguments);

        return chainAll
          ? new lodashWrapper(result, chainAll)
          : result;
      };
    });

    // add `Array` functions that return the existing wrapped value
    forEach(['push', 'reverse', 'sort', 'unshift'], function(methodName) {
      var func = arrayRef[methodName];
      lodash.prototype[methodName] = function() {
        func.apply(this.__wrapped__, arguments);
        return this;
      };
    });

    // add `Array` functions that return new wrapped values
    forEach(['concat', 'slice', 'splice'], function(methodName) {
      var func = arrayRef[methodName];
      lodash.prototype[methodName] = function() {
        return new lodashWrapper(func.apply(this.__wrapped__, arguments), this.__chain__);
      };
    });

    return lodash;
  }

  /*--------------------------------------------------------------------------*/

  // expose Lo-Dash
  var _ = runInContext();

  // some AMD build optimizers like r.js check for condition patterns like the following:
  if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
    // Expose Lo-Dash to the global object even when an AMD loader is present in
    // case Lo-Dash is loaded with a RequireJS shim config.
    // See http://requirejs.org/docs/api.html#config-shim
    root._ = _;

    // define as an anonymous module so, through path mapping, it can be
    // referenced as the "underscore" module
    define(function() {
      return _;
    });
  }
  // check for `exports` after `define` in case a build optimizer adds an `exports` object
  else if (freeExports && freeModule) {
    // in Node.js or RingoJS
    if (moduleExports) {
      (freeModule.exports = _)._ = _;
    }
    // in Narwhal or Rhino -require
    else {
      freeExports._ = _;
    }
  }
  else {
    // in a browser or Rhino
    root._ = _;
  }
}.call(this));

// wrapper for non-node envs
;(function (sax) {

    sax.parser = function (strict, opt) { return new SAXParser(strict, opt) }
    sax.SAXParser = SAXParser
    sax.SAXStream = SAXStream
    sax.createStream = createStream

// When we pass the MAX_BUFFER_LENGTH position, start checking for buffer overruns.
// When we check, schedule the next check for MAX_BUFFER_LENGTH - (max(buffer lengths)),
// since that's the earliest that a buffer overrun could occur.  This way, checks are
// as rare as required, but as often as necessary to ensure never crossing this bound.
// Furthermore, buffers are only tested at most once per write(), so passing a very
// large string into write() might have undesirable effects, but this is manageable by
// the caller, so it is assumed to be safe.  Thus, a call to write() may, in the extreme
// edge case, result in creating at most one complete copy of the string passed in.
// Set to Infinity to have unlimited buffers.
    sax.MAX_BUFFER_LENGTH = 64 * 1024

    var buffers = [
        "comment", "sgmlDecl", "textNode", "tagName", "doctype",
        "procInstName", "procInstBody", "entity", "attribName",
        "attribValue", "cdata", "script"
    ]

    sax.EVENTS = // for discoverability.
        [ "text"
            , "processinginstruction"
            , "sgmldeclaration"
            , "doctype"
            , "comment"
            , "attribute"
            , "opentag"
            , "closetag"
            , "opencdata"
            , "cdata"
            , "closecdata"
            , "error"
            , "end"
            , "ready"
            , "script"
            , "opennamespace"
            , "closenamespace"
        ]

    function SAXParser (strict, opt) {
        if (!(this instanceof SAXParser)) return new SAXParser(strict, opt)

        var parser = this
        clearBuffers(parser)
        parser.q = parser.c = ""
        parser.bufferCheckPosition = sax.MAX_BUFFER_LENGTH
        parser.opt = opt || {}
        parser.opt.lowercase = parser.opt.lowercase || parser.opt.lowercasetags
        parser.looseCase = parser.opt.lowercase ? "toLowerCase" : "toUpperCase"
        parser.tags = []
        parser.closed = parser.closedRoot = parser.sawRoot = false
        parser.tag = parser.error = null
        parser.strict = !!strict
        parser.noscript = !!(strict || parser.opt.noscript)
        parser.state = S.BEGIN
        parser.ENTITIES = Object.create(sax.ENTITIES)
        parser.attribList = []

        // namespaces form a prototype chain.
        // it always points at the current tag,
        // which protos to its parent tag.
        if (parser.opt.xmlns) parser.ns = Object.create(rootNS)

        // mostly just for error reporting
        parser.trackPosition = parser.opt.position !== false
        if (parser.trackPosition) {
            parser.position = parser.line = parser.column = 0
        }
        emit(parser, "onready")
    }

    if (!Object.create) Object.create = function (o) {
        function f () { this.__proto__ = o }
        f.prototype = o
        return new f
    }

    if (!Object.getPrototypeOf) Object.getPrototypeOf = function (o) {
        return o.__proto__
    }

    if (!Object.keys) Object.keys = function (o) {
        var a = []
        for (var i in o) if (o.hasOwnProperty(i)) a.push(i)
        return a
    }

    function checkBufferLength (parser) {
        var maxAllowed = Math.max(sax.MAX_BUFFER_LENGTH, 10)
            , maxActual = 0
        for (var i = 0, l = buffers.length; i < l; i ++) {
            var len = parser[buffers[i]].length
            if (len > maxAllowed) {
                // Text/cdata nodes can get big, and since they're buffered,
                // we can get here under normal conditions.
                // Avoid issues by emitting the text node now,
                // so at least it won't get any bigger.
                switch (buffers[i]) {
                    case "textNode":
                        closeText(parser)
                        break

                    case "cdata":
                        emitNode(parser, "oncdata", parser.cdata)
                        parser.cdata = ""
                        break

                    case "script":
                        emitNode(parser, "onscript", parser.script)
                        parser.script = ""
                        break

                    default:
                        error(parser, "Max buffer length exceeded: "+buffers[i])
                }
            }
            maxActual = Math.max(maxActual, len)
        }
        // schedule the next check for the earliest possible buffer overrun.
        parser.bufferCheckPosition = (sax.MAX_BUFFER_LENGTH - maxActual)
        + parser.position
    }

    function clearBuffers (parser) {
        for (var i = 0, l = buffers.length; i < l; i ++) {
            parser[buffers[i]] = ""
        }
    }

    function flushBuffers (parser) {
        closeText(parser)
        if (parser.cdata !== "") {
            emitNode(parser, "oncdata", parser.cdata)
            parser.cdata = ""
        }
        if (parser.script !== "") {
            emitNode(parser, "onscript", parser.script)
            parser.script = ""
        }
    }

    SAXParser.prototype =
    { end: function () { end(this) }
        , write: write
        , resume: function () { this.error = null; return this }
        , close: function () { return this.write(null) }
        , flush: function () { flushBuffers(this) }
    }

    try {
        var Stream = require("stream").Stream
    } catch (ex) {
        var Stream = function () {}
    }


    var streamWraps = sax.EVENTS.filter(function (ev) {
        return ev !== "error" && ev !== "end"
    })

    function createStream (strict, opt) {
        return new SAXStream(strict, opt)
    }

    function SAXStream (strict, opt) {
        if (!(this instanceof SAXStream)) return new SAXStream(strict, opt)

        Stream.apply(this)

        this._parser = new SAXParser(strict, opt)
        this.writable = true
        this.readable = true


        var me = this

        this._parser.onend = function () {
            me.emit("end")
        }

        this._parser.onerror = function (er) {
            me.emit("error", er)

            // if didn't throw, then means error was handled.
            // go ahead and clear error, so we can write again.
            me._parser.error = null
        }

        this._decoder = null;

        streamWraps.forEach(function (ev) {
            Object.defineProperty(me, "on" + ev, {
                get: function () { return me._parser["on" + ev] },
                set: function (h) {
                    if (!h) {
                        me.removeAllListeners(ev)
                        return me._parser["on"+ev] = h
                    }
                    me.on(ev, h)
                },
                enumerable: true,
                configurable: false
            })
        })
    }

    SAXStream.prototype = Object.create(Stream.prototype,
        { constructor: { value: SAXStream } })

    SAXStream.prototype.write = function (data) {
        if (typeof Buffer === 'function' &&
            typeof Buffer.isBuffer === 'function' &&
            Buffer.isBuffer(data)) {
            if (!this._decoder) {
                var SD = require('string_decoder').StringDecoder
                this._decoder = new SD('utf8')
            }
            data = this._decoder.write(data);
        }

        this._parser.write(data.toString())
        this.emit("data", data)
        return true
    }

    SAXStream.prototype.end = function (chunk) {
        if (chunk && chunk.length) this.write(chunk)
        this._parser.end()
        return true
    }

    SAXStream.prototype.on = function (ev, handler) {
        var me = this
        if (!me._parser["on"+ev] && streamWraps.indexOf(ev) !== -1) {
            me._parser["on"+ev] = function () {
                var args = arguments.length === 1 ? [arguments[0]]
                    : Array.apply(null, arguments)
                args.splice(0, 0, ev)
                me.emit.apply(me, args)
            }
        }

        return Stream.prototype.on.call(me, ev, handler)
    }



// character classes and tokens
    var whitespace = "\r\n\t "
    // this really needs to be replaced with character classes.
    // XML allows all manner of ridiculous numbers and digits.
        , number = "0124356789"
        , letter = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    // (Letter | "_" | ":")
        , quote = "'\""
        , entity = number+letter+"#"
        , attribEnd = whitespace + ">"
        , CDATA = "[CDATA["
        , DOCTYPE = "DOCTYPE"
        , XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace"
        , XMLNS_NAMESPACE = "http://www.w3.org/2000/xmlns/"
        , rootNS = { xml: XML_NAMESPACE, xmlns: XMLNS_NAMESPACE }

// turn all the string character sets into character class objects.
    whitespace = charClass(whitespace)
    number = charClass(number)
    letter = charClass(letter)

// http://www.w3.org/TR/REC-xml/#NT-NameStartChar
// This implementation works on strings, a single character at a time
// as such, it cannot ever support astral-plane characters (10000-EFFFF)
// without a significant breaking change to either this  parser, or the
// JavaScript language.  Implementation of an emoji-capable xml parser
// is left as an exercise for the reader.
    var nameStart = /[:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/

    var nameBody = /[:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u00B7\u0300-\u036F\u203F-\u2040\.\d-]/

    quote = charClass(quote)
    entity = charClass(entity)
    attribEnd = charClass(attribEnd)

    function charClass (str) {
        return str.split("").reduce(function (s, c) {
            s[c] = true
            return s
        }, {})
    }

    function isRegExp (c) {
        return Object.prototype.toString.call(c) === '[object RegExp]'
    }

    function is (charclass, c) {
        return isRegExp(charclass) ? !!c.match(charclass) : charclass[c]
    }

    function not (charclass, c) {
        return !is(charclass, c)
    }

    var S = 0
    sax.STATE =
    { BEGIN                     : S++
        , TEXT                      : S++ // general stuff
        , TEXT_ENTITY               : S++ // &amp and such.
        , OPEN_WAKA                 : S++ // <
        , SGML_DECL                 : S++ // <!BLARG
        , SGML_DECL_QUOTED          : S++ // <!BLARG foo "bar
        , DOCTYPE                   : S++ // <!DOCTYPE
        , DOCTYPE_QUOTED            : S++ // <!DOCTYPE "//blah
        , DOCTYPE_DTD               : S++ // <!DOCTYPE "//blah" [ ...
        , DOCTYPE_DTD_QUOTED        : S++ // <!DOCTYPE "//blah" [ "foo
        , COMMENT_STARTING          : S++ // <!-
        , COMMENT                   : S++ // <!--
        , COMMENT_ENDING            : S++ // <!-- blah -
        , COMMENT_ENDED             : S++ // <!-- blah --
        , CDATA                     : S++ // <![CDATA[ something
        , CDATA_ENDING              : S++ // ]
        , CDATA_ENDING_2            : S++ // ]]
        , PROC_INST                 : S++ // <?hi
        , PROC_INST_BODY            : S++ // <?hi there
        , PROC_INST_ENDING          : S++ // <?hi "there" ?
        , OPEN_TAG                  : S++ // <strong
        , OPEN_TAG_SLASH            : S++ // <strong /
        , ATTRIB                    : S++ // <a
        , ATTRIB_NAME               : S++ // <a foo
        , ATTRIB_NAME_SAW_WHITE     : S++ // <a foo _
        , ATTRIB_VALUE              : S++ // <a foo=
        , ATTRIB_VALUE_QUOTED       : S++ // <a foo="bar
        , ATTRIB_VALUE_CLOSED       : S++ // <a foo="bar"
        , ATTRIB_VALUE_UNQUOTED     : S++ // <a foo=bar
        , ATTRIB_VALUE_ENTITY_Q     : S++ // <foo bar="&quot;"
        , ATTRIB_VALUE_ENTITY_U     : S++ // <foo bar=&quot;
        , CLOSE_TAG                 : S++ // </a
        , CLOSE_TAG_SAW_WHITE       : S++ // </a   >
        , SCRIPT                    : S++ // <script> ...
        , SCRIPT_ENDING             : S++ // <script> ... <
    }

    sax.ENTITIES =
    { "amp" : "&"
        , "gt" : ">"
        , "lt" : "<"
        , "quot" : "\""
        , "apos" : "'"
        , "AElig" : 198
        , "Aacute" : 193
        , "Acirc" : 194
        , "Agrave" : 192
        , "Aring" : 197
        , "Atilde" : 195
        , "Auml" : 196
        , "Ccedil" : 199
        , "ETH" : 208
        , "Eacute" : 201
        , "Ecirc" : 202
        , "Egrave" : 200
        , "Euml" : 203
        , "Iacute" : 205
        , "Icirc" : 206
        , "Igrave" : 204
        , "Iuml" : 207
        , "Ntilde" : 209
        , "Oacute" : 211
        , "Ocirc" : 212
        , "Ograve" : 210
        , "Oslash" : 216
        , "Otilde" : 213
        , "Ouml" : 214
        , "THORN" : 222
        , "Uacute" : 218
        , "Ucirc" : 219
        , "Ugrave" : 217
        , "Uuml" : 220
        , "Yacute" : 221
        , "aacute" : 225
        , "acirc" : 226
        , "aelig" : 230
        , "agrave" : 224
        , "aring" : 229
        , "atilde" : 227
        , "auml" : 228
        , "ccedil" : 231
        , "eacute" : 233
        , "ecirc" : 234
        , "egrave" : 232
        , "eth" : 240
        , "euml" : 235
        , "iacute" : 237
        , "icirc" : 238
        , "igrave" : 236
        , "iuml" : 239
        , "ntilde" : 241
        , "oacute" : 243
        , "ocirc" : 244
        , "ograve" : 242
        , "oslash" : 248
        , "otilde" : 245
        , "ouml" : 246
        , "szlig" : 223
        , "thorn" : 254
        , "uacute" : 250
        , "ucirc" : 251
        , "ugrave" : 249
        , "uuml" : 252
        , "yacute" : 253
        , "yuml" : 255
        , "copy" : 169
        , "reg" : 174
        , "nbsp" : 160
        , "iexcl" : 161
        , "cent" : 162
        , "pound" : 163
        , "curren" : 164
        , "yen" : 165
        , "brvbar" : 166
        , "sect" : 167
        , "uml" : 168
        , "ordf" : 170
        , "laquo" : 171
        , "not" : 172
        , "shy" : 173
        , "macr" : 175
        , "deg" : 176
        , "plusmn" : 177
        , "sup1" : 185
        , "sup2" : 178
        , "sup3" : 179
        , "acute" : 180
        , "micro" : 181
        , "para" : 182
        , "middot" : 183
        , "cedil" : 184
        , "ordm" : 186
        , "raquo" : 187
        , "frac14" : 188
        , "frac12" : 189
        , "frac34" : 190
        , "iquest" : 191
        , "times" : 215
        , "divide" : 247
        , "OElig" : 338
        , "oelig" : 339
        , "Scaron" : 352
        , "scaron" : 353
        , "Yuml" : 376
        , "fnof" : 402
        , "circ" : 710
        , "tilde" : 732
        , "Alpha" : 913
        , "Beta" : 914
        , "Gamma" : 915
        , "Delta" : 916
        , "Epsilon" : 917
        , "Zeta" : 918
        , "Eta" : 919
        , "Theta" : 920
        , "Iota" : 921
        , "Kappa" : 922
        , "Lambda" : 923
        , "Mu" : 924
        , "Nu" : 925
        , "Xi" : 926
        , "Omicron" : 927
        , "Pi" : 928
        , "Rho" : 929
        , "Sigma" : 931
        , "Tau" : 932
        , "Upsilon" : 933
        , "Phi" : 934
        , "Chi" : 935
        , "Psi" : 936
        , "Omega" : 937
        , "alpha" : 945
        , "beta" : 946
        , "gamma" : 947
        , "delta" : 948
        , "epsilon" : 949
        , "zeta" : 950
        , "eta" : 951
        , "theta" : 952
        , "iota" : 953
        , "kappa" : 954
        , "lambda" : 955
        , "mu" : 956
        , "nu" : 957
        , "xi" : 958
        , "omicron" : 959
        , "pi" : 960
        , "rho" : 961
        , "sigmaf" : 962
        , "sigma" : 963
        , "tau" : 964
        , "upsilon" : 965
        , "phi" : 966
        , "chi" : 967
        , "psi" : 968
        , "omega" : 969
        , "thetasym" : 977
        , "upsih" : 978
        , "piv" : 982
        , "ensp" : 8194
        , "emsp" : 8195
        , "thinsp" : 8201
        , "zwnj" : 8204
        , "zwj" : 8205
        , "lrm" : 8206
        , "rlm" : 8207
        , "ndash" : 8211
        , "mdash" : 8212
        , "lsquo" : 8216
        , "rsquo" : 8217
        , "sbquo" : 8218
        , "ldquo" : 8220
        , "rdquo" : 8221
        , "bdquo" : 8222
        , "dagger" : 8224
        , "Dagger" : 8225
        , "bull" : 8226
        , "hellip" : 8230
        , "permil" : 8240
        , "prime" : 8242
        , "Prime" : 8243
        , "lsaquo" : 8249
        , "rsaquo" : 8250
        , "oline" : 8254
        , "frasl" : 8260
        , "euro" : 8364
        , "image" : 8465
        , "weierp" : 8472
        , "real" : 8476
        , "trade" : 8482
        , "alefsym" : 8501
        , "larr" : 8592
        , "uarr" : 8593
        , "rarr" : 8594
        , "darr" : 8595
        , "harr" : 8596
        , "crarr" : 8629
        , "lArr" : 8656
        , "uArr" : 8657
        , "rArr" : 8658
        , "dArr" : 8659
        , "hArr" : 8660
        , "forall" : 8704
        , "part" : 8706
        , "exist" : 8707
        , "empty" : 8709
        , "nabla" : 8711
        , "isin" : 8712
        , "notin" : 8713
        , "ni" : 8715
        , "prod" : 8719
        , "sum" : 8721
        , "minus" : 8722
        , "lowast" : 8727
        , "radic" : 8730
        , "prop" : 8733
        , "infin" : 8734
        , "ang" : 8736
        , "and" : 8743
        , "or" : 8744
        , "cap" : 8745
        , "cup" : 8746
        , "int" : 8747
        , "there4" : 8756
        , "sim" : 8764
        , "cong" : 8773
        , "asymp" : 8776
        , "ne" : 8800
        , "equiv" : 8801
        , "le" : 8804
        , "ge" : 8805
        , "sub" : 8834
        , "sup" : 8835
        , "nsub" : 8836
        , "sube" : 8838
        , "supe" : 8839
        , "oplus" : 8853
        , "otimes" : 8855
        , "perp" : 8869
        , "sdot" : 8901
        , "lceil" : 8968
        , "rceil" : 8969
        , "lfloor" : 8970
        , "rfloor" : 8971
        , "lang" : 9001
        , "rang" : 9002
        , "loz" : 9674
        , "spades" : 9824
        , "clubs" : 9827
        , "hearts" : 9829
        , "diams" : 9830
    }

    Object.keys(sax.ENTITIES).forEach(function (key) {
        var e = sax.ENTITIES[key]
        var s = typeof e === 'number' ? String.fromCharCode(e) : e
        sax.ENTITIES[key] = s
    })

    for (var S in sax.STATE) sax.STATE[sax.STATE[S]] = S

// shorthand
    S = sax.STATE

    function emit (parser, event, data) {
        parser[event] && parser[event](data)
    }

    function emitNode (parser, nodeType, data) {
        if (parser.textNode) closeText(parser)
        emit(parser, nodeType, data)
    }

    function closeText (parser) {
        parser.textNode = textopts(parser.opt, parser.textNode)
        if (parser.textNode) emit(parser, "ontext", parser.textNode)
        parser.textNode = ""
    }

    function textopts (opt, text) {
        if (opt.trim) text = text.trim()
        if (opt.normalize) text = text.replace(/\s+/g, " ")
        return text
    }

    function error (parser, er) {
        closeText(parser)
        if (parser.trackPosition) {
            er += "\nLine: "+parser.line+
            "\nColumn: "+parser.column+
            "\nChar: "+parser.c
        }
        er = new Error(er)
        parser.error = er
        emit(parser, "onerror", er)
        return parser
    }

    function end (parser) {
        if (!parser.closedRoot) strictFail(parser, "Unclosed root tag")
        if ((parser.state !== S.BEGIN) && (parser.state !== S.TEXT)) error(parser, "Unexpected end")
        closeText(parser)
        parser.c = ""
        parser.closed = true
        emit(parser, "onend")
        SAXParser.call(parser, parser.strict, parser.opt)
        return parser
    }

    function strictFail (parser, message) {
        if (typeof parser !== 'object' || !(parser instanceof SAXParser))
            throw new Error('bad call to strictFail');
        if (parser.strict) error(parser, message)
    }

    function newTag (parser) {
        if (!parser.strict) parser.tagName = parser.tagName[parser.looseCase]()
        var parent = parser.tags[parser.tags.length - 1] || parser
            , tag = parser.tag = { name : parser.tagName, attributes : {} }

        // will be overridden if tag contails an xmlns="foo" or xmlns:foo="bar"
        if (parser.opt.xmlns) tag.ns = parent.ns
        parser.attribList.length = 0
    }

    function qname (name, attribute) {
        var i = name.indexOf(":")
            , qualName = i < 0 ? [ "", name ] : name.split(":")
            , prefix = qualName[0]
            , local = qualName[1]

        // <x "xmlns"="http://foo">
        if (attribute && name === "xmlns") {
            prefix = "xmlns"
            local = ""
        }

        return { prefix: prefix, local: local }
    }

    function attrib (parser) {
        if (!parser.strict) parser.attribName = parser.attribName[parser.looseCase]()

        if (parser.attribList.indexOf(parser.attribName) !== -1 ||
            parser.tag.attributes.hasOwnProperty(parser.attribName)) {
            return parser.attribName = parser.attribValue = ""
        }

        if (parser.opt.xmlns) {
            var qn = qname(parser.attribName, true)
                , prefix = qn.prefix
                , local = qn.local

            if (prefix === "xmlns") {
                // namespace binding attribute; push the binding into scope
                if (local === "xml" && parser.attribValue !== XML_NAMESPACE) {
                    strictFail( parser
                        , "xml: prefix must be bound to " + XML_NAMESPACE + "\n"
                        + "Actual: " + parser.attribValue )
                } else if (local === "xmlns" && parser.attribValue !== XMLNS_NAMESPACE) {
                    strictFail( parser
                        , "xmlns: prefix must be bound to " + XMLNS_NAMESPACE + "\n"
                        + "Actual: " + parser.attribValue )
                } else {
                    var tag = parser.tag
                        , parent = parser.tags[parser.tags.length - 1] || parser
                    if (tag.ns === parent.ns) {
                        tag.ns = Object.create(parent.ns)
                    }
                    tag.ns[local] = parser.attribValue
                }
            }

            // defer onattribute events until all attributes have been seen
            // so any new bindings can take effect; preserve attribute order
            // so deferred events can be emitted in document order
            parser.attribList.push([parser.attribName, parser.attribValue])
        } else {
            // in non-xmlns mode, we can emit the event right away
            parser.tag.attributes[parser.attribName] = parser.attribValue
            emitNode( parser
                , "onattribute"
                , { name: parser.attribName
                    , value: parser.attribValue } )
        }

        parser.attribName = parser.attribValue = ""
    }

    function openTag (parser, selfClosing) {
        if (parser.opt.xmlns) {
            // emit namespace binding events
            var tag = parser.tag

            // add namespace info to tag
            var qn = qname(parser.tagName)
            tag.prefix = qn.prefix
            tag.local = qn.local
            tag.uri = tag.ns[qn.prefix] || ""

            if (tag.prefix && !tag.uri) {
                strictFail(parser, "Unbound namespace prefix: "
                + JSON.stringify(parser.tagName))
                tag.uri = qn.prefix
            }

            var parent = parser.tags[parser.tags.length - 1] || parser
            if (tag.ns && parent.ns !== tag.ns) {
                Object.keys(tag.ns).forEach(function (p) {
                    emitNode( parser
                        , "onopennamespace"
                        , { prefix: p , uri: tag.ns[p] } )
                })
            }

            // handle deferred onattribute events
            // Note: do not apply default ns to attributes:
            //   http://www.w3.org/TR/REC-xml-names/#defaulting
            for (var i = 0, l = parser.attribList.length; i < l; i ++) {
                var nv = parser.attribList[i]
                var name = nv[0]
                    , value = nv[1]
                    , qualName = qname(name, true)
                    , prefix = qualName.prefix
                    , local = qualName.local
                    , uri = prefix == "" ? "" : (tag.ns[prefix] || "")
                    , a = { name: name
                        , value: value
                        , prefix: prefix
                        , local: local
                        , uri: uri
                    }

                // if there's any attributes with an undefined namespace,
                // then fail on them now.
                if (prefix && prefix != "xmlns" && !uri) {
                    strictFail(parser, "Unbound namespace prefix: "
                    + JSON.stringify(prefix))
                    a.uri = prefix
                }
                parser.tag.attributes[name] = a
                emitNode(parser, "onattribute", a)
            }
            parser.attribList.length = 0
        }

        parser.tag.isSelfClosing = !!selfClosing

        // process the tag
        parser.sawRoot = true
        parser.tags.push(parser.tag)
        emitNode(parser, "onopentag", parser.tag)
        if (!selfClosing) {
            // special case for <script> in non-strict mode.
            if (!parser.noscript && parser.tagName.toLowerCase() === "script") {
                parser.state = S.SCRIPT
            } else {
                parser.state = S.TEXT
            }
            parser.tag = null
            parser.tagName = ""
        }
        parser.attribName = parser.attribValue = ""
        parser.attribList.length = 0
    }

    function closeTag (parser) {
        if (!parser.tagName) {
            strictFail(parser, "Weird empty close tag.")
            parser.textNode += "</>"
            parser.state = S.TEXT
            return
        }

        if (parser.script) {
            if (parser.tagName !== "script") {
                parser.script += "</" + parser.tagName + ">"
                parser.tagName = ""
                parser.state = S.SCRIPT
                return
            }
            emitNode(parser, "onscript", parser.script)
            parser.script = ""
        }

        // first make sure that the closing tag actually exists.
        // <a><b></c></b></a> will close everything, otherwise.
        var t = parser.tags.length
        var tagName = parser.tagName
        if (!parser.strict) tagName = tagName[parser.looseCase]()
        var closeTo = tagName
        while (t --) {
            var close = parser.tags[t]
            if (close.name !== closeTo) {
                // fail the first time in strict mode
                strictFail(parser, "Unexpected close tag")
            } else break
        }

        // didn't find it.  we already failed for strict, so just abort.
        if (t < 0) {
            strictFail(parser, "Unmatched closing tag: "+parser.tagName)
            parser.textNode += "</" + parser.tagName + ">"
            parser.state = S.TEXT
            return
        }
        parser.tagName = tagName
        var s = parser.tags.length
        while (s --> t) {
            var tag = parser.tag = parser.tags.pop()
            parser.tagName = parser.tag.name
            emitNode(parser, "onclosetag", parser.tagName)

            var x = {}
            for (var i in tag.ns) x[i] = tag.ns[i]

            var parent = parser.tags[parser.tags.length - 1] || parser
            if (parser.opt.xmlns && tag.ns !== parent.ns) {
                // remove namespace bindings introduced by tag
                Object.keys(tag.ns).forEach(function (p) {
                    var n = tag.ns[p]
                    emitNode(parser, "onclosenamespace", { prefix: p, uri: n })
                })
            }
        }
        if (t === 0) parser.closedRoot = true
        parser.tagName = parser.attribValue = parser.attribName = ""
        parser.attribList.length = 0
        parser.state = S.TEXT
    }

    function parseEntity (parser) {
        var entity = parser.entity
            , entityLC = entity.toLowerCase()
            , num
            , numStr = ""
        if (parser.ENTITIES[entity])
            return parser.ENTITIES[entity]
        if (parser.ENTITIES[entityLC])
            return parser.ENTITIES[entityLC]
        entity = entityLC
        if (entity.charAt(0) === "#") {
            if (entity.charAt(1) === "x") {
                entity = entity.slice(2)
                num = parseInt(entity, 16)
                numStr = num.toString(16)
            } else {
                entity = entity.slice(1)
                num = parseInt(entity, 10)
                numStr = num.toString(10)
            }
        }
        entity = entity.replace(/^0+/, "")
        if (numStr.toLowerCase() !== entity) {
            strictFail(parser, "Invalid character entity")
            return "&"+parser.entity + ";"
        }

        return String.fromCodePoint(num)
    }

    function write (chunk) {
        var parser = this
        if (this.error) throw this.error
        if (parser.closed) return error(parser,
            "Cannot write after close. Assign an onready handler.")
        if (chunk === null) return end(parser)
        var i = 0, c = ""
        while (parser.c = c = chunk.charAt(i++)) {
            if (parser.trackPosition) {
                parser.position ++
                if (c === "\n") {
                    parser.line ++
                    parser.column = 0
                } else parser.column ++
            }
            switch (parser.state) {

                case S.BEGIN:
                    if (c === "<") {
                        parser.state = S.OPEN_WAKA
                        parser.startTagPosition = parser.position
                    } else if (not(whitespace,c)) {
                        // have to process this as a text node.
                        // weird, but happens.
                        strictFail(parser, "Non-whitespace before first tag.")
                        parser.textNode = c
                        parser.state = S.TEXT
                    }
                    continue

                case S.TEXT:
                    if (parser.sawRoot && !parser.closedRoot) {
                        var starti = i-1
                        while (c && c!=="<" && c!=="&") {
                            c = chunk.charAt(i++)
                            if (c && parser.trackPosition) {
                                parser.position ++
                                if (c === "\n") {
                                    parser.line ++
                                    parser.column = 0
                                } else parser.column ++
                            }
                        }
                        parser.textNode += chunk.substring(starti, i-1)
                    }
                    if (c === "<") {
                        parser.state = S.OPEN_WAKA
                        parser.startTagPosition = parser.position
                    } else {
                        if (not(whitespace, c) && (!parser.sawRoot || parser.closedRoot))
                            strictFail(parser, "Text data outside of root node.")
                        if (c === "&") parser.state = S.TEXT_ENTITY
                        else parser.textNode += c
                    }
                    continue

                case S.SCRIPT:
                    // only non-strict
                    if (c === "<") {
                        parser.state = S.SCRIPT_ENDING
                    } else parser.script += c
                    continue

                case S.SCRIPT_ENDING:
                    if (c === "/") {
                        parser.state = S.CLOSE_TAG
                    } else {
                        parser.script += "<" + c
                        parser.state = S.SCRIPT
                    }
                    continue

                case S.OPEN_WAKA:
                    // either a /, ?, !, or text is coming next.
                    if (c === "!") {
                        parser.state = S.SGML_DECL
                        parser.sgmlDecl = ""
                    } else if (is(whitespace, c)) {
                        // wait for it...
                    } else if (is(nameStart,c)) {
                        parser.state = S.OPEN_TAG
                        parser.tagName = c
                    } else if (c === "/") {
                        parser.state = S.CLOSE_TAG
                        parser.tagName = ""
                    } else if (c === "?") {
                        parser.state = S.PROC_INST
                        parser.procInstName = parser.procInstBody = ""
                    } else {
                        strictFail(parser, "Unencoded <")
                        // if there was some whitespace, then add that in.
                        if (parser.startTagPosition + 1 < parser.position) {
                            var pad = parser.position - parser.startTagPosition
                            c = new Array(pad).join(" ") + c
                        }
                        parser.textNode += "<" + c
                        parser.state = S.TEXT
                    }
                    continue

                case S.SGML_DECL:
                    if ((parser.sgmlDecl+c).toUpperCase() === CDATA) {
                        emitNode(parser, "onopencdata")
                        parser.state = S.CDATA
                        parser.sgmlDecl = ""
                        parser.cdata = ""
                    } else if (parser.sgmlDecl+c === "--") {
                        parser.state = S.COMMENT
                        parser.comment = ""
                        parser.sgmlDecl = ""
                    } else if ((parser.sgmlDecl+c).toUpperCase() === DOCTYPE) {
                        parser.state = S.DOCTYPE
                        if (parser.doctype || parser.sawRoot) strictFail(parser,
                            "Inappropriately located doctype declaration")
                        parser.doctype = ""
                        parser.sgmlDecl = ""
                    } else if (c === ">") {
                        emitNode(parser, "onsgmldeclaration", parser.sgmlDecl)
                        parser.sgmlDecl = ""
                        parser.state = S.TEXT
                    } else if (is(quote, c)) {
                        parser.state = S.SGML_DECL_QUOTED
                        parser.sgmlDecl += c
                    } else parser.sgmlDecl += c
                    continue

                case S.SGML_DECL_QUOTED:
                    if (c === parser.q) {
                        parser.state = S.SGML_DECL
                        parser.q = ""
                    }
                    parser.sgmlDecl += c
                    continue

                case S.DOCTYPE:
                    if (c === ">") {
                        parser.state = S.TEXT
                        emitNode(parser, "ondoctype", parser.doctype)
                        parser.doctype = true // just remember that we saw it.
                    } else {
                        parser.doctype += c
                        if (c === "[") parser.state = S.DOCTYPE_DTD
                        else if (is(quote, c)) {
                            parser.state = S.DOCTYPE_QUOTED
                            parser.q = c
                        }
                    }
                    continue

                case S.DOCTYPE_QUOTED:
                    parser.doctype += c
                    if (c === parser.q) {
                        parser.q = ""
                        parser.state = S.DOCTYPE
                    }
                    continue

                case S.DOCTYPE_DTD:
                    parser.doctype += c
                    if (c === "]") parser.state = S.DOCTYPE
                    else if (is(quote,c)) {
                        parser.state = S.DOCTYPE_DTD_QUOTED
                        parser.q = c
                    }
                    continue

                case S.DOCTYPE_DTD_QUOTED:
                    parser.doctype += c
                    if (c === parser.q) {
                        parser.state = S.DOCTYPE_DTD
                        parser.q = ""
                    }
                    continue

                case S.COMMENT:
                    if (c === "-") parser.state = S.COMMENT_ENDING
                    else parser.comment += c
                    continue

                case S.COMMENT_ENDING:
                    if (c === "-") {
                        parser.state = S.COMMENT_ENDED
                        parser.comment = textopts(parser.opt, parser.comment)
                        if (parser.comment) emitNode(parser, "oncomment", parser.comment)
                        parser.comment = ""
                    } else {
                        parser.comment += "-" + c
                        parser.state = S.COMMENT
                    }
                    continue

                case S.COMMENT_ENDED:
                    if (c !== ">") {
                        strictFail(parser, "Malformed comment")
                        // allow <!-- blah -- bloo --> in non-strict mode,
                        // which is a comment of " blah -- bloo "
                        parser.comment += "--" + c
                        parser.state = S.COMMENT
                    } else parser.state = S.TEXT
                    continue

                case S.CDATA:
                    if (c === "]") parser.state = S.CDATA_ENDING
                    else parser.cdata += c
                    continue

                case S.CDATA_ENDING:
                    if (c === "]") parser.state = S.CDATA_ENDING_2
                    else {
                        parser.cdata += "]" + c
                        parser.state = S.CDATA
                    }
                    continue

                case S.CDATA_ENDING_2:
                    if (c === ">") {
                        if (parser.cdata) emitNode(parser, "oncdata", parser.cdata)
                        emitNode(parser, "onclosecdata")
                        parser.cdata = ""
                        parser.state = S.TEXT
                    } else if (c === "]") {
                        parser.cdata += "]"
                    } else {
                        parser.cdata += "]]" + c
                        parser.state = S.CDATA
                    }
                    continue

                case S.PROC_INST:
                    if (c === "?") parser.state = S.PROC_INST_ENDING
                    else if (is(whitespace, c)) parser.state = S.PROC_INST_BODY
                    else parser.procInstName += c
                    continue

                case S.PROC_INST_BODY:
                    if (!parser.procInstBody && is(whitespace, c)) continue
                    else if (c === "?") parser.state = S.PROC_INST_ENDING
                    else parser.procInstBody += c
                    continue

                case S.PROC_INST_ENDING:
                    if (c === ">") {
                        emitNode(parser, "onprocessinginstruction", {
                            name : parser.procInstName,
                            body : parser.procInstBody
                        })
                        parser.procInstName = parser.procInstBody = ""
                        parser.state = S.TEXT
                    } else {
                        parser.procInstBody += "?" + c
                        parser.state = S.PROC_INST_BODY
                    }
                    continue

                case S.OPEN_TAG:
                    if (is(nameBody, c)) parser.tagName += c
                    else {
                        newTag(parser)
                        if (c === ">") openTag(parser)
                        else if (c === "/") parser.state = S.OPEN_TAG_SLASH
                        else {
                            if (not(whitespace, c)) strictFail(
                                parser, "Invalid character in tag name")
                            parser.state = S.ATTRIB
                        }
                    }
                    continue

                case S.OPEN_TAG_SLASH:
                    if (c === ">") {
                        openTag(parser, true)
                        closeTag(parser)
                    } else {
                        strictFail(parser, "Forward-slash in opening tag not followed by >")
                        parser.state = S.ATTRIB
                    }
                    continue

                case S.ATTRIB:
                    // haven't read the attribute name yet.
                    if (is(whitespace, c)) continue
                    else if (c === ">") openTag(parser)
                    else if (c === "/") parser.state = S.OPEN_TAG_SLASH
                    else if (is(nameStart, c)) {
                        parser.attribName = c
                        parser.attribValue = ""
                        parser.state = S.ATTRIB_NAME
                    } else strictFail(parser, "Invalid attribute name")
                    continue

                case S.ATTRIB_NAME:
                    if (c === "=") parser.state = S.ATTRIB_VALUE
                    else if (c === ">") {
                        strictFail(parser, "Attribute without value")
                        parser.attribValue = parser.attribName
                        attrib(parser)
                        openTag(parser)
                    }
                    else if (is(whitespace, c)) parser.state = S.ATTRIB_NAME_SAW_WHITE
                    else if (is(nameBody, c)) parser.attribName += c
                    else strictFail(parser, "Invalid attribute name")
                    continue

                case S.ATTRIB_NAME_SAW_WHITE:
                    if (c === "=") parser.state = S.ATTRIB_VALUE
                    else if (is(whitespace, c)) continue
                    else {
                        strictFail(parser, "Attribute without value")
                        parser.tag.attributes[parser.attribName] = ""
                        parser.attribValue = ""
                        emitNode(parser, "onattribute",
                            { name : parser.attribName, value : "" })
                        parser.attribName = ""
                        if (c === ">") openTag(parser)
                        else if (is(nameStart, c)) {
                            parser.attribName = c
                            parser.state = S.ATTRIB_NAME
                        } else {
                            strictFail(parser, "Invalid attribute name")
                            parser.state = S.ATTRIB
                        }
                    }
                    continue

                case S.ATTRIB_VALUE:
                    if (is(whitespace, c)) continue
                    else if (is(quote, c)) {
                        parser.q = c
                        parser.state = S.ATTRIB_VALUE_QUOTED
                    } else {
                        strictFail(parser, "Unquoted attribute value")
                        parser.state = S.ATTRIB_VALUE_UNQUOTED
                        parser.attribValue = c
                    }
                    continue

                case S.ATTRIB_VALUE_QUOTED:
                    if (c !== parser.q) {
                        if (c === "&") parser.state = S.ATTRIB_VALUE_ENTITY_Q
                        else parser.attribValue += c
                        continue
                    }
                    attrib(parser)
                    parser.q = ""
                    parser.state = S.ATTRIB_VALUE_CLOSED
                    continue

                case S.ATTRIB_VALUE_CLOSED:
                    if (is(whitespace, c)) {
                        parser.state = S.ATTRIB
                    } else if (c === ">") openTag(parser)
                    else if (c === "/") parser.state = S.OPEN_TAG_SLASH
                    else if (is(nameStart, c)) {
                        strictFail(parser, "No whitespace between attributes")
                        parser.attribName = c
                        parser.attribValue = ""
                        parser.state = S.ATTRIB_NAME
                    } else strictFail(parser, "Invalid attribute name")
                    continue

                case S.ATTRIB_VALUE_UNQUOTED:
                    if (not(attribEnd,c)) {
                        if (c === "&") parser.state = S.ATTRIB_VALUE_ENTITY_U
                        else parser.attribValue += c
                        continue
                    }
                    attrib(parser)
                    if (c === ">") openTag(parser)
                    else parser.state = S.ATTRIB
                    continue

                case S.CLOSE_TAG:
                    if (!parser.tagName) {
                        if (is(whitespace, c)) continue
                        else if (not(nameStart, c)) {
                            if (parser.script) {
                                parser.script += "</" + c
                                parser.state = S.SCRIPT
                            } else {
                                strictFail(parser, "Invalid tagname in closing tag.")
                            }
                        } else parser.tagName = c
                    }
                    else if (c === ">") closeTag(parser)
                    else if (is(nameBody, c)) parser.tagName += c
                    else if (parser.script) {
                        parser.script += "</" + parser.tagName
                        parser.tagName = ""
                        parser.state = S.SCRIPT
                    } else {
                        if (not(whitespace, c)) strictFail(parser,
                            "Invalid tagname in closing tag")
                        parser.state = S.CLOSE_TAG_SAW_WHITE
                    }
                    continue

                case S.CLOSE_TAG_SAW_WHITE:
                    if (is(whitespace, c)) continue
                    if (c === ">") closeTag(parser)
                    else strictFail(parser, "Invalid characters in closing tag")
                    continue

                case S.TEXT_ENTITY:
                case S.ATTRIB_VALUE_ENTITY_Q:
                case S.ATTRIB_VALUE_ENTITY_U:
                    switch(parser.state) {
                        case S.TEXT_ENTITY:
                            var returnState = S.TEXT, buffer = "textNode"
                            break

                        case S.ATTRIB_VALUE_ENTITY_Q:
                            var returnState = S.ATTRIB_VALUE_QUOTED, buffer = "attribValue"
                            break

                        case S.ATTRIB_VALUE_ENTITY_U:
                            var returnState = S.ATTRIB_VALUE_UNQUOTED, buffer = "attribValue"
                            break
                    }
                    if (c === ";") {
                        parser[buffer] += parseEntity(parser)
                        parser.entity = ""
                        parser.state = returnState
                    }
                    else if (is(entity, c)) parser.entity += c
                    else {
                        strictFail(parser, "Invalid character entity")
                        parser[buffer] += "&" + parser.entity + c
                        parser.entity = ""
                        parser.state = returnState
                    }
                    continue

                default:
                    throw new Error(parser, "Unknown state: " + parser.state)
            }
        } // while
        // cdata blocks can get very big under normal conditions. emit and move on.
        // if (parser.state === S.CDATA && parser.cdata) {
        //   emitNode(parser, "oncdata", parser.cdata)
        //   parser.cdata = ""
        // }
        if (parser.position >= parser.bufferCheckPosition) checkBufferLength(parser)
        return parser
    }

    /*! http://mths.be/fromcodepoint v0.1.0 by @mathias */
    if (!String.fromCodePoint) {
        (function() {
            var stringFromCharCode = String.fromCharCode;
            var floor = Math.floor;
            var fromCodePoint = function() {
                var MAX_SIZE = 0x4000;
                var codeUnits = [];
                var highSurrogate;
                var lowSurrogate;
                var index = -1;
                var length = arguments.length;
                if (!length) {
                    return '';
                }
                var result = '';
                while (++index < length) {
                    var codePoint = Number(arguments[index]);
                    if (
                        !isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
                        codePoint < 0 || // not a valid Unicode code point
                        codePoint > 0x10FFFF || // not a valid Unicode code point
                        floor(codePoint) != codePoint // not an integer
                    ) {
                        throw RangeError('Invalid code point: ' + codePoint);
                    }
                    if (codePoint <= 0xFFFF) { // BMP code point
                        codeUnits.push(codePoint);
                    } else { // Astral code point; split in surrogate halves
                        // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
                        codePoint -= 0x10000;
                        highSurrogate = (codePoint >> 10) + 0xD800;
                        lowSurrogate = (codePoint % 0x400) + 0xDC00;
                        codeUnits.push(highSurrogate, lowSurrogate);
                    }
                    if (index + 1 == length || codeUnits.length > MAX_SIZE) {
                        result += stringFromCharCode.apply(null, codeUnits);
                        codeUnits.length = 0;
                    }
                }
                return result;
            };
            if (Object.defineProperty) {
                Object.defineProperty(String, 'fromCodePoint', {
                    'value': fromCodePoint,
                    'configurable': true,
                    'writable': true
                });
            } else {
                String.fromCodePoint = fromCodePoint;
            }
        }());
    }

})(typeof exports === "undefined" ? sax = {} : exports)
/**
 * Tiny stack for browser or server
 *
 * @author Jason Mulligan <jason.mulligan@avoidwork.com>
 * @copyright 2014 Jason Mulligan
 * @license BSD-3 <https://raw.github.com/avoidwork/tiny-stack/master/LICENSE>
 * @link http://avoidwork.github.io/tiny-stack
 * @module tiny-stack
 * @version 0.1.0
 */

;( function ( global ) {

"use strict";

/**
 * TinyStack
 *
 * @constructor
 */
function TinyStack () {
	this.data = [null];
	this.top  = 0;
}

/**
 * Clears the stack
 *
 * @method clear
 * @memberOf TinyStack
 * @return {Object} {@link TinyStack}
 */
TinyStack.prototype.clear = function clear () {
	this.data = [null];
	this.top  = 0;

	return this;
};

/**
 * Gets the size of the stack
 *
 * @method length
 * @memberOf TinyStack
 * @return {Number} Size of stack
 */
TinyStack.prototype.length = function length () {
	return this.top;
};

/**
 * Gets the item at the top of the stack
 *
 * @method peek
 * @memberOf TinyStack
 * @return {Mixed} Item at the top of the stack
 */
TinyStack.prototype.peek = function peek () {
	return this.data[this.top];
};

/**
 * Gets & removes the item at the top of the stack
 *
 * @method pop
 * @memberOf TinyStack
 * @return {Mixed} Item at the top of the stack
 */
TinyStack.prototype.pop = function pop () {
	if ( this.top > 0 ) {
		this.top--;

		return this.data.pop();
	}
	else {
		return undefined;
	}
};

/**
 * Pushes an item onto the stack
 *
 * @method push
 * @memberOf TinyStack
 * @return {Object} {@link TinyStack}
 */
TinyStack.prototype.push = function push ( arg ) {
	this.data[++this.top] = arg;

	return this;
};

/**
 * TinyStack factory
 *
 * @method factory
 * @return {Object} {@link TinyStack}
 */
function factory () {
	return new TinyStack();
}

// Node, AMD & window supported
if ( typeof exports != "undefined" ) {
	module.exports = factory;
}
else if ( typeof define == "function" ) {
	define( function () {
		return factory;
	} );
}
else {
	global.stack = factory;
}
} )( this );

'use strict';

function Base() { }

Base.prototype.get = function(name) {
    return this.$model.properties.get(this, name);
};

Base.prototype.set = function(name, value) {
    this.$model.properties.set(this, name, value);
};

//
//module.exports = Base;
var parseName = function(name, defaultPrefix) {
    var parts = name.split(/:/),
        localName, prefix;

    // no prefix (i.e. only local name)
    if (parts.length === 1) {
        localName = name;
        prefix = defaultPrefix;
    } else
    // prefix + local name
    if (parts.length === 2) {
        localName = parts[1];
        prefix = parts[0];
    } else {
        throw new Error('expected <prefix:localName> or <localName>, got ' + name);
    }

    name = (prefix ? prefix + ':' : '') + localName;

    return {
        name: name,
        prefix: prefix,
        localName: localName
    };
};




'use strict';


/**
 * A utility that gets and sets properties of model elements.
 *
 * @param {Model} model
 */
function Properties(model) {
    this.model = model;
}

//module.exports = Properties;


/**
 * Sets a named property on the target element
 *
 * @param {Object} target
 * @param {String} name
 * @param {Object} value
 */
Properties.prototype.set = function(target, name, value) {

    var property = this.model.getPropertyDescriptor(target, name);

    if (!property) {
        target.$attrs[name] = value;
    } else {
        Object.defineProperty(target, property.name, {
            enumerable: !property.isReference,
            writable: true,
            value: value
        });
    }
};

/**
 * Returns the named property of the given element
 *
 * @param  {Object} target
 * @param  {String} name
 *
 * @return {Object}
 */
Properties.prototype.get = function(target, name) {

    var property = this.model.getPropertyDescriptor(target, name);

    if (!property) {
        return target.$attrs[name];
    }

    var propertyName = property.name;

    // check if access to collection property and lazily initialize it
    if (!target[propertyName] && property.isMany) {
        Object.defineProperty(target, propertyName, {
            enumerable: !property.isReference,
            writable: true,
            value: []
        });
    }

    return target[propertyName];
};


/**
 * Define a property on the target element
 *
 * @param  {Object} target
 * @param  {String} name
 * @param  {Object} options
 */
Properties.prototype.define = function(target, name, options) {
    Object.defineProperty(target, name, options);
};


/**
 * Define the descriptor for an element
 */
Properties.prototype.defineDescriptor = function(target, descriptor) {
    this.define(target, '$descriptor', { value: descriptor });
};

/**
 * Define the model for an element
 */
Properties.prototype.defineModel = function(target, model) {
    this.define(target, '$model', { value: model });
};
'use strict';

//var _ = require('lodash');
//
//var Base = require('./base');


function Factory(model, properties) {
    this.model = model;
    this.properties = properties;
}

//module.exports = Factory;


Factory.prototype.createType = function(descriptor) {

    var model = this.model;

    var props = this.properties,
        prototype = Object.create(Base.prototype);

    // initialize default values
    _.forEach(descriptor.properties, function(p) {
        if (!p.isMany && p.default !== undefined) {
            prototype[p.name] = p.default;
        }
    });

    props.defineModel(prototype, model);
    props.defineDescriptor(prototype, descriptor);

    var name = descriptor.ns.name;

    /**
     * The new type constructor
     */
    function ModdleElement(attrs) {
        props.define(this, '$type', { value: name, enumerable: true });
        props.define(this, '$attrs', { value: {} });
        props.define(this, '$parent', { writable: true });

        _.forEach(attrs, function(val, key) {
            this.set(key, val);
        }, this);
    }

    ModdleElement.prototype = prototype;

    ModdleElement.hasType = prototype.$instanceOf = this.model.hasType;

    // static links
    props.defineModel(ModdleElement, model);
    props.defineDescriptor(ModdleElement, descriptor);

    return ModdleElement;
};

/**
 * Built-in moddle types
 */
var BUILTINS = {
    String: true,
    Boolean: true,
    Integer: true,
    Real: true,
    Element: true
};

/**
 * Converters for built in types from string representations
 */
var TYPE_CONVERTERS = {
    String: function(s) { return s; },
    Boolean: function(s) { return s === 'true'; },
    Integer: function(s) { return parseInt(s, 10); },
    Real: function(s) { return parseFloat(s, 10); }
};

/**
 * Convert a type to its real representation
 */
var coerceType = function(type, value) {

    var converter = TYPE_CONVERTERS[type];

    if (converter) {
        return converter(value);
    } else {
        return value;
    }
};

/**
 * Return whether the given type is built-in
 */
var isBuiltIn = function(type) {
    return !!BUILTINS[type];
};

/**
 * Return whether the given type is simple
 */
var isSimple = function(type) {
    return !!TYPE_CONVERTERS[type];
};
'use strict';

//var _ = require('lodash');
//
//var Types = require('./types');
//    DescriptorBuilder = require('./descriptor-builder');
//
//var this.parseName = parseName;


function Registry(packages, properties, options) {
    this.options = _.extend({ generateId: 'id' }, options || {});

    this.packageMap = {};
    this.typeMap = {};

    this.packages = [];

    this.properties = properties;

    _.forEach(packages, this.registerPackage, this);
}

//module.exports = Registry;


Registry.prototype.getPackage = function(uriOrPrefix) {
    return this.packageMap[uriOrPrefix];
};

Registry.prototype.getPackages = function() {
    return this.packages;
};


Registry.prototype.registerPackage = function(pkg) {

    // register types
    _.forEach(pkg.types, function(descriptor) {
        this.registerType(descriptor, pkg);
    }, this);

    this.packageMap[pkg.uri] = this.packageMap[pkg.prefix] = pkg;
    this.packages.push(pkg);
};


/**
 * Register a type from a specific package with us
 */
Registry.prototype.registerType = function(type, pkg) {

    var ns = this.parseName(type.name, pkg.prefix),
        name = ns.name,
        propertiesByName = {};

    // parse properties
    _.forEach(type.properties, function(p) {

        // namespace property names
        var propertyNs = this.parseName(p.name, ns.prefix),
            propertyName = propertyNs.name;

        // namespace property types
        if (!isBuiltIn( p.type)) {
            p.type = this.parseName(p.type, propertyNs.prefix).name;
        }

        _.extend(p, {
            ns: propertyNs,
            name: propertyName
        });

        propertiesByName[propertyName] = p;
    },this);

    // update ns + name
    _.extend(type, {
        ns: ns,
        name: name,
        propertiesByName: propertiesByName
    });

    // link to package
    this.definePackage(type, pkg);

    // register
    this.typeMap[name] = type;
};


/**
 * Traverse the type hierarchy from bottom to top.
 */
Registry.prototype.mapTypes = function(nsName, iterator) {

    var type = this.typeMap[nsName.name];

    if (!type) {
        throw new Error('unknown type <' + nsName.name + '>');
    }

    _.forEach(type.superClass, function(cls) {
        var parentNs = this.parseName(cls, nsName.prefix);
        this.mapTypes(parentNs, iterator);
    }, this);

    iterator(type);
};


/**
 * Returns the effective descriptor for a type.
 *
 * @param  {String} type the namespaced name (ns:localName) of the type
 *
 * @return {Descriptor} the resulting effective descriptor
 */
Registry.prototype.getEffectiveDescriptor = function(name) {

    var options = this.options,
        nsName = this.parseName(name);

    var builder = new DescriptorBuilder(nsName);

    this.mapTypes(nsName, function(type) {
        builder.addTrait(type);
    });

    // check we have an id assigned
    var id = this.options.generateId;
    if (id && !builder.hasProperty(id)) {
        builder.addIdProperty(id);
    }

    var descriptor = builder.build();

    // define package link
    this.definePackage(descriptor, descriptor.allTypes[descriptor.allTypes.length - 1].$pkg);

    return descriptor;
};


Registry.prototype.definePackage = function(target, pkg) {
    this.properties.define(target, '$pkg', { value: pkg });
};

Registry.prototype.parseName = function(name, defaultPrefix) {
    var parts = name.split(/:/),
        localName, prefix;

    // no prefix (i.e. only local name)
    if (parts.length === 1) {
        localName = name;
        prefix = defaultPrefix;
    } else
    // prefix + local name
    if (parts.length === 2) {
        localName = parts[1];
        prefix = parts[0];
    } else {
        throw new Error('expected <prefix:localName> or <localName>, got ' + name);
    }

    name = (prefix ? prefix + ':' : '') + localName;

    return {
        name: name,
        prefix: prefix,
        localName: localName
    };
};





'use strict';

//var _ = require('lodash');
//
//var parseNameNs = require('./ns').parseName;


function DescriptorBuilder(nameNs) {
    this.ns = nameNs;
    this.name = nameNs.name;
    this.allTypes = [];
    this.properties = [];
    this.propertiesByName = {};
}

//module.exports = DescriptorBuilder;


DescriptorBuilder.prototype.build = function() {
    return _.pick(this, [ 'ns', 'name', 'allTypes', 'properties', 'propertiesByName', 'bodyProperty' ]);
};

DescriptorBuilder.prototype.addProperty = function(p, idx) {
    this.addNamedProperty(p, true);

    var properties = this.properties;

    if (idx !== undefined) {
        properties.splice(idx, 0, p);
    } else {
        properties.push(p);
    }
};


DescriptorBuilder.prototype.replaceProperty = function(oldProperty, newProperty) {
    var oldNameNs = oldProperty.ns;

    var props = this.properties,
        propertiesByName = this.propertiesByName;

    if (oldProperty.isBody) {

        if (!newProperty.isBody) {
            throw new Error(
                'property <' + newProperty.ns.name + '> must be body property ' +
                'to refine <' + oldProperty.ns.name + '>');
        }

        // TODO: Check compatibility
        this.setBodyProperty(newProperty, false);
    }

    this.addNamedProperty(newProperty, true);

    // replace old property at index with new one
    var idx = props.indexOf(oldProperty);
    if (idx === -1) {
        throw new Error('property <' + oldNameNs.name + '> not found in property list');
    }

    props[idx] = newProperty;

    // replace propertiesByName entry with new property
    propertiesByName[oldNameNs.name] = propertiesByName[oldNameNs.localName] = newProperty;
};


DescriptorBuilder.prototype.redefineProperty = function(p) {

    var nsPrefix = p.ns.prefix;
    var parts = p.redefines.split('#');

    var name = parseNameNs(parts[0], nsPrefix);
    var attrName = parseNameNs(parts[1], name.prefix).name;

    var redefinedProperty = this.propertiesByName[attrName];
    if (!redefinedProperty) {
        throw new Error('refined property <' + attrName + '> not found');
    } else {
        this.replaceProperty(redefinedProperty, p);
    }

    delete p.redefines;
};

DescriptorBuilder.prototype.addNamedProperty = function(p, validate) {
    var ns = p.ns,
        propsByName = this.propertiesByName;

    if (validate) {
        this.assertNotDefined(p, ns.name);
        this.assertNotDefined(p, ns.localName);
    }

    propsByName[ns.name] = propsByName[ns.localName] = p;
};

DescriptorBuilder.prototype.removeNamedProperty = function(p) {
    var ns = p.ns,
        propsByName = this.propertiesByName;

    delete propsByName[ns.name];
    delete propsByName[ns.localName];
};

DescriptorBuilder.prototype.setBodyProperty = function(p, validate) {

    if (validate && this.bodyProperty) {
        throw new Error(
            'body property defined multiple times ' +
            '(<' + this.bodyProperty.ns.name + '>, <' + p.ns.name + '>)');
    }

    this.bodyProperty = p;
};

DescriptorBuilder.prototype.addIdProperty = function(name) {
    var nameNs = parseNameNs(name, this.ns.prefix);

    var p = {
        name: nameNs.localName,
        type: 'String',
        isAttr: true,
        ns: nameNs
    };

    // ensure that id is always the first attribute (if present)
    this.addProperty(p, 0);
};

DescriptorBuilder.prototype.assertNotDefined = function(p, name) {
    var propertyName = p.name,
        definedProperty = this.propertiesByName[propertyName];

    if (definedProperty) {
        throw new Error(
            'property <' + propertyName + '> already defined; ' +
            'override of <' + definedProperty.definedBy.ns.name + '#' + definedProperty.ns.name + '> by ' +
            '<' + p.definedBy.ns.name + '#' + p.ns.name + '> not allowed without redefines');
    }
};

DescriptorBuilder.prototype.hasProperty = function(name) {
    return this.propertiesByName[name];
};

DescriptorBuilder.prototype.addTrait = function(t) {

    var allTypes = this.allTypes;

    if (allTypes.indexOf(t) !== -1) {
        return;
    }

    _.forEach(t.properties, function(p) {

        // clone property to allow extensions
        p = _.extend({}, p, {
            name: p.ns.localName
        });

        Object.defineProperty(p, 'definedBy', {
            value: t
        });

        // add redefine support
        if (p.redefines) {
            this.redefineProperty(p);
        } else {
            if (p.isBody) {
                this.setBodyProperty(p);
            }
            this.addProperty(p);
        }
    }, this);

    allTypes.push(t);
};
'use strict';

//var _ = require('lodash');
//
//var Types = require('./types'),
//    Factory = require('./factory'),
//    Registry = require('./registry'),
//    Properties = require('./properties');
//
//var parseNameNs = require('./ns').parseName;


//// Moddle implementation /////////////////////////////////////////////////

/**
 * @class Moddle
 *
 * A model that can be used to create elements of a specific type.
 *
 * @example
 *
 * var Moddle = require('moddle');
 *
 * var pkg = {
 *   name: 'mypackage',
 *   prefix: 'my',
 *   types: [
 *     { name: 'Root' }
 *   ]
 * };
 *
 * var moddle = new Moddle([pkg]);
 *
 * @param {Array<Package>} packages  the packages to contain
 * @param {Object} options  additional options to pass to the model
 */
function Moddle(packages, options) {

    options = options || {};

    this.properties = new Properties(this);

    this.factory = new Factory(this, this.properties);
    this.registry = new Registry(packages, this.properties, options);

    this.typeCache = {};
}

//module.exports = Moddle;


/**
 * Create an instance of the specified type.
 *
 * @method Moddle#create
 *
 * @example
 *
 * var foo = moddle.create('my:Foo');
 * var bar = moddle.create('my:Bar', { id: 'BAR_1' });
 *
 * @param  {String|Object} descriptor the type descriptor or name know to the model
 * @param  {Object} attrs   a number of attributes to initialize the model instance with
 * @return {Object}         model instance
 */
Moddle.prototype.create = function(descriptor, attrs) {
    var Type = this.getType(descriptor);

    if (!Type) {
        throw new Error('unknown type <' + descriptor + '>');
    }

    return new Type(attrs);
};


/**
 * Returns the type representing a given descriptor
 *
 * @method Moddle#getType
 *
 * @example
 *
 * var Foo = moddle.getType('my:Foo');
 * var foo = new Foo({ 'id' : 'FOO_1' });
 *
 * @param  {String|Object} descriptor the type descriptor or name know to the model
 * @return {Object}         the type representing the descriptor
 */
Moddle.prototype.getType = function(descriptor) {

    var cache = this.typeCache;

    var name = _.isString(descriptor) ? descriptor : descriptor.ns.name;

    var type = cache[name];

    if (!type) {
        descriptor = this.registry.getEffectiveDescriptor(name);
        type = cache[descriptor.name] = this.factory.createType(descriptor);
    }

    return type;
};


/**
 * Creates an any-element type to be used within model instances.
 *
 * This can be used to create custom elements that lie outside the meta-model.
 * The created element contains all the meta-data required to serialize it
 * as part of meta-model elements.
 *
 * @method Moddle#createAny
 *
 * @example
 *
 * var foo = moddle.createAny('vendor:Foo', 'http://vendor', {
 *   value: 'bar'
 * });
 *
 * var container = moddle.create('my:Container', 'http://my', {
 *   any: [ foo ]
 * });
 *
 * // go ahead and serialize the stuff
 *
 *
 * @param  {String} name  the name of the element
 * @param  {String} nsUri the namespace uri of the element
 * @param  {Object} [properties] a map of properties to initialize the instance with
 * @return {Object} the any type instance
 */
Moddle.prototype.createAny = function(name, nsUri, properties) {

    var nameNs = parseNameNs(name);

    var element = {
        $type: name
    };

    var descriptor = {
        name: name,
        isGeneric: true,
        ns: {
            prefix: nameNs.prefix,
            localName: nameNs.localName,
            uri: nsUri
        }
    };

    this.properties.defineDescriptor(element, descriptor);
    this.properties.defineModel(element, this);
    this.properties.define(element, '$parent', { enumerable: false, writable: true });

    _.forEach(properties, function(a, key) {
        if (_.isObject(a) && a.value !== undefined) {
            element[a.name] = a.value;
        } else {
            element[key] = a;
        }
    });

    return element;
};

/**
 * Returns a registered package by uri or prefix
 *
 * @return {Object} the package
 */
Moddle.prototype.getPackage = function(uriOrPrefix) {
    return this.registry.getPackage(uriOrPrefix);
};

/**
 * Returns a snapshot of all known packages
 *
 * @return {Object} the package
 */
Moddle.prototype.getPackages = function() {
    return this.registry.getPackages();
};

/**
 * Returns the descriptor for an element
 */
Moddle.prototype.getElementDescriptor = function(element) {
    return element.$descriptor;
};

/**
 * Returns true if the given descriptor or instance
 * represents the given type.
 *
 * May be applied to this, if element is omitted.
 */
Moddle.prototype.hasType = function(element, type) {
    if (type === undefined) {
        type = element;
        element = this;
    }

    var descriptor = element.$model.getElementDescriptor(element);

    return !!_.find(descriptor.allTypes, function(t) {
        return t.name === type;
    });
};


/**
 * Returns the descriptor of an elements named property
 */
Moddle.prototype.getPropertyDescriptor = function(element, property) {
    return this.getElementDescriptor(element).propertiesByName[property];
};
'use strict';


function capitalize(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
}

function lower(string) {
    return string.charAt(0).toLowerCase() + string.slice(1);
}

function hasLowerCaseAlias(pkg) {
    return pkg.xml && pkg.xml.alias === 'lowerCase';
}


var aliasToName = function(alias, pkg) {
    if (hasLowerCaseAlias(pkg)) {
        return capitalize(alias);
    } else {
        return alias;
    }
};

var nameToAlias = function(name, pkg) {
    if (hasLowerCaseAlias(pkg)) {
        return lower(name);
    } else {
        return name;
    }
};

var DEFAULT_NS_MAP = {
    'xsi': 'http://www.w3.org/2001/XMLSchema-instance'
};

//
//module.exports.DEFAULT_NS_MAP = {
//    'xsi': 'http://www.w3.org/2001/XMLSchema-instance'
//};
'use strict';

//var sax = require('sax'),
//    _ = require('lodash');
//


//var common = require('./common'),
//    Types = require('moddle').types,
//    Stack = require('tiny-stack'),
//    parseNameNs = require('moddle').ns.parseName,
var parseNameNs = parseName;
var aliasToName = aliasToName;


function parseNodeAttributes(node) {
    var nodeAttrs = node.attributes;

    return _.reduce(nodeAttrs, function(result, v, k) {
        var name, ns;

        if (!v.local) {
            name = v.prefix;
        } else {
            ns = parseNameNs(v.name, v.prefix);
            name = ns.name;
        }

        result[name] = v.value;
        return result;
    }, {});
}

/**
 * Normalizes namespaces for a node given an optional default namespace and a
 * number of mappings from uris to default prefixes.
 *
 * @param  {XmlNode} node
 * @param  {Model} model the model containing all registered namespaces
 * @param  {Uri} defaultNsUri
 */
function normalizeNamespaces(node, model, defaultNsUri) {
    var uri, childUri, prefix;

    uri = node.uri || defaultNsUri;

    if (uri) {
        var pkg = model.getPackage(uri);

        if (pkg) {
            prefix = pkg.prefix;
        } else {
            prefix = node.prefix;
        }

        node.prefix = prefix;
        node.uri = uri;
    }

    _.forEach(node.attributes, function(attr) {
        normalizeNamespaces(attr, model, null);
    });
}

/**
 * A parse context.
 *
 * @class
 *
 * @param {ElementHandler} parseRoot the root handler for parsing a document
 */
function Context(parseRoot) {

    var elementsById = {};
    var references = [];

    var warnings = [];

    this.addReference = function(reference) {
        references.push(reference);
    };

    this.addElement = function(id, element) {

        if (!id || !element) {
            throw new Error('[xml-reader] id or ctx must not be null');
        }

        elementsById[id] = element;
    };

    this.addWarning = function (w) {
        warnings.push(w);
    };

    this.warnings = warnings;
    this.references = references;

    this.elementsById = elementsById;

    this.parseRoot = parseRoot;
}


function BaseHandler() {}

BaseHandler.prototype.handleEnd = function() {};
BaseHandler.prototype.handleText = function() {};
BaseHandler.prototype.handleNode = function() {};

function BodyHandler() {}

BodyHandler.prototype = new BaseHandler();

BodyHandler.prototype.handleText = function(text) {
    this.body = (this.body || '') + text;
};

function ReferenceHandler(property, context) {
    this.property = property;
    this.context = context;
}

ReferenceHandler.prototype = new BodyHandler();

ReferenceHandler.prototype.handleNode = function(node) {

    if (this.element) {
        throw new Error('expected no sub nodes');
    } else {
        this.element = this.createReference(node);
    }

    return this;
};

ReferenceHandler.prototype.handleEnd = function() {
    this.element.id = this.body;
};

ReferenceHandler.prototype.createReference = function() {
    return {
        property: this.property.ns.name,
        id: ''
    };
};

function ValueHandler(propertyDesc, element) {
    this.element = element;
    this.propertyDesc = propertyDesc;
}

ValueHandler.prototype = new BodyHandler();

ValueHandler.prototype.handleEnd = function() {

    var value = this.body,
        element = this.element,
        propertyDesc = this.propertyDesc;

    value = coerceType(propertyDesc.type, value);

    if (propertyDesc.isMany) {
        element.get(propertyDesc.name).push(value);
    } else {
        element.set(propertyDesc.name, value);
    }
};


function BaseElementHandler() {}

BaseElementHandler.prototype = Object.create(BodyHandler.prototype);

BaseElementHandler.prototype.handleNode = function(node) {
    var parser = this;

    if (!this.element) {
        this.element = this.createElement(node);
        var id = this.element.id;

        if (id) {
            this.context.addElement(id, this.element);
        }
    } else {
        parser = this.handleChild(node);
    }

    return parser;
};

/**
 * @class XMLReader.ElementHandler
 *
 */
function ElementHandler(model, type, context) {
    this.model = model;
    this.type = model.getType(type);
    this.context = context;
}

ElementHandler.prototype = new BaseElementHandler();

ElementHandler.prototype.addReference = function(reference) {
    this.context.addReference(reference);
};

ElementHandler.prototype.handleEnd = function() {

    var value = this.body,
        element = this.element,
        descriptor = element.$descriptor,
        bodyProperty = descriptor.bodyProperty;

    if (bodyProperty && value !== undefined) {
        value = coerceType(bodyProperty.type, value);
        element.set(bodyProperty.name, value);
    }
};

/**
 * Create an instance of the model from the given node.
 *
 * @param  {Element} node the xml node
 */
ElementHandler.prototype.createElement = function(node) {
    var attributes = parseNodeAttributes(node),
        Type = this.type,
        descriptor = Type.$descriptor,
        context = this.context,
        instance = new Type({});

    _.forEach(attributes, function(value, name) {

        var prop = descriptor.propertiesByName[name];

        if (prop && prop.isReference) {
            context.addReference({
                element: instance,
                property: prop.ns.name,
                id: value
            });
        } else {
            if (prop) {
                value = coerceType(prop.type, value);
            }

            instance.set(name, value);
        }
    });

    return instance;
};

ElementHandler.prototype.getPropertyForElement = function(nameNs) {
    if (_.isString(nameNs)) {
        nameNs = parseNameNs(nameNs);
    }

    var type = this.type,
        model = this.model,
        descriptor = type.$descriptor;

    var propertyName = nameNs.name;

    var property = descriptor.propertiesByName[propertyName];

    // search for properties by name first
    if (property) {
        return property;
    }

    var pkg = model.getPackage(nameNs.prefix);

    if (pkg) {
        var typeName = nameNs.prefix + ':' + aliasToName(nameNs.localName, descriptor.$pkg),
            elementType = model.getType(typeName);

        // search for collection members later
        property = _.find(descriptor.properties, function(p) {
            return !p.isVirtual && !p.isReference && !p.isAttribute && elementType.hasType(p.type);
        });

        if (property) {
            return _.extend({}, property, { effectiveType: elementType.$descriptor.name });
        }
    } else {
        // parse unknown element (maybe extension)
        property = _.find(descriptor.properties, function(p) {
            return !p.isReference && !p.isAttribute && p.type === 'Element';
        });

        if (property) {
            return property;
        }
    }

    throw new Error('unrecognized element <' + nameNs.name + '>');
};

ElementHandler.prototype.toString = function() {
    return 'ElementDescriptor[' + this.type.$descriptor.name + ']';
};

ElementHandler.prototype.valueHandler = function(propertyDesc, element) {
    return new ValueHandler(propertyDesc, element);
};

ElementHandler.prototype.referenceHandler = function(propertyDesc) {
    return new ReferenceHandler(propertyDesc, this.context);
};

ElementHandler.prototype.handler = function(type) {
    if (type === 'Element') {
        return new GenericElementHandler(this.model, type, this.context);
    } else {
        return new ElementHandler(this.model, type, this.context);
    }
};

/**
 * Handle the child element parsing
 *
 * @param  {Element} node the xml node
 */
ElementHandler.prototype.handleChild = function(node) {
    var nameNs = parseNameNs(node.local, node.prefix);

    var propertyDesc, type, element, childHandler;

    propertyDesc = this.getPropertyForElement(nameNs);
    element = this.element;

    type = propertyDesc.effectiveType || propertyDesc.type;

    if (isSimple(propertyDesc.type)) {
        return this.valueHandler(propertyDesc, element);
    }

    if (propertyDesc.isReference) {
        childHandler = this.referenceHandler(propertyDesc).handleNode(node);
    } else {
        childHandler = this.handler(type).handleNode(node);
    }

    var newElement = childHandler.element;

    // child handles may decide to skip elements
    // by not returning anything
    if (newElement !== undefined) {

        if (propertyDesc.isMany) {
            element.get(propertyDesc.name).push(newElement);
        } else {
            element.set(propertyDesc.name, newElement);
        }

        if (propertyDesc.isReference) {
            _.extend(newElement, {
                element: element
            });

            this.context.addReference(newElement);
        } else {
            // establish child -> parent relationship
            newElement.$parent = element;
        }
    }

    return childHandler;
};


function GenericElementHandler(model, type, context) {
    this.model = model;
    this.context = context;
}

GenericElementHandler.prototype = Object.create(BaseElementHandler.prototype);

GenericElementHandler.prototype.createElement = function(node) {

    var name = node.name,
        prefix = node.prefix,
        uri = node.ns[prefix],
        attributes = node.attributes;

    return this.model.createAny(name, uri, attributes);
};

GenericElementHandler.prototype.handleChild = function(node) {

    var handler = new GenericElementHandler(this.model, 'Element', this.context).handleNode(node),
        element = this.element;

    var newElement = handler.element,
        children;

    if (newElement !== undefined) {
        children = element.$children = element.$children || [];
        children.push(newElement);

        // establish child -> parent relationship
        newElement.$parent = element;
    }

    return handler;
};

GenericElementHandler.prototype.handleText = function(text) {
    this.body = this.body || '' + text;
};

GenericElementHandler.prototype.handleEnd = function() {
    if (this.body) {
        this.element.$body = this.body;
    }
};

/**
 * A reader for a meta-model
 *
 * @class XMLReader
 *
 * @param {Model} model used to read xml files
 */
function XMLReader(model) {
    this.model = model;
}


XMLReader.prototype.fromXML = function(xml, rootHandler, done) {

    var context = new Context(rootHandler);

    var parser = sax.parser(true, { xmlns: true, trim: true });
    var stackInstance =  stack();

    var model = this.model,
        self = this;

    rootHandler.context = context;

    // push root handler
    stackInstance.push(rootHandler);


    function resolveReferences() {

        var elementsById = context.elementsById;
        var references = context.references;

        var i, r;

        for (i = 0; !!(r = references[i]); i++) {
            var element = r.element;
            var reference = elementsById[r.id];
            var property = element.$descriptor.propertiesByName[r.property];

            if (!reference) {
                context.addWarning({
                    message: 'unresolved reference <' + r.id + '>',
                    element: r.element,
                    property: r.property,
                    value: r.id
                });
            }

            if (property.isMany) {
                var collection = element.get(property.name),
                    idx = collection.indexOf(r);

                if (!reference) {
                    // remove unresolvable reference
                    collection.splice(idx, 1);
                } else {
                    // update reference
                    collection[idx] = reference;
                }
            } else {
                element.set(property.name, reference);
            }
        }
    }

    function handleClose(tagName) {
        stackInstance.pop().handleEnd();
    }

    function handleOpen(node) {
        var handler = stackInstance.peek();

        normalizeNamespaces(node, model);

        try {
            stackInstance.push(handler.handleNode(node));
        } catch (e) {

            var line = this.line,
                column = this.column;

            throw new Error(
                'unparsable content <' + node.name + '> detected\n\t' +
                'line: ' + line + '\n\t' +
                'column: ' + column + '\n\t' +
                'nested error: ' + e.message);
        }
    }

    function handleText(text) {
        stackInstance.peek().handleText(text);
    }

    parser.onopentag = handleOpen;
    parser.oncdata = parser.ontext = handleText;
    parser.onclosetag = handleClose;
    parser.onend = resolveReferences;

    // deferred parse XML to make loading really ascnchronous
    // this ensures the execution environment (node or browser)
    // is kept responsive and that certain optimization strategies
    // can kick in
    _.defer(function() {
        var error;

        try {
            parser.write(xml).close();
        } catch (e) {
            error = e;
        }

        done(error, error ? undefined : rootHandler.element, context);
    });
};

XMLReader.prototype.handler = function(name) {
    return new ElementHandler(this.model, name);
};
'use strict';

//var _ = require('lodash');

//var Types = require('moddle').types,
//    common = require('./common'),
    var parseNameNs = parseName;
    var nameToAlias = nameToAlias;

var XML_PREAMBLE = '<?xml version="1.0" encoding="UTF-8"?>\n';

var CDATA_ESCAPE = /[<>"&]+/;

var DEFAULT_NS_MAP = DEFAULT_NS_MAP;


function nsName(ns) {
    if (_.isString(ns)) {
        return ns;
    } else {
        return (ns.prefix ? ns.prefix + ':' : '') + ns.localName;
    }
}

function getElementNs(ns, descriptor) {
    if (descriptor.isGeneric) {
        return descriptor.name;
    } else {
        return _.extend({ localName: nameToAlias(descriptor.ns.localName, descriptor.$pkg) }, ns);
    }
}

function getPropertyNs(ns, descriptor) {
    return _.extend({ localName: descriptor.ns.localName }, ns);
}

function getSerializableProperties(element) {
    var descriptor = element.$descriptor;

    return _.filter(descriptor.properties, function(p) {
        var name = p.name;

        // do not serialize defaults
        if (!element.hasOwnProperty(name)) {
            return false;
        }

        var value = element[name];

        // do not serialize default equals
        if (value === p.default) {
            return false;
        }

        return p.isMany ? value ? value.length : true : true;
    });
}

/**
 * Escape a string attribute to not contain any bad values (line breaks, '"', ...)
 *
 * @param {String} str the string to escape
 * @return {String} the escaped string
 */
function escapeAttr(str) {
    var escapeMap = {
        '\n': '&#10;',
        '\n\r': '&#10;',
        '"': '&quot;'
    };

    // ensure we are handling strings here
    str = _.isString(str) ? str : '' + str;

    return str.replace(/(\n|\n\r|")/g, function(str) {
        return escapeMap[str];
    });
}

function filterAttributes(props) {
    return _.filter(props, function(p) { return p.isAttr; });
}

function filterContained(props) {
    return _.filter(props, function(p) { return !p.isAttr; });
}


function ReferenceSerializer(parent, ns) {
    this.ns = ns;
}

ReferenceSerializer.prototype.build = function(element) {
    this.element = element;
    return this;
};

ReferenceSerializer.prototype.serializeTo = function(writer) {
    writer
        .appendIndent()
        .append('<' + nsName(this.ns) + '>' + this.element.id + '</' + nsName(this.ns) + '>')
        .appendNewLine();
};

function BodySerializer() {}

BodySerializer.prototype.serializeValue = BodySerializer.prototype.serializeTo = function(writer) {
    var value = this.value,
        escape = this.escape;

    if (escape) {
        writer.append('<![CDATA[');
    }

    writer.append(this.value);

    if (escape) {
        writer.append(']]>');
    }
};

BodySerializer.prototype.build = function(prop, value) {
    this.value = value;

    if (prop.type === 'String' && CDATA_ESCAPE.test(value)) {
        this.escape = true;
    }

    return this;
};

function ValueSerializer(ns) {
    this.ns = ns;
}

ValueSerializer.prototype = new BodySerializer();

ValueSerializer.prototype.serializeTo = function(writer) {

    writer
        .appendIndent()
        .append('<' + nsName(this.ns) + '>');

    this.serializeValue(writer);

    writer
        .append( '</' + nsName(this.ns) + '>')
        .appendNewLine();
};

function ElementSerializer(parent, ns) {
    this.body = [];
    this.attrs = [];

    this.parent = parent;
    this.ns = ns;
}

ElementSerializer.prototype.build = function(element) {
    this.element = element;

    var otherAttrs = this.parseNsAttributes(element);

    if (!this.ns) {
        this.ns = this.nsTagName(element.$descriptor);
    }

    if (element.$descriptor.isGeneric) {
        this.parseGeneric(element);
    } else {
        var properties = getSerializableProperties(element);

        this.parseAttributes(filterAttributes(properties));
        this.parseContainments(filterContained(properties));

        this.parseGenericAttributes(element, otherAttrs);
    }

    return this;
};

ElementSerializer.prototype.nsTagName = function(descriptor) {
    var effectiveNs = this.logNamespaceUsed(descriptor.ns);
    return getElementNs(effectiveNs, descriptor);
};

ElementSerializer.prototype.nsPropertyTagName = function(descriptor) {
    var effectiveNs = this.logNamespaceUsed(descriptor.ns);
    return getPropertyNs(effectiveNs, descriptor);
};

ElementSerializer.prototype.isLocalNs = function(ns) {
    return ns.uri === this.ns.uri;
};

ElementSerializer.prototype.nsAttributeName = function(element) {

    var ns;

    if (_.isString(element)) {
        ns = parseNameNs(element);
    } else
    if (element.ns) {
        ns = element.ns;
    }

    var effectiveNs = this.logNamespaceUsed(ns);

    // strip prefix if same namespace like parent
    if (this.isLocalNs(effectiveNs)) {
        return { localName: ns.localName };
    } else {
        return _.extend({ localName: ns.localName }, effectiveNs);
    }
};

ElementSerializer.prototype.parseGeneric = function(element) {

    var self = this,
        body = this.body,
        attrs = this.attrs;

    _.forEach(element, function(val, key) {

        if (key === '$body') {
            body.push(new BodySerializer().build({ type: 'String' }, val));
        } else
        if (key === '$children') {
            _.forEach(val, function(child) {
                body.push(new ElementSerializer(self).build(child));
            });
        } else
        if (key.indexOf('$') !== 0) {
            attrs.push({ name: key, value: escapeAttr(val) });
        }
    });
};

/**
 * Parse namespaces and return a list of left over generic attributes
 *
 * @param  {Object} element
 * @return {Array<Object>}
 */
ElementSerializer.prototype.parseNsAttributes = function(element) {
    var self = this;

    var genericAttrs = element.$attrs;

    var attributes = [];

    // parse namespace attributes first
    // and log them. push non namespace attributes to a list
    // and process them later
    _.forEach(genericAttrs, function(value, name) {
        var nameNs = parseNameNs(name);

        if (nameNs.prefix === 'xmlns') {
            self.logNamespace({ prefix: nameNs.localName, uri: value });
        } else
        if (!nameNs.prefix && nameNs.localName === 'xmlns') {
            self.logNamespace({ uri: value });
        } else {
            attributes.push({ name: name, value: value });
        }
    });

    return attributes;
};

ElementSerializer.prototype.parseGenericAttributes = function(element, attributes) {

    var self = this;

    _.forEach(attributes, function(attr) {
        try {
            self.addAttribute(self.nsAttributeName(attr.name), attr.value);
        } catch (e) {
            console.warn('[writer] missing namespace information for ', attr.name, '=', attr.value, 'on', element, e);
        }
    });
};

ElementSerializer.prototype.parseContainments = function(properties) {

    var self = this,
        body = this.body,
        element = this.element,
        typeDesc = element.$descriptor;

    _.forEach(properties, function(p) {
        var value = element.get(p.name),
            isReference = p.isReference,
            isMany = p.isMany;

        var ns = self.nsPropertyTagName(p);

        if (!isMany) {
            value = [ value ];
        }

        if (p.isBody) {
            body.push(new BodySerializer().build(p, value[0]));
        } else
        //if (Types.isSimple(p.type)) {
        if (isSimple(p.type)) {
            _.forEach(value, function(v) {
                body.push(new ValueSerializer(ns).build(p, v));
            });
        } else
        if (isReference) {
            _.forEach(value, function(v) {
                body.push(new ReferenceSerializer(self, ns).build(v));
            });
        } else {
            // allow serialization via type
            // rather than element name
            var asType = p.serialize === 'xsi:type';

            _.forEach(value, function(v) {
                var serializer;

                if (asType) {
                    serializer = new TypeSerializer(self, ns);
                } else {
                    serializer = new ElementSerializer(self);
                }

                body.push(serializer.build(v));
            });
        }
    });
};

ElementSerializer.prototype.getNamespaces = function() {
    if (!this.parent) {
        if (!this.namespaces) {
            this.namespaces = {
                prefixMap: {},
                uriMap: {},
                used: {}
            };
        }
    } else {
        this.namespaces = this.parent.getNamespaces();
    }

    return this.namespaces;
};

ElementSerializer.prototype.logNamespace = function(ns) {
    var namespaces = this.getNamespaces();

    var existing = namespaces.uriMap[ns.uri];

    if (!existing) {
        namespaces.uriMap[ns.uri] = ns;
    }

    namespaces.prefixMap[ns.prefix] = ns.uri;

    return ns;
};

ElementSerializer.prototype.logNamespaceUsed = function(ns) {
    var element = this.element,
        model = element.$model,
        namespaces = this.getNamespaces();

    // ns may be
    //
    //   * prefix only
    //   * prefix:uri

    var prefix = ns.prefix;
    var uri = ns.uri || DEFAULT_NS_MAP[prefix] ||
        namespaces.prefixMap[prefix] || (model ? (model.getPackage(prefix) || {}).uri : null);

    if (!uri) {
        throw new Error('no namespace uri given for prefix <' + ns.prefix + '>');
    }

    ns = namespaces.uriMap[uri];

    if (!ns) {
        ns = this.logNamespace({ prefix: prefix, uri: uri });
    }

    if (!namespaces.used[ns.uri]) {
        namespaces.used[ns.uri] = ns;
    }

    return ns;
};

ElementSerializer.prototype.parseAttributes = function(properties) {
    var self = this,
        element = this.element;

    _.forEach(properties, function(p) {
        self.logNamespaceUsed(p.ns);

        var value = element.get(p.name);

        if (p.isReference) {
            value = value.id;
        }

        self.addAttribute(self.nsAttributeName(p), value);
    });
};

ElementSerializer.prototype.addAttribute = function(name, value) {
    var attrs = this.attrs;

    if (_.isString(value)) {
        value = escapeAttr(value);
    }

    attrs.push({ name: name, value: value });
};

ElementSerializer.prototype.serializeAttributes = function(writer) {
    var element = this.element,
        attrs = this.attrs,
        root = !this.parent,
        namespaces = this.namespaces;

    function collectNsAttrs() {
        return _.collect(namespaces.used, function(ns) {
            var name = 'xmlns' + (ns.prefix ? ':' + ns.prefix : '');
            return { name: name, value: ns.uri };
        });
    }

    if (root) {
        attrs = collectNsAttrs().concat(attrs);
    }

    _.forEach(attrs, function(a) {
        writer
            .append(' ')
            .append(nsName(a.name)).append('="').append(a.value).append('"');
    });
};

ElementSerializer.prototype.serializeTo = function(writer) {
    var hasBody = this.body.length;

    writer
        .appendIndent()
        .append('<' + nsName(this.ns));

    this.serializeAttributes(writer);

    writer
        .append(hasBody ? '>' : ' />')
        .appendNewLine();

    writer.indent();

    _.forEach(this.body, function(b) {
        b.serializeTo(writer);
    });

    writer.unindent();

    if (hasBody) {
        writer
            .appendIndent()
            .append('</' + nsName(this.ns) + '>')
            .appendNewLine();
    }
};

/**
 * A serializer for types that handles serialization of data types
 */
function TypeSerializer(parent, ns) {
    ElementSerializer.call(this, parent, ns);
}

TypeSerializer.prototype = new ElementSerializer();

TypeSerializer.prototype.build = function(element) {
    this.element = element;
    this.typeNs = this.nsTagName(element.$descriptor);

    return ElementSerializer.prototype.build.call(this, element);
};

TypeSerializer.prototype.isLocalNs = function(ns) {
    return ns.uri === this.typeNs.uri;
};

function SavingWriter() {
    this.value = '';

    this.write = function(str) {
        this.value += str;
    };
}

function FormatingWriter(out, format) {

    var indent = [''];

    this.append = function(str) {
        out.write(str);

        return this;
    };

    this.appendNewLine = function() {
        if (format) {
            out.write('\n');
        }

        return this;
    };

    this.appendIndent = function() {
        if (format) {
            out.write(indent.join('  '));
        }

        return this;
    };

    this.indent = function() {
        indent.push('');
        return this;
    };

    this.unindent = function() {
        indent.pop();
        return this;
    };
}

/**
 * A writer for meta-model backed document trees
 *
 * @class XMLWriter
 */
function XMLWriter(options) {

    options = _.extend({ format: false, preamble: true }, options || {});

    function toXML(tree, writer) {
        var internalWriter = writer || new SavingWriter();
        var formatingWriter = new FormatingWriter(internalWriter, options.format);

        if (options.preamble) {
            formatingWriter.append(XML_PREAMBLE);
        }

        new ElementSerializer().build(tree).serializeTo(formatingWriter);

        if (!writer) {
            return internalWriter.value;
        }
    }

    return {
        toXML: toXML
    };
}

//module.exports = XMLWriter;
'use strict';
//
//var _ = require('lodash');
//
//var Moddle = require('bpmn-moddle');
    //ModdleXml = require('moddle-xml');
//

function createModel(packages) {
    return new Moddle(packages);
}

/**
 * A sub class of {@link Moddle} with support for import and export of BPMN 2.0 xml files.
 *
 * @class BpmnModdle
 * @extends Moddle
 *
 * @param {Object|Array} packages to use for instantiating the model
 * @param {Object} [options] additional options to pass over
 */
function BpmnModdle(packages, options) {
    var packages = {
        bpmn: {
            "name": "BPMN20",
            "uri": "http://www.omg.org/spec/BPMN/20100524/MODEL",
            "associations": [],
            "types": [
                {
                    "name": "Interface",
                    "superClass": [
                        "RootElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "operations",
                            "type": "Operation",
                            "association": "A_operations_interface",
                            "isMany": true
                        },
                        {
                            "name": "implementationRef",
                            "type": "String",
                            "isAttr": true
                        }
                    ]
                },
                {
                    "name": "Operation",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "inMessageRef",
                            "type": "Message",
                            "association": "A_inMessageRef_operation",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "outMessageRef",
                            "type": "Message",
                            "association": "A_outMessageRef_operation",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "errorRefs",
                            "type": "Error",
                            "association": "A_errorRefs_operation",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "implementationRef",
                            "type": "String",
                            "isAttr": true
                        }
                    ]
                },
                {
                    "name": "EndPoint",
                    "superClass": [
                        "RootElement"
                    ]
                },
                {
                    "name": "Auditing",
                    "superClass": [
                        "BaseElement"
                    ]
                },
                {
                    "name": "GlobalTask",
                    "superClass": [
                        "CallableElement"
                    ],
                    "properties": [
                        {
                            "name": "resources",
                            "type": "ResourceRole",
                            "association": "A_resources_globalTask",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "Monitoring",
                    "superClass": [
                        "BaseElement"
                    ]
                },
                {
                    "name": "Performer",
                    "superClass": [
                        "ResourceRole"
                    ]
                },
                {
                    "name": "Process",
                    "superClass": [
                        "FlowElementsContainer",
                        "CallableElement"
                    ],
                    "properties": [
                        {
                            "name": "processType",
                            "type": "ProcessType",
                            "isAttr": true
                        },
                        {
                            "name": "isClosed",
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "auditing",
                            "type": "Auditing",
                            "association": "A_auditing_process"
                        },
                        {
                            "name": "monitoring",
                            "type": "Monitoring",
                            "association": "A_monitoring_process"
                        },
                        {
                            "name": "properties",
                            "type": "Property",
                            "association": "A_properties_process",
                            "isMany": true
                        },
                        {
                            "name": "supports",
                            "type": "Process",
                            "association": "A_supports_process",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "definitionalCollaborationRef",
                            "type": "Collaboration",
                            "association": "A_definitionalCollaborationRef_process",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "isExecutable",
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "resources",
                            "type": "ResourceRole",
                            "association": "A_resources_process",
                            "isMany": true
                        },
                        {
                            "name": "artifacts",
                            "type": "Artifact",
                            "association": "A_artifacts_process",
                            "isMany": true
                        },
                        {
                            "name": "correlationSubscriptions",
                            "type": "CorrelationSubscription",
                            "association": "A_correlationSubscriptions_process",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "LaneSet",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "lanes",
                            "type": "Lane",
                            "association": "A_lanes_laneSet",
                            "isMany": true
                        },
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        }
                    ]
                },
                {
                    "name": "Lane",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "childLaneSet",
                            "type": "LaneSet",
                            "association": "A_childLaneSet_parentLane"
                        },
                        {
                            "name": "partitionElementRef",
                            "type": "BaseElement",
                            "association": "A_partitionElementRef_lane",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "flowNodeRef",
                            "type": "FlowNode",
                            "association": "A_flowNodeRefs_lanes",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "partitionElement",
                            "type": "BaseElement",
                            "association": "A_partitionElement_lane"
                        }
                    ]
                },
                {
                    "name": "GlobalManualTask",
                    "superClass": [
                        "GlobalTask"
                    ]
                },
                {
                    "name": "ManualTask",
                    "superClass": [
                        "Task"
                    ]
                },
                {
                    "name": "UserTask",
                    "superClass": [
                        "Task"
                    ],
                    "properties": [
                        {
                            "name": "renderings",
                            "type": "Rendering",
                            "association": "A_renderings_usertask",
                            "isMany": true
                        },
                        {
                            "name": "implementation",
                            "isAttr": true,
                            "type": "String"
                        }
                    ]
                },
                {
                    "name": "Rendering",
                    "superClass": [
                        "BaseElement"
                    ]
                },
                {
                    "name": "HumanPerformer",
                    "superClass": [
                        "Performer"
                    ]
                },
                {
                    "name": "PotentialOwner",
                    "superClass": [
                        "HumanPerformer"
                    ]
                },
                {
                    "name": "GlobalUserTask",
                    "superClass": [
                        "GlobalTask"
                    ],
                    "properties": [
                        {
                            "name": "implementation",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "renderings",
                            "type": "Rendering",
                            "association": "A_renderings_globalUserTask",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "Gateway",
                    "isAbstract": true,
                    "superClass": [
                        "FlowNode"
                    ],
                    "properties": [
                        {
                            "name": "gatewayDirection",
                            "type": "GatewayDirection",
                            "default": "Unspecified",
                            "isAttr": true
                        }
                    ]
                },
                {
                    "name": "EventBasedGateway",
                    "superClass": [
                        "Gateway"
                    ],
                    "properties": [
                        {
                            "name": "instantiate",
                            "default": false,
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "eventGatewayType",
                            "type": "EventBasedGatewayType",
                            "isAttr": true,
                            "default": "Exclusive"
                        }
                    ]
                },
                {
                    "name": "ComplexGateway",
                    "superClass": [
                        "Gateway"
                    ],
                    "properties": [
                        {
                            "name": "activationCondition",
                            "type": "Expression",
                            "association": "A_activationCondition_complexGateway"
                        },
                        {
                            "name": "default",
                            "type": "SequenceFlow",
                            "association": "A_default_complexGateway",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "ExclusiveGateway",
                    "superClass": [
                        "Gateway"
                    ],
                    "properties": [
                        {
                            "name": "default",
                            "type": "SequenceFlow",
                            "association": "A_default_exclusiveGateway",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "InclusiveGateway",
                    "superClass": [
                        "Gateway"
                    ],
                    "properties": [
                        {
                            "name": "default",
                            "type": "SequenceFlow",
                            "association": "A_default_inclusiveGateway",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "ParallelGateway",
                    "superClass": [
                        "Gateway"
                    ]
                },
                {
                    "name": "RootElement",
                    "isAbstract": true,
                    "superClass": [
                        "BaseElement"
                    ]
                },
                {
                    "name": "Relationship",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "type",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "direction",
                            "type": "RelationshipDirection",
                            "isAttr": true
                        },
                        {
                            "name": "sources",
                            "association": "A_sources_relationship",
                            "isMany": true,
                            "isReference": true,
                            "type": "Element"
                        },
                        {
                            "name": "targets",
                            "association": "A_targets_relationship",
                            "isMany": true,
                            "isReference": true,
                            "type": "Element"
                        }
                    ]
                },
                {
                    "name": "BaseElement",
                    "isAbstract": true,
                    "properties": [
                        {
                            "name": "id",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "extensionDefinitions",
                            "type": "ExtensionDefinition",
                            "association": "A_extensionDefinitions_baseElement",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "extensionElements",
                            "type": "ExtensionElements",
                            "association": "A_extensionElements_baseElement"
                        },
                        {
                            "name": "documentation",
                            "type": "Documentation",
                            "association": "A_documentation_baseElement",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "Extension",
                    "properties": [
                        {
                            "name": "mustUnderstand",
                            "default": false,
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "definition",
                            "type": "ExtensionDefinition",
                            "association": "A_definition_extension"
                        }
                    ]
                },
                {
                    "name": "ExtensionDefinition",
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "extensionAttributeDefinitions",
                            "type": "ExtensionAttributeDefinition",
                            "association": "A_extensionAttributeDefinitions_extensionDefinition",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "ExtensionAttributeDefinition",
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "type",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "isReference",
                            "default": false,
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "extensionDefinition",
                            "type": "ExtensionDefinition",
                            "association": "A_extensionAttributeDefinitions_extensionDefinition",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "ExtensionElements",
                    "properties": [
                        {
                            "name": "valueRef",
                            "association": "A_valueRef_extensionElements",
                            "isAttr": true,
                            "isReference": true,
                            "type": "Element"
                        },
                        {
                            "name": "values",
                            "association": "A_value_extensionElements",
                            "type": "Element",
                            "isMany": true
                        },
                        {
                            "name": "extensionAttributeDefinition",
                            "type": "ExtensionAttributeDefinition",
                            "association": "A_extensionAttributeDefinition_extensionElements",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "Documentation",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "text",
                            "type": "String",
                            "isBody": true
                        },
                        {
                            "name": "textFormat",
                            "default": "text/plain",
                            "isAttr": true,
                            "type": "String"
                        }
                    ]
                },
                {
                    "name": "Event",
                    "isAbstract": true,
                    "superClass": [
                        "FlowNode",
                        "InteractionNode"
                    ],
                    "properties": [
                        {
                            "name": "properties",
                            "type": "Property",
                            "association": "A_properties_event",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "IntermediateCatchEvent",
                    "superClass": [
                        "CatchEvent"
                    ]
                },
                {
                    "name": "IntermediateThrowEvent",
                    "superClass": [
                        "ThrowEvent"
                    ]
                },
                {
                    "name": "EndEvent",
                    "superClass": [
                        "ThrowEvent"
                    ]
                },
                {
                    "name": "StartEvent",
                    "superClass": [
                        "CatchEvent"
                    ],
                    "properties": [
                        {
                            "name": "isInterrupting",
                            "default": true,
                            "isAttr": true,
                            "type": "Boolean"
                        }
                    ]
                },
                {
                    "name": "ThrowEvent",
                    "isAbstract": true,
                    "superClass": [
                        "Event"
                    ],
                    "properties": [
                        {
                            "name": "inputSet",
                            "type": "InputSet",
                            "association": "A_inputSet_throwEvent"
                        },
                        {
                            "name": "eventDefinitionRefs",
                            "type": "EventDefinition",
                            "association": "A_eventDefinitionRefs_throwEvent",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "dataInputAssociation",
                            "type": "DataInputAssociation",
                            "association": "A_dataInputAssociation_throwEvent",
                            "isMany": true
                        },
                        {
                            "name": "dataInputs",
                            "type": "DataInput",
                            "association": "A_dataInputs_throwEvent",
                            "isMany": true
                        },
                        {
                            "name": "eventDefinitions",
                            "type": "EventDefinition",
                            "association": "A_eventDefinitions_throwEvent",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "CatchEvent",
                    "isAbstract": true,
                    "superClass": [
                        "Event"
                    ],
                    "properties": [
                        {
                            "name": "parallelMultiple",
                            "isAttr": true,
                            "type": "Boolean",
                            "default": false
                        },
                        {
                            "name": "outputSet",
                            "type": "OutputSet",
                            "association": "A_outputSet_catchEvent"
                        },
                        {
                            "name": "eventDefinitionRefs",
                            "type": "EventDefinition",
                            "association": "A_eventDefinitionRefs_catchEvent",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "dataOutputAssociation",
                            "type": "DataOutputAssociation",
                            "association": "A_dataOutputAssociation_catchEvent",
                            "isMany": true
                        },
                        {
                            "name": "dataOutputs",
                            "type": "DataOutput",
                            "association": "A_dataOutputs_catchEvent",
                            "isMany": true
                        },
                        {
                            "name": "eventDefinitions",
                            "type": "EventDefinition",
                            "association": "A_eventDefinitions_catchEvent",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "BoundaryEvent",
                    "superClass": [
                        "CatchEvent"
                    ],
                    "properties": [
                        {
                            "name": "cancelActivity",
                            "default": true,
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "attachedToRef",
                            "type": "Activity",
                            "association": "A_boundaryEventRefs_attachedToRef",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "EventDefinition",
                    "isAbstract": true,
                    "superClass": [
                        "RootElement"
                    ]
                },
                {
                    "name": "CancelEventDefinition",
                    "superClass": [
                        "EventDefinition"
                    ]
                },
                {
                    "name": "ErrorEventDefinition",
                    "superClass": [
                        "EventDefinition"
                    ],
                    "properties": [
                        {
                            "name": "errorRef",
                            "type": "Error",
                            "association": "A_errorRef_errorEventDefinition",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "TerminateEventDefinition",
                    "superClass": [
                        "EventDefinition"
                    ]
                },
                {
                    "name": "EscalationEventDefinition",
                    "superClass": [
                        "EventDefinition"
                    ],
                    "properties": [
                        {
                            "name": "escalationRef",
                            "type": "Escalation",
                            "association": "A_escalationRef_escalationEventDefinition",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "Escalation",
                    "properties": [
                        {
                            "name": "structureRef",
                            "type": "ItemDefinition",
                            "association": "A_structureRef_escalation",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "escalationCode",
                            "isAttr": true,
                            "type": "String"
                        }
                    ],
                    "superClass": [
                        "RootElement"
                    ]
                },
                {
                    "name": "CompensateEventDefinition",
                    "superClass": [
                        "EventDefinition"
                    ],
                    "properties": [
                        {
                            "name": "waitForCompletion",
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "activityRef",
                            "type": "Activity",
                            "association": "A_activityRef_compensateEventDefinition",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "TimerEventDefinition",
                    "superClass": [
                        "EventDefinition"
                    ],
                    "properties": [
                        {
                            "name": "timeDate",
                            "type": "Expression",
                            "association": "A_timeDate_timerEventDefinition"
                        },
                        {
                            "name": "timeCycle",
                            "type": "Expression",
                            "association": "A_timeCycle_timerEventDefinition"
                        },
                        {
                            "name": "timeDuration",
                            "type": "Expression",
                            "association": "A_timeDuration_timerEventDefinition"
                        }
                    ]
                },
                {
                    "name": "LinkEventDefinition",
                    "superClass": [
                        "EventDefinition"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "target",
                            "type": "LinkEventDefinition",
                            "association": "A_target_source",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "source",
                            "type": "LinkEventDefinition",
                            "association": "A_target_source",
                            "isMany": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "MessageEventDefinition",
                    "superClass": [
                        "EventDefinition"
                    ],
                    "properties": [
                        {
                            "name": "messageRef",
                            "type": "Message",
                            "association": "A_messageRef_messageEventDefinition",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "operationRef",
                            "type": "Operation",
                            "association": "A_operationRef_messageEventDefinition",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "ConditionalEventDefinition",
                    "superClass": [
                        "EventDefinition"
                    ],
                    "properties": [
                        {
                            "name": "condition",
                            "type": "Expression",
                            "association": "A_condition_conditionalEventDefinition",
                            "serialize": "xsi:type"
                        }
                    ]
                },
                {
                    "name": "SignalEventDefinition",
                    "superClass": [
                        "EventDefinition"
                    ],
                    "properties": [
                        {
                            "name": "signalRef",
                            "type": "Signal",
                            "association": "A_signalRef_signalEventDefinition",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "Signal",
                    "superClass": [
                        "RootElement"
                    ],
                    "properties": [
                        {
                            "name": "structureRef",
                            "type": "ItemDefinition",
                            "association": "A_structureRef_signal",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        }
                    ]
                },
                {
                    "name": "ImplicitThrowEvent",
                    "superClass": [
                        "ThrowEvent"
                    ]
                },
                {
                    "name": "DataState",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        }
                    ]
                },
                {
                    "name": "ItemAwareElement",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "itemSubjectRef",
                            "type": "ItemDefinition",
                            "association": "A_itemSubjectRef_itemAwareElement",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "dataState",
                            "type": "DataState",
                            "association": "A_dataState_itemAwareElement"
                        }
                    ]
                },
                {
                    "name": "DataAssociation",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "transformation",
                            "type": "FormalExpression",
                            "association": "A_transformation_dataAssociation"
                        },
                        {
                            "name": "assignment",
                            "type": "Assignment",
                            "association": "A_assignment_dataAssociation",
                            "isMany": true
                        },
                        {
                            "name": "sourceRef",
                            "type": "ItemAwareElement",
                            "association": "A_sourceRef_dataAssociation",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "targetRef",
                            "type": "ItemAwareElement",
                            "association": "A_targetRef_dataAssociation",
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "DataInput",
                    "superClass": [
                        "ItemAwareElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "isCollection",
                            "default": false,
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "inputSetRefs",
                            "type": "InputSet",
                            "association": "A_dataInputRefs_inputSetRefs",
                            "isVirtual": true,
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "inputSetWithOptional",
                            "type": "InputSet",
                            "association": "A_optionalInputRefs_inputSetWithOptional",
                            "isVirtual": true,
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "inputSetWithWhileExecuting",
                            "type": "InputSet",
                            "association": "A_whileExecutingInputRefs_inputSetWithWhileExecuting",
                            "isVirtual": true,
                            "isMany": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "DataOutput",
                    "superClass": [
                        "ItemAwareElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "isCollection",
                            "default": false,
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "outputSetRefs",
                            "type": "OutputSet",
                            "association": "A_dataOutputRefs_outputSetRefs",
                            "isVirtual": true,
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "outputSetWithOptional",
                            "type": "OutputSet",
                            "association": "A_outputSetWithOptional_optionalOutputRefs",
                            "isVirtual": true,
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "outputSetWithWhileExecuting",
                            "type": "OutputSet",
                            "association": "A_outputSetWithWhileExecuting_whileExecutingOutputRefs",
                            "isVirtual": true,
                            "isMany": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "InputSet",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "dataInputRefs",
                            "type": "DataInput",
                            "association": "A_dataInputRefs_inputSetRefs",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "optionalInputRefs",
                            "type": "DataInput",
                            "association": "A_optionalInputRefs_inputSetWithOptional",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "whileExecutingInputRefs",
                            "type": "DataInput",
                            "association": "A_whileExecutingInputRefs_inputSetWithWhileExecuting",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "outputSetRefs",
                            "type": "OutputSet",
                            "association": "A_inputSetRefs_outputSetRefs",
                            "isMany": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "OutputSet",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "dataOutputRefs",
                            "type": "DataOutput",
                            "association": "A_dataOutputRefs_outputSetRefs",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "inputSetRefs",
                            "type": "InputSet",
                            "association": "A_inputSetRefs_outputSetRefs",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "optionalOutputRefs",
                            "type": "DataOutput",
                            "association": "A_outputSetWithOptional_optionalOutputRefs",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "whileExecutingOutputRefs",
                            "type": "DataOutput",
                            "association": "A_outputSetWithWhileExecuting_whileExecutingOutputRefs",
                            "isMany": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "Property",
                    "superClass": [
                        "ItemAwareElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        }
                    ]
                },
                {
                    "name": "DataInputAssociation",
                    "superClass": [
                        "DataAssociation"
                    ]
                },
                {
                    "name": "DataOutputAssociation",
                    "superClass": [
                        "DataAssociation"
                    ]
                },
                {
                    "name": "InputOutputSpecification",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "inputSets",
                            "type": "InputSet",
                            "association": "A_inputSets_inputOutputSpecification",
                            "isMany": true
                        },
                        {
                            "name": "outputSets",
                            "type": "OutputSet",
                            "association": "A_outputSets_inputOutputSpecification",
                            "isMany": true
                        },
                        {
                            "name": "dataInputs",
                            "type": "DataInput",
                            "association": "A_dataInputs_inputOutputSpecification",
                            "isMany": true
                        },
                        {
                            "name": "dataOutputs",
                            "type": "DataOutput",
                            "association": "A_dataOutputs_inputOutputSpecification",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "DataObject",
                    "superClass": [
                        "FlowElement",
                        "ItemAwareElement"
                    ],
                    "properties": [
                        {
                            "name": "isCollection",
                            "default": false,
                            "isAttr": true,
                            "type": "Boolean"
                        }
                    ]
                },
                {
                    "name": "InputOutputBinding",
                    "properties": [
                        {
                            "name": "inputDataRef",
                            "type": "InputSet",
                            "association": "A_inputDataRef_inputOutputBinding",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "outputDataRef",
                            "type": "OutputSet",
                            "association": "A_outputDataRef_inputOutputBinding",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "operationRef",
                            "type": "Operation",
                            "association": "A_operationRef_ioBinding",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "Assignment",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "from",
                            "type": "Expression",
                            "association": "A_from_assignment"
                        },
                        {
                            "name": "to",
                            "type": "Expression",
                            "association": "A_to_assignment"
                        }
                    ]
                },
                {
                    "name": "DataStore",
                    "superClass": [
                        "RootElement",
                        "ItemAwareElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "capacity",
                            "isAttr": true,
                            "type": "Integer"
                        },
                        {
                            "name": "isUnlimited",
                            "default": true,
                            "isAttr": true,
                            "type": "Boolean"
                        }
                    ]
                },
                {
                    "name": "DataStoreReference",
                    "superClass": [
                        "ItemAwareElement",
                        "FlowElement"
                    ],
                    "properties": [
                        {
                            "name": "dataStoreRef",
                            "type": "DataStore",
                            "association": "A_dataStoreRef_dataStoreReference",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "DataObjectReference",
                    "superClass": [
                        "ItemAwareElement",
                        "FlowElement"
                    ],
                    "properties": [
                        {
                            "name": "dataObjectRef",
                            "type": "DataObject",
                            "association": "A_dataObjectRef_dataObject",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "ConversationLink",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "sourceRef",
                            "type": "InteractionNode",
                            "association": "A_sourceRef_outgoingConversationLinks",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "targetRef",
                            "type": "InteractionNode",
                            "association": "A_targetRef_incomingConversationLinks",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        }
                    ]
                },
                {
                    "name": "ConversationAssociation",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "innerConversationNodeRef",
                            "type": "ConversationNode",
                            "association": "A_innerConversationNodeRef_conversationAssociation",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "outerConversationNodeRef",
                            "type": "ConversationNode",
                            "association": "A_outerConversationNodeRef_conversationAssociation",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "CallConversation",
                    "superClass": [
                        "ConversationNode"
                    ],
                    "properties": [
                        {
                            "name": "calledCollaborationRef",
                            "type": "Collaboration",
                            "association": "A_calledCollaborationRef_callConversation",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "participantAssociations",
                            "type": "ParticipantAssociation",
                            "association": "A_participantAssociations_callConversation",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "Conversation",
                    "superClass": [
                        "ConversationNode"
                    ]
                },
                {
                    "name": "SubConversation",
                    "superClass": [
                        "ConversationNode"
                    ],
                    "properties": [
                        {
                            "name": "conversationNodes",
                            "type": "ConversationNode",
                            "association": "A_conversationNodes_subConversation",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "ConversationNode",
                    "isAbstract": true,
                    "superClass": [
                        "InteractionNode",
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "participantRefs",
                            "type": "Participant",
                            "association": "A_participantRefs_conversationNode",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "messageFlowRefs",
                            "type": "MessageFlow",
                            "association": "A_messageFlowRefs_communication",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "correlationKeys",
                            "type": "CorrelationKey",
                            "association": "A_correlationKeys_conversationNode",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "GlobalConversation",
                    "superClass": [
                        "Collaboration"
                    ]
                },
                {
                    "name": "PartnerEntity",
                    "superClass": [
                        "RootElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "participantRef",
                            "type": "Participant",
                            "association": "A_partnerEntityRef_participantRef",
                            "isMany": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "PartnerRole",
                    "superClass": [
                        "RootElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "participantRef",
                            "type": "Participant",
                            "association": "A_partnerRoleRef_participantRef",
                            "isMany": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "CorrelationProperty",
                    "superClass": [
                        "RootElement"
                    ],
                    "properties": [
                        {
                            "name": "correlationPropertyRetrievalExpression",
                            "type": "CorrelationPropertyRetrievalExpression",
                            "association": "A_correlationPropertyRetrievalExpression_correlationproperty",
                            "isMany": true
                        },
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "type",
                            "type": "ItemDefinition",
                            "association": "A_type_correlationProperty",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "Error",
                    "superClass": [
                        "RootElement"
                    ],
                    "properties": [
                        {
                            "name": "structureRef",
                            "type": "ItemDefinition",
                            "association": "A_structureRef_error",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "errorCode",
                            "isAttr": true,
                            "type": "String"
                        }
                    ]
                },
                {
                    "name": "CorrelationKey",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "correlationPropertyRef",
                            "type": "CorrelationProperty",
                            "association": "A_correlationPropertyRef_correlationKey",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        }
                    ]
                },
                {
                    "name": "Expression",
                    "superClass": [
                        "BaseElement"
                    ]
                },
                {
                    "name": "FormalExpression",
                    "superClass": [
                        "Expression"
                    ],
                    "properties": [
                        {
                            "name": "language",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "body",
                            "type": "Element"
                        },
                        {
                            "name": "evaluatesToTypeRef",
                            "type": "ItemDefinition",
                            "association": "A_evaluatesToTypeRef_formalExpression",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "Message",
                    "superClass": [
                        "RootElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "itemRef",
                            "type": "ItemDefinition",
                            "association": "A_itemRef_message",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "ItemDefinition",
                    "superClass": [
                        "RootElement"
                    ],
                    "properties": [
                        {
                            "name": "itemKind",
                            "type": "ItemKind",
                            "isAttr": true
                        },
                        {
                            "name": "structureRef",
                            "type": "String",
                            "isAttr": true
                        },
                        {
                            "name": "isCollection",
                            "default": false,
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "import",
                            "type": "Import",
                            "association": "A_import_itemDefinition",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "FlowElement",
                    "isAbstract": true,
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "auditing",
                            "type": "Auditing",
                            "association": "A_auditing_flowElement"
                        },
                        {
                            "name": "monitoring",
                            "type": "Monitoring",
                            "association": "A_monitoring_flowElement"
                        },
                        {
                            "name": "categoryValueRef",
                            "type": "CategoryValue",
                            "association": "A_categorizedFlowElements_categoryValueRef",
                            "isMany": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "SequenceFlow",
                    "superClass": [
                        "FlowElement"
                    ],
                    "properties": [
                        {
                            "name": "isImmediate",
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "conditionExpression",
                            "type": "Expression",
                            "association": "A_conditionExpression_sequenceFlow"
                        },
                        {
                            "name": "sourceRef",
                            "type": "FlowNode",
                            "association": "A_sourceRef_outgoing_flow",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "targetRef",
                            "type": "FlowNode",
                            "association": "A_targetRef_incoming_flow",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "FlowElementsContainer",
                    "isAbstract": true,
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "laneSets",
                            "type": "LaneSet",
                            "association": "A_laneSets_flowElementsContainer",
                            "isMany": true
                        },
                        {
                            "name": "flowElements",
                            "type": "FlowElement",
                            "association": "A_flowElements_container",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "CallableElement",
                    "isAbstract": true,
                    "superClass": [
                        "RootElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "ioSpecification",
                            "type": "InputOutputSpecification",
                            "association": "A_ioSpecification_callableElement"
                        },
                        {
                            "name": "supportedInterfaceRefs",
                            "type": "Interface",
                            "association": "A_supportedInterfaceRefs_callableElements",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "ioBinding",
                            "type": "InputOutputBinding",
                            "association": "A_ioBinding_callableElement",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "FlowNode",
                    "isAbstract": true,
                    "superClass": [
                        "FlowElement"
                    ],
                    "properties": [
                        {
                            "name": "incoming",
                            "type": "SequenceFlow",
                            "association": "A_targetRef_incoming_flow",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "outgoing",
                            "type": "SequenceFlow",
                            "association": "A_sourceRef_outgoing_flow",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "lanes",
                            "type": "Lane",
                            "association": "A_flowNodeRefs_lanes",
                            "isVirtual": true,
                            "isMany": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "CorrelationPropertyRetrievalExpression",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "messagePath",
                            "type": "FormalExpression",
                            "association": "A_messagePath_correlationset"
                        },
                        {
                            "name": "messageRef",
                            "type": "Message",
                            "association": "A_messageRef_correlationPropertyRetrievalExpression",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "CorrelationPropertyBinding",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "dataPath",
                            "type": "FormalExpression",
                            "association": "A_dataPath_correlationPropertyBinding"
                        },
                        {
                            "name": "correlationPropertyRef",
                            "type": "CorrelationProperty",
                            "association": "A_correlationPropertyRef_correlationPropertyBinding",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "Resource",
                    "superClass": [
                        "RootElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "resourceParameters",
                            "type": "ResourceParameter",
                            "association": "A_resourceParameters_resource",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "ResourceParameter",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "isRequired",
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "type",
                            "type": "ItemDefinition",
                            "association": "A_type_resourceParameter",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "CorrelationSubscription",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "correlationKeyRef",
                            "type": "CorrelationKey",
                            "association": "A_correlationKeyRef_correlationSubscription",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "correlationPropertyBinding",
                            "type": "CorrelationPropertyBinding",
                            "association": "A_correlationPropertyBinding_correlationSubscription",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "MessageFlow",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "sourceRef",
                            "type": "InteractionNode",
                            "association": "A_sourceRef_messageFlow",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "targetRef",
                            "type": "InteractionNode",
                            "association": "A_targetRef_messageFlow",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "messageRef",
                            "type": "Message",
                            "association": "A_messageRef_messageFlow",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "MessageFlowAssociation",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "innerMessageFlowRef",
                            "type": "MessageFlow",
                            "association": "A_innerMessageFlowRef_messageFlowAssociation",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "outerMessageFlowRef",
                            "type": "MessageFlow",
                            "association": "A_outerMessageFlowRef_messageFlowAssociation",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "InteractionNode",
                    "isAbstract": true,
                    "properties": [
                        {
                            "name": "incomingConversationLinks",
                            "type": "ConversationLink",
                            "association": "A_targetRef_incomingConversationLinks",
                            "isVirtual": true,
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "outgoingConversationLinks",
                            "type": "ConversationLink",
                            "association": "A_sourceRef_outgoingConversationLinks",
                            "isVirtual": true,
                            "isMany": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "Participant",
                    "superClass": [
                        "InteractionNode",
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "interfaceRefs",
                            "type": "Interface",
                            "association": "A_interfaceRefs_participant",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "participantMultiplicity",
                            "type": "ParticipantMultiplicity",
                            "association": "A_participantMultiplicity_participant"
                        },
                        {
                            "name": "endPointRefs",
                            "type": "EndPoint",
                            "association": "A_endPointRefs_participant",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "processRef",
                            "type": "Process",
                            "association": "A_processRef_participant",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "ParticipantAssociation",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "innerParticipantRef",
                            "type": "Participant",
                            "association": "A_innerParticipantRef_participantAssociation",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "outerParticipantRef",
                            "type": "Participant",
                            "association": "A_outerParticipantRef_participantAssociation",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "ParticipantMultiplicity",
                    "properties": [
                        {
                            "name": "minimum",
                            "default": 0,
                            "isAttr": true,
                            "type": "Integer"
                        },
                        {
                            "name": "maximum",
                            "default": 1,
                            "isAttr": true,
                            "type": "Integer"
                        }
                    ]
                },
                {
                    "name": "Collaboration",
                    "superClass": [
                        "RootElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "isClosed",
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "choreographyRef",
                            "type": "Choreography",
                            "association": "A_choreographyRef_collaboration",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "artifacts",
                            "type": "Artifact",
                            "association": "A_artifacts_collaboration",
                            "isMany": true
                        },
                        {
                            "name": "participantAssociations",
                            "type": "ParticipantAssociation",
                            "association": "A_participantAssociations_collaboration",
                            "isMany": true
                        },
                        {
                            "name": "messageFlowAssociations",
                            "type": "MessageFlowAssociation",
                            "association": "A_messageFlowAssociations_collaboration",
                            "isMany": true
                        },
                        {
                            "name": "conversationAssociations",
                            "type": "ConversationAssociation",
                            "association": "A_conversationAssociations_converstaionAssociations"
                        },
                        {
                            "name": "participants",
                            "type": "Participant",
                            "association": "A_participants_collaboration",
                            "isMany": true
                        },
                        {
                            "name": "messageFlows",
                            "type": "MessageFlow",
                            "association": "A_messageFlows_collaboration",
                            "isMany": true
                        },
                        {
                            "name": "correlationKeys",
                            "type": "CorrelationKey",
                            "association": "A_correlationKeys_collaboration",
                            "isMany": true
                        },
                        {
                            "name": "conversations",
                            "type": "ConversationNode",
                            "association": "A_conversations_collaboration",
                            "isMany": true
                        },
                        {
                            "name": "conversationLinks",
                            "type": "ConversationLink",
                            "association": "A_conversationLinks_collaboration",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "ChoreographyActivity",
                    "isAbstract": true,
                    "superClass": [
                        "FlowNode"
                    ],
                    "properties": [
                        {
                            "name": "participantRefs",
                            "type": "Participant",
                            "association": "A_participantRefs_choreographyActivity",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "initiatingParticipantRef",
                            "type": "Participant",
                            "association": "A_initiatingParticipantRef_choreographyActivity",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "correlationKeys",
                            "type": "CorrelationKey",
                            "association": "A_correlationKeys_choreographyActivity",
                            "isMany": true
                        },
                        {
                            "name": "loopType",
                            "type": "ChoreographyLoopType",
                            "default": "None",
                            "isAttr": true
                        }
                    ]
                },
                {
                    "name": "CallChoreography",
                    "superClass": [
                        "ChoreographyActivity"
                    ],
                    "properties": [
                        {
                            "name": "calledChoreographyRef",
                            "type": "Choreography",
                            "association": "A_calledChoreographyRef_callChoreographyActivity",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "participantAssociations",
                            "type": "ParticipantAssociation",
                            "association": "A_participantAssociations_callChoreographyActivity",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "SubChoreography",
                    "superClass": [
                        "ChoreographyActivity",
                        "FlowElementsContainer"
                    ],
                    "properties": [
                        {
                            "name": "artifacts",
                            "type": "Artifact",
                            "association": "A_artifacts_subChoreography",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "ChoreographyTask",
                    "superClass": [
                        "ChoreographyActivity"
                    ],
                    "properties": [
                        {
                            "name": "messageFlowRef",
                            "type": "MessageFlow",
                            "association": "A_messageFlowRef_choreographyTask",
                            "isMany": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "Choreography",
                    "superClass": [
                        "FlowElementsContainer",
                        "Collaboration"
                    ]
                },
                {
                    "name": "GlobalChoreographyTask",
                    "superClass": [
                        "Choreography"
                    ],
                    "properties": [
                        {
                            "name": "initiatingParticipantRef",
                            "type": "Participant",
                            "association": "A_initiatingParticipantRef_globalChoreographyTask",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "TextAnnotation",
                    "superClass": [
                        "Artifact"
                    ],
                    "properties": [
                        {
                            "name": "text",
                            "type": "String"
                        },
                        {
                            "name": "textFormat",
                            "default": "text/plain",
                            "isAttr": true,
                            "type": "String"
                        }
                    ]
                },
                {
                    "name": "Group",
                    "superClass": [
                        "Artifact"
                    ],
                    "properties": [
                        {
                            "name": "categoryValueRef",
                            "type": "CategoryValue",
                            "association": "A_categoryValueRef_categoryValueRef",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "Association",
                    "superClass": [
                        "Artifact"
                    ],
                    "properties": [
                        {
                            "name": "associationDirection",
                            "type": "AssociationDirection",
                            "isAttr": true
                        },
                        {
                            "name": "sourceRef",
                            "type": "BaseElement",
                            "association": "A_sourceRef_outgoing_association",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "targetRef",
                            "type": "BaseElement",
                            "association": "A_targetRef_incoming_association",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "Category",
                    "superClass": [
                        "RootElement"
                    ],
                    "properties": [
                        {
                            "name": "categoryValue",
                            "type": "CategoryValue",
                            "association": "A_categoryValue_category",
                            "isMany": true
                        },
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        }
                    ]
                },
                {
                    "name": "Artifact",
                    "isAbstract": true,
                    "superClass": [
                        "BaseElement"
                    ]
                },
                {
                    "name": "CategoryValue",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "categorizedFlowElements",
                            "type": "FlowElement",
                            "association": "A_categorizedFlowElements_categoryValueRef",
                            "isVirtual": true,
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "value",
                            "isAttr": true,
                            "type": "String"
                        }
                    ]
                },
                {
                    "name": "Activity",
                    "isAbstract": true,
                    "superClass": [
                        "FlowNode"
                    ],
                    "properties": [
                        {
                            "name": "isForCompensation",
                            "default": false,
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "loopCharacteristics",
                            "type": "LoopCharacteristics",
                            "association": "A_loopCharacteristics_activity"
                        },
                        {
                            "name": "resources",
                            "type": "ResourceRole",
                            "association": "A_resources_activity",
                            "isMany": true
                        },
                        {
                            "name": "default",
                            "type": "SequenceFlow",
                            "association": "A_default_activity",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "properties",
                            "type": "Property",
                            "association": "A_properties_activity",
                            "isMany": true
                        },
                        {
                            "name": "ioSpecification",
                            "type": "InputOutputSpecification",
                            "association": "A_ioSpecification_activity"
                        },
                        {
                            "name": "boundaryEventRefs",
                            "type": "BoundaryEvent",
                            "association": "A_boundaryEventRefs_attachedToRef",
                            "isMany": true,
                            "isReference": true
                        },
                        {
                            "name": "dataInputAssociations",
                            "type": "DataInputAssociation",
                            "association": "A_dataInputAssociations_activity",
                            "isMany": true
                        },
                        {
                            "name": "dataOutputAssociations",
                            "type": "DataOutputAssociation",
                            "association": "A_dataOutputAssociations_activity",
                            "isMany": true
                        },
                        {
                            "name": "startQuantity",
                            "default": 1,
                            "isAttr": true,
                            "type": "Integer"
                        },
                        {
                            "name": "completionQuantity",
                            "default": 1,
                            "isAttr": true,
                            "type": "Integer"
                        }
                    ]
                },
                {
                    "name": "ServiceTask",
                    "superClass": [
                        "Task"
                    ],
                    "properties": [
                        {
                            "name": "implementation",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "operationRef",
                            "type": "Operation",
                            "association": "A_operationRef_serviceTask",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "SubProcess",
                    "superClass": [
                        "Activity",
                        "FlowElementsContainer"
                    ],
                    "properties": [
                        {
                            "name": "triggeredByEvent",
                            "default": false,
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "artifacts",
                            "type": "Artifact",
                            "association": "A_artifacts_subProcess",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "LoopCharacteristics",
                    "isAbstract": true,
                    "superClass": [
                        "BaseElement"
                    ]
                },
                {
                    "name": "MultiInstanceLoopCharacteristics",
                    "superClass": [
                        "LoopCharacteristics"
                    ],
                    "properties": [
                        {
                            "name": "isSequential",
                            "default": false,
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "behavior",
                            "type": "MultiInstanceBehavior",
                            "default": "All",
                            "isAttr": true
                        },
                        {
                            "name": "loopCardinality",
                            "type": "Expression",
                            "association": "A_loopCardinality_multiInstanceLoopCharacteristics"
                        },
                        {
                            "name": "loopDataInputRef",
                            "type": "ItemAwareElement",
                            "association": "A_loopDataInputRef_multiInstanceLoopCharacteristics",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "loopDataOutputRef",
                            "type": "ItemAwareElement",
                            "association": "A_loopDataOutputRef_multiInstanceLoopCharacteristics",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "inputDataItem",
                            "type": "DataInput",
                            "association": "A_inputDataItem_multiInstanceLoopCharacteristics"
                        },
                        {
                            "name": "outputDataItem",
                            "type": "DataOutput",
                            "association": "A_outputDataItem_multiInstanceLoopCharacteristics"
                        },
                        {
                            "name": "completionCondition",
                            "type": "Expression",
                            "association": "A_completionCondition_multiInstanceLoopCharacteristics"
                        },
                        {
                            "name": "complexBehaviorDefinition",
                            "type": "ComplexBehaviorDefinition",
                            "association": "A_complexBehaviorDefinition_multiInstanceLoopCharacteristics",
                            "isMany": true
                        },
                        {
                            "name": "oneBehaviorEventRef",
                            "type": "EventDefinition",
                            "association": "A_oneBehaviorEventRef_multiInstanceLoopCharacteristics",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "noneBehaviorEventRef",
                            "type": "EventDefinition",
                            "association": "A_noneBehaviorEventRef_multiInstanceLoopCharacteristics",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "StandardLoopCharacteristics",
                    "superClass": [
                        "LoopCharacteristics"
                    ],
                    "properties": [
                        {
                            "name": "testBefore",
                            "default": false,
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "loopCondition",
                            "type": "Expression",
                            "association": "A_loopCondition_standardLoopCharacteristics"
                        },
                        {
                            "name": "loopMaximum",
                            "type": "Expression",
                            "association": "A_loopMaximum_standardLoopCharacteristics"
                        }
                    ]
                },
                {
                    "name": "CallActivity",
                    "superClass": [
                        "Activity"
                    ],
                    "properties": [
                        {
                            "name": "calledElement",
                            "type": "String",
                            "association": "A_calledElementRef_callActivity",
                            "isAttr": true
                        }
                    ]
                },
                {
                    "name": "Task",
                    "superClass": [
                        "Activity",
                        "InteractionNode"
                    ]
                },
                {
                    "name": "SendTask",
                    "superClass": [
                        "Task"
                    ],
                    "properties": [
                        {
                            "name": "implementation",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "operationRef",
                            "type": "Operation",
                            "association": "A_operationRef_sendTask",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "messageRef",
                            "type": "Message",
                            "association": "A_messageRef_sendTask",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "ReceiveTask",
                    "superClass": [
                        "Task"
                    ],
                    "properties": [
                        {
                            "name": "implementation",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "instantiate",
                            "default": false,
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "operationRef",
                            "type": "Operation",
                            "association": "A_operationRef_receiveTask",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "messageRef",
                            "type": "Message",
                            "association": "A_messageRef_receiveTask",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "ScriptTask",
                    "superClass": [
                        "Task"
                    ],
                    "properties": [
                        {
                            "name": "scriptFormat",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "script",
                            "type": "String"
                        }
                    ]
                },
                {
                    "name": "BusinessRuleTask",
                    "superClass": [
                        "Task"
                    ],
                    "properties": [
                        {
                            "name": "implementation",
                            "isAttr": true,
                            "type": "String"
                        }
                    ]
                },
                {
                    "name": "AdHocSubProcess",
                    "superClass": [
                        "SubProcess"
                    ],
                    "properties": [
                        {
                            "name": "completionCondition",
                            "type": "Expression",
                            "association": "A_completionCondition_adHocSubProcess"
                        },
                        {
                            "name": "ordering",
                            "type": "AdHocOrdering",
                            "isAttr": true
                        },
                        {
                            "name": "cancelRemainingInstances",
                            "default": true,
                            "isAttr": true,
                            "type": "Boolean"
                        }
                    ]
                },
                {
                    "name": "Transaction",
                    "superClass": [
                        "SubProcess"
                    ],
                    "properties": [
                        {
                            "name": "protocol",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "method",
                            "isAttr": true,
                            "type": "String"
                        }
                    ]
                },
                {
                    "name": "GlobalScriptTask",
                    "superClass": [
                        "GlobalTask"
                    ],
                    "properties": [
                        {
                            "name": "scriptLanguage",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "script",
                            "isAttr": true,
                            "type": "String"
                        }
                    ]
                },
                {
                    "name": "GlobalBusinessRuleTask",
                    "superClass": [
                        "GlobalTask"
                    ],
                    "properties": [
                        {
                            "name": "implementation",
                            "isAttr": true,
                            "type": "String"
                        }
                    ]
                },
                {
                    "name": "ComplexBehaviorDefinition",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "condition",
                            "type": "FormalExpression",
                            "association": "A_condition_complexBehaviorDefinition"
                        },
                        {
                            "name": "event",
                            "type": "ImplicitThrowEvent",
                            "association": "A_event_complexBehaviorDefinition"
                        }
                    ]
                },
                {
                    "name": "ResourceRole",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "resourceRef",
                            "type": "Resource",
                            "association": "A_resourceRef_activityResource",
                            "isAttr": true,
                            "isReference": true
                        },
                        {
                            "name": "resourceParameterBindings",
                            "type": "ResourceParameterBinding",
                            "association": "A_resourceParameterBindings_activityResource",
                            "isMany": true
                        },
                        {
                            "name": "resourceAssignmentExpression",
                            "type": "ResourceAssignmentExpression",
                            "association": "A_resourceAssignmentExpression_activityResource"
                        },
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        }
                    ]
                },
                {
                    "name": "ResourceParameterBinding",
                    "properties": [
                        {
                            "name": "expression",
                            "type": "Expression",
                            "association": "A_expression_resourceParameterBinding"
                        },
                        {
                            "name": "parameterRef",
                            "type": "ResourceParameter",
                            "association": "A_parameterRef_resourceParameterBinding",
                            "isAttr": true,
                            "isReference": true
                        }
                    ]
                },
                {
                    "name": "ResourceAssignmentExpression",
                    "properties": [
                        {
                            "name": "expression",
                            "type": "Expression",
                            "association": "A_expression_resourceAssignmentExpression"
                        }
                    ]
                },
                {
                    "name": "Import",
                    "properties": [
                        {
                            "name": "importType",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "location",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "namespace",
                            "isAttr": true,
                            "type": "String"
                        }
                    ]
                },
                {
                    "name": "Definitions",
                    "superClass": [
                        "BaseElement"
                    ],
                    "properties": [
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "targetNamespace",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "expressionLanguage",
                            "default": "http://www.w3.org/1999/XPath",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "typeLanguage",
                            "default": "http://www.w3.org/2001/XMLSchema",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "imports",
                            "type": "Import",
                            "association": "A_imports_definition",
                            "isMany": true
                        },
                        {
                            "name": "extensions",
                            "type": "Extension",
                            "association": "A_extensions_definitions",
                            "isMany": true
                        },
                        {
                            "name": "relationships",
                            "type": "Relationship",
                            "association": "A_relationships_definition",
                            "isMany": true
                        },
                        {
                            "name": "rootElements",
                            "type": "RootElement",
                            "association": "A_rootElements_definition",
                            "isMany": true
                        },
                        {
                            "name": "diagrams",
                            "association": "A_diagrams_definitions",
                            "isMany": true,
                            "type": "bpmndi:BPMNDiagram"
                        },
                        {
                            "name": "exporter",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "exporterVersion",
                            "isAttr": true,
                            "type": "String"
                        }
                    ]
                }
            ],
            "emumerations": [
                {
                    "name": "ProcessType",
                    "literalValues": [
                        {
                            "name": "None"
                        },
                        {
                            "name": "Public"
                        },
                        {
                            "name": "Private"
                        }
                    ]
                },
                {
                    "name": "GatewayDirection",
                    "literalValues": [
                        {
                            "name": "Unspecified"
                        },
                        {
                            "name": "Converging"
                        },
                        {
                            "name": "Diverging"
                        },
                        {
                            "name": "Mixed"
                        }
                    ]
                },
                {
                    "name": "EventBasedGatewayType",
                    "literalValues": [
                        {
                            "name": "Parallel"
                        },
                        {
                            "name": "Exclusive"
                        }
                    ]
                },
                {
                    "name": "RelationshipDirection",
                    "literalValues": [
                        {
                            "name": "None"
                        },
                        {
                            "name": "Forward"
                        },
                        {
                            "name": "Backward"
                        },
                        {
                            "name": "Both"
                        }
                    ]
                },
                {
                    "name": "ItemKind",
                    "literalValues": [
                        {
                            "name": "Physical"
                        },
                        {
                            "name": "Information"
                        }
                    ]
                },
                {
                    "name": "ChoreographyLoopType",
                    "literalValues": [
                        {
                            "name": "None"
                        },
                        {
                            "name": "Standard"
                        },
                        {
                            "name": "MultiInstanceSequential"
                        },
                        {
                            "name": "MultiInstanceParallel"
                        }
                    ]
                },
                {
                    "name": "AssociationDirection",
                    "literalValues": [
                        {
                            "name": "None"
                        },
                        {
                            "name": "One"
                        },
                        {
                            "name": "Both"
                        }
                    ]
                },
                {
                    "name": "MultiInstanceBehavior",
                    "literalValues": [
                        {
                            "name": "None"
                        },
                        {
                            "name": "One"
                        },
                        {
                            "name": "All"
                        },
                        {
                            "name": "Complex"
                        }
                    ]
                },
                {
                    "name": "AdHocOrdering",
                    "literalValues": [
                        {
                            "name": "Parallel"
                        },
                        {
                            "name": "Sequential"
                        }
                    ]
                }
            ],
            "prefix": "bpmn",
            "xml": {
                "alias": "lowerCase"
            }
        },
        bpmndi: {
            "name": "BPMNDI",
            "uri": "http://www.omg.org/spec/BPMN/20100524/DI",
            "types": [
                {
                    "name": "BPMNDiagram",
                    "properties": [
                        {
                            "name": "plane",
                            "type": "BPMNPlane",
                            "association": "A_plane_diagram",
                            "redefines": "di:Diagram#rootElement"
                        },
                        {
                            "name": "labelStyle",
                            "type": "BPMNLabelStyle",
                            "association": "A_labelStyle_diagram",
                            "isMany": true
                        }
                    ],
                    "superClass": [
                        "di:Diagram"
                    ]
                },
                {
                    "name": "BPMNPlane",
                    "properties": [
                        {
                            "name": "bpmnElement",
                            "association": "A_bpmnElement_plane",
                            "isAttr": true,
                            "isReference": true,
                            "type": "bpmn:BaseElement",
                            "redefines": "di:DiagramElement#modelElement"
                        }
                    ],
                    "superClass": [
                        "di:Plane"
                    ]
                },
                {
                    "name": "BPMNShape",
                    "properties": [
                        {
                            "name": "bpmnElement",
                            "association": "A_bpmnElement_shape",
                            "isAttr": true,
                            "isReference": true,
                            "type": "bpmn:BaseElement",
                            "redefines": "di:DiagramElement#modelElement"
                        },
                        {
                            "name": "isHorizontal",
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "isExpanded",
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "isMarkerVisible",
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "label",
                            "type": "BPMNLabel",
                            "association": "A_label_shape"
                        },
                        {
                            "name": "isMessageVisible",
                            "isAttr": true,
                            "type": "Boolean"
                        },
                        {
                            "name": "participantBandKind",
                            "type": "ParticipantBandKind",
                            "isAttr": true
                        },
                        {
                            "name": "choreographyActivityShape",
                            "type": "BPMNShape",
                            "association": "A_choreographyActivityShape_participantBandShape",
                            "isAttr": true,
                            "isReference": true
                        }
                    ],
                    "superClass": [
                        "di:LabeledShape"
                    ]
                },
                {
                    "name": "BPMNEdge",
                    "properties": [
                        {
                            "name": "label",
                            "type": "BPMNLabel",
                            "association": "A_label_edge"
                        },
                        {
                            "name": "bpmnElement",
                            "association": "A_bpmnElement_edge",
                            "isAttr": true,
                            "isReference": true,
                            "type": "bpmn:BaseElement",
                            "redefines": "di:DiagramElement#modelElement"
                        },
                        {
                            "name": "sourceElement",
                            "association": "A_sourceElement_sourceEdge",
                            "isAttr": true,
                            "isReference": true,
                            "type": "di:DiagramElement",
                            "redefines": "di:Edge#source"
                        },
                        {
                            "name": "targetElement",
                            "association": "A_targetElement_targetEdge",
                            "isAttr": true,
                            "isReference": true,
                            "type": "di:DiagramElement",
                            "redefines": "di:Edge#target"
                        },
                        {
                            "name": "messageVisibleKind",
                            "type": "MessageVisibleKind",
                            "isAttr": true,
                            "default": "initiating"
                        }
                    ],
                    "superClass": [
                        "di:LabeledEdge"
                    ]
                },
                {
                    "name": "BPMNLabel",
                    "properties": [
                        {
                            "name": "labelStyle",
                            "type": "BPMNLabelStyle",
                            "association": "A_labelStyle_label",
                            "isAttr": true,
                            "isReference": true,
                            "redefines": "di:DiagramElement#style"
                        }
                    ],
                    "superClass": [
                        "di:Label"
                    ]
                },
                {
                    "name": "BPMNLabelStyle",
                    "properties": [
                        {
                            "name": "font",
                            "type": "dc:Font"
                        }
                    ],
                    "superClass": [
                        "di:Style"
                    ]
                }
            ],
            "emumerations": [
                {
                    "name": "ParticipantBandKind",
                    "literalValues": [
                        {
                            "name": "top_initiating"
                        },
                        {
                            "name": "middle_initiating"
                        },
                        {
                            "name": "bottom_initiating"
                        },
                        {
                            "name": "top_non_initiating"
                        },
                        {
                            "name": "middle_non_initiating"
                        },
                        {
                            "name": "bottom_non_initiating"
                        }
                    ]
                },
                {
                    "name": "MessageVisibleKind",
                    "literalValues": [
                        {
                            "name": "initiating"
                        },
                        {
                            "name": "non_initiating"
                        }
                    ]
                }
            ],
            "associations": [],
            "prefix": "bpmndi"
        },
        dc: {
            "name": "DC",
            "uri": "http://www.omg.org/spec/DD/20100524/DC",
            "types": [
                {
                    "name": "Boolean"
                },
                {
                    "name": "Integer"
                },
                {
                    "name": "Real"
                },
                {
                    "name": "String"
                },
                {
                    "name": "Font",
                    "properties": [
                        {
                            "name": "name",
                            "type": "String",
                            "isAttr": true
                        },
                        {
                            "name": "size",
                            "type": "Real",
                            "isAttr": true
                        },
                        {
                            "name": "isBold",
                            "type": "Boolean",
                            "isAttr": true
                        },
                        {
                            "name": "isItalic",
                            "type": "Boolean",
                            "isAttr": true
                        },
                        {
                            "name": "isUnderline",
                            "type": "Boolean",
                            "isAttr": true
                        },
                        {
                            "name": "isStrikeThrough",
                            "type": "Boolean",
                            "isAttr": true
                        }
                    ]
                },
                {
                    "name": "Point",
                    "properties": [
                        {
                            "name": "x",
                            "type": "Real",
                            "default": "0",
                            "isAttr": true
                        },
                        {
                            "name": "y",
                            "type": "Real",
                            "default": "0",
                            "isAttr": true
                        }
                    ]
                },
                {
                    "name": "Bounds",
                    "properties": [
                        {
                            "name": "x",
                            "type": "Real",
                            "default": "0",
                            "isAttr": true
                        },
                        {
                            "name": "y",
                            "type": "Real",
                            "default": "0",
                            "isAttr": true
                        },
                        {
                            "name": "width",
                            "type": "Real",
                            "isAttr": true
                        },
                        {
                            "name": "height",
                            "type": "Real",
                            "isAttr": true
                        }
                    ]
                }
            ],
            "prefix": "dc",
            "associations": []
        },
        di: {
            "name": "DI",
            "uri": "http://www.omg.org/spec/DD/20100524/DI",
            "types": [
                {
                    "name": "DiagramElement",
                    "isAbstract": true,
                    "properties": [
                        {
                            "name": "owningDiagram",
                            "type": "Diagram",
                            "isReadOnly": true,
                            "association": "A_rootElement_owningDiagram",
                            "isVirtual": true,
                            "isReference": true
                        },
                        {
                            "name": "owningElement",
                            "type": "DiagramElement",
                            "isReadOnly": true,
                            "association": "A_ownedElement_owningElement",
                            "isVirtual": true,
                            "isReference": true
                        },
                        {
                            "name": "modelElement",
                            "isReadOnly": true,
                            "association": "A_modelElement_diagramElement",
                            "isVirtual": true,
                            "isReference": true,
                            "type": "Element"
                        },
                        {
                            "name": "style",
                            "type": "Style",
                            "isReadOnly": true,
                            "association": "A_style_diagramElement",
                            "isVirtual": true,
                            "isReference": true
                        },
                        {
                            "name": "ownedElement",
                            "type": "DiagramElement",
                            "isReadOnly": true,
                            "association": "A_ownedElement_owningElement",
                            "isVirtual": true,
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "Node",
                    "isAbstract": true,
                    "superClass": [
                        "DiagramElement"
                    ]
                },
                {
                    "name": "Edge",
                    "isAbstract": true,
                    "superClass": [
                        "DiagramElement"
                    ],
                    "properties": [
                        {
                            "name": "source",
                            "type": "DiagramElement",
                            "isReadOnly": true,
                            "association": "A_source_sourceEdge",
                            "isVirtual": true,
                            "isReference": true
                        },
                        {
                            "name": "target",
                            "type": "DiagramElement",
                            "isReadOnly": true,
                            "association": "A_target_targetEdge",
                            "isVirtual": true,
                            "isReference": true
                        },
                        {
                            "name": "waypoint",
                            "isUnique": false,
                            "isMany": true,
                            "type": "dc:Point",
                            "serialize": "xsi:type"
                        }
                    ]
                },
                {
                    "name": "Diagram",
                    "isAbstract": true,
                    "properties": [
                        {
                            "name": "rootElement",
                            "type": "DiagramElement",
                            "isReadOnly": true,
                            "association": "A_rootElement_owningDiagram",
                            "isVirtual": true
                        },
                        {
                            "name": "name",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "documentation",
                            "isAttr": true,
                            "type": "String"
                        },
                        {
                            "name": "resolution",
                            "isAttr": true,
                            "type": "Real"
                        },
                        {
                            "name": "ownedStyle",
                            "type": "Style",
                            "isReadOnly": true,
                            "association": "A_ownedStyle_owningDiagram",
                            "isVirtual": true,
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "Shape",
                    "isAbstract": true,
                    "superClass": [
                        "Node"
                    ],
                    "properties": [
                        {
                            "name": "bounds",
                            "type": "dc:Bounds"
                        }
                    ]
                },
                {
                    "name": "Plane",
                    "isAbstract": true,
                    "superClass": [
                        "Node"
                    ],
                    "properties": [
                        {
                            "name": "planeElement",
                            "type": "DiagramElement",
                            "subsettedProperty": "DiagramElement-ownedElement",
                            "association": "A_planeElement_plane",
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "LabeledEdge",
                    "isAbstract": true,
                    "superClass": [
                        "Edge"
                    ],
                    "properties": [
                        {
                            "name": "ownedLabel",
                            "type": "Label",
                            "isReadOnly": true,
                            "subsettedProperty": "DiagramElement-ownedElement",
                            "association": "A_ownedLabel_owningEdge",
                            "isVirtual": true,
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "LabeledShape",
                    "isAbstract": true,
                    "superClass": [
                        "Shape"
                    ],
                    "properties": [
                        {
                            "name": "ownedLabel",
                            "type": "Label",
                            "isReadOnly": true,
                            "subsettedProperty": "DiagramElement-ownedElement",
                            "association": "A_ownedLabel_owningShape",
                            "isVirtual": true,
                            "isMany": true
                        }
                    ]
                },
                {
                    "name": "Label",
                    "isAbstract": true,
                    "superClass": [
                        "Node"
                    ],
                    "properties": [
                        {
                            "name": "bounds",
                            "type": "dc:Bounds"
                        }
                    ]
                },
                {
                    "name": "Style",
                    "isAbstract": true
                }
            ],
            "associations": [],
            "prefix": "di"
        }
    };
    
    Moddle.call(this, packages, options);
}

BpmnModdle.prototype = Object.create(Moddle.prototype);

//module.exports = BpmnModdle;


/**
 * Instantiates a BPMN model tree from a given xml string.
 *
 * @param {String}   xmlStr
 * @param {String}   [typeName]   name of the root element, defaults to 'bpmn:Definitions'
 * @param {Object}   [options]    options to pass to the underlying reader
 * @param {Function} done         callback that is invoked with (err, result, parseContext) once the import completes
 */
BpmnModdle.prototype.fromXML = function(xmlStr, typeName, options, done) {

    if (!_.isString(typeName)) {
        done = options;
        options = typeName;
        typeName = 'bpmn:Definitions';
    }

    if (_.isFunction(options)) {
        done = options;
        options = {};
    }

    var reader = new XMLReader(this, options);
    var rootHandler = reader.handler(typeName);

    reader.fromXML(xmlStr, rootHandler, done);
};


/**
 * Serializes a BPMN 2.0 object tree to XML.
 *
 * @param {String}   element    the root element, typically an instance of `bpmn:Definitions`
 * @param {Object}   [options]  to pass to the underlying writer
 * @param {Function} done       callback invoked with (err, xmlStr) once the import completes
 */
BpmnModdle.prototype.toXML = function(element, options, done) {

    if (_.isFunction(options)) {
        done = options;
        options = {};
    }

    var writer = new XMLWriter(options);
    try {
        var result = writer.toXML(element);
        done(null, result);
    } catch (e) {
        done(e);
    }
};
/**
 * Intro.js v1.0.0
 * https://github.com/usablica/intro.js
 * MIT licensed
 *
 * Copyright (C) 2013 usabli.ca - A weekend project by Afshin Mehrabani (@afshinmeh)
 */

(function (root, factory) {
  if (typeof exports === 'object') {
    // CommonJS
    factory(exports);
  } else if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define(['exports'], factory);
  } else {
    // Browser globals
    factory(root);
  }
} (this, function (exports) {
  //Default config/variables
  var VERSION = '1.0.0';

  /**
   * IntroJs main class
   *
   * @class IntroJs
   */
  function IntroJs(obj) {
    this._targetElement = obj;

    this._options = {
      /* Next button label in tooltip box */
      nextLabel: 'Next &rarr;',
      /* Previous button label in tooltip box */
      prevLabel: '&larr; Back',
      /* Skip button label in tooltip box */
      skipLabel: 'Skip',
      /* Done button label in tooltip box */
      doneLabel: 'Done',
      /* Default tooltip box position */
      tooltipPosition: 'bottom',
      /* Next CSS class for tooltip boxes */
      tooltipClass: '',
      /* CSS class that is added to the helperLayer */
      highlightClass: '',
      /* Close introduction when pressing Escape button? */
      exitOnEsc: true,
      /* Close introduction when clicking on overlay layer? */
      exitOnOverlayClick: true,
      /* Show step numbers in introduction? */
      showStepNumbers: true,
      /* Let user use keyboard to navigate the tour? */
      keyboardNavigation: true,
      /* Show tour control buttons? */
      showButtons: true,
      /* Show tour bullets? */
      showBullets: true,
      /* Show tour progress? */
      showProgress: false,
      /* Scroll to highlighted element? */
      scrollToElement: true,
      /* Set the overlay opacity */
      overlayOpacity: 0.8,
      /* Precedence of positions, when auto is enabled */
      positionPrecedence: ["bottom", "top", "right", "left"],
      /* Disable an interaction with element? */
      disableInteraction: false
    };
  }

  /**
   * Initiate a new introduction/guide from an element in the page
   *
   * @api private
   * @method _introForElement
   * @param {Object} targetElm
   * @returns {Boolean} Success or not?
   */
  function _introForElement(targetElm) {
    var introItems = [],
        self = this;

    if (this._options.steps) {
      //use steps passed programmatically
      var allIntroSteps = [];

      for (var i = 0, stepsLength = this._options.steps.length; i < stepsLength; i++) {
        var currentItem = _cloneObject(this._options.steps[i]);
        //set the step
        currentItem.step = introItems.length + 1;
        //use querySelector function only when developer used CSS selector
        if (typeof(currentItem.element) === 'string') {
          //grab the element with given selector from the page
          currentItem.element = document.querySelector(currentItem.element);
        }

        //intro without element
        if (typeof(currentItem.element) === 'undefined' || currentItem.element == null) {
          var floatingElementQuery = document.querySelector(".introjsFloatingElement");

          if (floatingElementQuery == null) {
            floatingElementQuery = document.createElement('div');
            floatingElementQuery.className = 'introjsFloatingElement';

            document.body.appendChild(floatingElementQuery);
          }

          currentItem.element  = floatingElementQuery;
          currentItem.position = 'floating';
        }

        if (currentItem.element != null) {
          introItems.push(currentItem);
        }
      }

    } else {
      //use steps from data-* annotations
      var allIntroSteps = targetElm.querySelectorAll('*[data-intro]');
      //if there's no element to intro
      if (allIntroSteps.length < 1) {
        return false;
      }

      //first add intro items with data-step
      for (var i = 0, elmsLength = allIntroSteps.length; i < elmsLength; i++) {
        var currentElement = allIntroSteps[i];
        var step = parseInt(currentElement.getAttribute('data-step'), 10);

        if (step > 0) {
          introItems[step - 1] = {
            element: currentElement,
            intro: currentElement.getAttribute('data-intro'),
            step: parseInt(currentElement.getAttribute('data-step'), 10),
            tooltipClass: currentElement.getAttribute('data-tooltipClass'),
            highlightClass: currentElement.getAttribute('data-highlightClass'),
            position: currentElement.getAttribute('data-position') || this._options.tooltipPosition
          };
        }
      }

      //next add intro items without data-step
      //todo: we need a cleanup here, two loops are redundant
      var nextStep = 0;
      for (var i = 0, elmsLength = allIntroSteps.length; i < elmsLength; i++) {
        var currentElement = allIntroSteps[i];

        if (currentElement.getAttribute('data-step') == null) {

          while (true) {
            if (typeof introItems[nextStep] == 'undefined') {
              break;
            } else {
              nextStep++;
            }
          }

          introItems[nextStep] = {
            element: currentElement,
            intro: currentElement.getAttribute('data-intro'),
            step: nextStep + 1,
            tooltipClass: currentElement.getAttribute('data-tooltipClass'),
            highlightClass: currentElement.getAttribute('data-highlightClass'),
            position: currentElement.getAttribute('data-position') || this._options.tooltipPosition
          };
        }
      }
    }

    //removing undefined/null elements
    var tempIntroItems = [];
    for (var z = 0; z < introItems.length; z++) {
      introItems[z] && tempIntroItems.push(introItems[z]);  // copy non-empty values to the end of the array
    }

    introItems = tempIntroItems;

    //Ok, sort all items with given steps
    introItems.sort(function (a, b) {
      return a.step - b.step;
    });

    //set it to the introJs object
    self._introItems = introItems;

    //add overlay layer to the page
    if(_addOverlayLayer.call(self, targetElm)) {
      //then, start the show
      _nextStep.call(self);

      var skipButton     = targetElm.querySelector('.introjs-skipbutton'),
          nextStepButton = targetElm.querySelector('.introjs-nextbutton');

      self._onKeyDown = function(e) {
        if (e.keyCode === 27 && self._options.exitOnEsc == true) {
          //escape key pressed, exit the intro
          _exitIntro.call(self, targetElm);
          //check if any callback is defined
          if (self._introExitCallback != undefined) {
            self._introExitCallback.call(self);
          }
        } else if(e.keyCode === 37) {
          //left arrow
          _previousStep.call(self);
        } else if (e.keyCode === 39) {
          //right arrow
          _nextStep.call(self);
        } else if (e.keyCode === 13) {
          //srcElement === ie
          var target = e.target || e.srcElement;
          if (target && target.className.indexOf('introjs-prevbutton') > 0) {
            //user hit enter while focusing on previous button
            _previousStep.call(self);
          } else if (target && target.className.indexOf('introjs-skipbutton') > 0) {
            //user hit enter while focusing on skip button
            _exitIntro.call(self, targetElm);
          } else {
            //default behavior for responding to enter
            _nextStep.call(self);
          }

          //prevent default behaviour on hitting Enter, to prevent steps being skipped in some browsers
          if(e.preventDefault) {
            e.preventDefault();
          } else {
            e.returnValue = false;
          }
        }
      };

      self._onResize = function(e) {
        _setHelperLayerPosition.call(self, document.querySelector('.introjs-helperLayer'));
        _setHelperLayerPosition.call(self, document.querySelector('.introjs-tooltipReferenceLayer'));
      };

      if (window.addEventListener) {
        if (this._options.keyboardNavigation) {
          window.addEventListener('keydown', self._onKeyDown, true);
        }
        //for window resize
        window.addEventListener('resize', self._onResize, true);
      } else if (document.attachEvent) { //IE
        if (this._options.keyboardNavigation) {
          document.attachEvent('onkeydown', self._onKeyDown);
        }
        //for window resize
        document.attachEvent('onresize', self._onResize);
      }
    }
    return false;
  }

 /*
   * makes a copy of the object
   * @api private
   * @method _cloneObject
  */
  function _cloneObject(object) {
      if (object == null || typeof (object) != 'object' || typeof (object.nodeType) != 'undefined') {
          return object;
      }
      var temp = {};
      for (var key in object) {
          temp[key] = _cloneObject(object[key]);
      }
      return temp;
  }
  /**
   * Go to specific step of introduction
   *
   * @api private
   * @method _goToStep
   */
  function _goToStep(step) {
    //because steps starts with zero
    this._currentStep = step - 2;
    if (typeof (this._introItems) !== 'undefined') {
      _nextStep.call(this);
    }
  }

  /**
   * Go to next step on intro
   *
   * @api private
   * @method _nextStep
   */
  function _nextStep() {
    this._direction = 'forward';

    if (typeof (this._currentStep) === 'undefined') {
      this._currentStep = 0;
    } else {
      ++this._currentStep;
    }

    if ((this._introItems.length) <= this._currentStep) {
      //end of the intro
      //check if any callback is defined
      if (typeof (this._introCompleteCallback) === 'function') {
        this._introCompleteCallback.call(this);
      }
      _exitIntro.call(this, this._targetElement);
      return;
    }

    var nextStep = this._introItems[this._currentStep];
    if (typeof (this._introBeforeChangeCallback) !== 'undefined') {
      this._introBeforeChangeCallback.call(this, nextStep.element);
    }

    _showElement.call(this, nextStep);
  }

  /**
   * Go to previous step on intro
   *
   * @api private
   * @method _nextStep
   */
  function _previousStep() {
    this._direction = 'backward';

    if (this._currentStep === 0) {
      return false;
    }

    var nextStep = this._introItems[--this._currentStep];
    if (typeof (this._introBeforeChangeCallback) !== 'undefined') {
      this._introBeforeChangeCallback.call(this, nextStep.element);
    }

    _showElement.call(this, nextStep);
  }

  /**
   * Exit from intro
   *
   * @api private
   * @method _exitIntro
   * @param {Object} targetElement
   */
  function _exitIntro(targetElement) {
    //remove overlay layer from the page
    var overlayLayer = targetElement.querySelector('.introjs-overlay');

    //return if intro already completed or skipped
    if (overlayLayer == null) {
      return;
    }

    //for fade-out animation
    overlayLayer.style.opacity = 0;
    setTimeout(function () {
      if (overlayLayer.parentNode) {
        overlayLayer.parentNode.removeChild(overlayLayer);
      }
    }, 500);

    //remove all helper layers
    var helperLayer = targetElement.querySelector('.introjs-helperLayer');
    if (helperLayer) {
      helperLayer.parentNode.removeChild(helperLayer);
    }

    var referenceLayer = targetElement.querySelector('.introjs-tooltipReferenceLayer');
    if (referenceLayer) {
      referenceLayer.parentNode.removeChild(referenceLayer);
	}
    //remove disableInteractionLayer
    var disableInteractionLayer = targetElement.querySelector('.introjs-disableInteraction');
    if (disableInteractionLayer) {
      disableInteractionLayer.parentNode.removeChild(disableInteractionLayer);
    }

    //remove intro floating element
    var floatingElement = document.querySelector('.introjsFloatingElement');
    if (floatingElement) {
      floatingElement.parentNode.removeChild(floatingElement);
    }

    //remove `introjs-showElement` class from the element
    var showElement = document.querySelector('.introjs-showElement');
    if (showElement) {
      showElement.className = showElement.className.replace(/introjs-[a-zA-Z]+/g, '').replace(/^\s+|\s+$/g, ''); // This is a manual trim.
    }

    //remove `introjs-fixParent` class from the elements
    var fixParents = document.querySelectorAll('.introjs-fixParent');
    if (fixParents && fixParents.length > 0) {
      for (var i = fixParents.length - 1; i >= 0; i--) {
        fixParents[i].className = fixParents[i].className.replace(/introjs-fixParent/g, '').replace(/^\s+|\s+$/g, '');
      };
    }

    //clean listeners
    if (window.removeEventListener) {
      window.removeEventListener('keydown', this._onKeyDown, true);
    } else if (document.detachEvent) { //IE
      document.detachEvent('onkeydown', this._onKeyDown);
    }

    //set the step to zero
    this._currentStep = undefined;
  }

  /**
   * Render tooltip box in the page
   *
   * @api private
   * @method _placeTooltip
   * @param {Object} targetElement
   * @param {Object} tooltipLayer
   * @param {Object} arrowLayer
   */
  function _placeTooltip(targetElement, tooltipLayer, arrowLayer, helperNumberLayer) {
    var tooltipCssClass = '',
        currentStepObj,
        tooltipOffset,
        targetElementOffset;

    //reset the old style
    tooltipLayer.style.top        = null;
    tooltipLayer.style.right      = null;
    tooltipLayer.style.bottom     = null;
    tooltipLayer.style.left       = null;
    tooltipLayer.style.marginLeft = null;
    tooltipLayer.style.marginTop  = null;

    arrowLayer.style.display = 'inherit';

    if (typeof(helperNumberLayer) != 'undefined' && helperNumberLayer != null) {
      helperNumberLayer.style.top  = null;
      helperNumberLayer.style.left = null;
    }

    //prevent error when `this._currentStep` is undefined
    if (!this._introItems[this._currentStep]) return;

    //if we have a custom css class for each step
    currentStepObj = this._introItems[this._currentStep];
    if (typeof (currentStepObj.tooltipClass) === 'string') {
      tooltipCssClass = currentStepObj.tooltipClass;
    } else {
      tooltipCssClass = this._options.tooltipClass;
    }

    tooltipLayer.className = ('introjs-tooltip ' + tooltipCssClass).replace(/^\s+|\s+$/g, '');

    //custom css class for tooltip boxes
    var tooltipCssClass = this._options.tooltipClass;

    currentTooltipPosition = this._introItems[this._currentStep].position;
    if ((currentTooltipPosition == "auto" || this._options.tooltipPosition == "auto")) {
      if (currentTooltipPosition != "floating") { // Floating is always valid, no point in calculating
        currentTooltipPosition = _determineAutoPosition.call(this, targetElement, tooltipLayer, currentTooltipPosition)
      }
    }
    var targetOffset = _getOffset(targetElement)
    var tooltipHeight = _getOffset(tooltipLayer).height
    var windowSize = _getWinSize()
    switch (currentTooltipPosition) {
      case 'top':
        tooltipLayer.style.left = '15px';
        tooltipLayer.style.top = '-' + (tooltipHeight + 10) + 'px';
        arrowLayer.className = 'introjs-arrow bottom';
        break;
      case 'right':
        tooltipLayer.style.left = (_getOffset(targetElement).width + 20) + 'px';
        if (targetOffset.top + tooltipHeight > windowSize.height) {
          // In this case, right would have fallen below the bottom of the screen.
          // Modify so that the bottom of the tooltip connects with the target
          arrowLayer.className = "introjs-arrow left-bottom";
          tooltipLayer.style.top = "-" + (tooltipHeight - targetOffset.height - 20) + "px"
        }
        arrowLayer.className = 'introjs-arrow left';
        break;
      case 'left':
        if (this._options.showStepNumbers == true) {
          tooltipLayer.style.top = '15px';
        }

        if (targetOffset.top + tooltipHeight > windowSize.height) {
          // In this case, left would have fallen below the bottom of the screen.
          // Modify so that the bottom of the tooltip connects with the target
          tooltipLayer.style.top = "-" + (tooltipHeight - targetOffset.height - 20) + "px"
          arrowLayer.className = 'introjs-arrow right-bottom';
        } else {
          arrowLayer.className = 'introjs-arrow right';
        }
        tooltipLayer.style.right = (targetOffset.width + 20) + 'px';


        break;
      case 'floating':
        arrowLayer.style.display = 'none';

        //we have to adjust the top and left of layer manually for intro items without element
        tooltipOffset = _getOffset(tooltipLayer);

        tooltipLayer.style.left   = '50%';
        tooltipLayer.style.top    = '50%';
        tooltipLayer.style.marginLeft = '-' + (tooltipOffset.width / 2)  + 'px';
        tooltipLayer.style.marginTop  = '-' + (tooltipOffset.height / 2) + 'px';

        if (typeof(helperNumberLayer) != 'undefined' && helperNumberLayer != null) {
          helperNumberLayer.style.left = '-' + ((tooltipOffset.width / 2) + 18) + 'px';
          helperNumberLayer.style.top  = '-' + ((tooltipOffset.height / 2) + 18) + 'px';
        }

        break;
      case 'bottom-right-aligned':
        arrowLayer.className      = 'introjs-arrow top-right';
        tooltipLayer.style.right  = '0px';
        tooltipLayer.style.bottom = '-' + (_getOffset(tooltipLayer).height + 10) + 'px';
        break;
      case 'bottom-middle-aligned':
        targetElementOffset = _getOffset(targetElement);
        tooltipOffset       = _getOffset(tooltipLayer);

        arrowLayer.className      = 'introjs-arrow top-middle';
        tooltipLayer.style.left   = (targetElementOffset.width / 2 - tooltipOffset.width / 2) + 'px';
        tooltipLayer.style.bottom = '-' + (tooltipOffset.height + 10) + 'px';
        break;
      case 'bottom-left-aligned':
      // Bottom-left-aligned is the same as the default bottom
      case 'bottom':
      // Bottom going to follow the default behavior
      default:
        tooltipLayer.style.bottom = '-' + (_getOffset(tooltipLayer).height + 10) + 'px';
        tooltipLayer.style.left = (_getOffset(targetElement).width / 2 - _getOffset(tooltipLayer).width / 2) + 'px';

        arrowLayer.className = 'introjs-arrow top';
        break;
    }
  }

  /**
   * Determines the position of the tooltip based on the position precedence and availability
   * of screen space.
   *
   * @param {Object} targetElement
   * @param {Object} tooltipLayer
   * @param {Object} desiredTooltipPosition
   *
   */
  function _determineAutoPosition(targetElement, tooltipLayer, desiredTooltipPosition) {

    // Take a clone of position precedence. These will be the available
    var possiblePositions = this._options.positionPrecedence.slice()

    var windowSize = _getWinSize()
    var tooltipHeight = _getOffset(tooltipLayer).height + 10
    var tooltipWidth = _getOffset(tooltipLayer).width + 20
    var targetOffset = _getOffset(targetElement)

    // If we check all the possible areas, and there are no valid places for the tooltip, the element
    // must take up most of the screen real estate. Show the tooltip floating in the middle of the screen.
    var calculatedPosition = "floating"

    // Check if the width of the tooltip + the starting point would spill off the right side of the screen
    // If no, neither bottom or top are valid
    if (targetOffset.left + tooltipWidth > windowSize.width || ((targetOffset.left + (targetOffset.width / 2)) - tooltipWidth) < 0) {
      _removeEntry(possiblePositions, "bottom")
      _removeEntry(possiblePositions, "top");
    } else {
      // Check for space below
      if ((targetOffset.height + targetOffset.top + tooltipHeight) > windowSize.height) {
        _removeEntry(possiblePositions, "bottom")
      }

      // Check for space above
      if (targetOffset.top - tooltipHeight < 0) {
        _removeEntry(possiblePositions, "top");
      }
    }

    // Check for space to the right
    if (targetOffset.width + targetOffset.left + tooltipWidth > windowSize.width) {
      _removeEntry(possiblePositions, "right");
    }

    // Check for space to the left
    if (targetOffset.left - tooltipWidth < 0) {
      _removeEntry(possiblePositions, "left");
    }

    // At this point, our array only has positions that are valid. Pick the first one, as it remains in order
    if (possiblePositions.length > 0) {
      calculatedPosition = possiblePositions[0];
    }

    // If the requested position is in the list, replace our calculated choice with that
    if (desiredTooltipPosition && desiredTooltipPosition != "auto") {
      if (possiblePositions.indexOf(desiredTooltipPosition) > -1) {
        calculatedPosition = desiredTooltipPosition
      }
    }

    return calculatedPosition
  }

  /**
   * Remove an entry from a string array if it's there, does nothing if it isn't there.
   *
   * @param {Array} stringArray
   * @param {String} stringToRemove
   */
  function _removeEntry(stringArray, stringToRemove) {
    if (stringArray.indexOf(stringToRemove) > -1) {
      stringArray.splice(stringArray.indexOf(stringToRemove), 1);
    }
  }

  /**
   * Update the position of the helper layer on the screen
   *
   * @api private
   * @method _setHelperLayerPosition
   * @param {Object} helperLayer
   */
  function _setHelperLayerPosition(helperLayer) {
    if (helperLayer) {
      //prevent error when `this._currentStep` in undefined
      if (!this._introItems[this._currentStep]) return;

      var currentElement  = this._introItems[this._currentStep],
          elementPosition = _getOffset(currentElement.element),
          widthHeightPadding = 10;

      if (currentElement.position == 'floating') {
        widthHeightPadding = 0;
      }

      //set new position to helper layer
      helperLayer.setAttribute('style', 'width: ' + (elementPosition.width  + widthHeightPadding)  + 'px; ' +
                                        'height:' + (elementPosition.height + widthHeightPadding)  + 'px; ' +
                                        'top:'    + (elementPosition.top    - 5)   + 'px;' +
                                        'left: '  + (elementPosition.left   - 5)   + 'px;');

    }
  }

  /**
   * Add disableinteraction layer and adjust the size and position of the layer
   *
   * @api private
   * @method _disableInteraction
   */
  function _disableInteraction () {
    var disableInteractionLayer = document.querySelector('.introjs-disableInteraction');
    if (disableInteractionLayer === null) {
      disableInteractionLayer = document.createElement('div');
      disableInteractionLayer.className = 'introjs-disableInteraction';
      this._targetElement.appendChild(disableInteractionLayer);
    }

    _setHelperLayerPosition.call(this, disableInteractionLayer);
  }

  /**
   * Show an element on the page
   *
   * @api private
   * @method _showElement
   * @param {Object} targetElement
   */
  function _showElement(targetElement) {

    if (typeof (this._introChangeCallback) !== 'undefined') {
      this._introChangeCallback.call(this, targetElement.element);
    }

    var self = this,
        oldHelperLayer = document.querySelector('.introjs-helperLayer'),
        oldReferenceLayer = document.querySelector('.introjs-tooltipReferenceLayer'),
        highlightClass = 'introjs-helperLayer',
        elementPosition = _getOffset(targetElement.element);

    //check for a current step highlight class
    if (typeof (targetElement.highlightClass) === 'string') {
      highlightClass += (' ' + targetElement.highlightClass);
    }
    //check for options highlight class
    if (typeof (this._options.highlightClass) === 'string') {
      highlightClass += (' ' + this._options.highlightClass);
    }

    if (oldHelperLayer != null) {
      var oldHelperNumberLayer = oldReferenceLayer.querySelector('.introjs-helperNumberLayer'),
          oldtooltipLayer      = oldReferenceLayer.querySelector('.introjs-tooltiptext'),
          oldArrowLayer        = oldReferenceLayer.querySelector('.introjs-arrow'),
          oldtooltipContainer  = oldReferenceLayer.querySelector('.introjs-tooltip'),
          skipTooltipButton    = oldReferenceLayer.querySelector('.introjs-skipbutton'),
          prevTooltipButton    = oldReferenceLayer.querySelector('.introjs-prevbutton'),
          nextTooltipButton    = oldReferenceLayer.querySelector('.introjs-nextbutton');

      //update or reset the helper highlight class
      oldHelperLayer.className = highlightClass;
      //hide the tooltip
      oldtooltipContainer.style.opacity = 0;
      oldtooltipContainer.style.display = "none";

      if (oldHelperNumberLayer != null) {
        var lastIntroItem = this._introItems[(targetElement.step - 2 >= 0 ? targetElement.step - 2 : 0)];

        if (lastIntroItem != null && (this._direction == 'forward' && lastIntroItem.position == 'floating') || (this._direction == 'backward' && targetElement.position == 'floating')) {
          oldHelperNumberLayer.style.opacity = 0;
        }
      }

      //set new position to helper layer
      _setHelperLayerPosition.call(self, oldHelperLayer);
      _setHelperLayerPosition.call(self, oldReferenceLayer);

      //remove `introjs-fixParent` class from the elements
      var fixParents = document.querySelectorAll('.introjs-fixParent');
      if (fixParents && fixParents.length > 0) {
        for (var i = fixParents.length - 1; i >= 0; i--) {
          fixParents[i].className = fixParents[i].className.replace(/introjs-fixParent/g, '').replace(/^\s+|\s+$/g, '');
        };
      }

      //remove old classes
      var oldShowElement = document.querySelector('.introjs-showElement');
      oldShowElement.className = oldShowElement.className.replace(/introjs-[a-zA-Z]+/g, '').replace(/^\s+|\s+$/g, '');

      //we should wait until the CSS3 transition is competed (it's 0.3 sec) to prevent incorrect `height` and `width` calculation
      if (self._lastShowElementTimer) {
        clearTimeout(self._lastShowElementTimer);
      }
      self._lastShowElementTimer = setTimeout(function() {
        //set current step to the label
        if (oldHelperNumberLayer != null) {
          oldHelperNumberLayer.innerHTML = targetElement.step;
        }
        //set current tooltip text
        oldtooltipLayer.innerHTML = targetElement.intro;
        //set the tooltip position
        oldtooltipContainer.style.display = "block";
        _placeTooltip.call(self, targetElement.element, oldtooltipContainer, oldArrowLayer, oldHelperNumberLayer);

        //change active bullet
        oldReferenceLayer.querySelector('.introjs-bullets li > a.active').className = '';
        oldReferenceLayer.querySelector('.introjs-bullets li > a[data-stepnumber="' + targetElement.step + '"]').className = 'active';

        oldReferenceLayer.querySelector('.introjs-progress .introjs-progressbar').setAttribute('style', 'width:' + _getProgress.call(self) + '%;');

        //show the tooltip
        oldtooltipContainer.style.opacity = 1;
        if (oldHelperNumberLayer) oldHelperNumberLayer.style.opacity = 1;

        //reset button focus
        if (nextTooltipButton.tabIndex === -1) {
          //tabindex of -1 means we are at the end of the tour - focus on skip / done
          skipTooltipButton.focus();
        } else {
          //still in the tour, focus on next
          nextTooltipButton.focus();
        }
      }, 350);

    } else {
      var helperLayer       = document.createElement('div'),
          referenceLayer    = document.createElement('div'),
          arrowLayer        = document.createElement('div'),
          tooltipLayer      = document.createElement('div'),
          tooltipTextLayer  = document.createElement('div'),
          bulletsLayer      = document.createElement('div'),
          progressLayer     = document.createElement('div'),
          buttonsLayer      = document.createElement('div');

      helperLayer.className = highlightClass;
      referenceLayer.className = 'introjs-tooltipReferenceLayer';

      //set new position to helper layer
      _setHelperLayerPosition.call(self, helperLayer);
      _setHelperLayerPosition.call(self, referenceLayer);

      //add helper layer to target element
      this._targetElement.appendChild(helperLayer);
      this._targetElement.appendChild(referenceLayer);

      arrowLayer.className = 'introjs-arrow';

      tooltipTextLayer.className = 'introjs-tooltiptext';
      tooltipTextLayer.innerHTML = targetElement.intro;

      bulletsLayer.className = 'introjs-bullets';

      if (this._options.showBullets === false) {
        bulletsLayer.style.display = 'none';
      }


      var ulContainer = document.createElement('ul');

      for (var i = 0, stepsLength = this._introItems.length; i < stepsLength; i++) {
        var innerLi    = document.createElement('li');
        var anchorLink = document.createElement('a');

        anchorLink.onclick = function() {
          self.goToStep(this.getAttribute('data-stepnumber'));
        };

        if (i === (targetElement.step-1)) anchorLink.className = 'active';

        anchorLink.href = 'javascript:void(0);';
        anchorLink.innerHTML = "&nbsp;";
        anchorLink.setAttribute('data-stepnumber', this._introItems[i].step);

        innerLi.appendChild(anchorLink);
        ulContainer.appendChild(innerLi);
      }

      bulletsLayer.appendChild(ulContainer);

      progressLayer.className = 'introjs-progress';

      if (this._options.showProgress === false) {
        progressLayer.style.display = 'none';
      }
      var progressBar = document.createElement('div');
      progressBar.className = 'introjs-progressbar';
      progressBar.setAttribute('style', 'width:' + _getProgress.call(this) + '%;');

      progressLayer.appendChild(progressBar);

      buttonsLayer.className = 'introjs-tooltipbuttons';
      if (this._options.showButtons === false) {
        buttonsLayer.style.display = 'none';
      }

      tooltipLayer.className = 'introjs-tooltip';
      tooltipLayer.appendChild(tooltipTextLayer);
      tooltipLayer.appendChild(bulletsLayer);
      tooltipLayer.appendChild(progressLayer);

      //add helper layer number
      if (this._options.showStepNumbers == true) {
        var helperNumberLayer = document.createElement('span');
        helperNumberLayer.className = 'introjs-helperNumberLayer';
        helperNumberLayer.innerHTML = targetElement.step;
        referenceLayer.appendChild(helperNumberLayer);
      }

      tooltipLayer.appendChild(arrowLayer);
      referenceLayer.appendChild(tooltipLayer);

      //next button
      var nextTooltipButton = document.createElement('a');

      nextTooltipButton.onclick = function() {
        if (self._introItems.length - 1 != self._currentStep) {
          _nextStep.call(self);
        }
      };

      nextTooltipButton.href = 'javascript:void(0);';
      nextTooltipButton.innerHTML = this._options.nextLabel;

      //previous button
      var prevTooltipButton = document.createElement('a');

      prevTooltipButton.onclick = function() {
        if (self._currentStep != 0) {
          _previousStep.call(self);
        }
      };

      prevTooltipButton.href = 'javascript:void(0);';
      prevTooltipButton.innerHTML = this._options.prevLabel;

      //skip button
      var skipTooltipButton = document.createElement('a');
      skipTooltipButton.className = 'introjs-button introjs-skipbutton';
      skipTooltipButton.href = 'javascript:void(0);';
      skipTooltipButton.innerHTML = this._options.skipLabel;

      skipTooltipButton.onclick = function() {
        if (self._introItems.length - 1 == self._currentStep && typeof (self._introCompleteCallback) === 'function') {
          self._introCompleteCallback.call(self);
        }

        if (self._introItems.length - 1 != self._currentStep && typeof (self._introExitCallback) === 'function') {
          self._introExitCallback.call(self);
        }

        _exitIntro.call(self, self._targetElement);
      };

      buttonsLayer.appendChild(skipTooltipButton);

      //in order to prevent displaying next/previous button always
      if (this._introItems.length > 1) {
        buttonsLayer.appendChild(prevTooltipButton);
        buttonsLayer.appendChild(nextTooltipButton);
      }

      tooltipLayer.appendChild(buttonsLayer);

      //set proper position
      _placeTooltip.call(self, targetElement.element, tooltipLayer, arrowLayer, helperNumberLayer);
    }

    //disable interaction
    if (this._options.disableInteraction === true) {
      _disableInteraction.call(self);
    }

    prevTooltipButton.removeAttribute('tabIndex');
    nextTooltipButton.removeAttribute('tabIndex');

    if (this._currentStep == 0 && this._introItems.length > 1) {
      prevTooltipButton.className = 'introjs-button introjs-prevbutton introjs-disabled';
      prevTooltipButton.tabIndex = '-1';
      nextTooltipButton.className = 'introjs-button introjs-nextbutton';
      skipTooltipButton.innerHTML = this._options.skipLabel;
    } else if (this._introItems.length - 1 == this._currentStep || this._introItems.length == 1) {
      skipTooltipButton.innerHTML = this._options.doneLabel;
      prevTooltipButton.className = 'introjs-button introjs-prevbutton';
      nextTooltipButton.className = 'introjs-button introjs-nextbutton introjs-disabled';
      nextTooltipButton.tabIndex = '-1';
    } else {
      prevTooltipButton.className = 'introjs-button introjs-prevbutton';
      nextTooltipButton.className = 'introjs-button introjs-nextbutton';
      skipTooltipButton.innerHTML = this._options.skipLabel;
    }

    //Set focus on "next" button, so that hitting Enter always moves you onto the next step
    nextTooltipButton.focus();

    //add target element position style
    targetElement.element.className += ' introjs-showElement';

    var currentElementPosition = _getPropValue(targetElement.element, 'position');
    if (currentElementPosition !== 'absolute' &&
        currentElementPosition !== 'relative') {
      //change to new intro item
      targetElement.element.className += ' introjs-relativePosition';
    }

    var parentElm = targetElement.element.parentNode;
    while (parentElm != null) {
      if (parentElm.tagName.toLowerCase() === 'body') break;

      //fix The Stacking Contenxt problem.
      //More detail: https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Understanding_z_index/The_stacking_context
      var zIndex = _getPropValue(parentElm, 'z-index');
      var opacity = parseFloat(_getPropValue(parentElm, 'opacity'));
      var transform = _getPropValue(parentElm, 'transform') || _getPropValue(parentElm, '-webkit-transform') || _getPropValue(parentElm, '-moz-transform') || _getPropValue(parentElm, '-ms-transform') || _getPropValue(parentElm, '-o-transform');
      if (/[0-9]+/.test(zIndex) || opacity < 1 || transform !== 'none') {
        parentElm.className += ' introjs-fixParent';
      }

      parentElm = parentElm.parentNode;
    }

    if (!_elementInViewport(targetElement.element) && this._options.scrollToElement === true) {
      var rect = targetElement.element.getBoundingClientRect(),
        winHeight = _getWinSize().height,
        top = rect.bottom - (rect.bottom - rect.top),
        bottom = rect.bottom - winHeight;

      //Scroll up
      if (top < 0 || targetElement.element.clientHeight > winHeight) {
        window.scrollBy(0, top - 30); // 30px padding from edge to look nice

      //Scroll down
      } else {
        window.scrollBy(0, bottom + 100); // 70px + 30px padding from edge to look nice
      }
    }

    if (typeof (this._introAfterChangeCallback) !== 'undefined') {
      this._introAfterChangeCallback.call(this, targetElement.element);
    }
  }

  /**
   * Get an element CSS property on the page
   * Thanks to JavaScript Kit: http://www.javascriptkit.com/dhtmltutors/dhtmlcascade4.shtml
   *
   * @api private
   * @method _getPropValue
   * @param {Object} element
   * @param {String} propName
   * @returns Element's property value
   */
  function _getPropValue (element, propName) {
    var propValue = '';
    if (element.currentStyle) { //IE
      propValue = element.currentStyle[propName];
    } else if (document.defaultView && document.defaultView.getComputedStyle) { //Others
      propValue = document.defaultView.getComputedStyle(element, null).getPropertyValue(propName);
    }

    //Prevent exception in IE
    if (propValue && propValue.toLowerCase) {
      return propValue.toLowerCase();
    } else {
      return propValue;
    }
  }

  /**
   * Provides a cross-browser way to get the screen dimensions
   * via: http://stackoverflow.com/questions/5864467/internet-explorer-innerheight
   *
   * @api private
   * @method _getWinSize
   * @returns {Object} width and height attributes
   */
  function _getWinSize() {
    if (window.innerWidth != undefined) {
      return { width: window.innerWidth, height: window.innerHeight };
    } else {
      var D = document.documentElement;
      return { width: D.clientWidth, height: D.clientHeight };
    }
  }

  /**
   * Add overlay layer to the page
   * http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
   *
   * @api private
   * @method _elementInViewport
   * @param {Object} el
   */
  function _elementInViewport(el) {
    var rect = el.getBoundingClientRect();

    return (
      rect.top >= 0 &&
      rect.left >= 0 &&
      (rect.bottom+80) <= window.innerHeight && // add 80 to get the text right
      rect.right <= window.innerWidth
    );
  }

  /**
   * Add overlay layer to the page
   *
   * @api private
   * @method _addOverlayLayer
   * @param {Object} targetElm
   */
  function _addOverlayLayer(targetElm) {
    var overlayLayer = document.createElement('div'),
        styleText = '',
        self = this;

    //set css class name
    overlayLayer.className = 'introjs-overlay';

    //check if the target element is body, we should calculate the size of overlay layer in a better way
    if (targetElm.tagName.toLowerCase() === 'body') {
      styleText += 'top: 0;bottom: 0; left: 0;right: 0;position: fixed;';
      overlayLayer.setAttribute('style', styleText);
    } else {
      //set overlay layer position
      var elementPosition = _getOffset(targetElm);
      if (elementPosition) {
        styleText += 'width: ' + elementPosition.width + 'px; height:' + elementPosition.height + 'px; top:' + elementPosition.top + 'px;left: ' + elementPosition.left + 'px;';
        overlayLayer.setAttribute('style', styleText);
      }
    }

    targetElm.appendChild(overlayLayer);

    overlayLayer.onclick = function() {
      if (self._options.exitOnOverlayClick == true) {
        _exitIntro.call(self, targetElm);

        //check if any callback is defined
        if (self._introExitCallback != undefined) {
          self._introExitCallback.call(self);
        }
      }
    };

    setTimeout(function() {
      styleText += 'opacity: ' + self._options.overlayOpacity.toString() + ';';
      overlayLayer.setAttribute('style', styleText);
    }, 10);

    return true;
  }

  /**
   * Get an element position on the page
   * Thanks to `meouw`: http://stackoverflow.com/a/442474/375966
   *
   * @api private
   * @method _getOffset
   * @param {Object} element
   * @returns Element's position info
   */
  function _getOffset(element) {
    var elementPosition = {};

    //set width
    elementPosition.width = element.offsetWidth;

    //set height
    elementPosition.height = element.offsetHeight;

    //calculate element top and left
    var _x = 0;
    var _y = 0;
    while (element && !isNaN(element.offsetLeft) && !isNaN(element.offsetTop)) {
      _x += element.offsetLeft;
      _y += element.offsetTop;
      element = element.offsetParent;
    }
    //set top
    elementPosition.top = _y;
    //set left
    elementPosition.left = _x;

    return elementPosition;
  }

  /**
   * Gets the current progress percentage
   *
   * @api private
   * @method _getProgress
   * @returns current progress percentage
   */
  function _getProgress() {
    // Steps are 0 indexed
    var currentStep = parseInt((this._currentStep + 1), 10);
    return ((currentStep / this._introItems.length) * 100);
  }

  /**
   * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
   * via: http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically
   *
   * @param obj1
   * @param obj2
   * @returns obj3 a new object based on obj1 and obj2
   */
  function _mergeOptions(obj1,obj2) {
    var obj3 = {};
    for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
    for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
    return obj3;
  }

  var introJs = function (targetElm) {
    if (typeof (targetElm) === 'object') {
      //Ok, create a new instance
      return new IntroJs(targetElm);

    } else if (typeof (targetElm) === 'string') {
      //select the target element with query selector
      var targetElement = document.querySelector(targetElm);

      if (targetElement) {
        return new IntroJs(targetElement);
      } else {
        throw new Error('There is no element with given selector.');
      }
    } else {
      return new IntroJs(document.body);
    }
  };

  /**
   * Current IntroJs version
   *
   * @property version
   * @type String
   */
  introJs.version = VERSION;

  //Prototype
  introJs.fn = IntroJs.prototype = {
    clone: function () {
      return new IntroJs(this);
    },
    setOption: function(option, value) {
      this._options[option] = value;
      return this;
    },
    setOptions: function(options) {
      this._options = _mergeOptions(this._options, options);
      return this;
    },
    start: function () {
      _introForElement.call(this, this._targetElement);
      return this;
    },
    goToStep: function(step) {
      _goToStep.call(this, step);
      return this;
    },
    nextStep: function() {
      _nextStep.call(this);
      return this;
    },
    previousStep: function() {
      _previousStep.call(this);
      return this;
    },
    exit: function() {
      _exitIntro.call(this, this._targetElement);
      return this;
    },
    refresh: function() {
      _setHelperLayerPosition.call(this, document.querySelector('.introjs-helperLayer'));
      _setHelperLayerPosition.call(this, document.querySelector('.introjs-tooltipReferenceLayer'));
      return this;
    },
    onbeforechange: function(providedCallback) {
      if (typeof (providedCallback) === 'function') {
        this._introBeforeChangeCallback = providedCallback;
      } else {
        throw new Error('Provided callback for onbeforechange was not a function');
      }
      return this;
    },
    onchange: function(providedCallback) {
      if (typeof (providedCallback) === 'function') {
        this._introChangeCallback = providedCallback;
      } else {
        throw new Error('Provided callback for onchange was not a function.');
      }
      return this;
    },
    onafterchange: function(providedCallback) {
      if (typeof (providedCallback) === 'function') {
        this._introAfterChangeCallback = providedCallback;
      } else {
        throw new Error('Provided callback for onafterchange was not a function');
      }
      return this;
    },
    oncomplete: function(providedCallback) {
      if (typeof (providedCallback) === 'function') {
        this._introCompleteCallback = providedCallback;
      } else {
        throw new Error('Provided callback for oncomplete was not a function.');
      }
      return this;
    },
    onexit: function(providedCallback) {
      if (typeof (providedCallback) === 'function') {
        this._introExitCallback = providedCallback;
      } else {
        throw new Error('Provided callback for onexit was not a function.');
      }
      return this;
    }
  };

  exports.introJs = introJs;
  return introJs;
}));

/*! DataTables 1.10.7
 * ©2008-2015 SpryMedia Ltd - datatables.net/license
 */
(function(Ea,Q,k){var P=function(h){function W(a){var b,c,e={};h.each(a,function(d){if((b=d.match(/^([^A-Z]+?)([A-Z])/))&&-1!=="a aa ai ao as b fn i m o s ".indexOf(b[1]+" "))c=d.replace(b[0],b[2].toLowerCase()),e[c]=d,"o"===b[1]&&W(a[d])});a._hungarianMap=e}function H(a,b,c){a._hungarianMap||W(a);var e;h.each(b,function(d){e=a._hungarianMap[d];if(e!==k&&(c||b[e]===k))"o"===e.charAt(0)?(b[e]||(b[e]={}),h.extend(!0,b[e],b[d]),H(a[e],b[e],c)):b[e]=b[d]})}function P(a){var b=m.defaults.oLanguage,c=a.sZeroRecords;
!a.sEmptyTable&&(c&&"No data available in table"===b.sEmptyTable)&&E(a,a,"sZeroRecords","sEmptyTable");!a.sLoadingRecords&&(c&&"Loading..."===b.sLoadingRecords)&&E(a,a,"sZeroRecords","sLoadingRecords");a.sInfoThousands&&(a.sThousands=a.sInfoThousands);(a=a.sDecimal)&&db(a)}function eb(a){A(a,"ordering","bSort");A(a,"orderMulti","bSortMulti");A(a,"orderClasses","bSortClasses");A(a,"orderCellsTop","bSortCellsTop");A(a,"order","aaSorting");A(a,"orderFixed","aaSortingFixed");A(a,"paging","bPaginate");
A(a,"pagingType","sPaginationType");A(a,"pageLength","iDisplayLength");A(a,"searching","bFilter");if(a=a.aoSearchCols)for(var b=0,c=a.length;b<c;b++)a[b]&&H(m.models.oSearch,a[b])}function fb(a){A(a,"orderable","bSortable");A(a,"orderData","aDataSort");A(a,"orderSequence","asSorting");A(a,"orderDataType","sortDataType");var b=a.aDataSort;b&&!h.isArray(b)&&(a.aDataSort=[b])}function gb(a){var a=a.oBrowser,b=h("<div/>").css({position:"absolute",top:0,left:0,height:1,width:1,overflow:"hidden"}).append(h("<div/>").css({position:"absolute",
top:1,left:1,width:100,overflow:"scroll"}).append(h('<div class="test"/>').css({width:"100%",height:10}))).appendTo("body"),c=b.find(".test");a.bScrollOversize=100===c[0].offsetWidth;a.bScrollbarLeft=1!==Math.round(c.offset().left);b.remove()}function hb(a,b,c,e,d,f){var g,j=!1;c!==k&&(g=c,j=!0);for(;e!==d;)a.hasOwnProperty(e)&&(g=j?b(g,a[e],e,a):a[e],j=!0,e+=f);return g}function Fa(a,b){var c=m.defaults.column,e=a.aoColumns.length,c=h.extend({},m.models.oColumn,c,{nTh:b?b:Q.createElement("th"),sTitle:c.sTitle?
c.sTitle:b?b.innerHTML:"",aDataSort:c.aDataSort?c.aDataSort:[e],mData:c.mData?c.mData:e,idx:e});a.aoColumns.push(c);c=a.aoPreSearchCols;c[e]=h.extend({},m.models.oSearch,c[e]);ka(a,e,h(b).data())}function ka(a,b,c){var b=a.aoColumns[b],e=a.oClasses,d=h(b.nTh);if(!b.sWidthOrig){b.sWidthOrig=d.attr("width")||null;var f=(d.attr("style")||"").match(/width:\s*(\d+[pxem%]+)/);f&&(b.sWidthOrig=f[1])}c!==k&&null!==c&&(fb(c),H(m.defaults.column,c),c.mDataProp!==k&&!c.mData&&(c.mData=c.mDataProp),c.sType&&
(b._sManualType=c.sType),c.className&&!c.sClass&&(c.sClass=c.className),h.extend(b,c),E(b,c,"sWidth","sWidthOrig"),c.iDataSort!==k&&(b.aDataSort=[c.iDataSort]),E(b,c,"aDataSort"));var g=b.mData,j=R(g),i=b.mRender?R(b.mRender):null,c=function(a){return"string"===typeof a&&-1!==a.indexOf("@")};b._bAttrSrc=h.isPlainObject(g)&&(c(g.sort)||c(g.type)||c(g.filter));b.fnGetData=function(a,b,c){var e=j(a,b,k,c);return i&&b?i(e,b,a,c):e};b.fnSetData=function(a,b,c){return S(g)(a,b,c)};"number"!==typeof g&&
(a._rowReadObject=!0);a.oFeatures.bSort||(b.bSortable=!1,d.addClass(e.sSortableNone));a=-1!==h.inArray("asc",b.asSorting);c=-1!==h.inArray("desc",b.asSorting);!b.bSortable||!a&&!c?(b.sSortingClass=e.sSortableNone,b.sSortingClassJUI=""):a&&!c?(b.sSortingClass=e.sSortableAsc,b.sSortingClassJUI=e.sSortJUIAscAllowed):!a&&c?(b.sSortingClass=e.sSortableDesc,b.sSortingClassJUI=e.sSortJUIDescAllowed):(b.sSortingClass=e.sSortable,b.sSortingClassJUI=e.sSortJUI)}function X(a){if(!1!==a.oFeatures.bAutoWidth){var b=
a.aoColumns;Ga(a);for(var c=0,e=b.length;c<e;c++)b[c].nTh.style.width=b[c].sWidth}b=a.oScroll;(""!==b.sY||""!==b.sX)&&Y(a);w(a,null,"column-sizing",[a])}function la(a,b){var c=Z(a,"bVisible");return"number"===typeof c[b]?c[b]:null}function $(a,b){var c=Z(a,"bVisible"),c=h.inArray(b,c);return-1!==c?c:null}function aa(a){return Z(a,"bVisible").length}function Z(a,b){var c=[];h.map(a.aoColumns,function(a,d){a[b]&&c.push(d)});return c}function Ha(a){var b=a.aoColumns,c=a.aoData,e=m.ext.type.detect,d,
f,g,j,i,h,l,q,n;d=0;for(f=b.length;d<f;d++)if(l=b[d],n=[],!l.sType&&l._sManualType)l.sType=l._sManualType;else if(!l.sType){g=0;for(j=e.length;g<j;g++){i=0;for(h=c.length;i<h;i++){n[i]===k&&(n[i]=x(a,i,d,"type"));q=e[g](n[i],a);if(!q&&g!==e.length-1)break;if("html"===q)break}if(q){l.sType=q;break}}l.sType||(l.sType="string")}}function ib(a,b,c,e){var d,f,g,j,i,o,l=a.aoColumns;if(b)for(d=b.length-1;0<=d;d--){o=b[d];var q=o.targets!==k?o.targets:o.aTargets;h.isArray(q)||(q=[q]);f=0;for(g=q.length;f<
g;f++)if("number"===typeof q[f]&&0<=q[f]){for(;l.length<=q[f];)Fa(a);e(q[f],o)}else if("number"===typeof q[f]&&0>q[f])e(l.length+q[f],o);else if("string"===typeof q[f]){j=0;for(i=l.length;j<i;j++)("_all"==q[f]||h(l[j].nTh).hasClass(q[f]))&&e(j,o)}}if(c){d=0;for(a=c.length;d<a;d++)e(d,c[d])}}function K(a,b,c,e){var d=a.aoData.length,f=h.extend(!0,{},m.models.oRow,{src:c?"dom":"data"});f._aData=b;a.aoData.push(f);for(var b=a.aoColumns,f=0,g=b.length;f<g;f++)c&&Ia(a,d,f,x(a,d,f)),b[f].sType=null;a.aiDisplayMaster.push(d);
(c||!a.oFeatures.bDeferRender)&&Ja(a,d,c,e);return d}function ma(a,b){var c;b instanceof h||(b=h(b));return b.map(function(b,d){c=na(a,d);return K(a,c.data,d,c.cells)})}function x(a,b,c,e){var d=a.iDraw,f=a.aoColumns[c],g=a.aoData[b]._aData,j=f.sDefaultContent,c=f.fnGetData(g,e,{settings:a,row:b,col:c});if(c===k)return a.iDrawError!=d&&null===j&&(I(a,0,"Requested unknown parameter "+("function"==typeof f.mData?"{function}":"'"+f.mData+"'")+" for row "+b,4),a.iDrawError=d),j;if((c===g||null===c)&&
null!==j)c=j;else if("function"===typeof c)return c.call(g);return null===c&&"display"==e?"":c}function Ia(a,b,c,e){a.aoColumns[c].fnSetData(a.aoData[b]._aData,e,{settings:a,row:b,col:c})}function Ka(a){return h.map(a.match(/(\\.|[^\.])+/g),function(a){return a.replace(/\\./g,".")})}function R(a){if(h.isPlainObject(a)){var b={};h.each(a,function(a,c){c&&(b[a]=R(c))});return function(a,c,f,g){var j=b[c]||b._;return j!==k?j(a,c,f,g):a}}if(null===a)return function(a){return a};if("function"===typeof a)return function(b,
c,f,g){return a(b,c,f,g)};if("string"===typeof a&&(-1!==a.indexOf(".")||-1!==a.indexOf("[")||-1!==a.indexOf("("))){var c=function(a,b,f){var g,j;if(""!==f){j=Ka(f);for(var i=0,h=j.length;i<h;i++){f=j[i].match(ba);g=j[i].match(T);if(f){j[i]=j[i].replace(ba,"");""!==j[i]&&(a=a[j[i]]);g=[];j.splice(0,i+1);j=j.join(".");i=0;for(h=a.length;i<h;i++)g.push(c(a[i],b,j));a=f[0].substring(1,f[0].length-1);a=""===a?g:g.join(a);break}else if(g){j[i]=j[i].replace(T,"");a=a[j[i]]();continue}if(null===a||a[j[i]]===
k)return k;a=a[j[i]]}}return a};return function(b,d){return c(b,d,a)}}return function(b){return b[a]}}function S(a){if(h.isPlainObject(a))return S(a._);if(null===a)return function(){};if("function"===typeof a)return function(b,e,d){a(b,"set",e,d)};if("string"===typeof a&&(-1!==a.indexOf(".")||-1!==a.indexOf("[")||-1!==a.indexOf("("))){var b=function(a,e,d){var d=Ka(d),f;f=d[d.length-1];for(var g,j,i=0,h=d.length-1;i<h;i++){g=d[i].match(ba);j=d[i].match(T);if(g){d[i]=d[i].replace(ba,"");a[d[i]]=[];
f=d.slice();f.splice(0,i+1);g=f.join(".");j=0;for(h=e.length;j<h;j++)f={},b(f,e[j],g),a[d[i]].push(f);return}j&&(d[i]=d[i].replace(T,""),a=a[d[i]](e));if(null===a[d[i]]||a[d[i]]===k)a[d[i]]={};a=a[d[i]]}if(f.match(T))a[f.replace(T,"")](e);else a[f.replace(ba,"")]=e};return function(c,e){return b(c,e,a)}}return function(b,e){b[a]=e}}function La(a){return D(a.aoData,"_aData")}function oa(a){a.aoData.length=0;a.aiDisplayMaster.length=0;a.aiDisplay.length=0}function pa(a,b,c){for(var e=-1,d=0,f=a.length;d<
f;d++)a[d]==b?e=d:a[d]>b&&a[d]--; -1!=e&&c===k&&a.splice(e,1)}function ca(a,b,c,e){var d=a.aoData[b],f,g=function(c,f){for(;c.childNodes.length;)c.removeChild(c.firstChild);c.innerHTML=x(a,b,f,"display")};if("dom"===c||(!c||"auto"===c)&&"dom"===d.src)d._aData=na(a,d,e,e===k?k:d._aData).data;else{var j=d.anCells;if(j)if(e!==k)g(j[e],e);else{c=0;for(f=j.length;c<f;c++)g(j[c],c)}}d._aSortData=null;d._aFilterData=null;g=a.aoColumns;if(e!==k)g[e].sType=null;else{c=0;for(f=g.length;c<f;c++)g[c].sType=null;
Ma(d)}}function na(a,b,c,e){var d=[],f=b.firstChild,g,j=0,i,o=a.aoColumns,l=a._rowReadObject,e=e||l?{}:[],q=function(a,b){if("string"===typeof a){var c=a.indexOf("@");-1!==c&&(c=a.substring(c+1),S(a)(e,b.getAttribute(c)))}},a=function(a){if(c===k||c===j)g=o[j],i=h.trim(a.innerHTML),g&&g._bAttrSrc?(S(g.mData._)(e,i),q(g.mData.sort,a),q(g.mData.type,a),q(g.mData.filter,a)):l?(g._setter||(g._setter=S(g.mData)),g._setter(e,i)):e[j]=i;j++};if(f)for(;f;){b=f.nodeName.toUpperCase();if("TD"==b||"TH"==b)a(f),
d.push(f);f=f.nextSibling}else{d=b.anCells;f=0;for(b=d.length;f<b;f++)a(d[f])}return{data:e,cells:d}}function Ja(a,b,c,e){var d=a.aoData[b],f=d._aData,g=[],j,i,h,l,q;if(null===d.nTr){j=c||Q.createElement("tr");d.nTr=j;d.anCells=g;j._DT_RowIndex=b;Ma(d);l=0;for(q=a.aoColumns.length;l<q;l++){h=a.aoColumns[l];i=c?e[l]:Q.createElement(h.sCellType);g.push(i);if(!c||h.mRender||h.mData!==l)i.innerHTML=x(a,b,l,"display");h.sClass&&(i.className+=" "+h.sClass);h.bVisible&&!c?j.appendChild(i):!h.bVisible&&c&&
i.parentNode.removeChild(i);h.fnCreatedCell&&h.fnCreatedCell.call(a.oInstance,i,x(a,b,l),f,b,l)}w(a,"aoRowCreatedCallback",null,[j,f,b])}d.nTr.setAttribute("role","row")}function Ma(a){var b=a.nTr,c=a._aData;if(b){c.DT_RowId&&(b.id=c.DT_RowId);if(c.DT_RowClass){var e=c.DT_RowClass.split(" ");a.__rowc=a.__rowc?Na(a.__rowc.concat(e)):e;h(b).removeClass(a.__rowc.join(" ")).addClass(c.DT_RowClass)}c.DT_RowAttr&&h(b).attr(c.DT_RowAttr);c.DT_RowData&&h(b).data(c.DT_RowData)}}function jb(a){var b,c,e,d,
f,g=a.nTHead,j=a.nTFoot,i=0===h("th, td",g).length,o=a.oClasses,l=a.aoColumns;i&&(d=h("<tr/>").appendTo(g));b=0;for(c=l.length;b<c;b++)f=l[b],e=h(f.nTh).addClass(f.sClass),i&&e.appendTo(d),a.oFeatures.bSort&&(e.addClass(f.sSortingClass),!1!==f.bSortable&&(e.attr("tabindex",a.iTabIndex).attr("aria-controls",a.sTableId),Oa(a,f.nTh,b))),f.sTitle!=e.html()&&e.html(f.sTitle),Pa(a,"header")(a,e,f,o);i&&da(a.aoHeader,g);h(g).find(">tr").attr("role","row");h(g).find(">tr>th, >tr>td").addClass(o.sHeaderTH);
h(j).find(">tr>th, >tr>td").addClass(o.sFooterTH);if(null!==j){a=a.aoFooter[0];b=0;for(c=a.length;b<c;b++)f=l[b],f.nTf=a[b].cell,f.sClass&&h(f.nTf).addClass(f.sClass)}}function ea(a,b,c){var e,d,f,g=[],j=[],i=a.aoColumns.length,o;if(b){c===k&&(c=!1);e=0;for(d=b.length;e<d;e++){g[e]=b[e].slice();g[e].nTr=b[e].nTr;for(f=i-1;0<=f;f--)!a.aoColumns[f].bVisible&&!c&&g[e].splice(f,1);j.push([])}e=0;for(d=g.length;e<d;e++){if(a=g[e].nTr)for(;f=a.firstChild;)a.removeChild(f);f=0;for(b=g[e].length;f<b;f++)if(o=
i=1,j[e][f]===k){a.appendChild(g[e][f].cell);for(j[e][f]=1;g[e+i]!==k&&g[e][f].cell==g[e+i][f].cell;)j[e+i][f]=1,i++;for(;g[e][f+o]!==k&&g[e][f].cell==g[e][f+o].cell;){for(c=0;c<i;c++)j[e+c][f+o]=1;o++}h(g[e][f].cell).attr("rowspan",i).attr("colspan",o)}}}}function M(a){var b=w(a,"aoPreDrawCallback","preDraw",[a]);if(-1!==h.inArray(!1,b))C(a,!1);else{var b=[],c=0,e=a.asStripeClasses,d=e.length,f=a.oLanguage,g=a.iInitDisplayStart,j="ssp"==B(a),i=a.aiDisplay;a.bDrawing=!0;g!==k&&-1!==g&&(a._iDisplayStart=
j?g:g>=a.fnRecordsDisplay()?0:g,a.iInitDisplayStart=-1);var g=a._iDisplayStart,o=a.fnDisplayEnd();if(a.bDeferLoading)a.bDeferLoading=!1,a.iDraw++,C(a,!1);else if(j){if(!a.bDestroying&&!kb(a))return}else a.iDraw++;if(0!==i.length){f=j?a.aoData.length:o;for(j=j?0:g;j<f;j++){var l=i[j],q=a.aoData[l];null===q.nTr&&Ja(a,l);l=q.nTr;if(0!==d){var n=e[c%d];q._sRowStripe!=n&&(h(l).removeClass(q._sRowStripe).addClass(n),q._sRowStripe=n)}w(a,"aoRowCallback",null,[l,q._aData,c,j]);b.push(l);c++}}else c=f.sZeroRecords,
1==a.iDraw&&"ajax"==B(a)?c=f.sLoadingRecords:f.sEmptyTable&&0===a.fnRecordsTotal()&&(c=f.sEmptyTable),b[0]=h("<tr/>",{"class":d?e[0]:""}).append(h("<td />",{valign:"top",colSpan:aa(a),"class":a.oClasses.sRowEmpty}).html(c))[0];w(a,"aoHeaderCallback","header",[h(a.nTHead).children("tr")[0],La(a),g,o,i]);w(a,"aoFooterCallback","footer",[h(a.nTFoot).children("tr")[0],La(a),g,o,i]);e=h(a.nTBody);e.children().detach();e.append(h(b));w(a,"aoDrawCallback","draw",[a]);a.bSorted=!1;a.bFiltered=!1;a.bDrawing=
!1}}function N(a,b){var c=a.oFeatures,e=c.bFilter;c.bSort&&lb(a);e?fa(a,a.oPreviousSearch):a.aiDisplay=a.aiDisplayMaster.slice();!0!==b&&(a._iDisplayStart=0);a._drawHold=b;M(a);a._drawHold=!1}function mb(a){var b=a.oClasses,c=h(a.nTable),c=h("<div/>").insertBefore(c),e=a.oFeatures,d=h("<div/>",{id:a.sTableId+"_wrapper","class":b.sWrapper+(a.nTFoot?"":" "+b.sNoFooter)});a.nHolding=c[0];a.nTableWrapper=d[0];a.nTableReinsertBefore=a.nTable.nextSibling;for(var f=a.sDom.split(""),g,j,i,o,l,q,n=0;n<f.length;n++){g=
null;j=f[n];if("<"==j){i=h("<div/>")[0];o=f[n+1];if("'"==o||'"'==o){l="";for(q=2;f[n+q]!=o;)l+=f[n+q],q++;"H"==l?l=b.sJUIHeader:"F"==l&&(l=b.sJUIFooter);-1!=l.indexOf(".")?(o=l.split("."),i.id=o[0].substr(1,o[0].length-1),i.className=o[1]):"#"==l.charAt(0)?i.id=l.substr(1,l.length-1):i.className=l;n+=q}d.append(i);d=h(i)}else if(">"==j)d=d.parent();else if("l"==j&&e.bPaginate&&e.bLengthChange)g=nb(a);else if("f"==j&&e.bFilter)g=ob(a);else if("r"==j&&e.bProcessing)g=pb(a);else if("t"==j)g=qb(a);else if("i"==
j&&e.bInfo)g=rb(a);else if("p"==j&&e.bPaginate)g=sb(a);else if(0!==m.ext.feature.length){i=m.ext.feature;q=0;for(o=i.length;q<o;q++)if(j==i[q].cFeature){g=i[q].fnInit(a);break}}g&&(i=a.aanFeatures,i[j]||(i[j]=[]),i[j].push(g),d.append(g))}c.replaceWith(d)}function da(a,b){var c=h(b).children("tr"),e,d,f,g,j,i,o,l,q,n;a.splice(0,a.length);f=0;for(i=c.length;f<i;f++)a.push([]);f=0;for(i=c.length;f<i;f++){e=c[f];for(d=e.firstChild;d;){if("TD"==d.nodeName.toUpperCase()||"TH"==d.nodeName.toUpperCase()){l=
1*d.getAttribute("colspan");q=1*d.getAttribute("rowspan");l=!l||0===l||1===l?1:l;q=!q||0===q||1===q?1:q;g=0;for(j=a[f];j[g];)g++;o=g;n=1===l?!0:!1;for(j=0;j<l;j++)for(g=0;g<q;g++)a[f+g][o+j]={cell:d,unique:n},a[f+g].nTr=e}d=d.nextSibling}}}function qa(a,b,c){var e=[];c||(c=a.aoHeader,b&&(c=[],da(c,b)));for(var b=0,d=c.length;b<d;b++)for(var f=0,g=c[b].length;f<g;f++)if(c[b][f].unique&&(!e[f]||!a.bSortCellsTop))e[f]=c[b][f].cell;return e}function ra(a,b,c){w(a,"aoServerParams","serverParams",[b]);
if(b&&h.isArray(b)){var e={},d=/(.*?)\[\]$/;h.each(b,function(a,b){var c=b.name.match(d);c?(c=c[0],e[c]||(e[c]=[]),e[c].push(b.value)):e[b.name]=b.value});b=e}var f,g=a.ajax,j=a.oInstance,i=function(b){w(a,null,"xhr",[a,b,a.jqXHR]);c(b)};if(h.isPlainObject(g)&&g.data){f=g.data;var o=h.isFunction(f)?f(b,a):f,b=h.isFunction(f)&&o?o:h.extend(!0,b,o);delete g.data}o={data:b,success:function(b){var c=b.error||b.sError;c&&I(a,0,c);a.json=b;i(b)},dataType:"json",cache:!1,type:a.sServerMethod,error:function(b,
c){var f=w(a,null,"xhr",[a,null,a.jqXHR]);-1===h.inArray(!0,f)&&("parsererror"==c?I(a,0,"Invalid JSON response",1):4===b.readyState&&I(a,0,"Ajax error",7));C(a,!1)}};a.oAjaxData=b;w(a,null,"preXhr",[a,b]);a.fnServerData?a.fnServerData.call(j,a.sAjaxSource,h.map(b,function(a,b){return{name:b,value:a}}),i,a):a.sAjaxSource||"string"===typeof g?a.jqXHR=h.ajax(h.extend(o,{url:g||a.sAjaxSource})):h.isFunction(g)?a.jqXHR=g.call(j,b,i,a):(a.jqXHR=h.ajax(h.extend(o,g)),g.data=f)}function kb(a){return a.bAjaxDataGet?
(a.iDraw++,C(a,!0),ra(a,tb(a),function(b){ub(a,b)}),!1):!0}function tb(a){var b=a.aoColumns,c=b.length,e=a.oFeatures,d=a.oPreviousSearch,f=a.aoPreSearchCols,g,j=[],i,o,l,q=U(a);g=a._iDisplayStart;i=!1!==e.bPaginate?a._iDisplayLength:-1;var n=function(a,b){j.push({name:a,value:b})};n("sEcho",a.iDraw);n("iColumns",c);n("sColumns",D(b,"sName").join(","));n("iDisplayStart",g);n("iDisplayLength",i);var k={draw:a.iDraw,columns:[],order:[],start:g,length:i,search:{value:d.sSearch,regex:d.bRegex}};for(g=
0;g<c;g++)o=b[g],l=f[g],i="function"==typeof o.mData?"function":o.mData,k.columns.push({data:i,name:o.sName,searchable:o.bSearchable,orderable:o.bSortable,search:{value:l.sSearch,regex:l.bRegex}}),n("mDataProp_"+g,i),e.bFilter&&(n("sSearch_"+g,l.sSearch),n("bRegex_"+g,l.bRegex),n("bSearchable_"+g,o.bSearchable)),e.bSort&&n("bSortable_"+g,o.bSortable);e.bFilter&&(n("sSearch",d.sSearch),n("bRegex",d.bRegex));e.bSort&&(h.each(q,function(a,b){k.order.push({column:b.col,dir:b.dir});n("iSortCol_"+a,b.col);
n("sSortDir_"+a,b.dir)}),n("iSortingCols",q.length));b=m.ext.legacy.ajax;return null===b?a.sAjaxSource?j:k:b?j:k}function ub(a,b){var c=sa(a,b),e=b.sEcho!==k?b.sEcho:b.draw,d=b.iTotalRecords!==k?b.iTotalRecords:b.recordsTotal,f=b.iTotalDisplayRecords!==k?b.iTotalDisplayRecords:b.recordsFiltered;if(e){if(1*e<a.iDraw)return;a.iDraw=1*e}oa(a);a._iRecordsTotal=parseInt(d,10);a._iRecordsDisplay=parseInt(f,10);e=0;for(d=c.length;e<d;e++)K(a,c[e]);a.aiDisplay=a.aiDisplayMaster.slice();a.bAjaxDataGet=!1;
M(a);a._bInitComplete||ta(a,b);a.bAjaxDataGet=!0;C(a,!1)}function sa(a,b){var c=h.isPlainObject(a.ajax)&&a.ajax.dataSrc!==k?a.ajax.dataSrc:a.sAjaxDataProp;return"data"===c?b.aaData||b[c]:""!==c?R(c)(b):b}function ob(a){var b=a.oClasses,c=a.sTableId,e=a.oLanguage,d=a.oPreviousSearch,f=a.aanFeatures,g='<input type="search" class="'+b.sFilterInput+'"/>',j=e.sSearch,j=j.match(/_INPUT_/)?j.replace("_INPUT_",g):j+g,b=h("<div/>",{id:!f.f?c+"_filter":null,"class":b.sFilter}).append(h("<label/>").append(j)),
f=function(){var b=!this.value?"":this.value;b!=d.sSearch&&(fa(a,{sSearch:b,bRegex:d.bRegex,bSmart:d.bSmart,bCaseInsensitive:d.bCaseInsensitive}),a._iDisplayStart=0,M(a))},g=null!==a.searchDelay?a.searchDelay:"ssp"===B(a)?400:0,i=h("input",b).val(d.sSearch).attr("placeholder",e.sSearchPlaceholder).bind("keyup.DT search.DT input.DT paste.DT cut.DT",g?ua(f,g):f).bind("keypress.DT",function(a){if(13==a.keyCode)return!1}).attr("aria-controls",c);h(a.nTable).on("search.dt.DT",function(b,c){if(a===c)try{i[0]!==
Q.activeElement&&i.val(d.sSearch)}catch(f){}});return b[0]}function fa(a,b,c){var e=a.oPreviousSearch,d=a.aoPreSearchCols,f=function(a){e.sSearch=a.sSearch;e.bRegex=a.bRegex;e.bSmart=a.bSmart;e.bCaseInsensitive=a.bCaseInsensitive};Ha(a);if("ssp"!=B(a)){vb(a,b.sSearch,c,b.bEscapeRegex!==k?!b.bEscapeRegex:b.bRegex,b.bSmart,b.bCaseInsensitive);f(b);for(b=0;b<d.length;b++)wb(a,d[b].sSearch,b,d[b].bEscapeRegex!==k?!d[b].bEscapeRegex:d[b].bRegex,d[b].bSmart,d[b].bCaseInsensitive);xb(a)}else f(b);a.bFiltered=
!0;w(a,null,"search",[a])}function xb(a){for(var b=m.ext.search,c=a.aiDisplay,e,d,f=0,g=b.length;f<g;f++){for(var j=[],i=0,h=c.length;i<h;i++)d=c[i],e=a.aoData[d],b[f](a,e._aFilterData,d,e._aData,i)&&j.push(d);c.length=0;c.push.apply(c,j)}}function wb(a,b,c,e,d,f){if(""!==b)for(var g=a.aiDisplay,e=Qa(b,e,d,f),d=g.length-1;0<=d;d--)b=a.aoData[g[d]]._aFilterData[c],e.test(b)||g.splice(d,1)}function vb(a,b,c,e,d,f){var e=Qa(b,e,d,f),d=a.oPreviousSearch.sSearch,f=a.aiDisplayMaster,g;0!==m.ext.search.length&&
(c=!0);g=yb(a);if(0>=b.length)a.aiDisplay=f.slice();else{if(g||c||d.length>b.length||0!==b.indexOf(d)||a.bSorted)a.aiDisplay=f.slice();b=a.aiDisplay;for(c=b.length-1;0<=c;c--)e.test(a.aoData[b[c]]._sFilterRow)||b.splice(c,1)}}function Qa(a,b,c,e){a=b?a:va(a);c&&(a="^(?=.*?"+h.map(a.match(/"[^"]+"|[^ ]+/g)||[""],function(a){if('"'===a.charAt(0))var b=a.match(/^"(.*)"$/),a=b?b[1]:a;return a.replace('"',"")}).join(")(?=.*?")+").*$");return RegExp(a,e?"i":"")}function va(a){return a.replace(Yb,"\\$1")}
function yb(a){var b=a.aoColumns,c,e,d,f,g,j,i,h,l=m.ext.type.search;c=!1;e=0;for(f=a.aoData.length;e<f;e++)if(h=a.aoData[e],!h._aFilterData){j=[];d=0;for(g=b.length;d<g;d++)c=b[d],c.bSearchable?(i=x(a,e,d,"filter"),l[c.sType]&&(i=l[c.sType](i)),null===i&&(i=""),"string"!==typeof i&&i.toString&&(i=i.toString())):i="",i.indexOf&&-1!==i.indexOf("&")&&(wa.innerHTML=i,i=Zb?wa.textContent:wa.innerText),i.replace&&(i=i.replace(/[\r\n]/g,"")),j.push(i);h._aFilterData=j;h._sFilterRow=j.join("  ");c=!0}return c}
function zb(a){return{search:a.sSearch,smart:a.bSmart,regex:a.bRegex,caseInsensitive:a.bCaseInsensitive}}function Ab(a){return{sSearch:a.search,bSmart:a.smart,bRegex:a.regex,bCaseInsensitive:a.caseInsensitive}}function rb(a){var b=a.sTableId,c=a.aanFeatures.i,e=h("<div/>",{"class":a.oClasses.sInfo,id:!c?b+"_info":null});c||(a.aoDrawCallback.push({fn:Bb,sName:"information"}),e.attr("role","status").attr("aria-live","polite"),h(a.nTable).attr("aria-describedby",b+"_info"));return e[0]}function Bb(a){var b=
a.aanFeatures.i;if(0!==b.length){var c=a.oLanguage,e=a._iDisplayStart+1,d=a.fnDisplayEnd(),f=a.fnRecordsTotal(),g=a.fnRecordsDisplay(),j=g?c.sInfo:c.sInfoEmpty;g!==f&&(j+=" "+c.sInfoFiltered);j+=c.sInfoPostFix;j=Cb(a,j);c=c.fnInfoCallback;null!==c&&(j=c.call(a.oInstance,a,e,d,f,g,j));h(b).html(j)}}function Cb(a,b){var c=a.fnFormatNumber,e=a._iDisplayStart+1,d=a._iDisplayLength,f=a.fnRecordsDisplay(),g=-1===d;return b.replace(/_START_/g,c.call(a,e)).replace(/_END_/g,c.call(a,a.fnDisplayEnd())).replace(/_MAX_/g,
c.call(a,a.fnRecordsTotal())).replace(/_TOTAL_/g,c.call(a,f)).replace(/_PAGE_/g,c.call(a,g?1:Math.ceil(e/d))).replace(/_PAGES_/g,c.call(a,g?1:Math.ceil(f/d)))}function ga(a){var b,c,e=a.iInitDisplayStart,d=a.aoColumns,f;c=a.oFeatures;if(a.bInitialised){mb(a);jb(a);ea(a,a.aoHeader);ea(a,a.aoFooter);C(a,!0);c.bAutoWidth&&Ga(a);b=0;for(c=d.length;b<c;b++)f=d[b],f.sWidth&&(f.nTh.style.width=s(f.sWidth));N(a);d=B(a);"ssp"!=d&&("ajax"==d?ra(a,[],function(c){var f=sa(a,c);for(b=0;b<f.length;b++)K(a,f[b]);
a.iInitDisplayStart=e;N(a);C(a,!1);ta(a,c)},a):(C(a,!1),ta(a)))}else setTimeout(function(){ga(a)},200)}function ta(a,b){a._bInitComplete=!0;b&&X(a);w(a,"aoInitComplete","init",[a,b])}function Ra(a,b){var c=parseInt(b,10);a._iDisplayLength=c;Sa(a);w(a,null,"length",[a,c])}function nb(a){for(var b=a.oClasses,c=a.sTableId,e=a.aLengthMenu,d=h.isArray(e[0]),f=d?e[0]:e,e=d?e[1]:e,d=h("<select/>",{name:c+"_length","aria-controls":c,"class":b.sLengthSelect}),g=0,j=f.length;g<j;g++)d[0][g]=new Option(e[g],
f[g]);var i=h("<div><label/></div>").addClass(b.sLength);a.aanFeatures.l||(i[0].id=c+"_length");i.children().append(a.oLanguage.sLengthMenu.replace("_MENU_",d[0].outerHTML));h("select",i).val(a._iDisplayLength).bind("change.DT",function(){Ra(a,h(this).val());M(a)});h(a.nTable).bind("length.dt.DT",function(b,c,f){a===c&&h("select",i).val(f)});return i[0]}function sb(a){var b=a.sPaginationType,c=m.ext.pager[b],e="function"===typeof c,d=function(a){M(a)},b=h("<div/>").addClass(a.oClasses.sPaging+b)[0],
f=a.aanFeatures;e||c.fnInit(a,b,d);f.p||(b.id=a.sTableId+"_paginate",a.aoDrawCallback.push({fn:function(a){if(e){var b=a._iDisplayStart,i=a._iDisplayLength,h=a.fnRecordsDisplay(),l=-1===i,b=l?0:Math.ceil(b/i),i=l?1:Math.ceil(h/i),h=c(b,i),q,l=0;for(q=f.p.length;l<q;l++)Pa(a,"pageButton")(a,f.p[l],l,h,b,i)}else c.fnUpdate(a,d)},sName:"pagination"}));return b}function Ta(a,b,c){var e=a._iDisplayStart,d=a._iDisplayLength,f=a.fnRecordsDisplay();0===f||-1===d?e=0:"number"===typeof b?(e=b*d,e>f&&(e=0)):
"first"==b?e=0:"previous"==b?(e=0<=d?e-d:0,0>e&&(e=0)):"next"==b?e+d<f&&(e+=d):"last"==b?e=Math.floor((f-1)/d)*d:I(a,0,"Unknown paging action: "+b,5);b=a._iDisplayStart!==e;a._iDisplayStart=e;b&&(w(a,null,"page",[a]),c&&M(a));return b}function pb(a){return h("<div/>",{id:!a.aanFeatures.r?a.sTableId+"_processing":null,"class":a.oClasses.sProcessing}).html(a.oLanguage.sProcessing).insertBefore(a.nTable)[0]}function C(a,b){a.oFeatures.bProcessing&&h(a.aanFeatures.r).css("display",b?"block":"none");w(a,
null,"processing",[a,b])}function qb(a){var b=h(a.nTable);b.attr("role","grid");var c=a.oScroll;if(""===c.sX&&""===c.sY)return a.nTable;var e=c.sX,d=c.sY,f=a.oClasses,g=b.children("caption"),j=g.length?g[0]._captionSide:null,i=h(b[0].cloneNode(!1)),o=h(b[0].cloneNode(!1)),l=b.children("tfoot");c.sX&&"100%"===b.attr("width")&&b.removeAttr("width");l.length||(l=null);c=h("<div/>",{"class":f.sScrollWrapper}).append(h("<div/>",{"class":f.sScrollHead}).css({overflow:"hidden",position:"relative",border:0,
width:e?!e?null:s(e):"100%"}).append(h("<div/>",{"class":f.sScrollHeadInner}).css({"box-sizing":"content-box",width:c.sXInner||"100%"}).append(i.removeAttr("id").css("margin-left",0).append("top"===j?g:null).append(b.children("thead"))))).append(h("<div/>",{"class":f.sScrollBody}).css({overflow:"auto",height:!d?null:s(d),width:!e?null:s(e)}).append(b));l&&c.append(h("<div/>",{"class":f.sScrollFoot}).css({overflow:"hidden",border:0,width:e?!e?null:s(e):"100%"}).append(h("<div/>",{"class":f.sScrollFootInner}).append(o.removeAttr("id").css("margin-left",
0).append("bottom"===j?g:null).append(b.children("tfoot")))));var b=c.children(),q=b[0],f=b[1],n=l?b[2]:null;if(e)h(f).on("scroll.DT",function(){var a=this.scrollLeft;q.scrollLeft=a;l&&(n.scrollLeft=a)});a.nScrollHead=q;a.nScrollBody=f;a.nScrollFoot=n;a.aoDrawCallback.push({fn:Y,sName:"scrolling"});return c[0]}function Y(a){var b=a.oScroll,c=b.sX,e=b.sXInner,d=b.sY,f=b.iBarWidth,g=h(a.nScrollHead),j=g[0].style,i=g.children("div"),o=i[0].style,l=i.children("table"),i=a.nScrollBody,q=h(i),n=i.style,
k=h(a.nScrollFoot).children("div"),p=k.children("table"),m=h(a.nTHead),r=h(a.nTable),t=r[0],O=t.style,L=a.nTFoot?h(a.nTFoot):null,ha=a.oBrowser,w=ha.bScrollOversize,v,u,y,x,z,A=[],B=[],C=[],D,E=function(a){a=a.style;a.paddingTop="0";a.paddingBottom="0";a.borderTopWidth="0";a.borderBottomWidth="0";a.height=0};r.children("thead, tfoot").remove();z=m.clone().prependTo(r);v=m.find("tr");y=z.find("tr");z.find("th, td").removeAttr("tabindex");L&&(x=L.clone().prependTo(r),u=L.find("tr"),x=x.find("tr"));
c||(n.width="100%",g[0].style.width="100%");h.each(qa(a,z),function(b,c){D=la(a,b);c.style.width=a.aoColumns[D].sWidth});L&&G(function(a){a.style.width=""},x);b.bCollapse&&""!==d&&(n.height=q[0].offsetHeight+m[0].offsetHeight+"px");g=r.outerWidth();if(""===c){if(O.width="100%",w&&(r.find("tbody").height()>i.offsetHeight||"scroll"==q.css("overflow-y")))O.width=s(r.outerWidth()-f)}else""!==e?O.width=s(e):g==q.width()&&q.height()<r.height()?(O.width=s(g-f),r.outerWidth()>g-f&&(O.width=s(g))):O.width=
s(g);g=r.outerWidth();G(E,y);G(function(a){C.push(a.innerHTML);A.push(s(h(a).css("width")))},y);G(function(a,b){a.style.width=A[b]},v);h(y).height(0);L&&(G(E,x),G(function(a){B.push(s(h(a).css("width")))},x),G(function(a,b){a.style.width=B[b]},u),h(x).height(0));G(function(a,b){a.innerHTML='<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+C[b]+"</div>";a.style.width=A[b]},y);L&&G(function(a,b){a.innerHTML="";a.style.width=B[b]},x);if(r.outerWidth()<g){u=i.scrollHeight>i.offsetHeight||
"scroll"==q.css("overflow-y")?g+f:g;if(w&&(i.scrollHeight>i.offsetHeight||"scroll"==q.css("overflow-y")))O.width=s(u-f);(""===c||""!==e)&&I(a,1,"Possible column misalignment",6)}else u="100%";n.width=s(u);j.width=s(u);L&&(a.nScrollFoot.style.width=s(u));!d&&w&&(n.height=s(t.offsetHeight+f));d&&b.bCollapse&&(n.height=s(d),b=c&&t.offsetWidth>i.offsetWidth?f:0,t.offsetHeight<i.offsetHeight&&(n.height=s(t.offsetHeight+b)));b=r.outerWidth();l[0].style.width=s(b);o.width=s(b);l=r.height()>i.clientHeight||
"scroll"==q.css("overflow-y");ha="padding"+(ha.bScrollbarLeft?"Left":"Right");o[ha]=l?f+"px":"0px";L&&(p[0].style.width=s(b),k[0].style.width=s(b),k[0].style[ha]=l?f+"px":"0px");q.scroll();if((a.bSorted||a.bFiltered)&&!a._drawHold)i.scrollTop=0}function G(a,b,c){for(var e=0,d=0,f=b.length,g,j;d<f;){g=b[d].firstChild;for(j=c?c[d].firstChild:null;g;)1===g.nodeType&&(c?a(g,j,e):a(g,e),e++),g=g.nextSibling,j=c?j.nextSibling:null;d++}}function Ga(a){var b=a.nTable,c=a.aoColumns,e=a.oScroll,d=e.sY,f=e.sX,
g=e.sXInner,j=c.length,e=Z(a,"bVisible"),i=h("th",a.nTHead),o=b.getAttribute("width"),l=b.parentNode,k=!1,n,m;(n=b.style.width)&&-1!==n.indexOf("%")&&(o=n);for(n=0;n<e.length;n++)m=c[e[n]],null!==m.sWidth&&(m.sWidth=Db(m.sWidthOrig,l),k=!0);if(!k&&!f&&!d&&j==aa(a)&&j==i.length)for(n=0;n<j;n++)c[n].sWidth=s(i.eq(n).width());else{j=h(b).clone().css("visibility","hidden").removeAttr("id");j.find("tbody tr").remove();var p=h("<tr/>").appendTo(j.find("tbody"));j.find("tfoot th, tfoot td").css("width",
"");i=qa(a,j.find("thead")[0]);for(n=0;n<e.length;n++)m=c[e[n]],i[n].style.width=null!==m.sWidthOrig&&""!==m.sWidthOrig?s(m.sWidthOrig):"";if(a.aoData.length)for(n=0;n<e.length;n++)k=e[n],m=c[k],h(Eb(a,k)).clone(!1).append(m.sContentPadding).appendTo(p);j.appendTo(l);f&&g?j.width(g):f?(j.css("width","auto"),j.width()<l.offsetWidth&&j.width(l.offsetWidth)):d?j.width(l.offsetWidth):o&&j.width(o);Fb(a,j[0]);if(f){for(n=g=0;n<e.length;n++)m=c[e[n]],d=h(i[n]).outerWidth(),g+=null===m.sWidthOrig?d:parseInt(m.sWidth,
10)+d-h(i[n]).width();j.width(s(g));b.style.width=s(g)}for(n=0;n<e.length;n++)if(m=c[e[n]],d=h(i[n]).width())m.sWidth=s(d);b.style.width=s(j.css("width"));j.remove()}o&&(b.style.width=s(o));if((o||f)&&!a._reszEvt)b=function(){h(Ea).bind("resize.DT-"+a.sInstance,ua(function(){X(a)}))},a.oBrowser.bScrollOversize?setTimeout(b,1E3):b(),a._reszEvt=!0}function ua(a,b){var c=b!==k?b:200,e,d;return function(){var b=this,g=+new Date,j=arguments;e&&g<e+c?(clearTimeout(d),d=setTimeout(function(){e=k;a.apply(b,
j)},c)):(e=g,a.apply(b,j))}}function Db(a,b){if(!a)return 0;var c=h("<div/>").css("width",s(a)).appendTo(b||Q.body),e=c[0].offsetWidth;c.remove();return e}function Fb(a,b){var c=a.oScroll;if(c.sX||c.sY)c=!c.sX?c.iBarWidth:0,b.style.width=s(h(b).outerWidth()-c)}function Eb(a,b){var c=Gb(a,b);if(0>c)return null;var e=a.aoData[c];return!e.nTr?h("<td/>").html(x(a,c,b,"display"))[0]:e.anCells[b]}function Gb(a,b){for(var c,e=-1,d=-1,f=0,g=a.aoData.length;f<g;f++)c=x(a,f,b,"display")+"",c=c.replace($b,""),
c.length>e&&(e=c.length,d=f);return d}function s(a){return null===a?"0px":"number"==typeof a?0>a?"0px":a+"px":a.match(/\d$/)?a+"px":a}function Hb(){var a=m.__scrollbarWidth;if(a===k){var b=h("<p/>").css({position:"absolute",top:0,left:0,width:"100%",height:150,padding:0,overflow:"scroll",visibility:"hidden"}).appendTo("body"),a=b[0].offsetWidth-b[0].clientWidth;m.__scrollbarWidth=a;b.remove()}return a}function U(a){var b,c,e=[],d=a.aoColumns,f,g,j,i;b=a.aaSortingFixed;c=h.isPlainObject(b);var o=[];
f=function(a){a.length&&!h.isArray(a[0])?o.push(a):o.push.apply(o,a)};h.isArray(b)&&f(b);c&&b.pre&&f(b.pre);f(a.aaSorting);c&&b.post&&f(b.post);for(a=0;a<o.length;a++){i=o[a][0];f=d[i].aDataSort;b=0;for(c=f.length;b<c;b++)g=f[b],j=d[g].sType||"string",o[a]._idx===k&&(o[a]._idx=h.inArray(o[a][1],d[g].asSorting)),e.push({src:i,col:g,dir:o[a][1],index:o[a]._idx,type:j,formatter:m.ext.type.order[j+"-pre"]})}return e}function lb(a){var b,c,e=[],d=m.ext.type.order,f=a.aoData,g=0,j,i=a.aiDisplayMaster,h;
Ha(a);h=U(a);b=0;for(c=h.length;b<c;b++)j=h[b],j.formatter&&g++,Ib(a,j.col);if("ssp"!=B(a)&&0!==h.length){b=0;for(c=i.length;b<c;b++)e[i[b]]=b;g===h.length?i.sort(function(a,b){var c,d,g,j,i=h.length,k=f[a]._aSortData,m=f[b]._aSortData;for(g=0;g<i;g++)if(j=h[g],c=k[j.col],d=m[j.col],c=c<d?-1:c>d?1:0,0!==c)return"asc"===j.dir?c:-c;c=e[a];d=e[b];return c<d?-1:c>d?1:0}):i.sort(function(a,b){var c,g,j,i,k=h.length,m=f[a]._aSortData,r=f[b]._aSortData;for(j=0;j<k;j++)if(i=h[j],c=m[i.col],g=r[i.col],i=d[i.type+
"-"+i.dir]||d["string-"+i.dir],c=i(c,g),0!==c)return c;c=e[a];g=e[b];return c<g?-1:c>g?1:0})}a.bSorted=!0}function Jb(a){for(var b,c,e=a.aoColumns,d=U(a),a=a.oLanguage.oAria,f=0,g=e.length;f<g;f++){c=e[f];var j=c.asSorting;b=c.sTitle.replace(/<.*?>/g,"");var i=c.nTh;i.removeAttribute("aria-sort");c.bSortable&&(0<d.length&&d[0].col==f?(i.setAttribute("aria-sort","asc"==d[0].dir?"ascending":"descending"),c=j[d[0].index+1]||j[0]):c=j[0],b+="asc"===c?a.sSortAscending:a.sSortDescending);i.setAttribute("aria-label",
b)}}function Ua(a,b,c,e){var d=a.aaSorting,f=a.aoColumns[b].asSorting,g=function(a,b){var c=a._idx;c===k&&(c=h.inArray(a[1],f));return c+1<f.length?c+1:b?null:0};"number"===typeof d[0]&&(d=a.aaSorting=[d]);c&&a.oFeatures.bSortMulti?(c=h.inArray(b,D(d,"0")),-1!==c?(b=g(d[c],!0),null===b&&1===d.length&&(b=0),null===b?d.splice(c,1):(d[c][1]=f[b],d[c]._idx=b)):(d.push([b,f[0],0]),d[d.length-1]._idx=0)):d.length&&d[0][0]==b?(b=g(d[0]),d.length=1,d[0][1]=f[b],d[0]._idx=b):(d.length=0,d.push([b,f[0]]),d[0]._idx=
0);N(a);"function"==typeof e&&e(a)}function Oa(a,b,c,e){var d=a.aoColumns[c];Va(b,{},function(b){!1!==d.bSortable&&(a.oFeatures.bProcessing?(C(a,!0),setTimeout(function(){Ua(a,c,b.shiftKey,e);"ssp"!==B(a)&&C(a,!1)},0)):Ua(a,c,b.shiftKey,e))})}function xa(a){var b=a.aLastSort,c=a.oClasses.sSortColumn,e=U(a),d=a.oFeatures,f,g;if(d.bSort&&d.bSortClasses){d=0;for(f=b.length;d<f;d++)g=b[d].src,h(D(a.aoData,"anCells",g)).removeClass(c+(2>d?d+1:3));d=0;for(f=e.length;d<f;d++)g=e[d].src,h(D(a.aoData,"anCells",
g)).addClass(c+(2>d?d+1:3))}a.aLastSort=e}function Ib(a,b){var c=a.aoColumns[b],e=m.ext.order[c.sSortDataType],d;e&&(d=e.call(a.oInstance,a,b,$(a,b)));for(var f,g=m.ext.type.order[c.sType+"-pre"],j=0,i=a.aoData.length;j<i;j++)if(c=a.aoData[j],c._aSortData||(c._aSortData=[]),!c._aSortData[b]||e)f=e?d[j]:x(a,j,b,"sort"),c._aSortData[b]=g?g(f):f}function ya(a){if(a.oFeatures.bStateSave&&!a.bDestroying){var b={time:+new Date,start:a._iDisplayStart,length:a._iDisplayLength,order:h.extend(!0,[],a.aaSorting),
search:zb(a.oPreviousSearch),columns:h.map(a.aoColumns,function(b,e){return{visible:b.bVisible,search:zb(a.aoPreSearchCols[e])}})};w(a,"aoStateSaveParams","stateSaveParams",[a,b]);a.oSavedState=b;a.fnStateSaveCallback.call(a.oInstance,a,b)}}function Kb(a){var b,c,e=a.aoColumns;if(a.oFeatures.bStateSave){var d=a.fnStateLoadCallback.call(a.oInstance,a);if(d&&d.time&&(b=w(a,"aoStateLoadParams","stateLoadParams",[a,d]),-1===h.inArray(!1,b)&&(b=a.iStateDuration,!(0<b&&d.time<+new Date-1E3*b)&&e.length===
d.columns.length))){a.oLoadedState=h.extend(!0,{},d);d.start!==k&&(a._iDisplayStart=d.start,a.iInitDisplayStart=d.start);d.length!==k&&(a._iDisplayLength=d.length);d.order!==k&&(a.aaSorting=[],h.each(d.order,function(b,c){a.aaSorting.push(c[0]>=e.length?[0,c[1]]:c)}));d.search!==k&&h.extend(a.oPreviousSearch,Ab(d.search));b=0;for(c=d.columns.length;b<c;b++){var f=d.columns[b];f.visible!==k&&(e[b].bVisible=f.visible);f.search!==k&&h.extend(a.aoPreSearchCols[b],Ab(f.search))}w(a,"aoStateLoaded","stateLoaded",
[a,d])}}}function za(a){var b=m.settings,a=h.inArray(a,D(b,"nTable"));return-1!==a?b[a]:null}function I(a,b,c,e){c="DataTables warning: "+(null!==a?"table id="+a.sTableId+" - ":"")+c;e&&(c+=". For more information about this error, please see http://datatables.net/tn/"+e);if(b)Ea.console&&console.log&&console.log(c);else if(b=m.ext,b=b.sErrMode||b.errMode,w(a,null,"error",[a,e,c]),"alert"==b)alert(c);else{if("throw"==b)throw Error(c);"function"==typeof b&&b(a,e,c)}}function E(a,b,c,e){h.isArray(c)?
h.each(c,function(c,f){h.isArray(f)?E(a,b,f[0],f[1]):E(a,b,f)}):(e===k&&(e=c),b[c]!==k&&(a[e]=b[c]))}function Lb(a,b,c){var e,d;for(d in b)b.hasOwnProperty(d)&&(e=b[d],h.isPlainObject(e)?(h.isPlainObject(a[d])||(a[d]={}),h.extend(!0,a[d],e)):a[d]=c&&"data"!==d&&"aaData"!==d&&h.isArray(e)?e.slice():e);return a}function Va(a,b,c){h(a).bind("click.DT",b,function(b){a.blur();c(b)}).bind("keypress.DT",b,function(a){13===a.which&&(a.preventDefault(),c(a))}).bind("selectstart.DT",function(){return!1})}function z(a,
b,c,e){c&&a[b].push({fn:c,sName:e})}function w(a,b,c,e){var d=[];b&&(d=h.map(a[b].slice().reverse(),function(b){return b.fn.apply(a.oInstance,e)}));null!==c&&(b=h.Event(c+".dt"),h(a.nTable).trigger(b,e),d.push(b.result));return d}function Sa(a){var b=a._iDisplayStart,c=a.fnDisplayEnd(),e=a._iDisplayLength;b>=c&&(b=c-e);b-=b%e;if(-1===e||0>b)b=0;a._iDisplayStart=b}function Pa(a,b){var c=a.renderer,e=m.ext.renderer[b];return h.isPlainObject(c)&&c[b]?e[c[b]]||e._:"string"===typeof c?e[c]||e._:e._}function B(a){return a.oFeatures.bServerSide?
"ssp":a.ajax||a.sAjaxSource?"ajax":"dom"}function Wa(a,b){var c=[],c=Mb.numbers_length,e=Math.floor(c/2);b<=c?c=V(0,b):a<=e?(c=V(0,c-2),c.push("ellipsis"),c.push(b-1)):(a>=b-1-e?c=V(b-(c-2),b):(c=V(a-e+2,a+e-1),c.push("ellipsis"),c.push(b-1)),c.splice(0,0,"ellipsis"),c.splice(0,0,0));c.DT_el="span";return c}function db(a){h.each({num:function(b){return Aa(b,a)},"num-fmt":function(b){return Aa(b,a,Xa)},"html-num":function(b){return Aa(b,a,Ba)},"html-num-fmt":function(b){return Aa(b,a,Ba,Xa)}},function(b,
c){u.type.order[b+a+"-pre"]=c;b.match(/^html\-/)&&(u.type.search[b+a]=u.type.search.html)})}function Nb(a){return function(){var b=[za(this[m.ext.iApiIndex])].concat(Array.prototype.slice.call(arguments));return m.ext.internal[a].apply(this,b)}}var m,u,t,r,v,Ya={},Ob=/[\r\n]/g,Ba=/<.*?>/g,ac=/^[\w\+\-]/,bc=/[\w\+\-]$/,Yb=RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\|\\$|\\^|\\-)","g"),Xa=/[',$\u00a3\u20ac\u00a5%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi,J=function(a){return!a||!0===a||
"-"===a?!0:!1},Pb=function(a){var b=parseInt(a,10);return!isNaN(b)&&isFinite(a)?b:null},Qb=function(a,b){Ya[b]||(Ya[b]=RegExp(va(b),"g"));return"string"===typeof a&&"."!==b?a.replace(/\./g,"").replace(Ya[b],"."):a},Za=function(a,b,c){var e="string"===typeof a;if(J(a))return!0;b&&e&&(a=Qb(a,b));c&&e&&(a=a.replace(Xa,""));return!isNaN(parseFloat(a))&&isFinite(a)},Rb=function(a,b,c){return J(a)?!0:!(J(a)||"string"===typeof a)?null:Za(a.replace(Ba,""),b,c)?!0:null},D=function(a,b,c){var e=[],d=0,f=a.length;
if(c!==k)for(;d<f;d++)a[d]&&a[d][b]&&e.push(a[d][b][c]);else for(;d<f;d++)a[d]&&e.push(a[d][b]);return e},ia=function(a,b,c,e){var d=[],f=0,g=b.length;if(e!==k)for(;f<g;f++)a[b[f]][c]&&d.push(a[b[f]][c][e]);else for(;f<g;f++)d.push(a[b[f]][c]);return d},V=function(a,b){var c=[],e;b===k?(b=0,e=a):(e=b,b=a);for(var d=b;d<e;d++)c.push(d);return c},Sb=function(a){for(var b=[],c=0,e=a.length;c<e;c++)a[c]&&b.push(a[c]);return b},Na=function(a){var b=[],c,e,d=a.length,f,g=0;e=0;a:for(;e<d;e++){c=a[e];for(f=
0;f<g;f++)if(b[f]===c)continue a;b.push(c);g++}return b},A=function(a,b,c){a[b]!==k&&(a[c]=a[b])},ba=/\[.*?\]$/,T=/\(\)$/,wa=h("<div>")[0],Zb=wa.textContent!==k,$b=/<.*?>/g;m=function(a){this.$=function(a,b){return this.api(!0).$(a,b)};this._=function(a,b){return this.api(!0).rows(a,b).data()};this.api=function(a){return a?new t(za(this[u.iApiIndex])):new t(this)};this.fnAddData=function(a,b){var c=this.api(!0),e=h.isArray(a)&&(h.isArray(a[0])||h.isPlainObject(a[0]))?c.rows.add(a):c.row.add(a);(b===
k||b)&&c.draw();return e.flatten().toArray()};this.fnAdjustColumnSizing=function(a){var b=this.api(!0).columns.adjust(),c=b.settings()[0],e=c.oScroll;a===k||a?b.draw(!1):(""!==e.sX||""!==e.sY)&&Y(c)};this.fnClearTable=function(a){var b=this.api(!0).clear();(a===k||a)&&b.draw()};this.fnClose=function(a){this.api(!0).row(a).child.hide()};this.fnDeleteRow=function(a,b,c){var e=this.api(!0),a=e.rows(a),d=a.settings()[0],h=d.aoData[a[0][0]];a.remove();b&&b.call(this,d,h);(c===k||c)&&e.draw();return h};
this.fnDestroy=function(a){this.api(!0).destroy(a)};this.fnDraw=function(a){this.api(!0).draw(a)};this.fnFilter=function(a,b,c,e,d,h){d=this.api(!0);null===b||b===k?d.search(a,c,e,h):d.column(b).search(a,c,e,h);d.draw()};this.fnGetData=function(a,b){var c=this.api(!0);if(a!==k){var e=a.nodeName?a.nodeName.toLowerCase():"";return b!==k||"td"==e||"th"==e?c.cell(a,b).data():c.row(a).data()||null}return c.data().toArray()};this.fnGetNodes=function(a){var b=this.api(!0);return a!==k?b.row(a).node():b.rows().nodes().flatten().toArray()};
this.fnGetPosition=function(a){var b=this.api(!0),c=a.nodeName.toUpperCase();return"TR"==c?b.row(a).index():"TD"==c||"TH"==c?(a=b.cell(a).index(),[a.row,a.columnVisible,a.column]):null};this.fnIsOpen=function(a){return this.api(!0).row(a).child.isShown()};this.fnOpen=function(a,b,c){return this.api(!0).row(a).child(b,c).show().child()[0]};this.fnPageChange=function(a,b){var c=this.api(!0).page(a);(b===k||b)&&c.draw(!1)};this.fnSetColumnVis=function(a,b,c){a=this.api(!0).column(a).visible(b);(c===
k||c)&&a.columns.adjust().draw()};this.fnSettings=function(){return za(this[u.iApiIndex])};this.fnSort=function(a){this.api(!0).order(a).draw()};this.fnSortListener=function(a,b,c){this.api(!0).order.listener(a,b,c)};this.fnUpdate=function(a,b,c,e,d){var h=this.api(!0);c===k||null===c?h.row(b).data(a):h.cell(b,c).data(a);(d===k||d)&&h.columns.adjust();(e===k||e)&&h.draw();return 0};this.fnVersionCheck=u.fnVersionCheck;var b=this,c=a===k,e=this.length;c&&(a={});this.oApi=this.internal=u.internal;for(var d in m.ext.internal)d&&
(this[d]=Nb(d));this.each(function(){var d={},d=1<e?Lb(d,a,!0):a,g=0,j,i=this.getAttribute("id"),o=!1,l=m.defaults,q=h(this);if("table"!=this.nodeName.toLowerCase())I(null,0,"Non-table node initialisation ("+this.nodeName+")",2);else{eb(l);fb(l.column);H(l,l,!0);H(l.column,l.column,!0);H(l,h.extend(d,q.data()));var n=m.settings,g=0;for(j=n.length;g<j;g++){var r=n[g];if(r.nTable==this||r.nTHead.parentNode==this||r.nTFoot&&r.nTFoot.parentNode==this){g=d.bRetrieve!==k?d.bRetrieve:l.bRetrieve;if(c||g)return r.oInstance;
if(d.bDestroy!==k?d.bDestroy:l.bDestroy){r.oInstance.fnDestroy();break}else{I(r,0,"Cannot reinitialise DataTable",3);return}}if(r.sTableId==this.id){n.splice(g,1);break}}if(null===i||""===i)this.id=i="DataTables_Table_"+m.ext._unique++;var p=h.extend(!0,{},m.models.oSettings,{sDestroyWidth:q[0].style.width,sInstance:i,sTableId:i});p.nTable=this;p.oApi=b.internal;p.oInit=d;n.push(p);p.oInstance=1===b.length?b:q.dataTable();eb(d);d.oLanguage&&P(d.oLanguage);d.aLengthMenu&&!d.iDisplayLength&&(d.iDisplayLength=
h.isArray(d.aLengthMenu[0])?d.aLengthMenu[0][0]:d.aLengthMenu[0]);d=Lb(h.extend(!0,{},l),d);E(p.oFeatures,d,"bPaginate bLengthChange bFilter bSort bSortMulti bInfo bProcessing bAutoWidth bSortClasses bServerSide bDeferRender".split(" "));E(p,d,["asStripeClasses","ajax","fnServerData","fnFormatNumber","sServerMethod","aaSorting","aaSortingFixed","aLengthMenu","sPaginationType","sAjaxSource","sAjaxDataProp","iStateDuration","sDom","bSortCellsTop","iTabIndex","fnStateLoadCallback","fnStateSaveCallback",
"renderer","searchDelay",["iCookieDuration","iStateDuration"],["oSearch","oPreviousSearch"],["aoSearchCols","aoPreSearchCols"],["iDisplayLength","_iDisplayLength"],["bJQueryUI","bJUI"]]);E(p.oScroll,d,[["sScrollX","sX"],["sScrollXInner","sXInner"],["sScrollY","sY"],["bScrollCollapse","bCollapse"]]);E(p.oLanguage,d,"fnInfoCallback");z(p,"aoDrawCallback",d.fnDrawCallback,"user");z(p,"aoServerParams",d.fnServerParams,"user");z(p,"aoStateSaveParams",d.fnStateSaveParams,"user");z(p,"aoStateLoadParams",
d.fnStateLoadParams,"user");z(p,"aoStateLoaded",d.fnStateLoaded,"user");z(p,"aoRowCallback",d.fnRowCallback,"user");z(p,"aoRowCreatedCallback",d.fnCreatedRow,"user");z(p,"aoHeaderCallback",d.fnHeaderCallback,"user");z(p,"aoFooterCallback",d.fnFooterCallback,"user");z(p,"aoInitComplete",d.fnInitComplete,"user");z(p,"aoPreDrawCallback",d.fnPreDrawCallback,"user");i=p.oClasses;d.bJQueryUI?(h.extend(i,m.ext.oJUIClasses,d.oClasses),d.sDom===l.sDom&&"lfrtip"===l.sDom&&(p.sDom='<"H"lfr>t<"F"ip>'),p.renderer)?
h.isPlainObject(p.renderer)&&!p.renderer.header&&(p.renderer.header="jqueryui"):p.renderer="jqueryui":h.extend(i,m.ext.classes,d.oClasses);q.addClass(i.sTable);if(""!==p.oScroll.sX||""!==p.oScroll.sY)p.oScroll.iBarWidth=Hb();!0===p.oScroll.sX&&(p.oScroll.sX="100%");p.iInitDisplayStart===k&&(p.iInitDisplayStart=d.iDisplayStart,p._iDisplayStart=d.iDisplayStart);null!==d.iDeferLoading&&(p.bDeferLoading=!0,g=h.isArray(d.iDeferLoading),p._iRecordsDisplay=g?d.iDeferLoading[0]:d.iDeferLoading,p._iRecordsTotal=
g?d.iDeferLoading[1]:d.iDeferLoading);var t=p.oLanguage;h.extend(!0,t,d.oLanguage);""!==t.sUrl&&(h.ajax({dataType:"json",url:t.sUrl,success:function(a){P(a);H(l.oLanguage,a);h.extend(true,t,a);ga(p)},error:function(){ga(p)}}),o=!0);null===d.asStripeClasses&&(p.asStripeClasses=[i.sStripeOdd,i.sStripeEven]);var g=p.asStripeClasses,s=q.children("tbody").find("tr").eq(0);-1!==h.inArray(!0,h.map(g,function(a){return s.hasClass(a)}))&&(h("tbody tr",this).removeClass(g.join(" ")),p.asDestroyStripes=g.slice());
n=[];g=this.getElementsByTagName("thead");0!==g.length&&(da(p.aoHeader,g[0]),n=qa(p));if(null===d.aoColumns){r=[];g=0;for(j=n.length;g<j;g++)r.push(null)}else r=d.aoColumns;g=0;for(j=r.length;g<j;g++)Fa(p,n?n[g]:null);ib(p,d.aoColumnDefs,r,function(a,b){ka(p,a,b)});if(s.length){var u=function(a,b){return a.getAttribute("data-"+b)!==null?b:null};h.each(na(p,s[0]).cells,function(a,b){var c=p.aoColumns[a];if(c.mData===a){var d=u(b,"sort")||u(b,"order"),e=u(b,"filter")||u(b,"search");if(d!==null||e!==
null){c.mData={_:a+".display",sort:d!==null?a+".@data-"+d:k,type:d!==null?a+".@data-"+d:k,filter:e!==null?a+".@data-"+e:k};ka(p,a)}}})}var v=p.oFeatures;d.bStateSave&&(v.bStateSave=!0,Kb(p,d),z(p,"aoDrawCallback",ya,"state_save"));if(d.aaSorting===k){n=p.aaSorting;g=0;for(j=n.length;g<j;g++)n[g][1]=p.aoColumns[g].asSorting[0]}xa(p);v.bSort&&z(p,"aoDrawCallback",function(){if(p.bSorted){var a=U(p),b={};h.each(a,function(a,c){b[c.src]=c.dir});w(p,null,"order",[p,a,b]);Jb(p)}});z(p,"aoDrawCallback",
function(){(p.bSorted||B(p)==="ssp"||v.bDeferRender)&&xa(p)},"sc");gb(p);g=q.children("caption").each(function(){this._captionSide=q.css("caption-side")});j=q.children("thead");0===j.length&&(j=h("<thead/>").appendTo(this));p.nTHead=j[0];j=q.children("tbody");0===j.length&&(j=h("<tbody/>").appendTo(this));p.nTBody=j[0];j=q.children("tfoot");if(0===j.length&&0<g.length&&(""!==p.oScroll.sX||""!==p.oScroll.sY))j=h("<tfoot/>").appendTo(this);0===j.length||0===j.children().length?q.addClass(i.sNoFooter):
0<j.length&&(p.nTFoot=j[0],da(p.aoFooter,p.nTFoot));if(d.aaData)for(g=0;g<d.aaData.length;g++)K(p,d.aaData[g]);else(p.bDeferLoading||"dom"==B(p))&&ma(p,h(p.nTBody).children("tr"));p.aiDisplay=p.aiDisplayMaster.slice();p.bInitialised=!0;!1===o&&ga(p)}});b=null;return this};var Tb=[],y=Array.prototype,cc=function(a){var b,c,e=m.settings,d=h.map(e,function(a){return a.nTable});if(a){if(a.nTable&&a.oApi)return[a];if(a.nodeName&&"table"===a.nodeName.toLowerCase())return b=h.inArray(a,d),-1!==b?[e[b]]:
null;if(a&&"function"===typeof a.settings)return a.settings().toArray();"string"===typeof a?c=h(a):a instanceof h&&(c=a)}else return[];if(c)return c.map(function(){b=h.inArray(this,d);return-1!==b?e[b]:null}).toArray()};t=function(a,b){if(!(this instanceof t))return new t(a,b);var c=[],e=function(a){(a=cc(a))&&c.push.apply(c,a)};if(h.isArray(a))for(var d=0,f=a.length;d<f;d++)e(a[d]);else e(a);this.context=Na(c);b&&this.push.apply(this,b.toArray?b.toArray():b);this.selector={rows:null,cols:null,opts:null};
t.extend(this,this,Tb)};m.Api=t;t.prototype={any:function(){return 0!==this.flatten().length},concat:y.concat,context:[],each:function(a){for(var b=0,c=this.length;b<c;b++)a.call(this,this[b],b,this);return this},eq:function(a){var b=this.context;return b.length>a?new t(b[a],this[a]):null},filter:function(a){var b=[];if(y.filter)b=y.filter.call(this,a,this);else for(var c=0,e=this.length;c<e;c++)a.call(this,this[c],c,this)&&b.push(this[c]);return new t(this.context,b)},flatten:function(){var a=[];
return new t(this.context,a.concat.apply(a,this.toArray()))},join:y.join,indexOf:y.indexOf||function(a,b){for(var c=b||0,e=this.length;c<e;c++)if(this[c]===a)return c;return-1},iterator:function(a,b,c,e){var d=[],f,g,h,i,o,l=this.context,q,n,m=this.selector;"string"===typeof a&&(e=c,c=b,b=a,a=!1);g=0;for(h=l.length;g<h;g++){var p=new t(l[g]);if("table"===b)f=c.call(p,l[g],g),f!==k&&d.push(f);else if("columns"===b||"rows"===b)f=c.call(p,l[g],this[g],g),f!==k&&d.push(f);else if("column"===b||"column-rows"===
b||"row"===b||"cell"===b){n=this[g];"column-rows"===b&&(q=Ca(l[g],m.opts));i=0;for(o=n.length;i<o;i++)f=n[i],f="cell"===b?c.call(p,l[g],f.row,f.column,g,i):c.call(p,l[g],f,g,i,q),f!==k&&d.push(f)}}return d.length||e?(a=new t(l,a?d.concat.apply([],d):d),b=a.selector,b.rows=m.rows,b.cols=m.cols,b.opts=m.opts,a):this},lastIndexOf:y.lastIndexOf||function(a,b){return this.indexOf.apply(this.toArray.reverse(),arguments)},length:0,map:function(a){var b=[];if(y.map)b=y.map.call(this,a,this);else for(var c=
0,e=this.length;c<e;c++)b.push(a.call(this,this[c],c));return new t(this.context,b)},pluck:function(a){return this.map(function(b){return b[a]})},pop:y.pop,push:y.push,reduce:y.reduce||function(a,b){return hb(this,a,b,0,this.length,1)},reduceRight:y.reduceRight||function(a,b){return hb(this,a,b,this.length-1,-1,-1)},reverse:y.reverse,selector:null,shift:y.shift,sort:y.sort,splice:y.splice,toArray:function(){return y.slice.call(this)},to$:function(){return h(this)},toJQuery:function(){return h(this)},
unique:function(){return new t(this.context,Na(this))},unshift:y.unshift};t.extend=function(a,b,c){if(c.length&&b&&(b instanceof t||b.__dt_wrapper)){var e,d,f,g=function(a,b,c){return function(){var d=b.apply(a,arguments);t.extend(d,d,c.methodExt);return d}};e=0;for(d=c.length;e<d;e++)f=c[e],b[f.name]="function"===typeof f.val?g(a,f.val,f):h.isPlainObject(f.val)?{}:f.val,b[f.name].__dt_wrapper=!0,t.extend(a,b[f.name],f.propExt)}};t.register=r=function(a,b){if(h.isArray(a))for(var c=0,e=a.length;c<
e;c++)t.register(a[c],b);else for(var d=a.split("."),f=Tb,g,j,c=0,e=d.length;c<e;c++){g=(j=-1!==d[c].indexOf("()"))?d[c].replace("()",""):d[c];var i;a:{i=0;for(var o=f.length;i<o;i++)if(f[i].name===g){i=f[i];break a}i=null}i||(i={name:g,val:{},methodExt:[],propExt:[]},f.push(i));c===e-1?i.val=b:f=j?i.methodExt:i.propExt}};t.registerPlural=v=function(a,b,c){t.register(a,c);t.register(b,function(){var a=c.apply(this,arguments);return a===this?this:a instanceof t?a.length?h.isArray(a[0])?new t(a.context,
a[0]):a[0]:k:a})};r("tables()",function(a){var b;if(a){b=t;var c=this.context;if("number"===typeof a)a=[c[a]];else var e=h.map(c,function(a){return a.nTable}),a=h(e).filter(a).map(function(){var a=h.inArray(this,e);return c[a]}).toArray();b=new b(a)}else b=this;return b});r("table()",function(a){var a=this.tables(a),b=a.context;return b.length?new t(b[0]):a});v("tables().nodes()","table().node()",function(){return this.iterator("table",function(a){return a.nTable},1)});v("tables().body()","table().body()",
function(){return this.iterator("table",function(a){return a.nTBody},1)});v("tables().header()","table().header()",function(){return this.iterator("table",function(a){return a.nTHead},1)});v("tables().footer()","table().footer()",function(){return this.iterator("table",function(a){return a.nTFoot},1)});v("tables().containers()","table().container()",function(){return this.iterator("table",function(a){return a.nTableWrapper},1)});r("draw()",function(a){return this.iterator("table",function(b){N(b,
!1===a)})});r("page()",function(a){return a===k?this.page.info().page:this.iterator("table",function(b){Ta(b,a)})});r("page.info()",function(){if(0===this.context.length)return k;var a=this.context[0],b=a._iDisplayStart,c=a._iDisplayLength,e=a.fnRecordsDisplay(),d=-1===c;return{page:d?0:Math.floor(b/c),pages:d?1:Math.ceil(e/c),start:b,end:a.fnDisplayEnd(),length:c,recordsTotal:a.fnRecordsTotal(),recordsDisplay:e}});r("page.len()",function(a){return a===k?0!==this.context.length?this.context[0]._iDisplayLength:
k:this.iterator("table",function(b){Ra(b,a)})});var Ub=function(a,b,c){if(c){var e=new t(a);e.one("draw",function(){c(e.ajax.json())})}"ssp"==B(a)?N(a,b):(C(a,!0),ra(a,[],function(c){oa(a);for(var c=sa(a,c),e=0,g=c.length;e<g;e++)K(a,c[e]);N(a,b);C(a,!1)}))};r("ajax.json()",function(){var a=this.context;if(0<a.length)return a[0].json});r("ajax.params()",function(){var a=this.context;if(0<a.length)return a[0].oAjaxData});r("ajax.reload()",function(a,b){return this.iterator("table",function(c){Ub(c,
!1===b,a)})});r("ajax.url()",function(a){var b=this.context;if(a===k){if(0===b.length)return k;b=b[0];return b.ajax?h.isPlainObject(b.ajax)?b.ajax.url:b.ajax:b.sAjaxSource}return this.iterator("table",function(b){h.isPlainObject(b.ajax)?b.ajax.url=a:b.ajax=a})});r("ajax.url().load()",function(a,b){return this.iterator("table",function(c){Ub(c,!1===b,a)})});var $a=function(a,b,c,e,d){var f=[],g,j,i,o,l,q;i=typeof b;if(!b||"string"===i||"function"===i||b.length===k)b=[b];i=0;for(o=b.length;i<o;i++){j=
b[i]&&b[i].split?b[i].split(","):[b[i]];l=0;for(q=j.length;l<q;l++)(g=c("string"===typeof j[l]?h.trim(j[l]):j[l]))&&g.length&&f.push.apply(f,g)}a=u.selector[a];if(a.length){i=0;for(o=a.length;i<o;i++)f=a[i](e,d,f)}return f},ab=function(a){a||(a={});a.filter&&a.search===k&&(a.search=a.filter);return h.extend({search:"none",order:"current",page:"all"},a)},bb=function(a){for(var b=0,c=a.length;b<c;b++)if(0<a[b].length)return a[0]=a[b],a[0].length=1,a.length=1,a.context=[a.context[b]],a;a.length=0;return a},
Ca=function(a,b){var c,e,d,f=[],g=a.aiDisplay;c=a.aiDisplayMaster;var j=b.search;e=b.order;d=b.page;if("ssp"==B(a))return"removed"===j?[]:V(0,c.length);if("current"==d){c=a._iDisplayStart;for(e=a.fnDisplayEnd();c<e;c++)f.push(g[c])}else if("current"==e||"applied"==e)f="none"==j?c.slice():"applied"==j?g.slice():h.map(c,function(a){return-1===h.inArray(a,g)?a:null});else if("index"==e||"original"==e){c=0;for(e=a.aoData.length;c<e;c++)"none"==j?f.push(c):(d=h.inArray(c,g),(-1===d&&"removed"==j||0<=d&&
"applied"==j)&&f.push(c))}return f};r("rows()",function(a,b){a===k?a="":h.isPlainObject(a)&&(b=a,a="");var b=ab(b),c=this.iterator("table",function(c){var d=b;return $a("row",a,function(a){var b=Pb(a);if(b!==null&&!d)return[b];var j=Ca(c,d);if(b!==null&&h.inArray(b,j)!==-1)return[b];if(!a)return j;if(typeof a==="function")return h.map(j,function(b){var d=c.aoData[b];return a(b,d._aData,d.nTr)?b:null});b=Sb(ia(c.aoData,j,"nTr"));return a.nodeName&&h.inArray(a,b)!==-1?[a._DT_RowIndex]:h(b).filter(a).map(function(){return this._DT_RowIndex}).toArray()},
c,d)},1);c.selector.rows=a;c.selector.opts=b;return c});r("rows().nodes()",function(){return this.iterator("row",function(a,b){return a.aoData[b].nTr||k},1)});r("rows().data()",function(){return this.iterator(!0,"rows",function(a,b){return ia(a.aoData,b,"_aData")},1)});v("rows().cache()","row().cache()",function(a){return this.iterator("row",function(b,c){var e=b.aoData[c];return"search"===a?e._aFilterData:e._aSortData},1)});v("rows().invalidate()","row().invalidate()",function(a){return this.iterator("row",
function(b,c){ca(b,c,a)})});v("rows().indexes()","row().index()",function(){return this.iterator("row",function(a,b){return b},1)});v("rows().remove()","row().remove()",function(){var a=this;return this.iterator("row",function(b,c,e){var d=b.aoData;d.splice(c,1);for(var f=0,g=d.length;f<g;f++)null!==d[f].nTr&&(d[f].nTr._DT_RowIndex=f);h.inArray(c,b.aiDisplay);pa(b.aiDisplayMaster,c);pa(b.aiDisplay,c);pa(a[e],c,!1);Sa(b)})});r("rows.add()",function(a){var b=this.iterator("table",function(b){var c,
f,g,h=[];f=0;for(g=a.length;f<g;f++)c=a[f],c.nodeName&&"TR"===c.nodeName.toUpperCase()?h.push(ma(b,c)[0]):h.push(K(b,c));return h},1),c=this.rows(-1);c.pop();c.push.apply(c,b.toArray());return c});r("row()",function(a,b){return bb(this.rows(a,b))});r("row().data()",function(a){var b=this.context;if(a===k)return b.length&&this.length?b[0].aoData[this[0]]._aData:k;b[0].aoData[this[0]]._aData=a;ca(b[0],this[0],"data");return this});r("row().node()",function(){var a=this.context;return a.length&&this.length?
a[0].aoData[this[0]].nTr||null:null});r("row.add()",function(a){a instanceof h&&a.length&&(a=a[0]);var b=this.iterator("table",function(b){return a.nodeName&&"TR"===a.nodeName.toUpperCase()?ma(b,a)[0]:K(b,a)});return this.row(b[0])});var cb=function(a,b){var c=a.context;c.length&&(c=c[0].aoData[b!==k?b:a[0]],c._details&&(c._details.remove(),c._detailsShow=k,c._details=k))},Vb=function(a,b){var c=a.context;if(c.length&&a.length){var e=c[0].aoData[a[0]];if(e._details){(e._detailsShow=b)?e._details.insertAfter(e.nTr):
e._details.detach();var d=c[0],f=new t(d),g=d.aoData;f.off("draw.dt.DT_details column-visibility.dt.DT_details destroy.dt.DT_details");0<D(g,"_details").length&&(f.on("draw.dt.DT_details",function(a,b){d===b&&f.rows({page:"current"}).eq(0).each(function(a){a=g[a];a._detailsShow&&a._details.insertAfter(a.nTr)})}),f.on("column-visibility.dt.DT_details",function(a,b){if(d===b)for(var c,e=aa(b),f=0,h=g.length;f<h;f++)c=g[f],c._details&&c._details.children("td[colspan]").attr("colspan",e)}),f.on("destroy.dt.DT_details",
function(a,b){if(d===b)for(var c=0,e=g.length;c<e;c++)g[c]._details&&cb(f,c)}))}}};r("row().child()",function(a,b){var c=this.context;if(a===k)return c.length&&this.length?c[0].aoData[this[0]]._details:k;if(!0===a)this.child.show();else if(!1===a)cb(this);else if(c.length&&this.length){var e=c[0],c=c[0].aoData[this[0]],d=[],f=function(a,b){if(h.isArray(a)||a instanceof h)for(var c=0,k=a.length;c<k;c++)f(a[c],b);else a.nodeName&&"tr"===a.nodeName.toLowerCase()?d.push(a):(c=h("<tr><td/></tr>").addClass(b),
h("td",c).addClass(b).html(a)[0].colSpan=aa(e),d.push(c[0]))};f(a,b);c._details&&c._details.remove();c._details=h(d);c._detailsShow&&c._details.insertAfter(c.nTr)}return this});r(["row().child.show()","row().child().show()"],function(){Vb(this,!0);return this});r(["row().child.hide()","row().child().hide()"],function(){Vb(this,!1);return this});r(["row().child.remove()","row().child().remove()"],function(){cb(this);return this});r("row().child.isShown()",function(){var a=this.context;return a.length&&
this.length?a[0].aoData[this[0]]._detailsShow||!1:!1});var dc=/^(.+):(name|visIdx|visible)$/,Wb=function(a,b,c,e,d){for(var c=[],e=0,f=d.length;e<f;e++)c.push(x(a,d[e],b));return c};r("columns()",function(a,b){a===k?a="":h.isPlainObject(a)&&(b=a,a="");var b=ab(b),c=this.iterator("table",function(c){var d=a,f=b,g=c.aoColumns,j=D(g,"sName"),i=D(g,"nTh");return $a("column",d,function(a){var b=Pb(a);if(a==="")return V(g.length);if(b!==null)return[b>=0?b:g.length+b];if(typeof a==="function"){var d=Ca(c,
f);return h.map(g,function(b,f){return a(f,Wb(c,f,0,0,d),i[f])?f:null})}var k=typeof a==="string"?a.match(dc):"";if(k)switch(k[2]){case "visIdx":case "visible":b=parseInt(k[1],10);if(b<0){var m=h.map(g,function(a,b){return a.bVisible?b:null});return[m[m.length+b]]}return[la(c,b)];case "name":return h.map(j,function(a,b){return a===k[1]?b:null})}else return h(i).filter(a).map(function(){return h.inArray(this,i)}).toArray()},c,f)},1);c.selector.cols=a;c.selector.opts=b;return c});v("columns().header()",
"column().header()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].nTh},1)});v("columns().footer()","column().footer()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].nTf},1)});v("columns().data()","column().data()",function(){return this.iterator("column-rows",Wb,1)});v("columns().dataSrc()","column().dataSrc()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].mData},1)});v("columns().cache()","column().cache()",
function(a){return this.iterator("column-rows",function(b,c,e,d,f){return ia(b.aoData,f,"search"===a?"_aFilterData":"_aSortData",c)},1)});v("columns().nodes()","column().nodes()",function(){return this.iterator("column-rows",function(a,b,c,e,d){return ia(a.aoData,d,"anCells",b)},1)});v("columns().visible()","column().visible()",function(a,b){return this.iterator("column",function(c,e){if(a===k)return c.aoColumns[e].bVisible;var d=c.aoColumns,f=d[e],g=c.aoData,j,i,m;if(a!==k&&f.bVisible!==a){if(a){var l=
h.inArray(!0,D(d,"bVisible"),e+1);j=0;for(i=g.length;j<i;j++)m=g[j].nTr,d=g[j].anCells,m&&m.insertBefore(d[e],d[l]||null)}else h(D(c.aoData,"anCells",e)).detach();f.bVisible=a;ea(c,c.aoHeader);ea(c,c.aoFooter);if(b===k||b)X(c),(c.oScroll.sX||c.oScroll.sY)&&Y(c);w(c,null,"column-visibility",[c,e,a]);ya(c)}})});v("columns().indexes()","column().index()",function(a){return this.iterator("column",function(b,c){return"visible"===a?$(b,c):c},1)});r("columns.adjust()",function(){return this.iterator("table",
function(a){X(a)},1)});r("column.index()",function(a,b){if(0!==this.context.length){var c=this.context[0];if("fromVisible"===a||"toData"===a)return la(c,b);if("fromData"===a||"toVisible"===a)return $(c,b)}});r("column()",function(a,b){return bb(this.columns(a,b))});r("cells()",function(a,b,c){h.isPlainObject(a)&&(a.row===k?(c=a,a=null):(c=b,b=null));h.isPlainObject(b)&&(c=b,b=null);if(null===b||b===k)return this.iterator("table",function(b){var d=a,e=ab(c),f=b.aoData,g=Ca(b,e),i=Sb(ia(f,g,"anCells")),
j=h([].concat.apply([],i)),l,m=b.aoColumns.length,o,r,t,s,u,v;return $a("cell",d,function(a){var c=typeof a==="function";if(a===null||a===k||c){o=[];r=0;for(t=g.length;r<t;r++){l=g[r];for(s=0;s<m;s++){u={row:l,column:s};if(c){v=b.aoData[l];a(u,x(b,l,s),v.anCells?v.anCells[s]:null)&&o.push(u)}else o.push(u)}}return o}return h.isPlainObject(a)?[a]:j.filter(a).map(function(a,b){l=b.parentNode._DT_RowIndex;return{row:l,column:h.inArray(b,f[l].anCells)}}).toArray()},b,e)});var e=this.columns(b,c),d=this.rows(a,
c),f,g,j,i,m,l=this.iterator("table",function(a,b){f=[];g=0;for(j=d[b].length;g<j;g++){i=0;for(m=e[b].length;i<m;i++)f.push({row:d[b][g],column:e[b][i]})}return f},1);h.extend(l.selector,{cols:b,rows:a,opts:c});return l});v("cells().nodes()","cell().node()",function(){return this.iterator("cell",function(a,b,c){return(a=a.aoData[b].anCells)?a[c]:k},1)});r("cells().data()",function(){return this.iterator("cell",function(a,b,c){return x(a,b,c)},1)});v("cells().cache()","cell().cache()",function(a){a=
"search"===a?"_aFilterData":"_aSortData";return this.iterator("cell",function(b,c,e){return b.aoData[c][a][e]},1)});v("cells().render()","cell().render()",function(a){return this.iterator("cell",function(b,c,e){return x(b,c,e,a)},1)});v("cells().indexes()","cell().index()",function(){return this.iterator("cell",function(a,b,c){return{row:b,column:c,columnVisible:$(a,c)}},1)});v("cells().invalidate()","cell().invalidate()",function(a){return this.iterator("cell",function(b,c,e){ca(b,c,a,e)})});r("cell()",
function(a,b,c){return bb(this.cells(a,b,c))});r("cell().data()",function(a){var b=this.context,c=this[0];if(a===k)return b.length&&c.length?x(b[0],c[0].row,c[0].column):k;Ia(b[0],c[0].row,c[0].column,a);ca(b[0],c[0].row,"data",c[0].column);return this});r("order()",function(a,b){var c=this.context;if(a===k)return 0!==c.length?c[0].aaSorting:k;"number"===typeof a?a=[[a,b]]:h.isArray(a[0])||(a=Array.prototype.slice.call(arguments));return this.iterator("table",function(b){b.aaSorting=a.slice()})});
r("order.listener()",function(a,b,c){return this.iterator("table",function(e){Oa(e,a,b,c)})});r(["columns().order()","column().order()"],function(a){var b=this;return this.iterator("table",function(c,e){var d=[];h.each(b[e],function(b,c){d.push([c,a])});c.aaSorting=d})});r("search()",function(a,b,c,e){var d=this.context;return a===k?0!==d.length?d[0].oPreviousSearch.sSearch:k:this.iterator("table",function(d){d.oFeatures.bFilter&&fa(d,h.extend({},d.oPreviousSearch,{sSearch:a+"",bRegex:null===b?!1:
b,bSmart:null===c?!0:c,bCaseInsensitive:null===e?!0:e}),1)})});v("columns().search()","column().search()",function(a,b,c,e){return this.iterator("column",function(d,f){var g=d.aoPreSearchCols;if(a===k)return g[f].sSearch;d.oFeatures.bFilter&&(h.extend(g[f],{sSearch:a+"",bRegex:null===b?!1:b,bSmart:null===c?!0:c,bCaseInsensitive:null===e?!0:e}),fa(d,d.oPreviousSearch,1))})});r("state()",function(){return this.context.length?this.context[0].oSavedState:null});r("state.clear()",function(){return this.iterator("table",
function(a){a.fnStateSaveCallback.call(a.oInstance,a,{})})});r("state.loaded()",function(){return this.context.length?this.context[0].oLoadedState:null});r("state.save()",function(){return this.iterator("table",function(a){ya(a)})});m.versionCheck=m.fnVersionCheck=function(a){for(var b=m.version.split("."),a=a.split("."),c,e,d=0,f=a.length;d<f;d++)if(c=parseInt(b[d],10)||0,e=parseInt(a[d],10)||0,c!==e)return c>e;return!0};m.isDataTable=m.fnIsDataTable=function(a){var b=h(a).get(0),c=!1;h.each(m.settings,
function(a,d){var f=d.nScrollHead?h("table",d.nScrollHead)[0]:null,g=d.nScrollFoot?h("table",d.nScrollFoot)[0]:null;if(d.nTable===b||f===b||g===b)c=!0});return c};m.tables=m.fnTables=function(a){return h.map(m.settings,function(b){if(!a||a&&h(b.nTable).is(":visible"))return b.nTable})};m.util={throttle:ua,escapeRegex:va};m.camelToHungarian=H;r("$()",function(a,b){var c=this.rows(b).nodes(),c=h(c);return h([].concat(c.filter(a).toArray(),c.find(a).toArray()))});h.each(["on","one","off"],function(a,
b){r(b+"()",function(){var a=Array.prototype.slice.call(arguments);a[0].match(/\.dt\b/)||(a[0]+=".dt");var e=h(this.tables().nodes());e[b].apply(e,a);return this})});r("clear()",function(){return this.iterator("table",function(a){oa(a)})});r("settings()",function(){return new t(this.context,this.context)});r("init()",function(){var a=this.context;return a.length?a[0].oInit:null});r("data()",function(){return this.iterator("table",function(a){return D(a.aoData,"_aData")}).flatten()});r("destroy()",
function(a){a=a||!1;return this.iterator("table",function(b){var c=b.nTableWrapper.parentNode,e=b.oClasses,d=b.nTable,f=b.nTBody,g=b.nTHead,j=b.nTFoot,i=h(d),f=h(f),k=h(b.nTableWrapper),l=h.map(b.aoData,function(a){return a.nTr}),q;b.bDestroying=!0;w(b,"aoDestroyCallback","destroy",[b]);a||(new t(b)).columns().visible(!0);k.unbind(".DT").find(":not(tbody *)").unbind(".DT");h(Ea).unbind(".DT-"+b.sInstance);d!=g.parentNode&&(i.children("thead").detach(),i.append(g));j&&d!=j.parentNode&&(i.children("tfoot").detach(),
i.append(j));i.detach();k.detach();b.aaSorting=[];b.aaSortingFixed=[];xa(b);h(l).removeClass(b.asStripeClasses.join(" "));h("th, td",g).removeClass(e.sSortable+" "+e.sSortableAsc+" "+e.sSortableDesc+" "+e.sSortableNone);b.bJUI&&(h("th span."+e.sSortIcon+", td span."+e.sSortIcon,g).detach(),h("th, td",g).each(function(){var a=h("div."+e.sSortJUIWrapper,this);h(this).append(a.contents());a.detach()}));!a&&c&&c.insertBefore(d,b.nTableReinsertBefore);f.children().detach();f.append(l);i.css("width",b.sDestroyWidth).removeClass(e.sTable);
(q=b.asDestroyStripes.length)&&f.children().each(function(a){h(this).addClass(b.asDestroyStripes[a%q])});c=h.inArray(b,m.settings);-1!==c&&m.settings.splice(c,1)})});h.each(["column","row","cell"],function(a,b){r(b+"s().every()",function(a){return this.iterator(b,function(e,d,f){a.call((new t(e))[b](d,f))})})});r("i18n()",function(a,b,c){var e=this.context[0],a=R(a)(e.oLanguage);a===k&&(a=b);c!==k&&h.isPlainObject(a)&&(a=a[c]!==k?a[c]:a._);return a.replace("%d",c)});m.version="1.10.7";m.settings=
[];m.models={};m.models.oSearch={bCaseInsensitive:!0,sSearch:"",bRegex:!1,bSmart:!0};m.models.oRow={nTr:null,anCells:null,_aData:[],_aSortData:null,_aFilterData:null,_sFilterRow:null,_sRowStripe:"",src:null};m.models.oColumn={idx:null,aDataSort:null,asSorting:null,bSearchable:null,bSortable:null,bVisible:null,_sManualType:null,_bAttrSrc:!1,fnCreatedCell:null,fnGetData:null,fnSetData:null,mData:null,mRender:null,nTh:null,nTf:null,sClass:null,sContentPadding:null,sDefaultContent:null,sName:null,sSortDataType:"std",
sSortingClass:null,sSortingClassJUI:null,sTitle:null,sType:null,sWidth:null,sWidthOrig:null};m.defaults={aaData:null,aaSorting:[[0,"asc"]],aaSortingFixed:[],ajax:null,aLengthMenu:[10,25,50,100],aoColumns:null,aoColumnDefs:null,aoSearchCols:[],asStripeClasses:null,bAutoWidth:!0,bDeferRender:!1,bDestroy:!1,bFilter:!0,bInfo:!0,bJQueryUI:!1,bLengthChange:!0,bPaginate:!0,bProcessing:!1,bRetrieve:!1,bScrollCollapse:!1,bServerSide:!1,bSort:!0,bSortMulti:!0,bSortCellsTop:!1,bSortClasses:!0,bStateSave:!1,
fnCreatedRow:null,fnDrawCallback:null,fnFooterCallback:null,fnFormatNumber:function(a){return a.toString().replace(/\B(?=(\d{3})+(?!\d))/g,this.oLanguage.sThousands)},fnHeaderCallback:null,fnInfoCallback:null,fnInitComplete:null,fnPreDrawCallback:null,fnRowCallback:null,fnServerData:null,fnServerParams:null,fnStateLoadCallback:function(a){try{return JSON.parse((-1===a.iStateDuration?sessionStorage:localStorage).getItem("DataTables_"+a.sInstance+"_"+location.pathname))}catch(b){}},fnStateLoadParams:null,
fnStateLoaded:null,fnStateSaveCallback:function(a,b){try{(-1===a.iStateDuration?sessionStorage:localStorage).setItem("DataTables_"+a.sInstance+"_"+location.pathname,JSON.stringify(b))}catch(c){}},fnStateSaveParams:null,iStateDuration:7200,iDeferLoading:null,iDisplayLength:10,iDisplayStart:0,iTabIndex:0,oClasses:{},oLanguage:{oAria:{sSortAscending:": activate to sort column ascending",sSortDescending:": activate to sort column descending"},oPaginate:{sFirst:"First",sLast:"Last",sNext:"Next",sPrevious:"Previous"},
sEmptyTable:"No data available in table",sInfo:"Showing _START_ to _END_ of _TOTAL_ entries",sInfoEmpty:"Showing 0 to 0 of 0 entries",sInfoFiltered:"(filtered from _MAX_ total entries)",sInfoPostFix:"",sDecimal:"",sThousands:",",sLengthMenu:"Show _MENU_ entries",sLoadingRecords:"Loading...",sProcessing:"Processing...",sSearch:"Search:",sSearchPlaceholder:"",sUrl:"",sZeroRecords:"No matching records found"},oSearch:h.extend({},m.models.oSearch),sAjaxDataProp:"data",sAjaxSource:null,sDom:"lfrtip",searchDelay:null,
sPaginationType:"simple_numbers",sScrollX:"",sScrollXInner:"",sScrollY:"",sServerMethod:"GET",renderer:null};W(m.defaults);m.defaults.column={aDataSort:null,iDataSort:-1,asSorting:["asc","desc"],bSearchable:!0,bSortable:!0,bVisible:!0,fnCreatedCell:null,mData:null,mRender:null,sCellType:"td",sClass:"",sContentPadding:"",sDefaultContent:null,sName:"",sSortDataType:"std",sTitle:null,sType:null,sWidth:null};W(m.defaults.column);m.models.oSettings={oFeatures:{bAutoWidth:null,bDeferRender:null,bFilter:null,
bInfo:null,bLengthChange:null,bPaginate:null,bProcessing:null,bServerSide:null,bSort:null,bSortMulti:null,bSortClasses:null,bStateSave:null},oScroll:{bCollapse:null,iBarWidth:0,sX:null,sXInner:null,sY:null},oLanguage:{fnInfoCallback:null},oBrowser:{bScrollOversize:!1,bScrollbarLeft:!1},ajax:null,aanFeatures:[],aoData:[],aiDisplay:[],aiDisplayMaster:[],aoColumns:[],aoHeader:[],aoFooter:[],oPreviousSearch:{},aoPreSearchCols:[],aaSorting:null,aaSortingFixed:[],asStripeClasses:null,asDestroyStripes:[],
sDestroyWidth:0,aoRowCallback:[],aoHeaderCallback:[],aoFooterCallback:[],aoDrawCallback:[],aoRowCreatedCallback:[],aoPreDrawCallback:[],aoInitComplete:[],aoStateSaveParams:[],aoStateLoadParams:[],aoStateLoaded:[],sTableId:"",nTable:null,nTHead:null,nTFoot:null,nTBody:null,nTableWrapper:null,bDeferLoading:!1,bInitialised:!1,aoOpenRows:[],sDom:null,searchDelay:null,sPaginationType:"two_button",iStateDuration:0,aoStateSave:[],aoStateLoad:[],oSavedState:null,oLoadedState:null,sAjaxSource:null,sAjaxDataProp:null,
bAjaxDataGet:!0,jqXHR:null,json:k,oAjaxData:k,fnServerData:null,aoServerParams:[],sServerMethod:null,fnFormatNumber:null,aLengthMenu:null,iDraw:0,bDrawing:!1,iDrawError:-1,_iDisplayLength:10,_iDisplayStart:0,_iRecordsTotal:0,_iRecordsDisplay:0,bJUI:null,oClasses:{},bFiltered:!1,bSorted:!1,bSortCellsTop:null,oInit:null,aoDestroyCallback:[],fnRecordsTotal:function(){return"ssp"==B(this)?1*this._iRecordsTotal:this.aiDisplayMaster.length},fnRecordsDisplay:function(){return"ssp"==B(this)?1*this._iRecordsDisplay:
this.aiDisplay.length},fnDisplayEnd:function(){var a=this._iDisplayLength,b=this._iDisplayStart,c=b+a,e=this.aiDisplay.length,d=this.oFeatures,f=d.bPaginate;return d.bServerSide?!1===f||-1===a?b+e:Math.min(b+a,this._iRecordsDisplay):!f||c>e||-1===a?e:c},oInstance:null,sInstance:null,iTabIndex:0,nScrollHead:null,nScrollFoot:null,aLastSort:[],oPlugins:{}};m.ext=u={buttons:{},classes:{},errMode:"alert",feature:[],search:[],selector:{cell:[],column:[],row:[]},internal:{},legacy:{ajax:null},pager:{},renderer:{pageButton:{},
header:{}},order:{},type:{detect:[],search:{},order:{}},_unique:0,fnVersionCheck:m.fnVersionCheck,iApiIndex:0,oJUIClasses:{},sVersion:m.version};h.extend(u,{afnFiltering:u.search,aTypes:u.type.detect,ofnSearch:u.type.search,oSort:u.type.order,afnSortData:u.order,aoFeatures:u.feature,oApi:u.internal,oStdClasses:u.classes,oPagination:u.pager});h.extend(m.ext.classes,{sTable:"dataTable",sNoFooter:"no-footer",sPageButton:"paginate_button",sPageButtonActive:"current",sPageButtonDisabled:"disabled",sStripeOdd:"odd",
sStripeEven:"even",sRowEmpty:"dataTables_empty",sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter",sInfo:"dataTables_info",sPaging:"dataTables_paginate paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"sorting_asc",sSortDesc:"sorting_desc",sSortable:"sorting",sSortableAsc:"sorting_asc_disabled",sSortableDesc:"sorting_desc_disabled",sSortableNone:"sorting_disabled",sSortColumn:"sorting_",sFilterInput:"",sLengthSelect:"",sScrollWrapper:"dataTables_scroll",sScrollHead:"dataTables_scrollHead",
sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody",sScrollFoot:"dataTables_scrollFoot",sScrollFootInner:"dataTables_scrollFootInner",sHeaderTH:"",sFooterTH:"",sSortJUIAsc:"",sSortJUIDesc:"",sSortJUI:"",sSortJUIAscAllowed:"",sSortJUIDescAllowed:"",sSortJUIWrapper:"",sSortIcon:"",sJUIHeader:"",sJUIFooter:""});var Da="",Da="",F=Da+"ui-state-default",ja=Da+"css_right ui-icon ui-icon-",Xb=Da+"fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix";h.extend(m.ext.oJUIClasses,
m.ext.classes,{sPageButton:"fg-button ui-button "+F,sPageButtonActive:"ui-state-disabled",sPageButtonDisabled:"ui-state-disabled",sPaging:"dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi ui-buttonset-multi paging_",sSortAsc:F+" sorting_asc",sSortDesc:F+" sorting_desc",sSortable:F+" sorting",sSortableAsc:F+" sorting_asc_disabled",sSortableDesc:F+" sorting_desc_disabled",sSortableNone:F+" sorting_disabled",sSortJUIAsc:ja+"triangle-1-n",sSortJUIDesc:ja+"triangle-1-s",sSortJUI:ja+"carat-2-n-s",
sSortJUIAscAllowed:ja+"carat-1-n",sSortJUIDescAllowed:ja+"carat-1-s",sSortJUIWrapper:"DataTables_sort_wrapper",sSortIcon:"DataTables_sort_icon",sScrollHead:"dataTables_scrollHead "+F,sScrollFoot:"dataTables_scrollFoot "+F,sHeaderTH:F,sFooterTH:F,sJUIHeader:Xb+" ui-corner-tl ui-corner-tr",sJUIFooter:Xb+" ui-corner-bl ui-corner-br"});var Mb=m.ext.pager;h.extend(Mb,{simple:function(){return["previous","next"]},full:function(){return["first","previous","next","last"]},simple_numbers:function(a,b){return["previous",
Wa(a,b),"next"]},full_numbers:function(a,b){return["first","previous",Wa(a,b),"next","last"]},_numbers:Wa,numbers_length:7});h.extend(!0,m.ext.renderer,{pageButton:{_:function(a,b,c,e,d,f){var g=a.oClasses,j=a.oLanguage.oPaginate,i,k,l=0,m=function(b,e){var n,r,t,s,u=function(b){Ta(a,b.data.action,true)};n=0;for(r=e.length;n<r;n++){s=e[n];if(h.isArray(s)){t=h("<"+(s.DT_el||"div")+"/>").appendTo(b);m(t,s)}else{k=i="";switch(s){case "ellipsis":b.append('<span class="ellipsis">&#x2026;</span>');break;
case "first":i=j.sFirst;k=s+(d>0?"":" "+g.sPageButtonDisabled);break;case "previous":i=j.sPrevious;k=s+(d>0?"":" "+g.sPageButtonDisabled);break;case "next":i=j.sNext;k=s+(d<f-1?"":" "+g.sPageButtonDisabled);break;case "last":i=j.sLast;k=s+(d<f-1?"":" "+g.sPageButtonDisabled);break;default:i=s+1;k=d===s?g.sPageButtonActive:""}if(i){t=h("<a>",{"class":g.sPageButton+" "+k,"aria-controls":a.sTableId,"data-dt-idx":l,tabindex:a.iTabIndex,id:c===0&&typeof s==="string"?a.sTableId+"_"+s:null}).html(i).appendTo(b);
Va(t,{action:s},u);l++}}}},n;try{n=h(Q.activeElement).data("dt-idx")}catch(r){}m(h(b).empty(),e);n&&h(b).find("[data-dt-idx="+n+"]").focus()}}});h.extend(m.ext.type.detect,[function(a,b){var c=b.oLanguage.sDecimal;return Za(a,c)?"num"+c:null},function(a){if(a&&!(a instanceof Date)&&(!ac.test(a)||!bc.test(a)))return null;var b=Date.parse(a);return null!==b&&!isNaN(b)||J(a)?"date":null},function(a,b){var c=b.oLanguage.sDecimal;return Za(a,c,!0)?"num-fmt"+c:null},function(a,b){var c=b.oLanguage.sDecimal;
return Rb(a,c)?"html-num"+c:null},function(a,b){var c=b.oLanguage.sDecimal;return Rb(a,c,!0)?"html-num-fmt"+c:null},function(a){return J(a)||"string"===typeof a&&-1!==a.indexOf("<")?"html":null}]);h.extend(m.ext.type.search,{html:function(a){return J(a)?a:"string"===typeof a?a.replace(Ob," ").replace(Ba,""):""},string:function(a){return J(a)?a:"string"===typeof a?a.replace(Ob," "):a}});var Aa=function(a,b,c,e){if(0!==a&&(!a||"-"===a))return-Infinity;b&&(a=Qb(a,b));a.replace&&(c&&(a=a.replace(c,"")),
e&&(a=a.replace(e,"")));return 1*a};h.extend(u.type.order,{"date-pre":function(a){return Date.parse(a)||0},"html-pre":function(a){return J(a)?"":a.replace?a.replace(/<.*?>/g,"").toLowerCase():a+""},"string-pre":function(a){return J(a)?"":"string"===typeof a?a.toLowerCase():!a.toString?"":a.toString()},"string-asc":function(a,b){return a<b?-1:a>b?1:0},"string-desc":function(a,b){return a<b?1:a>b?-1:0}});db("");h.extend(!0,m.ext.renderer,{header:{_:function(a,b,c,e){h(a.nTable).on("order.dt.DT",function(d,
f,g,h){if(a===f){d=c.idx;b.removeClass(c.sSortingClass+" "+e.sSortAsc+" "+e.sSortDesc).addClass(h[d]=="asc"?e.sSortAsc:h[d]=="desc"?e.sSortDesc:c.sSortingClass)}})},jqueryui:function(a,b,c,e){h("<div/>").addClass(e.sSortJUIWrapper).append(b.contents()).append(h("<span/>").addClass(e.sSortIcon+" "+c.sSortingClassJUI)).appendTo(b);h(a.nTable).on("order.dt.DT",function(d,f,g,h){if(a===f){d=c.idx;b.removeClass(e.sSortAsc+" "+e.sSortDesc).addClass(h[d]=="asc"?e.sSortAsc:h[d]=="desc"?e.sSortDesc:c.sSortingClass);
b.find("span."+e.sSortIcon).removeClass(e.sSortJUIAsc+" "+e.sSortJUIDesc+" "+e.sSortJUI+" "+e.sSortJUIAscAllowed+" "+e.sSortJUIDescAllowed).addClass(h[d]=="asc"?e.sSortJUIAsc:h[d]=="desc"?e.sSortJUIDesc:c.sSortingClassJUI)}})}}});m.render={number:function(a,b,c,e){return{display:function(d){if("number"!==typeof d&&"string"!==typeof d)return d;var f=0>d?"-":"",d=Math.abs(parseFloat(d)),g=parseInt(d,10),d=c?b+(d-g).toFixed(c).substring(2):"";return f+(e||"")+g.toString().replace(/\B(?=(\d{3})+(?!\d))/g,
a)+d}}}};h.extend(m.ext.internal,{_fnExternApiFunc:Nb,_fnBuildAjax:ra,_fnAjaxUpdate:kb,_fnAjaxParameters:tb,_fnAjaxUpdateDraw:ub,_fnAjaxDataSrc:sa,_fnAddColumn:Fa,_fnColumnOptions:ka,_fnAdjustColumnSizing:X,_fnVisibleToColumnIndex:la,_fnColumnIndexToVisible:$,_fnVisbleColumns:aa,_fnGetColumns:Z,_fnColumnTypes:Ha,_fnApplyColumnDefs:ib,_fnHungarianMap:W,_fnCamelToHungarian:H,_fnLanguageCompat:P,_fnBrowserDetect:gb,_fnAddData:K,_fnAddTr:ma,_fnNodeToDataIndex:function(a,b){return b._DT_RowIndex!==k?b._DT_RowIndex:
null},_fnNodeToColumnIndex:function(a,b,c){return h.inArray(c,a.aoData[b].anCells)},_fnGetCellData:x,_fnSetCellData:Ia,_fnSplitObjNotation:Ka,_fnGetObjectDataFn:R,_fnSetObjectDataFn:S,_fnGetDataMaster:La,_fnClearTable:oa,_fnDeleteIndex:pa,_fnInvalidate:ca,_fnGetRowElements:na,_fnCreateTr:Ja,_fnBuildHead:jb,_fnDrawHead:ea,_fnDraw:M,_fnReDraw:N,_fnAddOptionsHtml:mb,_fnDetectHeader:da,_fnGetUniqueThs:qa,_fnFeatureHtmlFilter:ob,_fnFilterComplete:fa,_fnFilterCustom:xb,_fnFilterColumn:wb,_fnFilter:vb,_fnFilterCreateSearch:Qa,
_fnEscapeRegex:va,_fnFilterData:yb,_fnFeatureHtmlInfo:rb,_fnUpdateInfo:Bb,_fnInfoMacros:Cb,_fnInitialise:ga,_fnInitComplete:ta,_fnLengthChange:Ra,_fnFeatureHtmlLength:nb,_fnFeatureHtmlPaginate:sb,_fnPageChange:Ta,_fnFeatureHtmlProcessing:pb,_fnProcessingDisplay:C,_fnFeatureHtmlTable:qb,_fnScrollDraw:Y,_fnApplyToChildren:G,_fnCalculateColumnWidths:Ga,_fnThrottle:ua,_fnConvertToWidth:Db,_fnScrollingWidthAdjust:Fb,_fnGetWidestNode:Eb,_fnGetMaxLenString:Gb,_fnStringToCss:s,_fnScrollBarWidth:Hb,_fnSortFlatten:U,
_fnSort:lb,_fnSortAria:Jb,_fnSortListener:Ua,_fnSortAttachListener:Oa,_fnSortingClasses:xa,_fnSortData:Ib,_fnSaveState:ya,_fnLoadState:Kb,_fnSettingsFromNode:za,_fnLog:I,_fnMap:E,_fnBindAction:Va,_fnCallbackReg:z,_fnCallbackFire:w,_fnLengthOverflow:Sa,_fnRenderer:Pa,_fnDataSource:B,_fnRowAttributes:Ma,_fnCalculateEnd:function(){}});h.fn.dataTable=m;h.fn.dataTableSettings=m.settings;h.fn.dataTableExt=m.ext;h.fn.DataTable=function(a){return h(this).dataTable(a).api()};h.each(m,function(a,b){h.fn.DataTable[a]=
b});return h.fn.dataTable};"function"===typeof define&&define.amd?define("datatables",["jquery"],P):"object"===typeof exports?module.exports=P(require("jquery")):jQuery&&!jQuery.fn.dataTable&&P(jQuery)})(window,document);

var PMCommandReconnect = function (rec, opts) {
    var CmdReconnect = function (port, settings) {
        // We skip one level in the class hierarchy by calling to the constructor of superclass of the current class superclass
        PMUI.command.CommandReconnect.superclass.call(this, port);

        this.before = {
            x: port.getX(),
            y: port.getY(),
            parent: port.getParent()
        };

        this.after = {
            x: settings.x,
            y: settings.y,
            parent: settings.shape
        };
    };

    CmdReconnect.prototype = new PMUI.command.CommandReconnect(rec);

    CmdReconnect.prototype.execute = function () {
        var connection = this.receiver.connection,
            canvas = this.before.parent.canvas,
            destElement,
            srcElement;

        this.receiver.setPosition(this.after.x, this.after.y)
            .dragging = false;

        PMUI.command.CommandReconnect.prototype.execute.call(this);

        if (this.after.parent !== this.before.parent) {
            canvas.regularShapes.insert(this.receiver);
        }

        srcElement = connection.getSrcPort().getParent();
        destElement = connection.getDestPort().getParent();

        srcElement.addOutgoingConnection(connection);
        destElement.addIncomingConnection(connection);
    };
    CmdReconnect.prototype.undo = function () {
        var connection,
            destPort,
            srcPort,
            otherActivity;

        PMUI.command.CommandReconnect.prototype.undo.call(this);

        connection =  this.receiver.connection;
        destPort = connection.getDestPort();
        srcPort = connection.getSrcPort();

        if (this.after.parent !== this.before.parent) {
            if (destPort === this.receiver) {
                otherActivity = srcPort.getParent();
                otherActivity.removeOutgoingConnection(connection);
                otherActivity.addOutgoingConnection(connection);
                this.after.parent.removeIncomingConnection(connection);
                this.before.parent.addIncomingConnection(connection);
            } else {
                otherActivity = destPort.getParent();
                otherActivity.removeIncomingConnection(connection);
                otherActivity.addIncomingConnection(connection);
                this.after.parent.removeOutgoingConnection(connection);
                this.before.parent.addOutgoingConnection(connection);
            }
        }
    };
    return new CmdReconnect(rec, opts);
};
var PMSegmentDragBehavior = function (options) {

};
PMSegmentDragBehavior.prototype = new PMUI.behavior.ConnectionDragBehavior();

/**
 * On drag handler, creates a connection segment from the shape to the current
 * mouse position
 * @param {PMUI.draw.CustomShape} customShape
 * @return {Function}
 */
PMSegmentDragBehavior.prototype.onDrag = function (customShape) {
    return function (e, ui) {
        var canvas = customShape.getCanvas(),
            endPoint = new PMUI.util.Point(),
            realPoint = canvas.relativePoint(e);
        if (canvas.connectionSegment) {
            //remove the connection segment in order to create another one
            $(canvas.connectionSegment.getHTML()).remove();
        }
        //Determine the point where the mouse currently is
        endPoint.x = realPoint.x * customShape.canvas.zoomFactor;
        endPoint.y = realPoint.y * customShape.canvas.zoomFactor;

        //creates a new segment from where the helper was created to the
        // currently mouse location
        canvas.connectionSegment = new PMUI.draw.Segment({
            startPoint: customShape.startConnectionPoint,
            endPoint: endPoint,
            parent: canvas,
            color: new PMUI.util.Color(92, 156, 204),
            zOrder: PMUI.util.Style.MAX_ZINDEX * 2
        });
        //We make the connection segment point to helper in order to get
        // information when the drop occurs
        canvas.connectionSegment.pointsTo = customShape;
        //create HTML and paint
        //canvas.connectionSegment.createHTML();
        canvas.connectionSegment.paint();
    };
};
var PMConnectHandler = function (options) {
    PMUI.draw.Handler.call(this, options);
    /**
     * Category of this resize handler
     * @type {"resizable"/"nonresizable"}
     */
    this.category = null;

    /**
     * Denotes whether the resize handle is visible or not.
     * @property boolean
     */
    this.visible = false;

    /**
     * JSON used to create an instance of the class Style used when the object is resizable.
     * @property {Object}
     */
    this.resizableStyle = null;

    /**
     * JSON used to create an instance of the class Style used when the object is not resizable.
     * @property {Object}
     */
    this.nonResizableStyle = null;
    this.relativeShape = null;
    // set defaults
    PMConnectHandler.prototype.init.call(this, options);
};

PMConnectHandler.prototype = new PMUI.draw.Handler();

/**
 * The type of each instance of this class.
 * @property {String}
 */
PMConnectHandler.prototype.type = "PMConnectHandler";

/**
 * Instance initializer which uses options to extend the config options to initialize the instance
 * @param {Object} options The object that contains the config
 * @private
 */
PMConnectHandler.prototype.init = function (options) {
    var defaults = {
        width: 10,
        height: 10,
        parent: null,
        orientation: null,
        representation: null,
        resizableStyle: {},
        nonResizableStyle: {},
        zOrder: 2
    };

    // extend recursively the defaultOptions with the given options
    $.extend(true, defaults, options);

    // add default zIndex to this handler
    if (defaults.resizableStyle.cssProperties) {
        defaults.resizableStyle.cssProperties.zIndex = defaults.zOrder;
    }
    if (defaults.nonResizableStyle.cssProperties) {
        defaults.nonResizableStyle.cssProperties.zIndex = defaults.zOrder;
    }
    this.setParent(defaults.parent)
        .setWidth(defaults.width)
        .setHeight(defaults.height)
        .setOrientation(defaults.orientation)
        .setRepresentation(defaults.representation)
        .setResizableStyle(defaults.resizableStyle)
        .setNonResizableStyle(defaults.nonResizableStyle);
};

/**
 * Sets the parent of this handler
 * @param {PMUI.draw.Shape} newParent
 * @chainable
 */
PMConnectHandler.prototype.setParent = function (newParent) {
    this.parent = newParent;
    return this;
};

/**
 * Gets the parent of this handler.
 * @return {PMUI.draw.Shape}
 */
PMConnectHandler.prototype.getParent = function () {
    return this.parent;
};

/**
 * Paints this resize handler by calling it's parent's `paint` and setting
 * the visibility of this resize handler
 * @chainable
 */
PMConnectHandler.prototype.paint = function () {
    if (!this.html) {
        throw new Error("paint():  This handler has no html");
    }
    // this line paints the representation (by default a rectangle)
    PMUI.draw.Handler.prototype.paint.call(this);
    this.setVisible(this.visible);
    return this;
};

/**
 * Sets the category of the resizeHandler (also adds the needed class to
 * make the element resizable)
 * @param newCategory
 * @chainable
 */
PMConnectHandler.prototype.setCategory = function (newCategory) {
    if (typeof newCategory === "string") {
        this.category = newCategory;
    }
    this.style.addClasses([newCategory]);
    return this;
};
/**
 * Sets the resizable style of this shape by creating an instance of the class Style
 * @param {Object} style
 * @chainable
 */
PMConnectHandler.prototype.setResizableStyle = function (style) {
    this.resizableStyle = new PMUI.util.Style({
        belongsTo: this,
        cssProperties: style.cssProperties,
        cssClasses: style.cssClasses
    });
    return this;
};

/**
 * Sets the non resizable style for this shape by creating an instance of the class Style
 * @param {Object} style
 * @chainable
 */
PMConnectHandler.prototype.setNonResizableStyle = function (style) {
    this.nonResizableStyle = new PMUI.util.Style({
        belongsTo: this,
        cssProperties: style.cssProperties,
        cssClasses: style.cssClasses
    });
    return this;
};

PMConnectHandler.prototype.attachListeners = function () {
    var $handler = $('.dragConnectHandler');
    $handler.mousedown(this.onMouseDown(this));
    if (this.relativeShape) {
        dragOptions = {
            revert: true,
            helper: "clone",
            cursorAt: false,
            revertDuration: 0,
            grid: [1, 1],
            start: this.onDragStart(this.relativeShape),
            stop: this.onDragEnd(this.relativeShape),
            drag: this.onDrag(this.relativeShape),
            refreshPositions: true,
            cursor: "pointer"
        };
        $(this.html).draggable(dragOptions);
    }
    return this;
};
PMConnectHandler.prototype.attachDrop = function () {
    dropOptions = {
        accept: '.dragConnectHandler, .pmui-oval',
        hoverClass: "ui-state-hover",
        drop: this.onDrop(this.relativeShape, this),
        over: this.onDropOver(this.relativeShape, this)
    };
    $('.dropConnectHandler').droppable(dropOptions);
};

PMConnectHandler.prototype.onMouseDown = function (customShape) {
    return function (e, ui) {
        e.preventDefault();
        e.stopPropagation();
    }
};

PMConnectHandler.prototype.onMouseOver = function (customShape) {
    return function (e, ui) {
        e.preventDefault();
        e.stopPropagation();
    }
};
PMConnectHandler.prototype.onMouseOut = function (customShape) {
    return function (e, ui) {
        e.preventDefault();
        e.stopPropagation();
        PMUI.getActiveCanvas.isMouseOverHelper = false;
        if (PMUI.getActiveCanvas.hightLightShape) {
            PMUI.getActiveCanvas.hideDragConnectHandlers();
        }
    }
};

PMConnectHandler.prototype.onDragStart = function (customShape) {
    return function (e, ui) {
        if (!customShape.canvas.currentConnection) {
            customShape.canvas.isDraggingConnectHandler = true;
            var canvas = customShape.canvas,
                currentLabel = canvas.currentLabel,
                realPoint = canvas.relativePoint(e),
                startPortX = e.pageX - customShape.getAbsoluteX(),
                startPortY = e.pageY - customShape.getAbsoluteY();
            // empty the current selection so that the segment created by the
            // helper is always on top
            customShape.canvas.emptyCurrentSelection();

            if (currentLabel) {
                currentLabel.loseFocus();
                $(currentLabel.textField).focusout();
            }
            if (customShape.family !== "CustomShape") {
                return false;
            }
            customShape.setOldX(customShape.getX());
            customShape.setOldY(customShape.getY());
            customShape.startConnectionPoint.x = customShape.canvas.zoomFactor * realPoint.x;
            customShape.startConnectionPoint.y = customShape.canvas.zoomFactor * realPoint.y - canvas.getY();
        } else {
            customShape.canvas.currentConnection.disconnect();
        }
        return true;

    };
};

PMConnectHandler.prototype.onDragEnd = function (customShape) {
    return function (e, ui) {
        if (customShape.canvas.connectionSegment) {
            $(customShape.canvas.connectionSegment.getHTML()).remove();
            customShape.canvas.currentConnection.connect()
                .updateIncomingAndOutgoingConnections('create');
        }
    };
};

PMConnectHandler.prototype.onDrag = function (customShape) {
    var sourceShape,
        destinyShape,
        startPoint;
    return function (e, ui) {
        var connections;

        if (customShape.canvas.currentConnection) {
            canvas = customShape.canvas;
            endPoint = new PMUI.util.Point();

            if (canvas.connectionSegment) {
                $(canvas.connectionSegment.getHTML()).remove();
            }

            endPoint.x = e.pageX - canvas.getX() + canvas.getLeftScroll() - canvas.getAbsoluteX();
            endPoint.y = e.pageY - canvas.getY() + canvas.getTopScroll() - canvas.getAbsoluteY();

            //make connection segment
            otherPort = customShape.connection.srcPort.getPoint(false)
                .equals(customShape.getPoint(false)) ? customShape.connection.destPort :
                customShape.connection.srcPort;

            //remove related shapes
            sourceShape = customShape.connection.srcPort.getParent();
            destinyShape = customShape.connection.destPort.getParent();

            connections = PMFlow.getConnections(sourceShape, destinyShape, false);
            if (sourceShape && connections.length) {
                // as we are invoking PMFlow.getConnections() with false as third parameter the result can have
                // at most 1 element, that's why we use the index 0 in the arrays below.
                sourceShape.removeOutgoingConnection(connections[0]);
                destinyShape.removeIncomingConnection(connections[0]);
            }

            connections = PMFlow.getConnections(destinyShape, sourceShape, false);
            if (destinyShape && connections.length) {
                // as we are invoking PMFlow.getConnections() with false as third parameter the result can have
                // at most 1 element, that's why we use the index 0 in the arrays below.
                destinyShape.removeOutgoingConnection(connections[0]);
                sourceShape.removeIncomingConnection(connections[0]);
            }

            startPoint = otherPort.getPoint(false);
            startPoint.x = startPoint.x - canvas.getAbsoluteX();
            startPoint.y = startPoint.y - canvas.getAbsoluteY();

            canvas.connectionSegment = new PMUI.draw.Segment({
                startPoint: startPoint,
                endPoint: endPoint,
                parent: canvas
            });
            canvas.connectionSegment.createHTML();
            canvas.connectionSegment.paint();
        } else {
            customShape.canvas.isDraggingConnectHandler = true;
            var canvas = customShape.getCanvas(),
                endPoint = new PMUI.util.Point(),
                realPoint = canvas.relativePoint(e);
            if (canvas.connectionSegment) {
                //remove the connection segment in order to create another one
                $(canvas.connectionSegment.getHTML()).remove();
            }
            //Determine the point where the mouse currently is
            endPoint.x = realPoint.x * customShape.canvas.zoomFactor - canvas.getX();
            endPoint.y = realPoint.y * customShape.canvas.zoomFactor - canvas.getY();
            //creates a new segment from where the helper was created to the
            // currently mouse location
            canvas.connectionSegment = new PMUI.draw.Segment({
                startPoint: customShape.startConnectionPoint,
                endPoint: endPoint,
                parent: canvas,
                zOrder: PMUI.util.Style.MAX_ZINDEX * 2
            });
            //We make the connection segment point to helper in order to get
            // information when the drop occurs
            canvas.connectionSegment.pointsTo = customShape;
            //create HTML and paint
            //canvas.connectionSegment.createHTML();
            canvas.connectionSegment.paint();
        }
    };
};

PMConnectHandler.prototype.onClick = function (obj) {
    return function (e, ui) {
        alert('clicked');
    };
};

/**
 * Drag enter hook for this drop behavior, marks that a shape is over a
 * droppable element
 * @param {PMUI.draw.Shape} shape
 * @return {Function}
 */
PMConnectHandler.prototype.onDropOver = function (shape, handler) {
    return function (e, ui) {
    };
};

/**
 * Drag leave hook for this drop behavior, marks that a shape has left a
 * droppable element
 * @param {PMUI.draw.Shape} shape
 * @return {Function}
 */
PMConnectHandler.prototype.onDropOut = function (shape, handler) {
    return function (e, ui) {
        shape.entered = false;
        handler.style.addClasses(['pmConnnectHandler']);
    };
};
/**
 * On drop handler for this drop behavior, creates a connection between the
 * droppable element and the dropped element, or move ports among those shapes
 * @param {PMUI.draw.Shape} shape
 * @return {Function}
 */
PMConnectHandler.prototype.onDrop = function (shape, handler) {
    return function (e, ui) {
        var connection,
            sourceShape,
            canvas = shape.getCanvas(),
            id = ui.draggable.attr('id'),
            targetShape,
            originalType,
            currLeft,
            currTop,
            startPoint,
            sourcePort,
            endPort,
            endPortXCoord,
            endPortYCoord,
            port,
            command,
            validationResult;

        if (!shape.canvas.currentConnection) {
            shape.entered = false;
            //if its the helper then we need to create two ports and draw a
            // connection
            //we get the points and the corresponding shapes involved
            startPoint = shape.canvas.connectionSegment.startPoint;
            sourceShape = shape.canvas.connectionSegment.pointsTo;
            //determine the points where the helper was created
            if (sourceShape.parent && sourceShape.parent.id === shape.id) {
                return true;
            }
            validationResult = PMDesigner.connectValidator.isValid(sourceShape, shape);
            if (!validationResult.result) {
                //show invalid message
                PMDesigner.msgFlash(validationResult.msg, document.body, 'info', 3000, 5);
                return false;
            }
            sourceShape.setPosition(sourceShape.oldX, sourceShape.oldY);
            startPoint.x -= sourceShape.absoluteX - shape.canvas.getAbsoluteX();
            startPoint.y -= sourceShape.absoluteY - shape.canvas.getAbsoluteY();
            //create the ports
            sourcePort = new PMUI.draw.Port({
                width: 10,
                height: 10
            });
            endPort = new PMUI.draw.Port({
                width: 10,
                height: 10
            });

            //determine the position where the helper was dropped
            endPortXCoord = ui.offset.left - shape.canvas.getX() -
                shape.getAbsoluteX() + shape.canvas.getLeftScroll();
            endPortYCoord = ui.offset.top - shape.canvas.getY() -
                shape.getAbsoluteY() + shape.canvas.getTopScroll();
            // add ports to the corresponding shapes
            // addPort() determines the position of the ports
            sourceShape.addPort(sourcePort, startPoint.x, startPoint.y);
            shape.addPort(endPort, endPortXCoord, endPortYCoord,
                false, sourcePort);

            //add ports to the canvas array for regularShapes
            //shape.canvas.regularShapes.insert(sourcePort).insert(endPort);
            //create the connection
            connection = new PMFlow({
                srcPort: sourcePort,
                destPort: endPort,
                segmentColor: new PMUI.util.Color(0, 0, 0),
                name: " ",
                canvas: shape.canvas,
                segmentStyle: shape.connectionType.segmentStyle,
                flo_type: shape.connectionType.type
            });

            connection.setSrcDecorator(new PMUI.draw.ConnectionDecorator({
                width: 11,
                height: 11,
                canvas: canvas,
                decoratorPrefix: (typeof shape.connectionType.srcDecorator !== 'undefined'
                && shape.connectionType.srcDecorator !== null) ?
                    shape.connectionType.srcDecorator : "mafe-decorator",
                decoratorType: "source",
                parent: connection
            }));

            connection.setDestDecorator(new PMUI.draw.ConnectionDecorator({
                width: 11,
                height: 11,
                canvas: canvas,
                decoratorPrefix: (typeof shape.connectionType.destDecorator !== 'undefined'
                && shape.connectionType.destDecorator !== null) ?
                    shape.connectionType.destDecorator : "mafe-decorator",
                decoratorType: "target",
                parent: connection
            }));
            connection.canvas.commandStack.add(new PMUI.command.CommandConnect(connection));
            //connect the two ports
            connection.connect();
            connection.setSegmentMoveHandlers();
            //add the connection to the canvas, that means insert its html to
            // the DOM and adding it to the connections array
            canvas.addConnection(connection);
            // Filling PMFlow fields
            connection.setTargetShape(endPort.parent);
            connection.setOriginShape(sourcePort.parent);
            // now that the connection was drawn try to create the intersections
            connection.checkAndCreateIntersectionsWithAll();
            //attaching port listeners
            sourcePort.attachListeners(sourcePort);
            endPort.attachListeners(endPort);
            // finally trigger createEvent
            canvas.triggerCreateEvent(connection, []);
        } else {
            connection = shape.canvas.currentConnection;

            if (shape.canvas.dragConnectHandlers.get(0).id === id) {
                port = shape.canvas.dragConnectHandlers.get(0).relativeShape;
                targetShape = shape.canvas.dragConnectHandlers.get(1).relativeShape.parent;
                sourceShape = shape;

            } else if (shape.canvas.dragConnectHandlers.get(1).id === id) {
                port = shape.canvas.dragConnectHandlers.get(1).relativeShape;
                sourceShape = shape.canvas.dragConnectHandlers.get(0).relativeShape.parent;
                targetShape = shape;
            } else {
                port = null;
            }
            originalType = connection.flo_type;
            validationResult = PMDesigner.connectValidator.isValid(sourceShape, targetShape, connection);
            if (!validationResult.result) {
                //show invalid message
                PMDesigner.msgFlash(validationResult.msg, document.body, 'info', 3000, 5);
                return false;
            }
            if (originalType !== 'DEFAULT' && originalType !== targetShape.connectionType.type) {
                PMDesigner.msgFlash('Invalid connection type'.translate(), document.body, 'info', 3000, 5);
                targetShape.connectionType.type = originalType;
                return false;
            }

            command = new PMCommandReconnect(port, {
                shape: shape,
                x: ui.offset.left - shape.canvas.getX() - shape.getAbsoluteX() + shape.canvas.getLeftScroll(),
                y: ui.offset.top - shape.canvas.getY() - shape.getAbsoluteY() + shape.canvas.getTopScroll()
            });
            canvas.commandStack.add(command);
            command.execute();
            canvas.currentConnection = connection;
            canvas.triggerPortChangeEvent(port);
            $(canvas.connectionSegment.getHTML()).remove();
            canvas.connectionSegment = null;
            connection.showPortsAndHandlers();
            canvas.isDraggingConnectHandler = false;
            canvas.hideDropConnectHandlers();
        }
        return false;
    };
};


var PMShape = function (options) {
    PMUI.draw.CustomShape.call(this, options);
    this.extended = null;
    this.extendedType = null;
    this.relationship = null;

    this.midPointArray = [];
    this.htmlPorts = null;
    this.hasConnectHandlers = false;
    /**
     * Stores the label object used to show into the canvas
     * @type {Object}
     * @private
     */
    this.label = this.labels.get(0);
    this.diffXMidPoint = [-1, -4, -1, 4];
    this.diffYMidPoint = [4, -1, -4, -1];
    this.focusLabel = false;
    /**
     * Array of markers added to this activity
     * @type {Array}
     */
    this.markersArray = new PMUI.util.ArrayList();
    this.validatorMarker = null;
    this.errors = new PMUI.util.ArrayList();
    this.businessObject = {};
    /**
     * Connections going out from this element.
     * @type {Array}
     */
    this.outgoingConnections = null;
    /**
     * Connections going to this element.
     * @type {Array}
     */
    this.incomingConnections = null;
    PMShape.prototype.init.call(this, options);
};

PMShape.prototype = new PMUI.draw.CustomShape();

PMShape.prototype.type = 'PMShape';
PMShape.prototype.pmConnectionDropBehavior = null;
PMShape.prototype.pmContainerDropBehavior = null;
PMShape.prototype.supportedArray = [];
PMShape.prototype.init = function (options) {
    var defaults = {
        extended: {},
        relationship: {},
        focusLabel: false
    };
    if (options) {
        jQuery.extend(true, defaults, options);

        this.outgoingConnections = new PMUI.util.ArrayList();
        this.incomingConnections = new PMUI.util.ArrayList();

        this.setExtended(defaults.extended)
            .setExtendedType(defaults.extendedType)
            .setRelationship(defaults.relationship)
            .setIncomingConnections(defaults.incomingConnections)
            .setOutgoingConnections(defaults.outgoingConnections);
        if (defaults.markers) {
            this.addMarkers(defaults.markers, this);
        }
        if (defaults.validatorMarker) {
            this.addValidatorMarkers(defaults.validatorMarker, this);
        }
        if (defaults.corona) {
            this.setCorona(defaults.corona, this.getEspecificType(defaults));
        }
        this.focusLabel = defaults.focusLabel;
    }
};
/**
 * Sets the label element
 * @param {String} value
 * @return {*}
 */
PMShape.prototype.setName = function (value) {
    if (this.label) {
        this.label.setMessage(value);
    }
    return this;
};

/**
 * Returns the label text
 * @return {String}
 */
PMShape.prototype.getName = function () {
    var text = "";
    if (this.label) {
        text = this.label.getMessage();
    }
    return text;
};
PMShape.prototype.setExtendedType = function (type) {
    this.extendedType = type;
    return this;
};
PMShape.prototype.getDataObject = function () {
    return {};
};
PMShape.prototype.setRelationship = function (relationship) {
    this.relationship = relationship;
    return this;
};
PMShape.prototype.addRelationship = function (object) {
    if (typeof object === "object") {
        jQuery.extend(true, this.relationship, object);
    }
    return this;
};
PMShape.prototype.setExtended = function (extended) {
    var ext;
    ext = (typeof extended === 'object') ? extended : {};
    this.extended = ext;
    return this;
};
PMShape.prototype.getExtendedObject = function () {
    this.extended = {
        extendedType: this.extendedType
    };
    return this.extended;
};
PMShape.prototype.getMarkers = function () {
    return this.markersArray;
};
/**
 * Factory method for drop behaviors.
 * @param {String} type
 * @param {Array} selectors An array in which each element is a valid JQuery selector to specify the accepted elements by
 * the drop operation.
 * @returns {*}
 */
PMShape.prototype.dropBehaviorFactory = function (type, selectors) {
    if (type === 'pmconnection') {
        if (!this.pmConnectionDropBehavior) {
            this.pmConnectionDropBehavior = new PMConnectionDropBehavior(selectors);
        }
        return this.pmConnectionDropBehavior;
    } else if (type === 'pmcontainer') {
        if (!this.pmContainerDropBehavior) {
            this.pmContainerDropBehavior = new PMContainerDropBehavior(selectors);
        }
        return this.pmContainerDropBehavior;
    } else {
        return PMUI.draw.CustomShape.prototype.dropBehaviorFactory.call(this, type, selectors);
    }
};

PMShape.prototype.setDragBehavior = function (obj) {
    var factory = new PMUI.behavior.BehaviorFactory({
        products: {
            "pmsegment": PMSegmentDragBehavior,
            "customshapedrag": PMCustomShapeDragBehavior,
            "regulardrag": PMUI.behavior.RegularDragBehavior,
            "connectiondrag": PMUI.behavior.ConnectionDragBehavior,
            "connection": PMUI.behavior.ConnectionDragBehavior,
            "nodrag": PMUI.behavior.NoDragBehavior
        },
        defaultProduct: "nodrag"
    });
    this.drag = factory.make(obj);
    if (this.html && this.drag) {
        this.drag.attachDragBehavior(this);

    }
    if (this.canvas) {
        this.canvas.hideDragConnectHandlers();
    }
    return this;
};
/**
 * This function will attach all the listeners corresponding to the CustomShape
 * @chainable
 */
PMShape.prototype.attachListeners = function () {
    var that = this;
    if (this.html === null) {
        return this;
    }

    if (!this.canvas.readOnly) {
        var $customShape = $(this.html).click(this.onClick(this));
        $customShape.on("mousedown", this.onMouseDown(this));
        $customShape.mouseup(this.onMouseUp(this));
        $customShape.mouseover(this.onMouseOver(this));
        $customShape.mouseout(this.onMouseOut(this));
        $customShape.dblclick(this.onDblClick(this));
        $customShape.on("contextmenu", function (e) {
            e.preventDefault();
        });
        this.updateBehaviors();
    } else {
        if (this.canvas.hasClickEvent) {
            var $customShape = $(this.html).click(function (e) {
                if (that.hasClick) {
                    that.hasClick(e);
                }
            });
            this.updateBehaviors();
        }
    }
    return this;
};

PMShape.prototype.showConnectDropHelper = function (i, customShape) {
    var connectHandler, x, y;
    connectHandler = customShape.canvas.dropConnectHandlers.get(i);
    connectHandler.setDimension(18 * customShape.canvas.getZoomFactor(), 18 * customShape.canvas.getZoomFactor());
    x = customShape.getAbsoluteX() - customShape.canvas.getAbsoluteX() + customShape.xMidPoints[i] - connectHandler.width / 2 - 1;
    y = customShape.getAbsoluteY() - customShape.canvas.getAbsoluteY() + customShape.yMidPoints[i] - connectHandler.height / 2 - 1;
    if (customShape.parent.type !== 'PMCanvas') {
        x += 3;
        y += 2;
    }
    connectHandler.setPosition(x, y);
    connectHandler.relativeShape = customShape;
    connectHandler.attachDrop();
    connectHandler.setVisible(true);
    connectHandler.setZOrder(103);
};

/**
 * Handler for the onmousedown event, changes the draggable properties
 * according to the drag behavior that is being applied
 * @param {PMUI.draw.CustomShape} CustomShape
 * @returns {Function}
 */
PMShape.prototype.onMouseDown = function (customShape) {
    return function (e, ui) {
        var canvas = customShape.canvas;
        if (customShape.getType() === 'PMPool' || customShape.getType() === 'PMLane') {
            canvas.cancelConnect();
        }
        if (e.which === 3) {
            $(canvas.html).trigger("rightclick", [e, customShape]);
        } else {
            canvas.hideDragConnectHandlers();
            canvas.hideDropConnectHandlers();
            customShape.dragType = 2;
            if (customShape.dragType === customShape.DRAG) {
                if (!(customShape.getType() === 'PMEvent' && customShape.getEventType() === 'BOUNDARY')) {
                    customShape.setDragBehavior("customshapedrag");
                }
            } else if (customShape.dragType === customShape.CONNECT) {
            } else {
                customShape.setDragBehavior("nodrag");
            }
        }
        customShape.dragging = true;
        e.stopPropagation();
    };
};

/**
 * @event mouseup
 * Moused up callback fired when the user mouse ups on the `shape`
 * @param {PMUI.draw.Shape} shape
 */
PMShape.prototype.onMouseUp = function (customShape) {
    return function (e, ui) {
        e.preventDefault();
        if (customShape.canvas.canConnect
            && customShape.canvas.connectStartShape.getID() !== customShape.getID()
            && customShape.getType() !== 'PMPool'
            && customShape.getType() !== 'PMLane') {

            customShape.canvas.connectProcedure(customShape, e);
        }
        if (customShape.canvas.canCreateShape
            && customShape.canvas.connectStartShape.getID() !== customShape.getID()
            && (customShape.getType() === 'PMPool' || customShape.getType() === 'PMLane')) {
            customShape.canvas.manualCreateShape(customShape, e);
        }
    };
};
PMShape.prototype.showConnectDragHelpers = function (i, shape) {
    var y, x, connectHandler;
    connectHandler = shape.canvas.dragConnectHandlers.get(i);
    connectHandler.setDimension(15 * shape.canvas.getZoomFactor(), 15 * shape.canvas.getZoomFactor());
    x = shape.getAbsoluteX() - shape.canvas.getAbsoluteX() + shape.xMidPoints[i] - connectHandler.width / 2 - 1;
    y = shape.getAbsoluteY() - shape.canvas.getAbsoluteY() + 1 + shape.yMidPoints[i] - connectHandler.height / 2 - 1;
    if (shape.parent.type !== 'PMCanvas') {
        x += 3;
        y += 2;
    }
    connectHandler.setPosition(x, y);
    connectHandler.setVisible(true);
    connectHandler.relativeShape = shape;
    connectHandler.attachListeners();
};

PMShape.prototype.showAllConnectDragHelpers = function () {
    var shape = this, i, connectHandler;
    if (shape.canvas.isDragging || shape.canvas.currentLabel || shape.entered || shape.canvas.isResizing || PMUI.isCtrl) {
        shape.canvas.hideDragConnectHandlers();
        return;
    }
    if (!shape.canvas.isDraggingConnectHandler && !shape.dragging && !shape.canvas.currentSelection.find('id', shape.id) && !shape.canvas.currentConnection && !shape.canvas.isMouseDown) {
        if (shape.extendedType === "TEXT_ANNOTATION") {
            shape.canvas.hideDragConnectHandlers();
            shape.showConnectDragHelpers(3, shape);
            for (i = 0; i < shape.canvas.dragConnectHandlers.getSize(); i += 1) {
                connectHandler = shape.canvas.dragConnectHandlers.get(i);
                connectHandler.relativeShape = shape;
                shape.canvas.hightLightShape = shape;
                connectHandler.attachListeners();
            }
        } else {
            if (shape.extendedType !== "H_LABEL" && shape.extendedType !== "V_LABEL"
                && shape.extendedType !== "LANE" && shape.extendedType !== "POOL"
                && shape.extendedType !== "GROUP") {
                shape.canvas.hideDragConnectHandlers();
                shape.canvas.hightLightShape = shape;
                for (i = 0; i < 4; i += 1) {
                    shape.showConnectDragHelpers(i, shape);
                }
                shape.canvas.emptyCurrentSelection();
            }
        }
    }
};

PMShape.prototype.onClick = function (customShape) {
    return function (e) {
        var isCtrl = false,
            canvas = customShape.canvas,
            currentSelection = canvas.currentSelection,
            currentLabel = canvas.currentLabel;
        //hide all coronas
        customShape.canvas.hideAllCoronas();
        if (e.ctrlKey) {
            isCtrl = true;
        }
        // hide the current connection if there was one
        customShape.canvas.hideCurrentConnection();
        if (e.which === 3) {
            e.preventDefault();
            // trigger right click
            customShape.canvas.triggerRightClickEvent(customShape);
        } else {
            if (!customShape.wasDragged) {
                // if the custom shape was not dragged (this var is set to true
                // in custom_shape_drag_behavior >> onDragEnd)
                if (isCtrl) {
                    if (currentSelection.contains(customShape)) {
                        // remove from the current selection
                        canvas.removeFromSelection(customShape);
                    } else {
                        // add to the current selection
                        canvas.addToSelection(customShape);
                    }

                } else {
                    canvas.emptyCurrentSelection();
                    canvas.addToSelection(customShape);
                    if (!customShape.canvas.canConnect) {
                        if (customShape.canvas.currentLabel === null && customShape.corona) {
                            customShape.corona.show();
                        }
                    }
                    canvas.coronaShape = customShape;
                }
            }
            if (!currentSelection.isEmpty()) {
                canvas.triggerSelectEvent(currentSelection.asArray());
            }
        }
        if (this.helper) {
            $(this.helper.html).remove();
        }
        customShape.wasDragged = false;
        e.stopPropagation();
    };
};
PMShape.prototype.onDblClick = function (customShape) {
    return function (e) {
        customShape.canvas.hideAllFocusLabels();
        customShape.label.getFocus();
        e.preventDefault();
        e.stopPropagation();
    };
};
PMShape.prototype.onMouseOver = function (customShape) {
    return function (e, ui) {
        var layer,
            canConnect = true;
        if (customShape.canvas.canConnect
            && customShape.canvas.connectStartShape.getID() !== customShape.getID()
            && customShape.getType() !== 'PMPool'
            && customShape.getType() !== 'PMLane') {
            //validate if can connect green, else is red
            layer = customShape.getLayers().find('layerName', 'second-layer');
            if (layer) {
                layer.setVisible(true);
                if (!PMDesigner.connectValidator.isValid(customShape.canvas.connectStartShape, customShape).result) {
                    canConnect = false;
                }
                layer.removeCSSClasses(['mafe-can-not-connect-layer', 'mafe-can-connect-layer']);
                if (canConnect) {
                    layer.addCSSClasses(['mafe-can-connect-layer']);
                } else {
                    layer.addCSSClasses(['mafe-can-not-connect-layer']);
                }
            }
        }
        e.preventDefault();
        e.stopPropagation();
    };
};
PMShape.prototype.onMouseOut = function (customShape) {
    var that = this;
    return function (e, ui) {
        var layer;
        customShape.dragging = false;
        e.stopPropagation();
        layer = customShape.getLayers().find('layerName', 'second-layer');
        if (layer) {
            layer.setVisible(false);
        }
    };
};

/**
 * Overwrite the parent function to set the dimension
 * @param {Number} x
 * @param {Number} y
 * @return {*}
 */
PMShape.prototype.setDimension = function (x, y) {
    var factor = 1;
    PMUI.draw.CustomShape.prototype.setDimension.call(this, x, y);
    if (this.getType() === 'PMEvent' || this.getType() === 'PMGateway' || this.getType() === 'PMData') {
        factor = 3;
    }
    if (this.label) {
        //validation for vertical labels case pool and lanes
        if (this.getType() === 'PMPool' || this.getType() === 'PMLane') {
            this.label.updateDimension(false);
        } else {
            this.label.setDimension((this.zoomWidth * 0.9 * factor) / this.canvas.zoomFactor,
                this.label.height);
            this.label.setLabelPosition(this.label.location, this.label.diffX, this.label.diffY);
            this.label.updateDimension(false);
        }
    }
    if (this.getType() === 'PMPool') {
        this.paint();
    }
    return this;
};
/**
 * Creates a drag helper for drag and drop operations for the helper property
 * in jquery ui draggable
 * @returns {String} html
 */
PMShape.prototype.createDragHelper = function () {
    var html = document.createElement("div");
    html.style.width = 8 + "px";
    html.style.height = 8 + "px";
    html.style.borderRadius = "5px";
    html.style.marginTop = "-5px";
    html.style.marginLeft = "-5px";
    html.style.backgroundColor = "rgb(92, 156, 204)";
    html.style.zIndex = 2 * PMUI.draw.Shape.prototype.MAX_ZINDEX;
    html.id = "drag-helper";
    html.className = "drag-helper";
    return html;
};

PMShape.prototype.getContextMenu = function () {
    return {};
};

PMShape.prototype.getHTMLPorts = function () {
    return this.htmlPorts;
};

PMShape.prototype.updatePropertiesHTMLPorts = function () {
    var items = jQuery(this.htmlPorts).children(), k,
        point = this.midPointArray;
    for (k = 0; k < items.length; k += 1) {
        items[k].style.left = point[k].x + "px";
        items[k].style.top = point[k].y + "px";
    }

    return this;
};

/**
 * Adds markers to the arrayMarker property
 * @param {Array} markers
 * @param {Object} parent
 * @return {*}
 */
PMShape.prototype.addMarkers = function (markers, parent) {
    var newMarker, i, factoryMarker;
    if (jQuery.isArray(markers)) {
        for (i = 0; i < markers.length; i += 1) {
            factoryMarker = markers[i];
            factoryMarker.parent = parent;
            factoryMarker.canvas = parent.canvas;
            newMarker = new PMMarker(factoryMarker);
            this.markersArray.insert(newMarker);
        }
    }
    return this;
};
/**
 * Adds markers to the arrayMarker property
 * @param {Array} markers
 * @param {Object} parent
 * @return {*}
 */
PMShape.prototype.addValidatorMarkers = function (marker, parent) {
    marker.parent = parent;
    marker.canvas = parent.canvas;
    this.validatorMarker = new PMVAlidatorMarker(marker);
    return this;
};
/**
 * Adds a corona to shape
 * @param corona
 */
PMShape.prototype.setCorona = function (options, type) {
    var self = this,
        objectCrown,
        typeEspecific = "DEFAULT";

    if (this.getType() !== "PMActivity") {
        typeEspecific = type;
    }
    objectCrown = {
        parent: self,
        parentType: typeEspecific,
        canvas: self.canvas
    };
    $.extend(true, objectCrown, options);
    this.corona = new Corona(objectCrown);
    return this;
};
PMShape.prototype.updateLabelsPosition = function () {
    var i,
        label;
    for (i = 0; i < this.labels.getSize(); i += 1) {
        label = this.labels.get(i);
        label.setLabelPosition(label.location, label.diffX, label.diffY);
        label.paint();
    }
    return this;
};
/**
 * Paint the shape
 */
PMShape.prototype.paint = function () {
    var m, marker, size, width;
    PMUI.draw.CustomShape.prototype.paint.call(this);
    size = this.markersArray.getSize();
    for (m = 0; m < size; m += 1) {
        marker = this.markersArray.get(m);
        marker.paint();
    }
    if (this.validatorMarker) {
        this.validatorMarker.paint();
    }
    if (this.corona) {
        this.corona.paint();
        this.corona.hide();
    }
    if (this.getType() === 'PMActivity') {
        width = this.getWidth() - 20;
        this.label.textField.style.width = width + 'px';
    }
};
/**
 * Extends fixZIndex from shape class
 * @chainable
 */
PMShape.prototype.fixZIndex = function (shape, value) {
    var i,
        port;
    PMUI.draw.CustomShape.prototype.fixZIndex.call(this, shape, value);
    // force to z-order if container parent is the canvas
    for (i = 0; i < shape.ports.getSize(); i += 1) {
        port = shape.ports.get(i);
        if (port.connection.getSrcPort().getParent().getParent().getType() === 'PMCanvas' &&
            port.connection.getDestPort().getParent().getParent().getType() === 'PMCanvas') {
            port.connection.setZOrder(1);
        }
    }
};

/**
 *  Extend applyZoom of CustomShape for apply Zoom into Markers
 *  @return {*}
 */
PMShape.prototype.applyZoom = function () {
    var i, marker;
    PMUI.draw.CustomShape.prototype.applyZoom.call(this);
    for (i = 0; i < this.markersArray.getSize(); i += 1) {
        marker = this.markersArray.get(i);
        marker.applyZoom();
    }
    if (this.validatorMarker) {
        this.validatorMarker.applyZoom();
    }
    return this;
};
/**
 * Set flow as a default and update the other flows
 * @param {String} destID
 * @returns {AdamShape}
 */
PMShape.prototype.setDefaultFlow = function (floID) {
    var i,
        port,
        connection;
    for (i = 0; i < this.getPorts().getSize(); i += 1) {
        port = this.getPorts().get(i);
        connection = port.connection;
        this.updateDefaultFlow(0);
        if (connection.srcPort.parent.getID() === this.getID()) {
            if (connection.getID() === floID) {
                this.updateDefaultFlow(floID);
                connection.setFlowCondition("");
                connection.changeFlowType('default');
                connection.setFlowType("DEFAULT");
            } else if (connection.getFlowType() === 'DEFAULT') {
                connection.changeFlowType('sequence');
                connection.setFlowType("SEQUENCE");
            }
        }

    }
    return this;
};

PMShape.prototype.hideAllChilds = function () {
    var i,
        child,
        j,
        flow,
        arrayFlow = {};
    for (i = 0; i < this.getChildren().getSize(); i += 1) {
        child = this.getChildren().get(i);
        child.hideElement();
    }
    this.canvas.hideFlowRecursively(this);
};

PMShape.prototype.showAllChilds = function () {
    var i, child;
    for (i = 0; i < this.getChildren().getSize(); i += 1) {
        child = this.getChildren().get(i);
        child.showElement();
    }
};

PMShape.prototype.hideElement = function () {
    this.html.style.visibility = 'hidden';
    return this;
};

PMShape.prototype.showElement = function () {
    this.html.style.visibility = 'visible';
    return this;
};

PMShape.prototype.getBpmnElementType = function () {
    var map = {
        'TASK': 'bpmn:Task',
        'START': 'bpmn:StartEvent',
        'END': 'bpmn:EndEvent',
        'EXCLUSIVE': 'bpmn:ExclusiveGateway',
        'INCLUSIVE': 'bpmn:InclusiveGateway',
        'PARALLEL': 'bpmn:ParallelGateway',
        'COMPLEX': 'bpmn:ComplexGateway',
        'EVENTBASED': 'bpmn:EventBasedGateway',
        'SUB_PROCESS': 'bpmn:SubProcess',
        'INTERMEDIATE': 'bpmn:IntermediateCatchEvent',
        'BOUNDARY': 'bpmn:BoundaryEvent',
        'TEXT_ANNOTATION': 'bpmn:TextAnnotation',
        'GROUP': 'bpmn:Group',
        'PARTICIPANT': 'bpmn:Participant',
        'POOL': 'bpmn:Participant',
        'LANE': 'bpmn:Lane',
        'DATASTORE': 'bpmn:DataStore'
    };
    if (this.evn_type === 'INTERMEDIATE' && this.evn_behavior === 'THROW') {
        return 'bpmn:IntermediateThrowEvent';
    } else {
        return map[this.extendedType];
    }
};

PMShape.prototype.createWithBpmn = function (bpmnElementType, name) {
    var businessObject = {};
    businessObject.elem = PMDesigner.bpmnFactory.create(bpmnElementType, {
        id: 'el_' + this.id,
        name: this.getName() ? PMDesigner.escapeXMLCharacters(this.getName()) : ""
    });
    if (!businessObject.di) {
        if (this.type === 'PMParticipant' || this.type === 'PMPool' || this.type === 'PMLane') {
            businessObject.di = PMDesigner.bpmnFactory.createDiShape(businessObject.elem, {}, {
                id: 'di_' + businessObject.elem.id,
                isHorizontal: true
            });
        } else {
            businessObject.di = PMDesigner.bpmnFactory.createDiShape(businessObject.elem, {}, {
                id: 'di_' + businessObject.elem.id
            });
        }
    }
    this[name] = businessObject;
};

PMShape.prototype.createHandlers = function (type, number, resizableStyle, nonResizableStyle) {
    var i;
    if (type === "Rectangle") {
        //First determine how many ResizeHandlers we are to create
        if (!number || (number !== 8 &&
            number !== 4 && number !== 0)) {
            number = 4;
        }
        //Then insert the corners first
        for (i = 0; i < number && i < 4; i += 1) {
            this.cornerResizeHandlers.insert(
                new PMUI.draw.ResizeHandler({
                    parent: this,
                    zOrder: PMUI.util.Style.MAX_ZINDEX + 23,
                    representation: new PMUI.draw.Rectangle(),
                    orientation: this.cornersIdentifiers[i],
                    resizableStyle: resizableStyle,
                    nonResizableStyle: nonResizableStyle
                })
            );
        }
        //subtract 4 just added resize points to the total
        number -= 4;
        //add the rest to the mid list
        for (i = 0; i < number; i += 1) {
            this.midResizeHandlers.insert(
                new PMUI.draw.ResizeHandler({
                    parent: this,
                    zOrder: PMUI.util.Style.MAX_ZINDEX + 23,
                    representation: new PMUI.draw.Rectangle(),
                    orientation: this.midPointIdentifiers[i],
                    resizableStyle: resizableStyle,
                    nonResizableStyle: nonResizableStyle
                })
            );
        }
    }
    return this;
};

/**
 * Recursive method to get correct parent busines object
 * @param root
 * @returns {PMPool}
 */
PMShape.prototype.getPoolParent = function (root) {
    while (root.getType() !== 'PMPool') {
        root = root.parent;
    }
    return root;
};
/**
 *
 * @param businessObject
 * @param parentBusinessObject
 */
PMShape.prototype.updateShapeParent = function (businessObject, parentBusinessObject) {
    var pool,
        parentDi,
        parentBusinessObjectAux = {};
    parentDi = parentBusinessObject && parentBusinessObject.di;
    //regular parent change
    if (parentBusinessObject.elem &&
        parentBusinessObject.elem.$type === 'bpmn:Lane') {
        //text annotation Data store Data object into lane
        if (!businessObject.elem.$lane && parentBusinessObject.elem) {
            businessObject.elem.$lane = parentBusinessObject.elem;
        }
        if (businessObject.elem.$type !== 'bpmn:TextAnnotation'
            && businessObject.elem.$type !== 'bpmn:DataStoreReference'
            && businessObject.elem.$type !== 'bpmn:DataObjectReference') {
            this.parent.updateLaneSetParent(businessObject, parentBusinessObject);
        }
        pool = this.getPoolParent(this.parent);
        parentBusinessObjectAux = pool.businessObject;
        parentDi = parentBusinessObjectAux && parentBusinessObjectAux.di;
        this.updateSemanticParent(businessObject, parentBusinessObjectAux);
    } else {
        this.updateSemanticParent(businessObject, parentBusinessObject);
    }
    this.updateDiParent(businessObject.di, parentDi);
};


PMShape.prototype.updateSemanticParent = function (businessObject, newParent) {
    var children;
    if (businessObject.elem.$parent === newParent.elem) {
        return;
    }
    if (businessObject.elem.$parent) {
        // remove from old parent
        children = businessObject.elem.$parent.get('flowElements');
        CollectionRemove(children, businessObject.elem);
    }

    if (!newParent.elem) {
        businessObject.elem.$parent = null;
    } else {
        // add to new parent
        children = newParent.elem.get('flowElements');
        children.push(businessObject.elem);
        businessObject.elem.$parent = newParent.elem;
    }
};

PMShape.prototype.updateDiParent = function (di, parentDi) {
    var planeElements;
    if (parentDi && !parentDi.$instanceOf('bpmndi:BPMNPlane')) {
        parentDi = parentDi.$parent;
    }
    if (di.$parent === parentDi) {
        return;
    }
    planeElements = (parentDi || di.$parent).get('planeElement');
    if (parentDi) {
        planeElements.push(di);
        di.$parent = parentDi;
    } else {
        CollectionRemove(planeElements, di);
        di.$parent = null;
    }
};

PMShape.prototype.updateBounds = function (di) {
    var bounds = this.type === 'label' ? this._getLabel(di).bounds : di.bounds,
        x = this.getX(), y = this.getY(),
        parent = this.parent;
    while (parent.type !== 'PMCanvas') {
        x = parent.getX() + x;
        y = parent.getY() + y;
        parent = parent.parent;
    }

    _.extend(bounds, {
        x: x,
        y: y,
        width: this.width,
        height: this.height
    });
};

PMShape.prototype._getLabel = function (di) {
    if (!di.label) {
        di.label = PMDesigner.bpmnFactory.createDiLabel();
    }
    return di.label;
};

PMShape.prototype.createBpmn = function (type) {
    if (!this.businessObject.elem && !(this instanceof PMUI.draw.MultipleSelectionContainer)) {
        this.createWithBpmn(type, 'businessObject');
    }
    this.updateBounds(this.businessObject.di);
    if (this.parent.getType() === 'PMCanvas' && !this.parent.businessObject.di) {
        this.canvas.createBPMNDiagram();
    }
    if (this.parent.businessObject.elem) {
        this.updateShapeParent(this.businessObject, this.parent.businessObject);
    } else {
        this.parent.createBusinesObject();
        this.updateShapeParent(this.businessObject, this.parent.businessObject);
    }
};

PMShape.prototype.removeBpmn = function () {
    var parentShape = this.parent;
    var businessObject = this.businessObject,
        parentBusinessObject = parentShape && parentShape.businessObject.elem,
        parentDi = parentBusinessObject && parentBusinessObject.di;

    if (this.parent.businessObject.elem
        && this.parent.businessObject.elem.$type === 'bpmn:Lane') {
        this.parent.updateLaneSetParent(businessObject, {elem: null});
    } else if (this.parent.type === 'PMCanvas') {
        this.parent.updateCanvasProcess();
    }
    this.updateSemanticParent(businessObject, {elem: null});
    this.updateDiParent(businessObject.di);
};

PMShape.prototype.updateBpmn = function () {
    this.updateBounds(this.businessObject.di);
    if (!this.parent.businessObject.elem) {
        this.parent.createBusinesObject();
    }
    this.updateShapeParent(this.businessObject, this.parent.businessObject);
};

PMShape.prototype.updateLaneSetParent = function (businessObject, newParent) {
    if (businessObject.elem.$lane) {
        children = businessObject.elem.$lane.get('flowNodeRef');
        CollectionRemove(children, businessObject.elem);
    }
    if (!newParent.elem) {
    } else {
        // add to new parent
        children = newParent.elem.get('flowNodeRef');
        children.push(businessObject.elem);
        businessObject.elem.$lane = newParent.elem;
    }
};

PMShape.prototype.setBPPMName = function (name) {
    if (this.businessObject && this.businessObject.elem) {
        this.businessObject.elem.name = name;
    }
    if (this.participantObject && this.participantObject.elem) {
        this.participantObject.elem.name = name;
    }
};

PMShape.prototype.isSupported = function () {
    return true;
};
/**
 * Get the number total of Errors and Warnings.
 * @returns {*}
 */
PMShape.prototype.getNumErrors = function () {
    var numErrors = 0;
    if (this.errors) {
        numErrors = this.errors.getSize();
    }
    return numErrors;
};
/**
 * Get Array of Errors.
 * @returns {*|Array}
 */
PMShape.prototype.getArrayErrors = function () {
    var arrayErrors = [];
    if (this.errors) {
        arrayErrors = this.errors.asArray();
    }
    return arrayErrors;
};
/**
 * Add new Error to Array of Errors.
 * @param error
 */
PMShape.prototype.addErrorLog = function (error) {
    error.name = this.getName();
    this.errors.insert(error);
    error.id = this.getID();
    PMDesigner.validTable.row.add([
            null,
            error.id,
            error.severity,
            error.name,
            error.type,
            error.description
        ]
    ).draw();
};
/**
 * Validate the Number of Errors of type Warning.
 * @param arrayErrors
 * @returns {Boolean}
 */
PMShape.prototype.validateWarning = function (arrayErrors) {
    var arrayLength,
        sw = false,
        i = 0;

    if (_.isArray(arrayErrors)) {
        sw = true;
        arrayLength = arrayErrors.length;
        while (arrayLength > 0) {
            if (arrayErrors[i].severity !== "Warning") {
                sw = false;
                arrayLength = 0;
            }
            arrayLength -= 1;
            i += 1;
        }
    }

    return sw;
};
/**
 * Set the severity Error (Error, Warning).
 * @param typeString
 */
PMShape.prototype.setTypeErrors = function (typeString) {
    var styleMarker;
    if (this.validatorMarker.styleMarker) {
        styleMarker = this.validatorMarker.styleMarker;
    }
    styleMarker.setTypeMarker(typeString);
};
/**
 * Validate connections between shapes.
 * @returns {boolean}
 * @constructor
 */
PMShape.prototype.ValidateConnections = function () {
    var ports = this.getPorts().asArray(),
        connection,
        segmentStyle,
        i,
        shapeSrc,
        shapeDest,
        isValid = true,
        maxPorts = ports.length;

    for (i = 0; i < maxPorts; i += 1) {
        connection = ports[i].getConnection();
        segmentStyle = connection.getSegmentStyle();
        if (connection && segmentStyle === "regular") {
            shapeSrc = connection.getSrcPort().getParent();
            shapeDest = connection.getDestPort().getParent();

            if (!this.ValidateContainer(shapeSrc.getParent(), shapeDest.getParent())) {
                isValid = false;
            }
        }
    }

    return isValid;
};
/**
 * Validate the container between two shapes.
 * @param shapeSrc
 * @param shapeDest
 * @returns {boolean}
 * @constructor
 */
PMShape.prototype.ValidateContainer = function (shapeSrc, shapeDest) {
    var equLevel = true;

    if (shapeSrc.getType() === shapeDest.getType()) {
        switch (shapeSrc.getType()) {
            case "PMPool":
                equLevel = (shapeSrc.id !== shapeDest.id) ? false : true;
                break;
            case "PMLane":
                equLevel = (this.getPoolParent(shapeSrc).id !== this.getPoolParent(shapeDest).id) ? false : true;
                break;
            default:
                equLevel = true;
        }
    } else {
        equLevel = false;
    }

    return equLevel;
};

PMShape.prototype.poolChildConnectionOnResize = function (resizing, root, delta, rootType) {
    var i,
        port,
        child,
        connection;
    if (root) {
        if (this.ports) {
            for (i = 0; i < this.ports.getSize(); i += 1) {
                port = this.ports.get(i);
                connection = port.connection;
                this.recalculatePortPosition(port);
                connection.disconnect().connect();
                if (!this.resizing) {
                    connection.setSegmentMoveHandlers();
                    connection.checkAndCreateIntersectionsWithAll();
                }
            }
        }
    } else {
        if (this.ports) {
            this.registerResizeConnection();
        }
    }
    // children
    for (i = 0; i < this.children.getSize(); i += 1) {
        child = this.children.get(i);
        child.setPosition(child.x, child.y);
        child.poolChildConnectionOnResize(child.resizing, false, delta, rootType);
    }
    return this;
};

PMShape.prototype.registerResizeConnection = function () {
    var i,
        port,
        connection;
    for (i = 0; i < this.ports.getSize(); i += 1) {
        port = this.ports.get(i);
        port.setPosition(port.x, port.y);
        connection = port.connection;
        if (!this.canvas.refreshArray.contains(connection)) {
            connection.canvas.refreshArray.insert(connection);
        }
    }
};

PMShape.prototype.fixConnectionsOnResize = function (resizing, root) {
    var i,
        port,
        child,
        connection,
        zoomFactor = this.canvas.zoomFactor;
    if (root) {
        if (this.ports) {
            // connections
            for (i = 0; i < this.ports.getSize(); i += 1) {
                port = this.ports.get(i);
                connection = port.connection;
                this.recalculatePortPosition(port);
                connection.disconnect().connect();
                if (!this.resizing) {
                    connection.setSegmentMoveHandlers();
                    connection.checkAndCreateIntersectionsWithAll();
                }
            }
        }
    } else {
        if (this.ports) {
            this.registerResizeConnection();
        }
    }
    // children
    for (i = 0; i < this.children.getSize(); i += 1) {
        child = this.children.get(i);
        child.setPosition(child.x, child.y);
        child.fixConnectionsOnResize(child.resizing, false);
    }
    return this;
};

PMShape.prototype.refreshChildrenPositions = function (onCommand, delta) {
    var i,
        children = this.children,
        child;
    for (i = 0; i < children.getSize(); i += 1) {
        child = children.get(i);
        child.setPosition(child.getX(), child.getY());
        child.refreshConnections(false, true);
        if (onCommand) {
            child.refreshAllMovedConnections(false, delta);
        }
        child.refreshChildrenPositions(onCommand, delta);
    }
    return this;
};

PMShape.prototype.refreshAllPoolConnections = function (inContainer, delta, rootType) {
    var i,
        connection,
        max;
    for (i = 0, max = this.canvas.refreshArray.getSize(); i < max; i += 1) {
        connection = this.canvas.refreshArray.get(i);
        connection.reconectSwitcher(delta, inContainer, rootType);
    }
    return this;
};

PMShape.prototype.laneRefreshConnections = function (inContainer, delta, rootType) {
    var i,
        connection,
        max;
    for (i = 0, max = this.canvas.refreshArray.getSize(); i < max; i += 1) {
        connection = this.canvas.refreshArray.get(i);
        connection.reconectSwitcher(delta, inContainer, rootType);
    }
    return this;
};

PMShape.prototype.refreshAllMovedConnections = function (inContainer, delta, rootType) {
    var i,
        connection,
        ports = this.ports,
        port,
        max;

    for (i = 0, max = ports.getSize(); i < max; i += 1) {
        port = ports.get(i);
        port.setPosition(port.x, port.y);
        connection = port.connection;
        if (!this.canvas.connToRefresh.contains(connection)) {
            connection.canvas.connToRefresh.insert(connection);
        }
    }
    return this;
};

PMShape.prototype.refreshChildConnections = function (inContainer, delta, rootType) {
    var i,
        connection,
        max;
    for (i = 0, max = this.canvas.refreshArray.getSize(); i < max; i += 1) {
        connection = this.canvas.refreshArray.get(i);
        connection.reconectSwitcher(delta, inContainer, rootType);
    }
    return this;
};

PMShape.prototype.refreshShape = function () {
    PMUI.draw.Shape.prototype.refreshShape.call(this);
    this.updatePortsOnZoom();
    this.paint();
    return this;
};
/**
 * Sets the incoming connections for the element.
 * @param elements{Array}
 * @returns {PMShape}
 */
PMShape.prototype.setIncomingConnections = function(elements){
    var i;
    if (jQuery.isArray(elements)) {
        this.incomingConnections.clear();
        for (i = 0; i < elements.length; i += 1) {
            this.addIncomingConnection(elements[i]);
        }
    }
    return this;
};
/**
 * Add an incoming connection.
 * @param element{PMShape}
 * @returns {PMShape}
 */
PMShape.prototype.addIncomingConnection = function(element){
    this.incomingConnections.insert(element);
    return this;
};
/**
 * Remove an incoming connection.
 * @param element{PMShape}
 * @returns {PMShape}
 */
PMShape.prototype.removeIncomingConnection = function(element){
    this.incomingConnections.remove(element);
    return this;
};
/**
 * Return the list of incoming connections.
 * @param {...String} [types] Optional, returns only the connections of the specified types.
 * @returns {Array}
 */
PMShape.prototype.getIncomingConnections = function(){
    var validTypes = [],
        i;

    if (arguments.length) {
        for (i = 0; i < arguments.length; i += 1) {
            validTypes.push(arguments[i]);
        }

        return this.incomingConnections.asArray().filter(function (i) {
            return validTypes.indexOf(i.flo_type) >= 0;
        });
    }
    return this.incomingConnections.asArray().slice(0);
};
/**
 * Returns the list of the elements connected to this element's incoming connections.
 * @param {...String} [connection_types] The incoming elements whose connections are of this specified type, optional.
 * @returns {Array|*|{applyDefaultStyles, childOptions, initChildLayout, destroyChildLayout, resizeChildLayout, resizeNestedLayout, resizeWhileDragging, resizeContentWhileDragging, triggerEventsWhileDragging, maskIframesOnResize, useStateCookie, [cookie.autoLoad], [cookie.autoSave], [cookie.keys], [cookie.name], [cookie.domain], [cookie.path], [cookie.expires], [cookie.secure], noRoomToOpenTip, togglerTip_open, togglerTip_closed, resizerTip, sliderTip}}
 */
PMShape.prototype.getIncomingElements = function () {
    return this.getIncomingConnections.apply(this, arguments).map(function (i) {
        return i.getSrcPort().getParent();
    });
};
/**
 * Sets all the outgoing connections for the element.
 * @param elements{Array}
 * @returns {PMShape}
 */
PMShape.prototype.setOutgoingConnections = function(elements){
    var i;
    if (jQuery.isArray(elements)) {
        this.outgoingConnections.clear();
        for (i = 0; i < elements.length; i += 1) {
            this.addOutgoinConnections(elements[i]);
        }
    }
    return this;
};
/**
 * Add an outgoing connection to the element.
 * @param element{PMShape}
 * @returns {PMShape}
 */
PMShape.prototype.addOutgoingConnection = function(element){
    this.outgoingConnections.insert(element);
    return this;
};
/**
 * Remove an outgoing connection to the element.
 * @param element{PMShape}
 * @returns {PMShape}
 */
PMShape.prototype.removeOutgoingConnection = function(element){
    this.outgoingConnections.remove(element);
    return this;
};
/**
 * Return the list of outgoing connections.
 * @param {...String} [types] Optional, returns only the connections of the specified types.
 * @returns {Array}
 */
PMShape.prototype.getOutgoingConnections = function(){
    var validTypes = [],
        i;

    if (arguments.length) {
        for (i = 0; i < arguments.length; i += 1) {
            validTypes.push(arguments[i]);
        }

        return this.outgoingConnections.asArray().filter(function (i) {
            return validTypes.indexOf(i.flo_type) >= 0;
        });
    }

    return this.outgoingConnections.asArray().slice(0);
};
/**
 * Returns a list of the elements connected to the element's outgoing connections.
 * @param {...String} [type] Optional, returns only the connections of the specified type.
 * @returns {Array}
 */
PMShape.prototype.getOutgoingElements = function (type) {
    return this.getOutgoingConnections.apply(this, arguments).map(function (i) {
        return i.getDestPort().getParent();
    });
};
/**
 * Get Especific Type of the Shape or the object parameter
 * @param object
 * @returns {string}
 */
PMShape.prototype.getEspecificType = function (object) {
    var especificType = "DEFAULT",
        typeShape = this.getType(),
        shape = this;
    if (typeof object === "object" && !jQuery.isEmptyObject(object)) {
        shape = object;
    }
    switch (typeShape) {
        case "PMActivity":
            especificType = shape.act_task_type;
            break;
        case "PMGateway":
            especificType = shape.gat_type;
            break;
        case "PMEvent":
            especificType = shape.evn_type + "_" + shape.evn_marker;
            break;
        case "PMArtifact":
            especificType = shape.art_type;
            break;
        case "PMData":
            especificType = shape.dat_type;
            break;
    }
    return especificType;
};
/**
 * @class PMFlow
 * Handle the designer flows
 *
 * @constructor
 * Create a new flow object
 * @param {Object} options
 */
var PMFlow = function (options) {
    PMUI.draw.Connection.call(this, options);
    /**
     * Unique Idenfier
     * @type {String}
     */
    this.flo_uid = null;
    /**
     * Defines the connecion/flow type
     * @type {String}
     */
    this.flo_type = null;
    /**
     * Defines the connection/flow name
     * @type {String}
     */
    this.flo_name = null;
    /**
     * Unique Identifier of the source shape
     * @type {String}
     */
    this.flo_element_origin = null;
    /**
     * Defines the type of shape for the source
     * @type {String}
     */
    this.flo_element_origin_type = null;
    /**
     * Unique Identifier of the target shape
     * @type {String}
     */
    this.flo_element_dest = null;
    /**
     * Defines the type of shape for the target
     * @type {String}
     */
    this.flo_element_dest_type = null;
    /**
     * Defines if the flow was followed inmediately
     * @type {Boolean}
     */
    this.flo_is_inmediate = null;
    /**
     * Defines the condition to follow the flow
     * @type {String}
     */
    this.flo_condition = null;
    /**
     * X1 Coordinate
     * @type {Number}
     */
    this.flo_x1 = null;
    /**
     * Y1 Coordinate
     * @type {Number}
     */
    this.flo_y1 = null;
    /**
     * X2 Coordinate
     * @type {Number}
     */
    this.flo_x2 = null;
    /**
     * Y2 Coordinate
     * @type {Number}
     */
    this.flo_y2 = null;
    /**
     * Array of segments that conform the connection
     * @type {Array}
     */
    this.flo_state = null;

    this.label = null;

    this.algorithm = 'manhattan';

    PMFlow.prototype.init.call(this, options);
};
/**
 * Return all the connections between two elements.
 * @param {PMShape} sourceElement The source element from which an outgoing connection will be searched
 * @param {PMShape} destElement The destination element the connection must income to.
 * @param {Boolean} [ignoreDirection = false] If true, all connections between the two elements will be returned regardless the direction.
 * @returns {Boolean}
 */
PMFlow.getConnections = function (sourceElement, destElement, ignoreDirection) {
    var result;

    result = sourceElement.getOutgoingConnections().filter(function (i) {
        return i.getDestPort().getParent() === destElement;
    });

    if (ignoreDirection) {
        result.concat(sourceElement.getIncomingConnections().filter(function (i) {
            return i.getSrcPort().getParent === destElement;
        }));
    }

    return result;
};
/**
 * Check if there is any connection between two elements.
 * @param {PMShape} sourceElement The source element from which an outgoing connection will be searched
 * @param {PMShape} destElement The destination element the connection must income to.
 * @param {Boolean} [ignoreDirection = false] If true, all connections between the two elements will be searched regardless the direction.
 * @returns {Boolean}
 */
PMFlow.existsConnection = function (sourceElement, destElement, ignoreDirection) {
    return PMFlow.getConnections(sourceElement, destElement, ignoreDirection).length > 0;
};

PMFlow.prototype = new PMUI.draw.Connection();
/**
 * Defines the object type
 * @type {String}
 */
PMFlow.prototype.type = "Connection";  //TODO Replace this type by PMFlow when jCore will be updated

/**
 * Initialize the object with default values
 * @param {Object} options
 */
PMFlow.prototype.init = function (options) {
    var defaults = {
        flo_type: 'SEQUENCE',
        flo_is_inmediate: true,
        flo_x1: 0,
        flo_y1: 0,
        flo_x2: 0,
        flo_y2: 0,
        name: ''
    };
    jQuery.extend(true, defaults, options);
    this.setFlowType(defaults.flo_type)
        .setFlowUid(defaults.flo_uid)
        .setIsInmediate(defaults.flo_is_inmediate)
        .setOriginPoint(defaults.flo_x1, defaults.flo_y1)
        .setTargetPoint(defaults.flo_x2, defaults.flo_y2);

    this.setFlowName(defaults.name || '');
    this.setFlowOrigin(defaults.flo_element_origin || null, defaults.flo_element_origin_type || null);
    this.setFlowTarget(defaults.flo_element_dest || null, defaults.flo_element_dest_type || null);
    this.setFlowCondition(defaults.flo_condition || null);
    this.setFlowState(defaults.flo_state || null);
};

/**
 * Returns the flow's name
 * @return {String}
 */
PMFlow.prototype.getName = function () {
    return this.flo_name;
};
/**
 * Sets the label element
 * @param {String} value
 * @return {*}
 */
PMFlow.prototype.setName = function (name) {
    if (typeof name !== 'undefined') {
        this.flo_name = name;
        this.setBPMName(name);
        if (this.label) {
            this.label.setMessage(name);
        }
    }
    return this;
};
/**
 * Returns the flow conditions
 * @return {String}
 */
PMFlow.prototype.getFlowCondition = function () {
    return this.flo_condition;
};

/**
 * Defines the unique identiier property
 * @param {String} value
 * @return {*}
 */
PMFlow.prototype.setFlowUid = function (value) {
    this.flo_uid = value;
    return this;
};

/**
 * Defines the connection type
 * @param {String} type
 * @return {*}
 */
PMFlow.prototype.setFlowType = function (type) {
    this.flo_type = type;
    return this;
};

/** Return Flow Type
 *
 * @returns {String}
 */
PMFlow.prototype.getFlowType = function () {
    return this.flo_type;
};

/**
 * Sets the inmediately behavior of the connection
 * @param {Boolean} value
 * @return {*}
 */
PMFlow.prototype.setIsInmediate = function (value) {
    this.flo_is_inmediate = value;
    return this;
};

/**
 * Sets the origin point
 * @param {Number} x
 * @param {Number} y
 * @return {*}
 */
PMFlow.prototype.setOriginPoint = function (x, y) {
    this.flo_x1 = x;
    this.flo_y1 = y;
    return this;
};

/**
 * Sets the target point
 * @param {Number} x
 * @param {Number} y
 * @return {*}
 */
PMFlow.prototype.setTargetPoint = function (x, y) {
    this.flo_x2 = x;
    this.flo_y2 = y;
    return this;
};

/**
 * Sets the connection label
 * @param {String} name
 * @return {*}
 */
PMFlow.prototype.setFlowName = function (name) {
    this.flo_name = name;
    return this;
};

/**
 * Set the shape origin using input data
 * @param {String} code
 * @param {String} type
 * @return {*}
 */
PMFlow.prototype.setFlowOrigin = function (code, type) {
    this.flo_element_origin = code;
    this.flo_element_origin_type = type;
    return this;
};

/**
 * Set the shape target using input data
 * @param {String} code
 * @param {String} type
 * @return {*}
 */
PMFlow.prototype.setFlowTarget = function (code, type) {
    this.flo_element_dest = code;
    this.flo_element_dest_type = type;
    return this;
};

/**
 * Sets the flow conditions
 * @param value
 * @return {*}
 */
PMFlow.prototype.setFlowCondition = function (value) {
    this.flo_condition = value;
    return this;
};

/**
 * Sets the array of segments that conform the connection
 * @param {Array} state
 * @return {*}
 */
PMFlow.prototype.setFlowState = function (state) {
    this.flo_state = state;
    return this;
};

/**
 * Sets the origin data from a Shape
 * @param {PMShape} shape
 * @return {*}
 */
PMFlow.prototype.setOriginShape = function (shape) {
    var data;
    if (shape instanceof PMShape) {
        data = this.getNativeType(shape);
        this.flo_element_origin = data.code;
        this.flo_element_origin_type = data.type;
    }
    return this;
};

/**
 * Sets the target data from a Shape
 * @param {PMShape} shape
 * @return {*}
 */
PMFlow.prototype.setTargetShape = function (shape) {
    var data;
    if (shape instanceof PMShape) {
        data = this.getNativeType(shape);
        this.flo_element_dest = data.code;
        this.flo_element_dest_type = data.type;
    }
    return this;
};

/**
 * Returns the clean object to be sent to the backend
 * @return {Object}
 */
PMFlow.prototype.getDataObject = function () {
    var typeMap = {
            regular: 'SEQUENCE',
            segmented: 'MESSAGE',
            dotted: 'ASSOCIATION'
        },
        flo_x1 = 0,
        flo_y1 = 0,
        flo_x2 = 0,
        flo_y2 = 0,
        state = this.zoomPoints,
        flowElementOrigin,
        flowElementDest,
        portsOrigin,
        portOrigin,
        portsDest,
        portDest,
        k, j,
        bpmnMap = {
            'PMActivity': 'bpmnActivity',
            'PMEvent': 'bpmnEvent',
            'PMGateway': 'bpmnGateway',
            'PMArtifact': 'bpmnArtifact',
            'PMData': 'bpmnData',
            'PMParticipant': 'bpmnParticipant'
        },
        relatedObject;

    //For get initial port and end port
    flowElementOrigin = this.canvas.items.find("id", this.getSrcPort().parent.id);
    relatedObject = flowElementOrigin.relatedObject;
    this.evaluateRelatedObject(relatedObject);
    if (!flowElementOrigin) {
        throw new Error("Element not found!");
    }
    flowElementDest = this.canvas.items.find("id", this.getDestPort().parent.id);
    if (!flowElementDest) {
        throw new Error("Element not found!");
    }
    //Updating the positions, getting the last ports
    portsOrigin = flowElementOrigin.relatedObject.getPorts().asArray();
    for (k = 0; k < portsOrigin.length; k += 1) {
        if (portsOrigin[k].connection) {
            if (portsOrigin[k].connection.flo_uid === this.flo_uid) {
                portOrigin = portsOrigin[k];
            }
        }
    }
    if (!portOrigin) {
        portOrigin = {absoluteX: this.flo_x1, absoluteY: this.flo_y1};
    }
    portsDest = flowElementDest.relatedObject.getPorts().asArray();
    for (j = 0; j < portsDest.length; j += 1) {
        if (portsDest[j].connection) {
            if (portsDest[j].connection.flo_uid === this.flo_uid) {
                portDest = portsDest[j];
            }
        }
    }
    if (!portDest) {
        portDest = {absoluteX: this.flo_x2, absoluteY: this.flo_y2};
    }
    //get origin and target points from state array
    flo_x1 = state[0]['x'];
    flo_y1 = state[0]['y'];
    flo_x2 = state[state.length - 1]['x'];
    flo_y2 = state[state.length - 1]['y'];
    return {
        flo_uid: this.flo_uid,
        flo_type: this.flo_type,
        flo_name: this.flo_name,
        flo_element_origin: flowElementOrigin.id,
        flo_element_origin_type: bpmnMap[flowElementOrigin.type],
        flo_element_dest: flowElementDest.id,
        flo_element_dest_type: bpmnMap[flowElementDest.type],
        flo_is_inmediate: this.flo_is_inmediate,
        flo_condition: this.flo_condition,
        flo_state: state,
        flo_x1: flo_x1,
        flo_y1: flo_y1,
        flo_x2: flo_x2,
        flo_y2: flo_y2
    };
};
/**
 * evaluates the related object
 * @param {relatedObject} shape
 * @return {Object}
 */
PMFlow.prototype.evaluateRelatedObject = function (relatedObject) {
    var type, gat_direction, extendedType;
    if (relatedObject) {
        type = relatedObject.getType();
        if (type === 'PMGateway') {
            gat_direction = relatedObject.gat_direction;
            extendedType = relatedObject.extendedType;
            if (extendedType === 'EXCLUSIVE' && gat_direction === 'CONVERGING') {
                this.changeProperty("flo_condition", true);
            }
        }
    }
    return this;
};
/**
 * Change the property values if there are
 * @param {prop} property
 * @param {value} value
 * @return {Object}
 */
PMFlow.prototype.changeProperty = function (prop, value) {
    if (prop !== undefined && prop !== null) {
        this[prop] = value;
    } else {
        throw new Error("property not exist!");
    }
    return this;
};

/**
 * Converts the type to be sent to backend
 * @param {PMShape} shape
 * @return {Object}
 */
PMFlow.prototype.getNativeType = function (shape) {
    var type,
        code;
    switch (shape.getType()) {
        case 'PMActivity':
            type = "bpmnActivity";
            code = shape.act_uid;
            break;
        case 'PMGateway':
            type = "bpmnGateway";
            code = shape.gat_uid;
            break;
        case 'PMEvent':
            type = 'bpmnEvent';
            code = shape.evn_uid;
            break;
        case 'PMArtifact':
            type = "bpmnArtifact";
            code = shape.art_uid;
            break;
        case 'PMData':
            type = "bpmnData";
            code = shape.dat_uid;
            break;
        case 'PMParticipant':
            type = "bpmnParticipant";
            code = shape.dat_uid;
            break;
    }
    return {
        "type": type,
        "code": code
    };
};

PMFlow.prototype.showMoveHandlers = function () {
    PMUI.draw.Connection.prototype.showMoveHandlers.call(this);
    this.canvas.updatedElement = [{
        relatedObject: this
    }];
    $(this.html).trigger('selectelement');
    return this;
};

/**
 * Get Segment Width
 * @returns {Number}
 */
PMFlow.prototype.getSegmentHeight = function (index) {
    if (this.lineSegments.getSize()) {
        return Math.abs(this.lineSegments.get(index).endPoint.y
            - this.lineSegments.get(index).startPoint.y);
    }
    return 0;
};
/**
 * Get Segment Width
 * @returns {Number}
 */
PMFlow.prototype.getSegmentWidth = function (index) {
    if (this.lineSegments.getSize()) {
        return Math.abs(this.lineSegments.get(index).endPoint.x
            - this.lineSegments.get(index).startPoint.x);
    }
    return 0;
};
/**
 * Get Label Coordinates
 * @returns {Point}
 */
PMFlow.prototype.getLabelCoordinates = function () {
    var x, y, index = 0, diffX, diffY, i, max;

    if (this.lineSegments.getSize()) {
        max = (this.getSegmentWidth(0) > this.getSegmentHeight(0)) ?
            this.getSegmentWidth(0) : this.getSegmentHeight(0);

        for (i = 1; i < this.lineSegments.getSize(); i += 1) {
            diffX = this.getSegmentWidth(i);
            diffY = this.getSegmentHeight(i);
            if (diffX > max + 1) {
                max = diffX;
                index = i;
            } else if (diffY > max + 1) {
                max = diffY;
                index = i;
            }
        }
        diffX = (this.lineSegments.get(index).endPoint.x
            - this.lineSegments.get(index).startPoint.x) / 2;
        diffY = (this.lineSegments.get(index).endPoint.y
            - this.lineSegments.get(index).startPoint.y) / 2;
        x = this.lineSegments.get(index).startPoint.x + diffX;
        y = this.lineSegments.get(index).startPoint.y + diffY;
    } else {
        x = this.srcPort.getAbsoluteX();
        y = this.srcPort.getAbsoluteY();
    }

    return new PMUI.util.Point(x, y);
};
/**
 * Extended paint connection
 * @param {Object} options Configuration options
 * @chainable
 */
PMFlow.prototype.paint = function (options) {
    PMUI.draw.Connection.prototype.paint.call(this, options);
    // force to z-order if container parent is the canvas
    if (this.getSrcPort().getParent().getParent().getType() === 'PMCanvas'
        && this.getDestPort().getParent().getParent().getType() === 'PMCanvas') {
        this.setZOrder(1);
    } else {
        this.setZOrder(102);
    }
};
/**
 * Connects two PM Figures
 * @returns {Connection}
 */
PMFlow.prototype.connect = function (options) {
    var labelPoint;
    PMUI.draw.Connection.prototype.connect.call(this, options);
    labelPoint = this.getLabelCoordinates();
    this.label = new PMUI.draw.Label({
        message: this.getName(),
        canvas: this.canvas,
        parent: this,
        position: {
            location: "bottom",
            diffX: labelPoint.getX() / this.canvas.zoomFactor,
            diffY: labelPoint.getY() / this.canvas.zoomFactor + 10

        }
    });
    this.html.appendChild(this.label.getHTML());
    this.label.paint()
    this.label.attachListeners();
    this.label.setDimension(100, "auto");
    this.label.setLabelPosition(this.label.location, this.label.diffX, this.label.diffY);
    return this;
};

PMFlow.prototype.changeFlowType = function (type) {
    var segmentStyle, destDecorator,
        typeMap = {
            'default': {
                srcPrefix: 'mafe-default',
                destPrefix: 'mafe-sequence'
            },
            'conditional': {
                srcPrefix: 'mafe-decorator_conditional',
                destPrefix: 'mafe-decorator_default'
            },
            'sequence': {
                srcPrefix: 'mafe-sequence',
                destPrefix: 'mafe-sequence'
            }
        }, srcDecorator;

    if (type === 'association') {
        segmentStyle = "dotted";
        destDecorator = "con-none";
    } else {
        segmentStyle = "regular";
    }
    this.setSegmentStyle(segmentStyle);
    this.originalSegmentStyle = segmentStyle;
    if (type === 'association') {
        if (srcDecorator && this.srcDecorator) {
            this.srcDecorator
                .setDecoratorPrefix(srcDecorator);
        } else {
            this.srcDecorator
                .setDecoratorPrefix("mafe-decorator");
        }
        this.srcDecorator.paint();
    } else {
        this.srcDecorator.setDecoratorPrefix(typeMap[type].srcPrefix)
            .setDecoratorType("source")
            .paint();

        this.destDecorator.setDecoratorPrefix(typeMap[type].destPrefix)
            .setDecoratorType("target")
            .paint();
        this.disconnect()
            .connect()
            .setSegmentMoveHandlers()
            .checkAndCreateIntersectionsWithAll();
        return this;
    }
    if (destDecorator && this.srcDecorator) {
        this.destDecorator
            .setDecoratorPrefix(destDecorator);
    } else {
        this.destDecorator
            .setDecoratorPrefix("mafe-decorator");
    }
    this.srcDecorator.paint();
    this.disconnect();
    this.connect();
    return this;
};

PMFlow.prototype.saveAndDestroy = function () {
    var otherConnection, sizeIntersection, bar;
    sizeIntersection = this.intersectionWith.getSize();
    PMUI.draw.Connection.prototype.saveAndDestroy.call(this);
    bar = this.intersectionWith.asArray().slice();
    bar.reverse();
    for (i = 0; i < sizeIntersection; i += 1) {
        otherConnection = bar[i];
        otherConnection
            .setSegmentColor(otherConnection.originalSegmentColor, false)
            .setSegmentStyle(otherConnection.originalSegmentStyle, false)
            .disconnect()
            .connect();
        otherConnection.setSegmentMoveHandlers();
        otherConnection.checkAndCreateIntersectionsWithAll();
    }
    if (this.getFlowType() === 'DEFAULT') {
        this.getSrcPort().getParent().updateDefaultFlow("");
    }
    this.updateIncomingAndOutgoingConnections("remove");

    return;
};

PMFlow.prototype.showPortsAndHandlers = function () {
    this.canvas.hideAllCoronas();
    this.showMoveHandlers();
    this.showPorts();
    return this;
};

PMFlow.prototype.showPorts = function () {
    var connectHandler,
        connectHandler2,
        portPoint,
        handlerOriginalSize = 15,
        handlerDimension = handlerOriginalSize * this.canvas.getZoomFactor();

    this.canvas.hideDragConnectHandlers();

    portPoint = this.srcPort.getPoint();
    connectHandler = this.canvas.dragConnectHandlers.get(0);
    connectHandler.setDimension(handlerDimension, handlerDimension);
    connectHandler.setPosition(portPoint.x - Math.floor(handlerDimension / 2), portPoint.y - Math.floor(handlerDimension / 2));
    connectHandler.setVisible(true);
    connectHandler.relativeShape = this.srcPort;
    connectHandler.attachListeners();
    
    portPoint = this.destPort.getPoint();
    connectHandler2 = this.canvas.dragConnectHandlers.get(1);
    connectHandler2.setDimension(handlerDimension, handlerDimension);
    connectHandler2.setPosition(portPoint.x - Math.floor(handlerDimension / 2), portPoint.y - Math.floor(handlerDimension / 2));
    connectHandler2.setVisible(true);
    connectHandler2.relativeShape = this.destPort;
    connectHandler2.attachListeners();
    return this;
};

PMFlow.prototype.hidePortsAndHandlers = function () {
    this.hideMoveHandlers();
    this.canvas.hideDragConnectHandlers();
    return this
};

PMFlow.prototype.getBpmnElementType = function () {
    var map = {
        'SEQUENCE': 'bpmn:SequenceFlow',
        'ASSOCIATION': 'bpmn:Association',
        'MESSAGE': 'bpmn:MessageFlow'
    };
    var type = map[this.flo_type] || 'bpmn:SequenceFlow';
    if (this.flo_type === 'DATAASSOCIATION') {
        if (this.flo_element_origin_type === 'bpmnData') {
            type = 'bpmn:DataInputAssociation';
        } else {
            type = 'bpmn:DataOutputAssociation';
        }
    }
    return type;
};

PMFlow.prototype.createWithBpmn = function (bpmnElementType) {
    var businessObject = PMDesigner.bpmnFactory.create(bpmnElementType, {
        id: 'flo_' + this.id,
        name: this.getName() ? PMDesigner.escapeXMLCharacters(this.getName()) : ""
    });
    businessObject.di = PMDesigner.bpmnFactory.createDiEdge(businessObject, [], {
        id: businessObject.id + '_di'
    });
    this.businessObject = businessObject;
};

PMFlow.prototype.updateConnectionWaypoints = function () {
    this.businessObject.di.set('waypoint', PMDesigner.bpmnFactory.createDiWaypoints(this.waypoints));
};

PMFlow.prototype.updateConnection = function (newSource, newTarget) {
    var businessObject = this.businessObject,
        children;
    if (this.flo_type === 'DATAASSOCIATION') {
        if (this.flo_element_origin_type === 'bpmnData') {
            children = newTarget.elem.get('dataInputAssociations');
            CollectionRemove(children, businessObject);
            businessObject.sourceRef = [];
            if (!children) {
                newTarget.elem.dataInputAssociations = [];
                newTarget.elem.dataInputAssociations.push(businessObject);
            } else {
                children.push(businessObject);
            }
            businessObject.sourceRef.push(newSource.elem);
        } else {
            children = newSource.elem.get('dataOutputAssociations');
            CollectionRemove(children, businessObject);
            businessObject.targetRef = [];
            newSource.elem.get('dataOutputAssociations').push(businessObject);
            businessObject.targetRef = newTarget.elem;
        }
    } else {
        var inverseSet = businessObject.$instanceOf('bpmn:SequenceFlow');
        if (businessObject.sourceRef !== newSource.elem) {
            if (inverseSet) {
                CollectionRemove(businessObject.sourceRef && businessObject.sourceRef.get('outgoing'), businessObject);

                if (newSource.elem) {
                    newSource.elem.get('outgoing').push(businessObject);
                }
            }

            businessObject.sourceRef = newSource.elem;
        }
        if (businessObject.targetRef !== newTarget.elem) {
            if (inverseSet) {
                CollectionRemove(businessObject.targetRef && businessObject.targetRef.get('incoming'), businessObject);

                if (newTarget.elem) {
                    newTarget.elem.get('incoming').push(businessObject);
                }
            }
            businessObject.targetRef = newTarget.elem;
        }
    }
    businessObject.di.set('waypoint', PMDesigner.bpmnFactory.createDiWaypoints(this.points));
};

PMFlow.prototype.updateShapeParent = function (businessObject, parentBusinessObject) {
    var parentBusinessObjectAux = {};
    if (this.flo_type === 'MESSAGE') {
        if (this.srcPort.parent.businessObject.elem
            && this.destPort.parent.businessObject.elem
            && this.srcPort.parent.businessObject.elem.$parent.id !== this.destPort.parent.businessObject.elem.$parent.id) {

            parentBusinessObjectAux.elem = _.findWhere(PMDesigner.businessObject.get('rootElements'), {$type: "bpmn:Collaboration"});
        } else {
            if (this.srcPort.parent.type === 'PMParticipant') {
                parentBusinessObjectAux.elem = this.srcPort.parent && this.srcPort.parent.participantObject.elem.$parent;
            } else if (this.srcPort.parent) {
                parentBusinessObjectAux.elem = this.destPort.parent && this.destPort.parent.participantObject.elem.$parent;
            }
        }
    }
    if (parentBusinessObjectAux.elem) {
        this.updateSemanticParent(businessObject, parentBusinessObjectAux);
    } else {
        this.updateSemanticParent(businessObject, parentBusinessObject);
    }

    this.updateDiParent(businessObject.di, parentBusinessObject.di);
};

PMFlow.prototype.updateSemanticParent = function (businessObject, newParent) {
    var children;
    if (businessObject.$parent === newParent.elem) {
        return;
    }
    if (this.flo_type !== 'DATAASSOCIATION') {
        if (this.flo_type === 'MESSAGE') {
            //HERE MESSAGE FLOW SET TO COLLABORATIONS
            if (businessObject.$parent) {
                // remove from old parent
                children = businessObject.$parent.get('messageFlows');
                CollectionRemove(children, businessObject);
            }

            if (!newParent.elem) {
                businessObject.$parent = null;
            } else {
                children = newParent.elem.get('messageFlows');
                children.push(businessObject);
                businessObject.$parent = newParent.elem;
            }
        } else {
            if (businessObject.$parent) {
                // remove from old parent
                children = businessObject.$parent.get('flowElements');
                CollectionRemove(children, businessObject);
            }

            if (!newParent.elem) {
                businessObject.$parent = null;
            } else {
                // add to new parent
                if (newParent.elem.$type === 'bpmn:Lane') {
                    children = newParent.elem.$parent.$parent.get('flowElements');
                } else if (this.getSrcPort().getParent().getType() === 'PMEvent'
                    && this.getSrcPort().getParent().getEventType() === 'BOUNDARY') {
                    children = newParent.elem.$parent.get('flowElements');
                } else {
                    children = newParent.elem.get('flowElements');
                }
                children.push(businessObject);
                businessObject.$parent = newParent.elem;
            }
        }
    }
};

PMFlow.prototype.updateDiParent = function (di, parentDi) {

    if (parentDi && !parentDi.$instanceOf('bpmndi:BPMNPlane')) {
        parentDi = parentDi.$parent;
    }

    if (di.$parent === parentDi) {
        return;
    }
    var planeElements = (parentDi || di.$parent).get('planeElement');
    if (parentDi) {
        planeElements.push(di);
        di.$parent = parentDi;
    } else {
        CollectionRemove(planeElements, di);
    }
};

PMFlow.prototype.createBpmn = function (bpmnElementType) {
    var newSource, newTarget;
    this.createWithBpmn(bpmnElementType);

    this.updateShapeParent(this.businessObject, this.srcPort.parent.parent.businessObject);
    newSource = this.srcPort.parent && this.srcPort.parent.businessObject;
    newTarget = this.destPort.parent && this.destPort.parent.businessObject;
    if (this.srcPort.parent.type == 'PMParticipant') {
        newSource = this.srcPort.parent && this.srcPort.parent.participantObject;
    }
    if (this.destPort.parent.type == 'PMParticipant') {
        newTarget = this.destPort.parent && this.destPort.parent.participantObject;
    }
    this.updateConnection(newSource, newTarget);
};
PMFlow.prototype.removeBpmn = function () {
    var parentShape,
        businessObject,
        newSource,
        newTarget,
        children,
        parentBusinessObject,
        parentDi;
    businessObject = this.businessObject;

    this.updateSemanticParent(businessObject, {elem: null});
    this.updateDiParent(businessObject.di);

    if (this.flo_type !== 'DATAASSOCIATION') {
        parentShape = this.parent;
        parentBusinessObject = parentShape && parentShape.businessObject;
        parentDi = parentBusinessObject && parentBusinessObject.di;
        CollectionRemove(businessObject.sourceRef && businessObject.sourceRef.get('outgoing'), businessObject);
        CollectionRemove(businessObject.targetRef && businessObject.targetRef.get('incoming'), businessObject);
    } else {
        newSource = this.srcPort.parent && this.srcPort.parent.businessObject.elem,
            newTarget = this.destPort.parent && this.destPort.parent.businessObject.elem;

        if (this.flo_element_origin_type === 'bpmnData') {
            children = newTarget.get('dataInputAssociations');
            CollectionRemove(children, businessObject);

        } else {
            children = newSource.get('dataOutputAssociations');
            CollectionRemove(children, businessObject);
        }
    }
};

PMFlow.prototype.updateBpmn = function () {
    var newSource = this.srcPort.parent && this.srcPort.parent.businessObject,
        newTarget = this.destPort.parent && this.destPort.parent.businessObject;
    this.updateConnection(newSource, newTarget);
};

PMFlow.prototype.setBPPMName = function (name) {
    if (this.businessObject || this.participantObject) {
        this.businessObject.name = name;
    }
};
/**
 * Sets flow name to export as bpmn xml standard
 * @param name
 */
PMFlow.prototype.setBPMName = function (name) {
    if (this.businessObject) {
        this.businessObject.name = name;
    }
};

PMFlow.prototype.reconectSwitcher = function (delta, inContainer, rootType) {
    var srcElem,
        destElem;
    //get source and target element
    srcElem = this.getSrcPort().parent;
    destElem = this.getDestPort().parent;
    //verify if is the same process
    if (srcElem.businessObject.elem
        && destElem.businessObject.elem
        && srcElem.businessObject.elem.$parent.id === destElem.businessObject.elem.$parent.id) {
        if (rootType && rootType === 'PMLane') {
            this.reconnectManhattah(inContainer);
        } else {
            this.reconnectUser(delta, inContainer);
        }
    } else {
        this.reconnectManhattah(inContainer);
    }
    this.setSegmentMoveHandlers();
    this.checkAndCreateIntersectionsWithAll();
    this.canvas.triggerUserStateChangeEvent(this);
};
/**
 * updates the list of items related input and output
 * @param action {String}, You can have two values, "remove" or "create"
 * @returns {PMFlow}
 */
PMFlow.prototype.updateIncomingAndOutgoingConnections = function(action){
    var source, destiny, sourceShape, destinyShape;
    source = this.srcPort;
    destiny = this.destPort;
    sourceShape = source.parent;
    destinyShape = destiny.parent;
    if (sourceShape && destinyShape){
        if (action === "remove"){
            sourceShape.removeOutgoingConnection(this);
            destinyShape.removeIncomingConnection(this);
        } else if (action === "create"){
            sourceShape.addOutgoingConnection(this);
            destinyShape.addIncomingConnection(this);
        }
    }
    return this;
};

/**
 * @class PMConnectionDropBehavior
 * Extends the functionality to handle creation of connections
 *
 * @constructor
 * Creates a new instance of the object
 */
var PMConnectionDropBehavior = function (selectors) {
    PMUI.behavior.ConnectionDropBehavior.call(this, selectors);
};
PMConnectionDropBehavior.prototype = new PMUI.behavior.ConnectionDropBehavior();
/**
 * Defines the object type
 * @type {String}
 */
PMConnectionDropBehavior.prototype.type = "PMConnectionDropBehavior";

/**
 * Defines a Map of the basic Rules
 * @type {Object}
 */
PMConnectionDropBehavior.prototype.basicRules = {
    PMEvent: {
        PMEvent: {
            connection: 'regular',
            type: 'SEQUENCE'
        },
        PMActivity: {
            connection: 'regular',
            type: 'SEQUENCE'
        }
    },
    PMActivity: {
        PMActivity: {
            connection: 'regular',
            type: 'SEQUENCE'
        },
        PMArtifact: {
            connection: 'dotted',
            destDecorator: 'con_none',
            type: 'ASSOCIATION'
        },
        PMIntermediateEvent: {
            connection: 'regular',
            type: 'SEQUENCE'
        },
        PMEndEvent: {
            connection: 'regular',
            type: 'SEQUENCE'
        },
        PMGateway: {
            connection: 'regular',
            type: 'SEQUENCE'
        }
    },
    PMStartEvent: {
        PMActivity: {
            connection: 'regular',
            type: 'SEQUENCE'
        },
        PMIntermediateEvent: {
            connection: 'regular',
            type: 'SEQUENCE'
        },
        PMGateway: {
            connection: 'regular',
            type: 'SEQUENCE'
        }
    },
    PMIntermediateEvent: {
        PMActivity: {
            connection: 'regular',
            type: 'SEQUENCE'
        },
        PMIntermediateEvent: {
            connection: 'regular',
            type: 'SEQUENCE'
        },
        PMEndEvent: {
            connection: 'regular',
            type: 'SEQUENCE'
        },
        PMGateway: {
            connection: 'regular',
            type: 'SEQUENCE'
        }
    },
    PMBoundaryEvent: {
        PMActivity: {
            connection: 'regular',
            type: 'SEQUENCE'
        },
        PMIntermediateEvent: {
            connection: 'regular',
            type: 'SEQUENCE'
        },
        PMEndEvent: {
            connection: 'regular',
            type: 'SEQUENCE'
        },
        PMGateway: {
            connection: 'regular',
            type: 'SEQUENCE'
        }
    },
    PMGateway: {
        PMActivity: {
            connection: 'regular',
            type: 'SEQUENCE'
        },
        PMIntermediateEvent: {
            connection: 'regular',
            type: 'SEQUENCE'
        }
    },
    PMArtifact: {
        PMActivity: {
            connection: 'dotted',
            destDecorator: 'con_none',
            type: 'ASSOCIATION'
        }
    }
};

/**
 * Defines a Map of the init Rules
 * @type {Object}
 */

PMConnectionDropBehavior.prototype.initRules = {
    PMCanvas: {
        PMCanvas: {
            name: 'PMCanvas to PMCanvas',
            rules: PMConnectionDropBehavior.prototype.basicRules
        }
    },
    PMActivity: {
        PMCanvas: {
            name: 'PMActivity to PMCanvas',
            rules: PMConnectionDropBehavior.prototype.basicRules
        }
    }
};

/**
 * Handle the hook functionality when a drop start
 *  @param shape
 */
PMConnectionDropBehavior.prototype.dropStartHook = function (shape, e, ui) {
    shape.srcDecorator = null;
    shape.destDecorator = null;
    var draggableId = ui.draggable.attr("id"),
        source = shape.canvas.customShapes.find('id', draggableId),
        prop;
    if (source) {
        prop = this.validate(source, shape);
        if (prop) {
            shape.setConnectionType({
                type: prop.type,
                segmentStyle: prop.connection,
                srcDecorator: prop.srcDecorator,
                destDecorator: prop.destDecorator
            });

        } else {
            // verif if port is changed
            if (typeof source !== 'undefined') {
                if (!(ui.helper && ui.helper.attr('id') === "drag-helper")) {
                    return false;
                }
                shape.setConnectionType('none');
            }
        }
    }

    return true;
};

/**
 * Connection validations method
 * return an object if is valid otherwise return false
 * @param {Connection} source
 * @param {Connection} target
 */
PMConnectionDropBehavior.prototype.validate = function (source, target) {
    var sType,
        tType,
        rules,
        initRules,
        initRulesName,
        BPMNAuxMap = {
            PMEvent: {
                'START': 'PMStartEvent',
                'END': 'PMEndEvent',
                'INTERMEDIATE': 'PMIntermediateEvent',
                'BOUNDARY': 'PMBoundaryEvent'
            },
            bpmnArtifact: {
                'TEXTANNOTATION': 'bpmnAnnotation'
            }
        };

    if (source && target) {
        if (source.getID() === target.getID()) {
            return false;
        }

        if (this.initRules[source.getParent().getType()]
            && this.initRules[source.getParent().getType()][target.getParent().getType()]) {
            initRules = this.initRules[source.getParent().getType()][target.getParent().getType()].rules;
            initRulesName = this.initRules[source.getParent().getType()][target.getParent().getType()].name;
            // get the types
            sType = source.getType();
            tType = target.getType();
            //Custimize all PM events
            if (sType === 'PMEvent') {
                if (BPMNAuxMap[sType] && BPMNAuxMap[sType][source.getEventType()]) {
                    sType = BPMNAuxMap[sType][source.getEventType()];
                }
            }
            if (tType === 'PMEvent') {
                if (BPMNAuxMap[tType] && BPMNAuxMap[tType][target.getEventType()]) {
                    tType = BPMNAuxMap[tType][target.getEventType()];
                }
            }

            if (initRules[sType] && initRules[sType][tType]) {
                rules = initRules[sType][tType];
            } else {
                rules = false;
            }
            if (initRules) {
                switch (initRulesName) {
                    case 'bpmnPool to bpmnPool':
                        if (source.getParent().getID() !== target.getParent().getID()) {
                            rules = false;
                        }
                        break;
                    case 'bpmnLane to bpmnLane':
                        if (source.getFirstPool(source.parent).getID()
                            !== target.getFirstPool(target.parent).getID()) {
                            if (this.extraRules[sType]
                                && this.extraRules[sType][tType]) {
                                rules = this.extraRules[sType][tType];
                            } else {
                                rules = false;
                            }
                        }
                        break;
                    case 'bpmnActivity to bpmnLane':
                        if (this.basicRules[sType]
                            && this.basicRules[sType][tType]) {
                            rules = this.basicRules[sType][tType];
                        } else {
                            rules = false;
                        }
                        break;
                    default:
                        break;
                }
            } else {
                rules = false;
            }

        } else {
            // get the types
            sType = source.getType();
            tType = target.getType();
            //
            if (sType === 'PMEvent') {
                if (BPMNAuxMap[sType] && BPMNAuxMap[sType][source.getEventType()]) {
                    sType = BPMNAuxMap[sType][source.getEventType()];
                }
            }
            if (tType === 'PMEvent') {
                if (BPMNAuxMap[tType] && BPMNAuxMap[tType][target.getEventType()]) {
                    tType = BPMNAuxMap[tType][target.getEventType()];
                }
            }
            if (this.advancedRules[sType] && this.advancedRules[sType][tType]) {
                rules = this.advancedRules[sType][tType];
            } else {
                rules = false;
            }
        }
        return rules;
    }
};
PMConnectionDropBehavior.prototype.onDragEnter = function (customShape) {
    return function (e, ui) {
        var shapeRelative, i;
        if (customShape.extendedType !== "PARTICIPANT") {
            if (ui.helper && ui.helper.hasClass("dragConnectHandler")) {
                shapeRelative = customShape.canvas.dragConnectHandlers.get(0).relativeShape;
                if (shapeRelative.id !== customShape.id) {
                    for (i = 0; i < 4; i += 1) {
                        customShape.showConnectDropHelper(i, customShape);
                    }
                }
            }
        } else {
            shapeRelative = customShape.canvas.dragConnectHandlers.get(0).relativeShape;
            if (shapeRelative && customShape && shapeRelative.id !== customShape.id) {
                if (ui.helper && ui.helper.hasClass("dragConnectHandler")) {
                    for (i = 0; i < 10; i += 1) {
                        connectHandler = customShape.canvas.dropConnectHandlers.get(i);
                        connectHandler.setDimension(18 * customShape.canvas.getZoomFactor(), 18 * customShape.canvas.getZoomFactor());
                        connectHandler.setPosition(customShape.getZoomX() + i * customShape.getZoomWidth() / 10, customShape.getZoomY() - connectHandler.height / 2 - 1);
                        connectHandler.relativeShape = customShape;
                        connectHandler.attachDrop();

                        connectHandler.setVisible(true);
                    }

                    for (i = 0; i < 10; i += 1) {
                        connectHandler = customShape.canvas.dropConnectHandlers.get(i + 10);
                        connectHandler.setDimension(18 * customShape.canvas.getZoomFactor(), 18 * customShape.canvas.getZoomFactor());
                        connectHandler.setPosition(customShape.getZoomX() + i * customShape.getZoomWidth() / 10, customShape.getZoomY() + customShape.getZoomHeight() - connectHandler.height / 2 - 1);
                        connectHandler.relativeShape = customShape;
                        connectHandler.attachDrop();

                        connectHandler.setVisible(true);
                    }
                }
            }
        }
    }
};
/**
 * Handle the functionality when a shape is dropped
 * @param shape
 */
PMConnectionDropBehavior.prototype.onDrop = function (shape) {
    var that = this;
    return function (e, ui) {
        var customShape,
            id = ui.draggable.attr('id');
        if (shape.getType() === "PMParticipant" && !(customShape = shape.canvas.shapeFactory(id))) {
            if (customShape = shape.canvas.customShapes.find('id', id)) {
                customShape.dropOnParticipant = true;
            }
        }
        return false;
    };
};
var PMCustomShapeDragBehavior = function () {
};
PMCustomShapeDragBehavior.prototype = new PMUI.behavior.CustomShapeDragBehavior();
/**
 * Type of the instances
 * @property {String}
 */
PMCustomShapeDragBehavior.prototype.type = "CustomShapeDragBehavior";
/**
 * Attach the drag behavior and ui properties to the corresponding shape
 * @param {PMUI.draw.CustomShape} customShape
 */
PMCustomShapeDragBehavior.prototype.attachDragBehavior = function (customShape) {
    var dragOptions,
        $customShape = $(customShape.getHTML());
    dragOptions = {
        revert: false,
        helper: "none",
        cursorAt: false,
        revertDuration: 0,
        disable: false,
        grid: [1, 1],
        start: this.onDragStart(customShape),
        drag: this.onDrag(customShape, true),
        stop: this.onDragEnd(customShape, true)
    };
    $customShape.draggable({ 'cursor': "move" });
    $customShape.draggable(dragOptions);
};
/**
 * On drag start handler, it uses the {@link PMUI.behavior.RegularDragBehavior}.onDragStart
 * method to initialize the drag, but also initializes other properties
 * @param {PMUI.draw.CustomShape} customShape
 * @return {Function}
 */
PMCustomShapeDragBehavior.prototype.onDragStart = function (customShape) {
    return function (e, ui) {
        customShape.canvas.hideAllCoronas();
        if (customShape.canvas.canConnect) {
            if (customShape.canvas.connectStartShape.getID() !== customShape.getID()) {
                customShape.canvas.connectProcedure(customShape, e);
            }
            customShape.canvas.cancelConnect();
            return false;
        }

        if (customShape.canvas.currentSelection.asArray().length == 0) {
            customShape.canvas.addToSelection(customShape);
        }
        PMUI.behavior.RegularDragBehavior.prototype.onDragStart.call(this,
            customShape)(e, ui);
        customShape.previousXDragPosition = customShape.getX();
        customShape.previousYDragPosition = customShape.getY();
        if (customShape.canvas.snapToGuide) {
            //init snappers
            customShape.canvas.startSnappers(e);
        }
        customShape.canvas.isDragging = true;
        //to validate drag container
        customShape.canvas.setLassoLimits();
    };
};

/**
 * Procedure executed while dragging, it takes care of multiple drag, moving
 * connections, updating positions and children of the shapes being dragged
 * @param {PMUI.draw.CustomShape} customShape shape being dragged
 * @param {boolean} root return whether this is the shape where the drag started
 * @param {number} childDiffX x distance needed for the non-root shapes to move
 * @param {number} childDiffY y distance needed for the non-root shapes to move
 * @param {Object} e jQuery object containing the properties when a drag event
 * occur
 * @param {Object} ui JQuery UI object containing the properties when a drag
 * event occur
 */
PMCustomShapeDragBehavior.prototype.onDragProcedure = function (customShape, root, childDiffX, childDiffY, e, ui) {
    var i,
        j,
        sibling,
        diffX,
        diffY,
        port,
        child,
        connection,
        shape1,
        shape2,
        canvas = customShape.canvas,
        k,
        uiOffset,
        positionsX1 = [];
    uiOffset = {};
    uiOffset.x = ui.helper.position().left / canvas.zoomFactor;
    uiOffset.y = ui.helper.position().top / canvas.zoomFactor;
    uiOffset.diffX = customShape.x - uiOffset.x;
    uiOffset.diffY = customShape.y - uiOffset.y;
    // shapes
    if (root) {
        // Commented for problem on snappers
        if (customShape.canvas.snapToGuide) {
            customShape.canvas.processGuides(e, ui, customShape);
        }
        customShape.setPosition(uiOffset.x, uiOffset.y);

        diffX = customShape.x - customShape.previousXDragPosition;
        diffY = customShape.y - customShape.previousYDragPosition;

        customShape.previousXDragPosition = customShape.x;
        customShape.previousYDragPosition = customShape.y;

        for (i = 0; i < customShape.canvas.currentSelection.getSize(); i += 1) {
            sibling = customShape.canvas.currentSelection.get(i);
            if (sibling.id !== customShape.id) {
                sibling.setPosition(sibling.x + diffX, sibling.y + diffY);
            }
        }
    } else {
        customShape.setPosition(customShape.x, customShape.y);
    }
    // children
    if (root) {
        for (i = 0; i < customShape.canvas.currentSelection.getSize(); i += 1) {
            sibling = customShape.canvas.currentSelection.get(i);
            for (j = 0; j < sibling.children.getSize(); j += 1) {
                child = sibling.children.get(j);
                PMCustomShapeDragBehavior.prototype.onDragProcedure.call(this, child,
                    false, diffX, diffY, e, ui);
            }
        }
    } else {
        for (i = 0; i < customShape.children.getSize(); i += 1) {
            child = customShape.children.get(i);
            PMCustomShapeDragBehavior.prototype.onDragProcedure.call(this, child,
                false, childDiffX, childDiffY, e, ui);
        }
    }
    // connections
    if (root) {
        for (i = 0; i < customShape.canvas.currentSelection.getSize(); i += 1) {
            sibling = customShape.canvas.currentSelection.get(i);
            this.updateAndRepaintPositions({
                customShape: customShape,
                sibling: sibling,
                diffX: diffX,
                diffY: diffY
            });

        }
    } else {
        this.updateAndRepaintPositions({
            customShape: customShape,
            sibling: customShape,
            diffX: childDiffX,
            diffY: childDiffY
        });
    }
    if (customShape) {
        customShape.wasDragged = true;
    }
};

/**
 * Updates port position and repaint connections
 * @param options
 */
PMCustomShapeDragBehavior.prototype.updateAndRepaintPositions = function (options) {
    var j,
        port,
        connection,
        customShape = options.customShape,
        sibling = options.sibling;
    // move the segments of this connections
    for (j = 0; j < customShape.canvas.sharedConnections.getSize(); j += 1) {
        connection = customShape.canvas.sharedConnections.get(j);
        if (connection.srcPort.parent.getID() ===
            sibling.getID()) {
            // to avoid moving the connection twice
            // (two times per shape), move it only if the shape
            connection.move(options.diffX * customShape.canvas.zoomFactor,
                options.diffY * customShape.canvas.zoomFactor);
        }
    }
    for (j = 0; j < sibling.ports.getSize(); j += 1) {
        //for each port update its absolute position and repaint its connection
        port = sibling.ports.get(j);
        connection = port.connection;
        port.setPosition(port.x, port.y);

        if (!customShape.canvas.sharedConnections.contains(connection)) {
            connection
                .setSegmentColor(PMUI.util.Color.GREY, false)
                .setSegmentStyle("regular", false)// repaint:  false
                .disconnect()
                .connect();
        }
    }
};
/**
 * On drag handler, calls the drag procedure while the dragging is occurring,
 * and also takes care of the snappers
 * @param {PMUI.draw.CustomShape} customShape shape being dragged
 * @param {boolean} root return whether this is the shape where the drag started
 * @param {number} childDiffX x distance needed for the non-root shapes to move
 * @param {number} childDiffY y distance needed for the non-root shapes to move
 * @return {Function}
 */
PMCustomShapeDragBehavior.prototype.onDrag = function (customShape, root, childDiffX, childDiffY) {
    var self = this;
    return function (e, ui) {
        self.onDragProcedure(customShape, root, childDiffX,
            childDiffY, e, ui);
    };
};
/**
 * Procedure executed on drag end, it takes care of multiple drag, moving
 * connections, updating positions and children of the shapes being dragged
 * @param {PMUI.draw.CustomShape} customShape shape being dragged
 * @param {boolean} root return whether this is the shape where the drag started
 * @param {Object} e jQuery object containing the properties when a drag event
 * occur
 * @param {Object} ui JQuery UI object containing the properties when a drag
 * event occur
 */
PMCustomShapeDragBehavior.prototype.dragEndProcedure = function (customShape, root, e, ui) {
    var i,
        j,
        sibling,
        port,
        child,
        connection,
        shape1,
        shape2,
        canvas = customShape.canvas,
        diffX = 0,
        diffY = 0;
    if (root) {
        // the difference between this segment of code and the segment of code
        // found in dragProcedure is that it's not needed to move the shapes
        // anymore using differentials
        customShape.wasDragged = false;
        customShape.canvas.isDragging = false;

        // validate lasso limits
        if (canvas.lassoEnabled && canvas.lassoLimits.x.shape && canvas.lassoLimits.x.shape.getX() < 0) {
            diffX -= canvas.lassoLimits.x.shape.getX();
        }
        if (canvas.lassoEnabled && canvas.lassoLimits.y.shape && canvas.lassoLimits.y.shape.getY() < 0) {
            diffY -= canvas.lassoLimits.y.shape.getY();
        }

        for (i = 0; i < customShape.canvas.currentSelection.getSize();
            i += 1) {
            sibling = customShape.canvas.currentSelection.get(i);
            // if dragging with lasso is rebasing the limit
            if (diffX > 0 || diffY > 0) {
                sibling.setPosition(sibling.getX() + diffX, sibling.getY() + diffY);
            }
            for (j = 0; j < sibling.children.getSize(); j += 1) {
                child = sibling.children.get(j);
                child.changedContainer = false;
                PMUI.behavior.CustomShapeDragBehavior.prototype.dragEndProcedure.call(this,
                    child, false, e, ui);
            }
        }
    } else {
        for (i = 0; i < customShape.children.getSize(); i += 1) {
            child = customShape.children.get(i);
            PMUI.behavior.CustomShapeDragBehavior.prototype.dragEndProcedure.call(this,
                child, false, e, ui);
        }
    }
    // connections
    if (root) {
        for (i = 0; i < customShape.canvas.currentSelection.getSize();
            i += 1) {
            sibling = customShape.canvas.currentSelection.get(i);
            for (j = 0; j < sibling.ports.getSize(); j += 1) {
                // for each port update its absolute position and repaint
                // its connection
                port = sibling.ports.get(j);
                connection = port.connection;

                port.setPosition(port.x, port.y);

                if (customShape.canvas.sharedConnections.
                    find('id', connection.getID())) {
                    // move the segments of this connections
                    if (connection.srcPort.parent.getID() ===
                        sibling.getID()) {
                        // to avoid moving the connection twice
                        // (two times per shape), move it only if the shape
                        // holds the sourcePort
                        connection.disconnect(true).connect({
                            algorithm: 'user',
                            points: connection.points,
                            dx: parseFloat($(connection.html).css('left')),
                            dy: parseFloat($(connection.html).css('top'))
                        });
                        connection.checkAndCreateIntersectionsWithAll();
                        connection.canvas.triggerUserStateChangeEvent(connection);
                    }
                } else {
                    connection
                        .setSegmentColor(connection.originalSegmentColor, false)
                        .setSegmentStyle(connection.originalSegmentStyle, false)
                        .disconnect()
                        .connect();
                    connection.setSegmentMoveHandlers();
                    connection.checkAndCreateIntersectionsWithAll();
                }
            }
        }
    } else {
        for (i = 0; i < customShape.ports.getSize(); i += 1) {
            //for each port update its absolute position and repaint
            //its connection
            port = customShape.ports.get(i);
            connection = port.connection;
            shape1 = connection.srcPort.parent;
            shape2 = connection.destPort.parent;

            port.setPosition(port.x, port.y);
            if (customShape.canvas.sharedConnections.
                find('id', connection.getID())) {
                // to avoid moving the connection twice
                // (two times per shape), move it only if the shape
                // holds the sourcePort
                if (connection.srcPort.parent.getID() ===
                    customShape.getID()) {
                    connection.checkAndCreateIntersectionsWithAll();
                }
            } else {
                connection
                    .setSegmentColor(connection.originalSegmentColor, false)
                    .setSegmentStyle(connection.originalSegmentStyle, false)
                    .disconnect()
                    .connect();
                connection.setSegmentMoveHandlers();
                connection.checkAndCreateIntersectionsWithAll();
            }
        }
    }
};
/**
 * On drag end handler, ot calls drag end procedure, removes the snappers and,
 * fires the command move if necessary
 * @param {PMUI.draw.CustomShape} customShape
 * @return {Function}
 */
PMCustomShapeDragBehavior.prototype.onDragEnd = function (customShape) {
    var command,
        self = this;
    return function (e, ui) {
        // call to dragEnd procedure
        self.dragEndProcedure(customShape, true, e, ui);
        customShape.dragging = false;
        // hide the snappers
        customShape.canvas.verticalSnapper.hide();
        customShape.canvas.horizontalSnapper.hide();
        if (!customShape.changedContainer) {
            if (customShape.parent.getType() === 'PMLane' && !customShape.dropOnParticipant) {
                command = new PMCommandMoveInLane(customShape.canvas.currentSelection);
            } else {
                command = new PMUI.command.CommandMove(customShape.canvas.currentSelection);
            }
            command.execute();
            customShape.canvas.commandStack.add(command);
            customShape.dropOnParticipant = false;
        }
        customShape.changedContainer = false;
        // decrease the zIndex of the oldParent of customShape
        customShape.decreaseParentZIndex(customShape.oldParent);
        // force to apply zoom, when move more than two figures
        if (customShape.getCanvas().getCurrentSelection().getSize() > 1) {
            customShape.getCanvas().applyZoom(customShape.getCanvas().getZoomPropertiesIndex() + 1);
        }
        customShape.getCanvas().emptyCurrentSelection();
    };
};

var ToolbarPanel = function (options) {
    this.tooltip = null;
    ToolbarPanel.prototype.init.call(this, options);
};

ToolbarPanel.prototype = new PMUI.core.Panel();
ToolbarPanel.prototype.type = "ToolbarPanel";

ToolbarPanel.prototype.init = function (options) {
    var defaults = {
        fields: [],
        tooltip: "",
        width: "96%"
    };
    jQuery.extend(true, defaults, options);
    PMUI.core.Panel.call(this, defaults);
    this.fields = [];
    this.setTooltip(defaults.tooltip);
    this.setFields(defaults.fields);
};
ToolbarPanel.prototype.setTooltip = function (message) {
    if (typeof message === "string") {
        this.tooltip = message;
    }
    return this;
};

ToolbarPanel.prototype.setFields = function (fields) {
    this.fields = fields;
    return this;
};
/**
 * Creates html structure for a button
 * @param {*} button
 */
ToolbarPanel.prototype.createButtonHTML = function (button) {
    var i,
        li = PMUI.createHTMLElement("li"),
        a = PMUI.createHTMLElement("a");

    li.id = button.selector;
    li.className = "mafe-toolbarpanel-btn";
    a.title = "";
    a.style.cursor = "move";
    jQuery(a).tooltip({
        content: button.tooltip,
        tooltipClass: "mafe-action-tooltip",
        position: {
            my: "left top",
            at: "left bottom",
            collision: "flipfit"
        }
    });

    for (i = 0; i < button.className.length; i += 1) {
        jQuery(a).addClass(button.className[i]);
    }

    li.appendChild(a);
    return li;
};

/**
 * Creates html structure for a switch tongle component
 * @param {*} element
 * @returns {String}
 */
ToolbarPanel.prototype.createSwitchHTML = function (element) {
    var li = PMUI.createHTMLElement("li"),
        input = PMUI.createHTMLElement("input"),
        label = PMUI.createHTMLElement("label"),
        labelDescription = PMUI.createHTMLElement("label");
    labelDescription.innerHTML = element.text || '';
    labelDescription.className = "tgl-label";
    input.type = "checkbox";
    li.className = "mafe-toolbarpanel-switch";
    input.type = "checkbox";
    input.id = element.selector;
    input.className = "tgl tgl-light";
    input.checked = element.checked || false;
    label.htmlFor = element.selector;
    label.className = "tgl-btn";
    input.addEventListener( 'change', function() {
        if (element.checkHandler) {
            if(this.checked) {
                element.checkHandler(true);
            } else {
                element.checkHandler(false);
            }
        }
    });
    li.appendChild(labelDescription);
    li.appendChild(input);
    li.appendChild(label);
    return li;
};

ToolbarPanel.prototype.createHTML = function () {
    var that = this,
        ul,
        html;
    PMUI.core.Panel.prototype.setElementTag.call(this, "ul");
    PMUI.core.Panel.prototype.createHTML.call(this);
    this.html.style.overflow = "visible";
    jQuery.each(this.fields, function (i, button) {
        if (button.type === "button") {
            html = that.createButtonHTML(button);
        } else if (button.type === "switch") {
            html = that.createSwitchHTML(button);
        }
        that.html.appendChild(html);
        button.html = html;
    });
    return this.html;
};

ToolbarPanel.prototype.activate = function () {
    var that = this;
    jQuery.each(this.fields, function (i, b) {
        if (b.type === "button") {
            jQuery(b.html).draggable({
                opacity: 0.7,
                helper: "clone",
                cursor: "hand"
            });
        }
    });
    return this;
};
/**
 * Enable the actions if the toolbar button has an action and is a button
 * @chainable
 */
ToolbarPanel.prototype.enableActions = function () {
    jQuery.each(this.fields, function (i, b) {
        if (b.type === "button") {
            if (b.actions) {
                new PMAction(b.actions);
            }
        }
        
    });
    return this;
};

ToolbarPanel.prototype.getSelectors = function () {
    var selectors = [],
        that = this;
    jQuery.each(this.fields, function (i, button) {
        selectors.push("#" + button.selector);
    });
    return selectors;
};

var PMProject;
PMProject = function (options) {
    this.diagrams = new PMUI.util.ArrayList();
    this.keys = null;
    this.waitingResponse = false;
    this.identifiers = {};
    this.isSave = false;
    this.XMLSupported = true;
    this.isClose = false;
    this.userSettings = null;
    this.definitions = null;
    this.loadingProcess = false;
    this.dirtyElements = [
        {
            laneset: {},
            lanes: {},
            activities: {},
            events: {}, 
            gateways: {},
            flows: {},
            artifacts: {},
            lines: {},
            data: {},
            participants: {}
        },
        {
            laneset: {},
            lanes: {},
            activities: {},
            events: {},
            gateways: {},
            flows: {},
            artifacts: {},
            lines: {},
            data: {},
            participants: {}
        }
    ];
    PMProject.prototype.init.call(this, options);
};

PMProject.prototype.init = function (options) {
    var defaults = {
        projectId: "",
        projectName: "",
        description: "",
        remoteProxy: null,
        localProxy: null,
        readOnly: false,
        keys: {
            access_token: null,
            expires_in: null,
            token_type: null,
            scope: null,
            refresh_token: null,
            client_id: null,
            client_secret: null
        },
        listeners: {
            create: function () {
            },
            remove: function () {
            },
            update: function () {
            },
            success: function () {
            },
            failure: function () {
            }
        }
    };
    jQuery.extend(true, defaults, options);

    this.setKeysClient(defaults.keys)
        .setID(defaults.id)
        .setTokens(defaults.keys)
        .setListeners(defaults.listeners)   
        .setReadOnly(defaults.readOnly);
    this.remoteProxy = new PMUI.proxy.RestProxy();
};

PMProject.prototype.setID = function (id) {
    this.id = id;
    return this;
};

PMProject.prototype.setProjectId = function (id) {
    if (typeof id === "string") {
        this.projectId = id;
    }
    return this;
};

PMProject.prototype.setXMLSupported = function (value) {
    if (typeof value == "boolean")
        this.XMLSupported = value;
    return this;
};
/**
 * Sets the readOnly Mode
 * @param {PMProject} value
 */
PMProject.prototype.setReadOnly = function(value) {
  if (typeof value === "boolean") this.readOnly = value;
  return this;
};

PMProject.prototype.setProjectName = function (name) {
    if (typeof name === "string") {
        this.projectName = name;
        jQuery(".navBar div").remove();
        if ($(".navBar h2").length > 0) {
            $(".navBar h2").text(name);
        } else {
            jQuery(".navBar").append("<h2>" + name + "</h2>");
        }

    }
    return this;
};
/**
 * Sets loading process
 * @param settings
 * @returns {PMProject}
 */
PMProject.prototype.setLoadingProcess = function(loading) {
    if (typeof loading === "boolean") {
        this.loadingProcess = loading;
    }
  return this;
};
/**
 * Sets the user settings to the local property
 * @param settings
 * @returns {PMProject}
 */
PMProject.prototype.setUserSettings= function (settings) {
    this.userSettings = settings;
    return this;
};

PMProject.prototype.setDescription = function (description) {
    this.description = description;
    return this;
};

PMProject.prototype.setKeysClient = function (keys) {
    if (typeof keys === "object") {
        this.keys = keys;
    }
    return this;
};

PMProject.prototype.setListeners = function (listeners) {
    if (typeof listeners === "object") {
        this.listeners = listeners;
    }
    return this;
};
/**
 * Sets the time interval used to save automatically
 * @param {Number} interval Expressed in miliseconds
 * @return {*}
 */
PMProject.prototype.setSaveInterval = function (interval) {
    this.saveInterval = interval;
    return this;
};

PMProject.prototype.getKeysClient = function () {
    var keys = this.keys;
    return {
        access_token: keys.access_token,
        expires_in: keys.expires_in,
        token_type: keys.token_type,
        scope: keys.scope,
        refresh_token: keys.refresh_token,
        client_id: keys.client_id,
        client_secret: keys.client_secret
    };
};
PMProject.prototype.buildCanvas = function (selectors, options) {
    var canvas = new PMCanvas({
        id: PMUI.generateUniqueId(),
        project: PMDesigner.project,
        top: 77,
        width: 6000,
        height: 6000,
        style: {
            cssProperties: {
                overflow: "hidden"
            }
        },
        drop: {
            type: 'canvasdrop',
            selectors: selectors
        },
        container: "pmcanvas",
        readOnly: this.readOnly,
        hasClickEvent: true,
        copyAndPasteReferences: {
            PMEvent: PMEvent,
            PMGateway: PMGateway,
            PMActivity: PMActivity,
            PMArtifact: PMArtifact,
            PMFlow: PMFlow
        }
    });
    jQuery("#div-layout-canvas").append(canvas.getHTML());
    canvas.toogleGridLine();
    canvas.setShapeFactory(PMDesigner.shapeFactory);
    canvas.attachListeners();
    canvas.createConnectHandlers('', '');
    var menuCanvas = PMDesigner.getMenuFactory("CANVAS");
    canvas.setContextMenu(menuCanvas);
    //enable gridLines
    options.userSettings && options.userSettings.enabled_grid ?
        canvas.enableGridLine(): canvas.disableGridLine();
    PMDesigner.canvasList.addOption(
        {
            label: options.name,
            value: canvas.getID()
        });

    this.diagrams.insert(canvas);
    return canvas;
};

PMProject.prototype.getKeysClient = function () {
    var keys = this.keys;
    return {
        access_token: keys.access_token,
        expires_in: keys.expires_in,
        token_type: keys.token_type,
        scope: keys.scope,
        refresh_token: keys.refresh_token,
        client_id: keys.client_id,
        client_secret: keys.client_secret
    };
};

PMProject.prototype.load = function () {
    var keys = this.getKeysClient(),
        that = this;
    $.ajax({
        url: that.remoteProxy.url,
        type: 'GET',
        contentType: "application/json",
        beforeSend: function (xhr) {
            xhr.setRequestHeader("Authorization", "Bearer " + keys.access_token);
            xhr.setRequestHeader("Accept-Language", LANG);
        },
        success: function (data, textStatus) {
            that.dirty = false;
            that.loadProject(data);
            $(".loader").fadeOut("slow");
        },
        error: function (xhr, textStatus, errorThrown) {
            $(".loader").fadeOut("slow");
            that.listeners.failure(that, xhr, response);
        }
    });
    return this;
};

PMProject.prototype.loadProject = function (project) {
    var that = this,
        i,
        j,
        diagram,
        canvas,
        sidebarCanvas = [];
    if (project) {
        this.setLoadingProcess(true);
        this.setProjectId(project.prj_uid);
        this.setProjectName(project.prj_name);
        this.setDescription(project.prj_description);
        this.setUserSettings(project.usr_setting_designer);
        if (project.prj_bpmn_file_upload) {
            that.importDiagram(project.prj_bpmn_file_upload);
        } else {
            for (i = 0; i < project.diagrams.length; i += 1) {
                diagram = project.diagrams[i];
                for (j = 0; j < PMDesigner.sidebar.length; j += 1) {
                    sidebarCanvas = sidebarCanvas.concat(PMDesigner.sidebar[j].getSelectors());
                    jQuery(".bpmn_shapes").append(PMDesigner.sidebar[j].getHTML());
                }
                //Remove Lane
                sidebarCanvas.splice(15, 1);
                //Remove Lasso and Validator
                sidebarCanvas.splice(17, 2);
                
                sidebarCanvas = sidebarCanvas.concat('.mafe-event-start');
                sidebarCanvas = sidebarCanvas.concat('.mafe-event-intermediate');
                sidebarCanvas = sidebarCanvas.concat('.mafe-event-end');
                sidebarCanvas = sidebarCanvas.concat('.pmui-pmactivity');
                sidebarCanvas = sidebarCanvas.concat('.pmui-pmgateway');
                sidebarCanvas = sidebarCanvas.concat('.pmui-pmdata');
                sidebarCanvas = sidebarCanvas.concat('.mafe-artifact-annotation');
                sidebarCanvas = sidebarCanvas.concat('.mafe-artifact-group');
                sidebarCanvas = sidebarCanvas.concat('.mafe-pool');
                sidebarCanvas = sidebarCanvas.concat('.mafe_participant');


                canvas = PMDesigner.project.buildCanvas(sidebarCanvas, {
                    name: 'Main',
                    userSettings: this.userSettings
                });

                PMUI.setActiveCanvas(canvas);
                jQuery("#p-center-layout").scroll(canvas.onScroll(canvas, jQuery("#p-center-layout")));

                var xmlStr =
                    '<?xml version="1.0" encoding="UTF-8"?>' +
                    '<bpmn2:definitions xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" id="BPMNProcessmaker" targetNamespace="http://bpmn.io/schema/bpmn">' +
                    '</bpmn2:definitions>';

                PMDesigner.moddle.fromXML(xmlStr, function (err, definitions) {
                    PMDesigner.businessObject = definitions;
                    canvas.buildDiagram(diagram);
                    if (!project.prj_update_date) {
                        canvas.setDefaultStartEvent();
                        PMDesigner.helper.startIntro();
                    }
                    that.setLoadingProcess(false);
                    that.loaded = true;
                    that.setSaveButtonDisabled();
                    PMDesigner.modeReadOnly();
                    PMDesigner.connectValidator.bpmnValidator();
                });

            }
        }

    }

};
/**
 * Imports a Diagram if this is a valid .bpmn file
 * @param data
 */

PMProject.prototype.importDiagram = function (data) {
    PMDesigner.moddle.fromXML(data, function (err, definitions) {
        if (err) {
            PMDesigner.msgFlash('Import Error: '.translate() + err.message, document.body, 'error', 5000, 5);
        } else {
            PMDesigner.definitions = definitions;
            var imp = new importBpmnDiagram(definitions);
            if (PMDesigner.project.XMLSupported) {
                PMDesigner.businessObject = definitions;
                imp.completeImportFlows();
                PMUI.getActiveCanvas().buildingDiagram = false;
                PMDesigner.project.setDirty(true);
                PMDesigner.project.save(false);
                PMUI.getActiveCanvas().hideAllFocusLabels();
                PMDesigner.project.setXMLSupported(true);
            } else {
                PMDesigner.msgFlash('The process definition that you are trying to import contains BPMN elements that are not supported in ProcessMaker. Please try with other process.'.translate(), document.body, 'error', 5000, 5);
            }
        }
    });
}

/**
 * Represents a flag if the project was saved or not
 */
PMProject.prototype.isDirty = function () {
    return this.dirty;
};
/**
 *  Saves old bpmn project
 * @param options
 * @returns {PMProject}
 */
PMProject.prototype.save = function (options) {
    var keys = this.getKeysClient(),
        that = this;
    if (!this.readOnly && this.isDirty()) {
        that.isSave = true;
        $.ajax({
            url: that.remoteProxy.url,
            type: "PUT",
            contentType: "application/json",
            data: JSON.stringify(that.getDirtyObject()),
            beforeSend: function (xhr, settings) {
                xhr.setRequestHeader("Authorization", "Bearer " + keys.access_token);
                xhr.setRequestHeader("Accept-Language", LANG);
            },
            success: function (data, textStatus, xhr) {
                that.listeners.success(that, textStatus, data);
                that.isSave = false;
            },
            error: function (xhr, textStatus, errorThrown) {
                if (xhr.status == 401 && typeof(xhr.responseJSON.error) != "undefined") {
                    //Refresh Token
                    $.ajax({
                        url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/token",
                        type: "POST",
                        contentType: "application/json",
                        data: JSON.stringify(
                            {
                                grant_type: "refresh_token",
                                client_id: that.keys.client_id,
                                client_secret: that.keys.client_secret,
                                refresh_token: that.keys.refresh_token
                            }
                        ),
                        success: function (data, textStatus, xhr) {
                            that.keys.access_token = xhr.responseJSON.access_token;
                            that.keys.expires_in = xhr.responseJSON.expires_in;
                            that.keys.token_type = xhr.responseJSON.token_type;
                            that.keys.scope = xhr.responseJSON.scope;
                            that.keys.refresh_token = xhr.responseJSON.refresh_token;

                            that.save(true);
                        },
                        error: function (xhr, textStatus, errorThrown) {
                            that.listeners.failure(that, textStatus, xhr);
                            that.isSave = false;
                        }
                    });
                } else {
                    that.listeners.failure(that, textStatus, xhr);

                    that.isSave = false;
                }
            }
        });
    }
    return this;
};

PMProject.prototype.saveClose = function (options) {
    var keys = this.getKeysClient(),
        that = this;
    if (!this.readOnly && this.isDirty()) {
        that.isSave = true;
        $.ajax({
            url: that.remoteProxy.url,
            type: 'PUT',
            data: JSON.stringify(that.getDirtyObject()),
            contentType: "application/json",
            beforeSend: function (xhr) {
                xhr.setRequestHeader("Authorization", "Bearer " + keys.access_token);
                xhr.setRequestHeader("Accept-Language", LANG);
            },
            success: function (data, textStatus) {
                var message_window,
                    browser = PMDesigner.getBrowser();
                url = parent.location.href;
                that.listeners.success(that, textStatus, data);
                that.isSave = false;
                if ((navigator.userAgent.indexOf("MSIE") != -1) || (navigator.userAgent.indexOf("Trident") != -1)) {
                    window.close();
                } else {
                    parent.location.href = url;
                }
            },
            error: function (xhr, textStatus, errorThrown) {
                that.listeners.failure(that, textStatus, xhr);
                that.isSave = false;
            }
        });
    }
    return this;
};

PMProject.prototype.getDirtyObject = function () {
    var that = this,
        diaArray = [],
        shape,
        isGridEnabled = false,
        diagram,
        lastDiagram;

    lastDiagram = this.diagrams.getSize() - 1;
    diagram = this.diagrams.get(lastDiagram);
    shape = this.getDataObject(diagram);
    diaArray.push({
        dia_uid: that.diagramId || PMUI.generateUniqueId(),
        pro_uid: that.id,
        laneset: shape.laneset,
        lanes: shape.lanes,
        activities: shape.activities,
        events: shape.events,
        gateways: shape.gateways,
        flows: shape.flows,
        artifacts: shape.artifacts,
        data: shape.data,
        participants: shape.participants
    });
    isGridEnabled = PMUI.getActiveCanvas().isGridLine &&  PMUI.getActiveCanvas().isGridLine? true: false;
    return {
        prj_uid: that.id,
        prj_name: that.projectName,
        prj_description: that.description,
        usr_setting_designer: {enabled_grid : isGridEnabled},
        diagrams: diaArray
    };
};

PMProject.prototype.getDataObject = function (canvas) {
    var object, i, elements, shapes;
    elements = canvas.items.asArray();
    shapes = {
        activities: [],
        gateways: [],
        events: [],
        flows: [],
        artifacts: [],
        laneset: [],
        lanes: [],
        data: [],
        participants: [],
        pools: []
    };
    if (canvas.items.getSize() > 0) {
        for (i = 0; i < elements.length; i += 1) {
            if (typeof elements[i].relatedObject.getDataObject() === "undefined") {
                object = elements[i].relatedObject;
            } else {
                object = elements[i].relatedObject.getDataObject();
            }
            switch (elements[i].type) {
                case "PMActivity":
                    shapes.activities.push(object);
                    break;
                case "PMGateway":
                    shapes.gateways.push(object);
                    break;
                case "PMEvent":
                    shapes.events.push(object);
                    break;
                case "PMFlow":
                case "Connection":
                    shapes.flows.push(object);
                    break;
                case "PMArtifact":
                    shapes.artifacts.push(object);
                    break;
                case "PMData":
                    shapes.data.push(object);
                    break;
                case "PMParticipant":
                    shapes.participants.push(object);
                    break;
                case "PMPool":
                    shapes.laneset.push(object);
                    break;
                case "PMLane":
                    shapes.lanes.push(object);
                    break;
            }
        }
    }
    return shapes;
};
PMProject.prototype.setDirty = function (dirty) {
    if (typeof dirty === "boolean") {
        this.dirty = dirty;
    }
    return this;
};

PMProject.prototype.addElement = function (element) {
    var object,
        pk_name,
        list,
        i,
        pasteElement,
        elementUndo,
        sh,
        contDivergent = 0,
        contConvergent = 0;
    if (element.relatedElements.length > 0) {
        for (i = element.relatedElements.length - 1; i >= 0; i -= 1) {
            pasteElement = element.relatedElements[i];
            list = this.getUpdateList(pasteElement.type);
            if (list === undefined) {
                return;
            }

            list[pasteElement.id] = object;
            elementUndo = {
                id: pasteElement.id,
                relatedElements: [],
                relatedObject: pasteElement,
                type: pasteElement.type || pasteElement.extendedType
            };
            PMUI.getActiveCanvas().items.insert(elementUndo);
            if (!(pasteElement instanceof PMUI.draw.MultipleSelectionContainer)
                && !(pasteElement instanceof PMLine)
                && !(pasteElement instanceof PMLabel)) {
                pasteElement.createBpmn(pasteElement.getBpmnElementType());
            }
        }
    } else {
        switch (element.type) {
            case "Connection":
                pk_name = this.formatProperty(element.type, 'uid');
                list = this.getUpdateList(element.type);
                element.relatedObject[pk_name] = element.id;

                if (typeof element.relatedObject.getDataObject === "undefined") {
                    object = element.relatedObject;
                }
                list[element.id] = object;
                break;
            default:
                pk_name = this.formatProperty(element.type, 'uid');
                list = this.getUpdateList(element.type);
                element.relatedObject[pk_name] = element.id;
                list[element.id] = object;
                break;
        }
        PMUI.getActiveCanvas().items.insert(element);

        var shape = element.relatedObject;
        if (!(shape instanceof PMUI.draw.MultipleSelectionContainer)
            && !(shape instanceof PMLine)
            && !(shape instanceof PMLabel)) {
            shape.createBpmn(shape.getBpmnElementType());
        }
    }
    if (!this.loadingProcess) {
        this.setDirty(true);
        if(element.type === "Connection") {
            PMDesigner.connectValidator.bpmnValidatorShape(element.relatedObject.destPort.parent);
            PMDesigner.connectValidator.bpmnValidatorShape(element.relatedObject.srcPort.parent);
        }
        
        PMDesigner.connectValidator.bpmnValidatorShape(element.relatedObject);
        //Call to Create callBack
        this.listeners.create(this, element);
    }
};

PMProject.prototype.updateElement = function (updateElement) {
    var element,
        i,
        shape,
        object,
        list,
        item;
    for (i = 0; i < updateElement.length; i += 1) {
        element = updateElement[i];
        shape = element.relatedObject;
        object = this.formatObject(element);
        list = this.getUpdateList(element.type);
        if (list[element.id]) {
            jQuery.extend(true, list[element.id], object);
            if (element.type === 'Connection') {
                list[element.id].flo_state = object.flo_state;
                item = PMUI.getActiveCanvas().items.find("id", element.id);
                item.relatedObject.flo_state = object.flo_state;
            }
        } else {
            list[element.id] = object;
        }
        if (shape) {
            if (shape instanceof PMUI.draw.Port) {
                shape.connection.updateBpmn();
            } else {
                if (!(shape instanceof PMUI.draw.MultipleSelectionContainer)
                    && !(shape instanceof PMLine)
                    && !(shape instanceof PMLabel)) {
                    shape.updateBpmn();
                }
            }
        }
    }
    //run the process validator only when the project has been loaded
    if (!this.loadingProcess) {
        this.setDirty(true);
        PMDesigner.connectValidator.bpmnValidateOnUpdate(updateElement);
        //Call to Update callBack
        this.listeners.update(this, updateElement);
    }
};



PMProject.prototype.removeElement = function (updatedElements) {
    var object,
        dirtyEmptyCounter,
        element,
        i,
        pk_name,
        list,
        emptyObject = {},
        currentItem;

    for (i = 0; i < updatedElements.length; i += 1) {
        element = updatedElements[i];
        currentItem = PMUI.getActiveCanvas().items.find("id", updatedElements[i].id);
        PMUI.getActiveCanvas().items.remove(currentItem);

        list = this.getUpdateList(element.type);
        if (list) {
            pk_name = this.formatProperty(element.type, 'uid');
            if (list[element.id]) {
                delete list[element.id];
            } else {
                pk_name = this.formatProperty(element.type, 'uid');
                object = {};
                object[pk_name] = element.id;
                list[element.id] = object;
            }
        }
        // to remove BpmnModdle in de exported xml
        if (!(element instanceof PMUI.draw.MultipleSelectionContainer)
            && !(element instanceof PMLine)
            && !(element instanceof PMLabel)) {
            element.removeBpmn();
            if (element.atachedDiagram) {
                this.removeAttachedDiagram(element);
            }
        }

    }

    if (!this.isWaitingResponse()) {
        dirtyEmptyCounter = true;
        dirtyEmptyCounter = dirtyEmptyCounter && (this.dirtyElements[0].activities === emptyObject);
        dirtyEmptyCounter = dirtyEmptyCounter && (this.dirtyElements[0].gateways === emptyObject);
        dirtyEmptyCounter = dirtyEmptyCounter && (this.dirtyElements[0].events === emptyObject);
        dirtyEmptyCounter = dirtyEmptyCounter && (this.dirtyElements[0].artifacts === emptyObject);
        dirtyEmptyCounter = dirtyEmptyCounter && (this.dirtyElements[0].flows === emptyObject);
        if (dirtyEmptyCounter) {
            this.setDirty(false);
        }
    }
    this.setDirty(true);
    //Call to Remove callBack
    this.listeners.remove(this, updatedElements);

    //validate bpmn rules on remove
    for (i = 0; i < updatedElements.length; i += 1) {
        if(updatedElements[i].type === "Connection") {
            PMDesigner.connectValidator.bpmnValidatorShape(updatedElements[i].destPort.parent);
            PMDesigner.connectValidator.bpmnValidatorShape(updatedElements[i].srcPort.parent);
        }
        
    }
};

PMProject.prototype.formatProperty = function (type, property) {
    var prefixes = {
            "PMActivity": "act",
            "PMGateway": "gat",
            "PMEvent": "evn",
            "PMArtifact": "art",
            "PMData": "dat",
            "PMParticipant": "par",
            "PMPool": "swl",
            "PMLane": "lan"
        },
        map = {
            x: "bou_x",
            y: "bou_y",
            width: "bou_width",
            height: "bou_height"
        },
        out;

    if (type === "PMFlow" || type === 'Connection') {
        out = "flo_" + property;
    } else if (map[property]) {
        out = map[property];
    } else {
        out = prefixes[type] + '_' + property;
    }
    return out;
};
PMProject.prototype.getUpdateList = function (type) {
    var listName = {
            "PMActivity": "activities",
            "PMGateway": "gateways",
            "PMEvent": "events",
            "PMFlow": "flows",
            "PMArtifact": "artifacts",
            "PMLabel": "artifacts",
            "Connection": "flows",
            "PMData": "data",
            "PMParticipant": "participants",
            "PMPool": "laneset",
            "PMLane": "lanes"
        },
        dirtyArray;
    dirtyArray = (this.isWaitingResponse()) ? 1 : 0;
    return this.dirtyElements[dirtyArray][listName[type]];
};

/**
 * Represents if the proxy is waiting any response from the server
 */
PMProject.prototype.isWaitingResponse = function () {
    return this.waitingResponse;
};

PMProject.prototype.updateIdentifiers = function (response) {
    var i, shape, that = this, connection, shapeCanvas;
    if (typeof response === "object") {
        for (i = 0; i < response.length; i += 1) {
            shape = PMUI.getActiveCanvas().items.find("id", response[i].old_uid);
            shapeCanvas = PMUI.getActiveCanvas().children.find("id", response[i].old_uid);
            connection = PMUI.getActiveCanvas().connections.find("flo_uid", response[i].old_uid);
            this.identifiers[response[i].old_uid] = response[i].new_uid;
            if (shape) {
                shape.id = response[i].new_uid;

                shape.relatedObject.id = response[i].new_uid;
                shape.relatedObject.html.id = response[i].new_uid;
                switch (shape.type) {
                    case "Connection":
                        shape.relatedObject.flo_uid = response[i].new_uid;
                        break;
                    case "PMActivity":
                        shape.relatedObject.act_uid = response[i].new_uid;
                        break;
                    case "PMEvent":
                        shape.relatedObject.evn_uid = response[i].new_uid;
                        break;
                    case "PMGateway":
                        shape.relatedObject.gat_uid = response[i].new_uid;
                        break;
                    case "PMArtifact":
                        shape.relatedObject.art_uid = response[i].new_uid;
                        break;
                    case "PMData":
                        shape.relatedObject.dat_uid = response[i].new_uid;
                        break;
                    case "PMParticipant":
                        shape.relatedObject.par_uid = response[i].new_uid;
                        break;
                    case "PMPool":
                        shape.relatedObject.lns_uid = response[i].new_uid;
                        shape.relatedObject.participantObject.id = 'el_' + response[i].new_uid;
                        break;
                    case "PMLane":
                        shape.relatedObject.lan_uid = response[i].new_uid;
                        break;
                }
            }
            if (shapeCanvas) {
                shapeCanvas.id = response[i].new_uid;
            }
            if (connection) {
                connection.flo_uid = response[i].new_uid;
                connection.id = response[i].new_uid;
            }
        }
    }
};

PMProject.prototype.formatObject = function (element) {
    var i,
        field,
        formattedElement = {},
        property;
    formattedElement[this.formatProperty(element.type, 'uid')] = element.id;

    if (element.adam) {
        for (i = 0; i < element.fields.length; i += 1) {
            field = element.fields[i];
            formattedElement[field.field] = field.newVal;
        }
    } else if (element.fields) {
        for (i = 0; i < element.fields.length; i += 1) {
            field = element.fields[i];
            property = this.formatProperty(element.type, field.field);
            if (property === "element_uid") {
                field.newVal = field.newVal.id;
            }
            formattedElement[property] = field.newVal;
        }
    }
    return formattedElement;
};

PMProject.prototype.subProcessDiagram = function (element) {
    var sidebarCanvas = [], opt = {name: element.act_name}, s, newCanvas, di;
    PMUI.getActiveCanvas().getHTML().style.display = 'none';
    if (!element.atachedDiagram) {
        for (s = 0; s < PMDesigner.sidebar.length; s += 1) {
            sidebarCanvas = sidebarCanvas.concat(PMDesigner.sidebar[s].getSelectors());
            jQuery(".bpmn_shapes").append(PMDesigner.sidebar[s].getHTML());
        }

        sidebarCanvas.splice(17, 1);
        sidebarCanvas.splice(5, 1);
        sidebarCanvas = sidebarCanvas.concat('.pmui-pmevent');
        sidebarCanvas = sidebarCanvas.concat('.pmui-pmactivity');
        sidebarCanvas = sidebarCanvas.concat('.pmui-pmgateway');
        sidebarCanvas = sidebarCanvas.concat('.pmui-pmdata');
        sidebarCanvas = sidebarCanvas.concat('.mafe-artifact-annotation');
        sidebarCanvas = sidebarCanvas.concat('.mafe-artifact-group');
        sidebarCanvas = sidebarCanvas.concat('.mafe-pool');
        sidebarCanvas = sidebarCanvas.concat('.mafe_participant');
        newCanvas = this.buildCanvas(sidebarCanvas, opt);
        PMUI.setActiveCanvas(newCanvas);
        jQuery("#p-center-layout").scroll(newCanvas.onScroll(newCanvas, jQuery("#p-center-layout")));
        newCanvas.getHTML().style.display = 'inline';
        element.atachedDiagram = newCanvas;
        PMDesigner.canvasList.setValue(newCanvas.getID());

        di = newCanvas.createBPMNDiagram();
        newCanvas.businessObject = element.businessObject;
        di.bpmnElement = element.businessObject; //update reference
        newCanvas.businessObject.di = di;
    } else {
        newCanvas = element.atachedDiagram;
        PMUI.setActiveCanvas(newCanvas);
        newCanvas.getHTML().style.display = 'inline';
        PMDesigner.canvasList.setValue(newCanvas.getID());
    }

};

PMProject.prototype.removeAttachedDiagram = function (element) {
    var canvas = element.atachedDiagram;
    this.diagrams.remove(canvas);
    if (canvas.html !== undefined) {
        jQuery(canvas.html).remove();
        canvas.html = null;
    }
    element.atachedDiagram = null;
    PMDesigner.canvasList.removeOption(canvas.getID());
};
PMProject.prototype.setTokens = function (response) {
    this.tokens = response;
    return this;
};

PMProject.prototype.setSaveButtonDisabled = function () {
    if (this.isDirty()) {
        if (document.getElementsByClassName("mafe-save-process").length > 0) {
            document.getElementsByClassName("mafe-save-process")[0].removeAttribute("style");
            document.getElementsByClassName("mafe-save-process")[0].childNodes[0].style.color = "#FFF";

            var mafebuttonMenu = document.getElementsByClassName("mafe-button-menu")[0];
            mafebuttonMenu.style.backgroundColor = "#0C9778";
            mafebuttonMenu.firstChild.src = "/lib/img/caret-down-w.png";
        }
    } else {
        if (document.getElementsByClassName("mafe-save-process").length > 0) {
            document.getElementsByClassName("mafe-save-process")[0].style.backgroundColor = "#e8e8e8";
            document.getElementsByClassName("mafe-save-process")[0].style.color = "#000";
            document.getElementsByClassName("mafe-save-process")[0].childNodes[0].style.color = "#000";
            document.getElementsByClassName("mafe-save-process")[0].childNodes[0].text = "Save".translate();

            var mafebuttonMenu = document.getElementsByClassName("mafe-button-menu")[0];
            mafebuttonMenu.style.backgroundColor = "#e8e8e8";
            mafebuttonMenu.firstChild.src = "/lib/img/caret-down.png";
        }
    }
};


/**
 * @class Snapper
 * Class snapper represents the helper shown while moving shapes.
 * @extend JCoreObject
 *
 * @constructor Creates an instance of the class Snapper
 * @param {Object} options Initialization options
 * @cfg {Point} [orientation="horizontal"] The default orientation of this snapper
 */
var PMSnapper = function (options) {
    PMUI.draw.Snapper.call(this, options);
    /**
     * Orientation of this snapper, it can be either "horizontal" or "vertical".
     * @property {string} [orientation=null]
     */
    this.orientation = null;
    /**
     * Data saved to define the positioning of this snapper in the canvas.
     * @property {Array} [data=[]]
     */
    this.data = [];
    /**
     * The visibility of this snapper.
     * @property {boolean} [visible=false]
     */
    this.visible = false;

    PMSnapper.prototype.initObject.call(this, options);
};

PMSnapper.prototype = new PMUI.draw.Snapper();

/**
 * The type of each instance of this class
 * @property {String}
 */
PMSnapper.prototype.type = "Snapper";

/**
 * Instance initializer which uses options to extend the config options to initialize the instance.
 * @param {Object} options The object that contains the config
 * @private
 */
PMSnapper.prototype.initObject = function (options) {
    var defaults = {
        orientation: "horizontal"
    };
    // extend recursively the defaultOptions with the given options
    $.extend(true, defaults, options);
    // call setters using the defaults object
    this.setOrientation(defaults.orientation);
    this.setDimension(defaults.width, defaults.height);
    // create the html (it's hidden initially)
    this.hide();
};
PMSnapper.prototype.getHTML = function () {
    if (!this.html) {
        this.createHTML();
    }
    return this.html;
};
/**
 * Creates the HTML representation of the snapper.
 * @returns {HTMLElement}
 */
PMSnapper.prototype.createHTML = function () {
    if (!this.html) {
        this.html = document.createElement("div");
        this.style.applyStyle();
        this.style.addProperties({
            position: "absolute",
            left: this.zoomX,
            top: this.zoomY,
            width: this.zoomWidth,
            height: this.zoomHeight,
            zIndex: this.zOrder
        });
        this.html.id = this.id;
        this.canvas.html.appendChild(this.html);
        this.setZOrder(99);
        this.html.className = 'mafe-snapper';
        if (this.getOrientation() === 'horizontal') {
            this.html.id = 'guide-h';
            this.html.style.borderTop = '1px dashed #55f';
            this.html.style.width = '100%';
        } else {
            this.html.id = 'guide-v';
            this.html.style.borderLeft = '1px dashed #55f';
            this.html.style.height = '100%';
        }
    }
    return this.html;
};

/**
 * Hides the snapper.
 * @chainable
 */
PMSnapper.prototype.hide = function () {
    this.visible = false;
    this.setVisible(this.visible);
    return this;
};

/**
 * Shows the snapper.
 * @chainable
 */
PMSnapper.prototype.show = function () {
    this.visible = true;
    this.setVisible(this.visible);
    return this;
};

/**
 * Fills the data for the snapper (using customShapes and regularShapes).
 * The data considered for each shape is:
 *
 * - Its absoluteX
 * - Its absoluteY
 * - Its absoluteX + width
 * - Its absoluteY + height
 *
 * @chainable
 */
PMSnapper.prototype.createSnapData = function () {
    var i,
        index = 0,
        shape,
        border = 0;

    // clear the data before populating it
    this.data = [];
    // populate the data array using the customShapes
    for (i = 0; i < this.canvas.customShapes.getSize(); i += 1) {
        shape = this.canvas.customShapes.get(i);
        if (!this.canvas.currentSelection.find('id', shape.getID())) {
            border = parseInt($(shape.getHTML()).css('borderTopWidth'), 10);
            if (this.orientation === 'horizontal') {
                this.data[index * 2] = shape.getAbsoluteY() - border;
                this.data[index * 2 + 1] = shape.getAbsoluteY() + shape.getZoomHeight();
            } else {
                this.data[index * 2] = shape.getAbsoluteX() - border;
                this.data[index * 2 + 1] = shape.getAbsoluteX() + shape.getZoomWidth();
            }
            index += 1;
        }

    }
    // populate the data array using the regularShapes
    for (i = 0; i < this.canvas.regularShapes.getSize(); i += 1) {
        shape = this.canvas.regularShapes.get(i);
        border = parseInt($(shape.getHTML()).css('borderTopWidth'), 10);
        if (this.orientation === 'horizontal') {
            this.data[index * 2] = shape.getAbsoluteY() - border;
            this.data[index * 2 + 1] = shape.getAbsoluteY() +
                shape.getZoomHeight();
        } else {
            this.data[index * 2] = shape.getAbsoluteX() - border;
            this.data[index * 2 + 1] = shape.getAbsoluteX() +
                shape.getZoomWidth();
        }
        index += 1;
    }
    return this;
};

/**
 * Sorts the data using the builtin `sort()` function, so that there's an strictly increasing order.
 * @chainable
 */
PMSnapper.prototype.sortData = function () {
    this.data.sort(function (a, b) {
        return a > b;
    });
    return this;
};

/**
 * Performs a binary search for `value` in `this.data`, return true if `value` was found in the data.
 * @param {number} value
 * @return {boolean}
 */
PMSnapper.prototype.binarySearch = function (value) {
    var low = 0,
        up = this.data.length - 1,
        mid;

    while (low <= up) {
        mid = parseInt((low + up) / 2, 10);
        if (this.data[mid] === value) {
            return value;
        }
        if (this.data[mid] > value) {
            up = mid - 1;
        } else {
            low = mid + 1;
        }
    }
    return false;
};

/**
 * Attaches listeners to this snapper, currently it only has the
 * mouseMove event which hides the snapper.
 * @param {Snapper} snapper
 * @chainable
 */
PMSnapper.prototype.attachListeners = function (snapper) {
    var $snapper = $(snapper.html).mousemove(
        function () {
            snapper.hide();
        }
    );
    return this;
};

/**
 * Sets the orientation of this snapper.
 * @param {string} orientation
 * @chainable
 */
PMSnapper.prototype.setOrientation = function (orientation) {
    if (orientation === "horizontal" || orientation === "vertical") {
        this.orientation = orientation;
    } else {
        throw new Error("setOrientation(): parameter is not valid".translate());
    }
    return this;
};

/**
 * Gets the orientation of this snapper.
 * @return {string}
 */
PMSnapper.prototype.getOrientation = function () {
    return this.orientation;
};
var PMCanvas = function (options) {
    PMUI.draw.Canvas.call(this, options);
    this.project = null;
    this.items = null;
    /**
     * Minimum distance to "snap" to a guide
     * @type {number}
     */
    this.MIN_DISTANCE = 4;
    /**
     * Array which contains a list of all coordinates  to snap
     * @type {number}
     */
    this.guides = [];

    this.attachedListeners = null;

    this.hasClickEvent = false;
    this.isDragging = false;
    this.isGridLine = true;
    this.dragConnectHandlers = new PMUI.util.ArrayList();
    this.dropConnectHandlers = new PMUI.util.ArrayList();
    this.isDraggingConnectHandler = false;
    this.businessObject = {};
    this.isMouseOverHelper = false;
    this.canConnect = false;
    this.canCreateShape = false;
    this.canCreateShapeType = null;
    this.canCreateShapeClass = null;
    this.shapeHelper = null;
    this.coronaClick = false;
    this.connectStartShape = null;
    this.coronaShape = null;
    this.lassoEnabled = false;
    this.lassoLimits = null;
    PMCanvas.prototype.init.call(this, options);
};

PMCanvas.prototype = new PMUI.draw.Canvas();

PMCanvas.prototype.type = "PMCanvas";

this.canvasContainerBehavior = null;

PMCanvas.prototype.init = function (options) {
    var defaults = {
        project: null,
        snapToGuide: true,
        enabledMenu: false,
        hasClickEvent: false
    };
    jQuery.extend(true, defaults, options);
    this.setProject(defaults.project)
        .setEnabledMenu(defaults.enabledMenu)
        .setHasClickEvent(defaults.hasClickEvent)
        .setSnapToGuide(defaults.snapToGuide);

    this.items = new PMUI.util.ArrayList();
    this.attachedListeners = false;
};


PMCanvas.prototype.setHasClickEvent = function (value) {
    this.hasClickEvent = value;
    return this;
};
PMCanvas.prototype.setEnabledMenu = function (value) {
    this.enabledMenu = value;
    return this;
};
PMCanvas.prototype.setParent = function (parent) {
    this.parent = parent;
    return this;
};

PMCanvas.prototype.setProject = function (project) {
    if (project instanceof PMProject) {
        this.project = project;
    }
    return this;
};
PMCanvas.prototype.onCreateElementHandler = function (element) {
    var id,
        label,
        menuElement,
        shapeElement;
    if (this.project) {

        this.project.addElement(element);
        if (!this.project.loadingProcess) {
            if (element.relatedObject && (element.relatedObject.type === 'PMPool'
                || element.relatedObject.type === 'PMActivity')) {
                element.relatedObject.canvas.emptyCurrentSelection();
                element.relatedObject.canvas.addToSelection(element.relatedObject);
            }
        }
        if (element.type === "Connection") {
            return;
        }
    }
};

/**
 * Factory of canvas behaviors. It uses lazy instantiation to create
 * instances of the different container behaviors
 * @param {String} type An string that specifies the container behavior we want
 * an instance to have, it can be regular or nocontainer
 * @return {ContainerBehavior}
 */
PMCanvas.prototype.containerBehaviorFactory = function (type) {
    if (type === 'pmcanvas') {
        if (!this.canvasContainerBehavior) {
            this.canvasContainerBehavior = new CanvasContainerBehavior();
        }
        return this.canvasContainerBehavior;
    } else {
        return PMShape.prototype.containerBehaviorFactory.call(this, type);
    }
};

PMCanvas.prototype.dropBehaviorFactory = function (type, selectors) {
    if (type === 'canvasdrop') {
        if (!this.pmConnectionDropBehavior) {
            this.pmConnectionDropBehavior = new PMContainerDropBehavior(selectors);
        }
        return this.pmConnectionDropBehavior;
    } else {
        return PMUI.draw.CustomShape.prototype.dropBehaviorFactory.call(this, type, selectors);
    }
};

PMCanvas.prototype.triggerTextChangeEvent = function (element, oldText, newText) {
    var valid, reg, e, nText, mp, id;
    if (element.parent.getType() === 'PMActivity' && !this.validateName(element, newText, oldText)) {
        newText = oldText;
    }
    reg = /<[^\s]/g;
    nText = newText.trim();
    e = reg.test(nText);
    if (e) {
        nText = nText.replace(/</g, '< ');
    }

    this.updatedElement = [{
        id: element.parent.id,
        type: element.parent.type,
        parent: element.parent,
        fields: [{
            field: "name",
            oldVal: oldText,
            newVal: nText
        }]
    }];
    element.parent.setName(nText);
    element.updateDimension();
    element.parent.setBPPMName(nText);
    if (element.parent.atachedDiagram) {
        id = PMDesigner.canvasList.getID();
        $('#' + id + ' option[value=' + element.parent.atachedDiagram.getID() + ']')
            .text(nText);
    }

    jQuery(this.html).trigger("changeelement");
};

PMCanvas.prototype.triggerConnectionStateChangeEvent = function (connection) {
    var points = [],
        Point = PMUI.util.Point,
        point,
        i;
    for (i = 0; i < connection.points.length; i += 1) {
        point = connection.points[i];
        points.push(new Point(point.x / this.zoomFactor, point.y / this.zoomFactor));
    }
    this.updatedElement = [{
        id: connection.getID(),
        type: connection.type,
        fields: [
            {
                field: 'state',
                oldVal: connection.getOldPoints(),
                newVal: points
            }
        ],
        relatedObject: connection
    }];
    connection.algorithm = 'user';
    $(this.html).trigger('changeelement');
    this.hideDragConnectHandlers();
    return this;
};

PMCanvas.prototype.triggerUserStateChangeEvent = function (connection) {
    var points = [],
        Point = PMUI.util.Point,
        point,
        i;
    for (i = 0; i < connection.points.length; i += 1) {
        point = connection.points[i];
        points.push(new Point(point.x / this.zoomFactor, point.y / this.zoomFactor));
    }
    this.updatedElement = [{
        id: connection.getID(),
        type: connection.type,
        fields: [
            {
                field: 'state',
                oldVal: connection.getOldPoints(),
                newVal: points
            }
        ],
        relatedObject: connection
    }];
    connection.algorithm = 'user';
    return this;
};

PMCanvas.prototype.updateDimensionLabel = function (element) {
    var width,
        width = element.relatedObject.width;
    newWidth = Math.max(width, this.zoomWidth);
    element.relatedObject.label.setWidth(width);
    return this;
};
PMCanvas.prototype.onChangeElementHandler = function (element) {
    var textNode,
        currentElement;
    if (this.project && element.length > 0) {
        try {
            this.hideAllCoronas();
            this.project.updateElement(element);
        } catch (e) {
            throw new Error("Error, There are problems updating the element".translate(), e);
        }
    }
};
PMCanvas.prototype.onRemoveElementHandler = function (elements) {
    var i,
        element,
        shapeElement;
    if (this.project) {
        this.project.removeElement(elements);
        try {
            for (i = 0; i < elements.length; i += 1) {
                if (elements[i].type === "Connection") {
                    element = elements[i];
                    element.updateIncomingAndOutgoingConnections("remove");
                    shapeElement = element.destPort.getParent();
                    if (shapeElement instanceof PMGateway) {
                        shapeElement.evaluateGatewayDirection();
                    }
                    shapeElement = element.srcPort.getParent();
                    if (shapeElement instanceof PMGateway) {
                        shapeElement.evaluateGatewayDirection();
                    }
                    PMDesigner.project.updateElement([]);
                    break;
                }
            }
        } catch (e) {
            throw new Error("Error, There are problems removing the element".translate(), e);
        }
    }
};
PMCanvas.prototype.onSelectElementHandler = function (element) {
    PMUI.removeCurrentMenu();
    if (element.length === 1) {
        switch (element[0].type) {
            case 'PMActivity':
            case 'PMEvent':
            case 'PMGateway':
                break;
        }
    }
    if (this.currentLabel != null) {
        this.hideAllFocusedLabels();
    }
    this.isSelected = true;
    return this;
};

PMCanvas.prototype.defineEvents = function () {
    if (!this.readOnly) {
        return PMUI.draw.Canvas.prototype.defineEvents.call(this);
    }
};
PMCanvas.prototype.getContextMenu = function () {
    return {};
};
PMCanvas.prototype.onRightClick = function () {
    var that = this;
    return function (a, b, c) {
    };
};
/**
 * Set guide Lines to canvas and create vertican and horizontal snappers
 * @param {Boolean} snap new value to verify if canvas has enabled snappes
 * @chainable
 */
PMCanvas.prototype.setSnapToGuide = function (snap) {
    this.snapToGuide = snap;
    // create snappers

    this.horizontalSnapper = new PMSnapper({
        orientation: 'horizontal',
        canvas: this,
        width: 4000,
        height: 1
    });


    this.verticalSnapper = new PMSnapper({
        orientation: 'vertical',
        canvas: this,
        width: 1,
        height: 4000
    });

    return this;
};
/**
 * Build the data of the snappers recreating the arrays,
 * this method is called from {@link RegularDragBehavior#onDragStart} (it might
 * be an overrided method `onDragStart` if the instance of {@link RegularDragBehavior} was changed).
 * @chainable
 */
PMCanvas.prototype.startSnappers = function (event) {
    var shape, i, parent;
    this.horizontalSnapper.getHTML();
    this.verticalSnapper.getHTML();
    this.guides = [];
    for (i = 0; i < this.customShapes.getSize(); i += 1) {
        shape = this.customShapes.get(i);
        if (!this.currentSelection.find('id', shape.getID())) {
            this.computeGuidesForElement(shape);
        }
    }
    return this;

};

PMCanvas.prototype.computeGuidesForElement = function (shape) {
    var x = shape.getHTML().offsetLeft, y = shape.getHTML().offsetTop,
        w, h;

    w = shape.getZoomWidth() - 1;
    h = shape.getZoomHeight() - 1;
    this.guides.push(
        {type: "h", x: x, y: y},
        {type: "h", x: x, y: y + h},
        {type: "v", x: x, y: y},
        {type: "v", x: x + w, y: y}
    );
    return this;
};

/**
 * Process the snappers according to this criteria and show and hide:
 *
 * - To show the vertical snapper
 *      - `shape.absoluteX` must equal a value in the data of `this.verticalSnapper`
 *      - `shape.absoluteX + shape.width` must equal a value in the data of `this.verticalSnapper`
 *
 * - To show the horizontal snapper
 *      - `shape.absoluteY` must equal a value in the data of `this.horizontalSnapper`
 *      - `shape.absoluteY + shape.height` must equal a value in the data of `this.horizontalSnapper`
 *
 * @param {Object} e
 * @parem {Object} ui
 * @param {Shape} customShape
 * @chainable
 */
PMCanvas.prototype.processGuides = function (e, ui, customShape) {
    // iterate all guides, remember the closest h and v guides
    var guideV,
        guideH,
        distV = this.MIN_DISTANCE + 1,
        distH = this.MIN_DISTANCE + 1,
        offsetV,
        offsetH,
        mouseRelX,
        mouseRelY,
        pos,
        w = customShape.getZoomWidth() - 1,
        h = customShape.getZoomHeight() - 1,
        d;

    mouseRelY = e.originalEvent.pageY - ui.offset.top;
    mouseRelX = e.originalEvent.pageX - ui.offset.left;
    pos = {
        top: e.originalEvent.pageY - customShape.canvas.getY() - mouseRelY
        + customShape.canvas.getTopScroll(),
        left: e.originalEvent.pageX - customShape.canvas.getX() - mouseRelX
        + customShape.canvas.getLeftScroll()
    };
    $.each(this.guides, function (i, guide) {
        if (guide.type === "h") {
            d = Math.abs(pos.top - guide.y);
            if (d < distH) {
                distH = d;
                guideH = guide;
                offsetH = 0;
            }
            d = Math.abs(pos.top - guide.y + h);
            if (d < distH) {
                distH = d;
                guideH = guide;
                offsetH = h;
            }
        }
        if (guide.type === "v") {
            d = Math.abs(pos.left - guide.x);
            if (d < distV) {
                distV = d;
                guideV = guide;
                offsetV = 0;
            }
            d = Math.abs(pos.left - guide.x + w);
            if (d < distV) {
                distV = d;
                guideV = guide;
                offsetV = w;
            }
        }
    });

    if (distH <= this.MIN_DISTANCE) {
        $("#guide-h").css("top", guideH.y - this.absoluteY).show();
        if (customShape.parent.family !== 'Canvas') {
            ui.position.top = guideH.y - offsetH - customShape.getParent().getAbsoluteY();
        } else {
            ui.position.top = guideH.y - offsetH;
        }
    } else {
        $("#guide-h").hide();
    }

    if (distV <= this.MIN_DISTANCE) {
        $("#guide-v").css("left", guideV.x - this.absoluteX).show();
        if (customShape.parent.family !== 'Canvas') {
            ui.position.left = guideV.x - offsetV - customShape.getParent().getAbsoluteX();
        } else {
            ui.position.left = guideV.x - offsetV;
        }

    } else {
        $("#guide-v").hide();
    }
    return this;
};

/**
 * Fires the {@link PMUI.draw.Canvas#event-changeelement} event, and elaborates the structure of the object that will
 * be passed to the handlers, the structure contains the following fields (considering old values and new values):
 *
 * - x
 * - y
 * - parent (the shape that is parent of this shape)
 * - state (of the connection)
 *
 * @param {PMUI.draw.Port} port The port updated
 * @chainable
 */
PMCanvas.prototype.triggerPortChangeEvent = function (port) {
    var direction = port.connection.srcPort.getID() === port.getID() ?
            "src" : "dest",
        map = {
            src: {
                x: "x1",
                y: "y1",
                parent: "element_origin",
                type: 'element_origin_type'
            },
            dest: {
                x: "x2",
                y: "y2",
                parent: "element_dest",
                type: 'element_dest_type'
            }
        },
        point,
        state,
        zomeedState = [],
        i;
    state = port.connection.getPoints();

    for (i = 0; i < state.length; i += 1) {
        point = port.connection.points[i];
        zomeedState.push(new PMUI.util.Point(point.x / this.zoomFactor, point.y / this.zoomFactor));
    }
    point = direction === "src" ? zomeedState[0] : zomeedState[state.length - 1];

    this.updatedElement = [{
        id: port.connection.getID(),
        type: port.connection.type,
        fields: [
            {
                field: map[direction].x,
                oldVal: point.x,        // there's no old value
                newVal: point.x
            },
            {
                field: map[direction].y,
                oldVal: point.y,        // there's no old value
                newVal: point.y
            },
            {
                field: map[direction].parent,
                oldVal: (port.getOldParent()) ? port.getOldParent().getID() : null,
                newVal: port.getParent().getID()
            },
            {
                field: map[direction].type,
                oldVal: port.connection.getNativeType(port.getParent()).type,
                newVal: port.connection.getNativeType(port.getParent()).type
            },
            {
                field: "state",
                oldVal: port.connection.getOldPoints(),
                newVal: zomeedState
            },
            {
                field: "condition",
                oldVal: "",
                newVal: port.connection.getFlowCondition()
            }
        ],
        relatedObject: port
    }];
    $(this.html).trigger('changeelement');
};

/**
 * Attaches event listeners to this canvas, it also creates some custom triggers
 * used to save the data (to send it to the database later).
 *
 * The events attached to this canvas are:
 *
 * - {@link PMUI.draw.Canvas#event-mousedown Mouse down event}
 * - {@link PMUI.draw.Canvas#event-mousemove Mouse move event}
 * - {@link PMUI.draw.Canvas#event-mouseup Mouse up event}
 * - {@link PMUI.draw.Canvas#event-click Click event}
 * - {@link PMUI.draw.Canvas#event-scroll Scroll event}
 *
 * The custom events are:
 *
 * - {@link PMUI.draw.Canvas#event-createelement Create element event}
 * - {@link PMUI.draw.Canvas#event-removeelement Remove element event}
 * - {@link PMUI.draw.Canvas#event-changeelement Change element event}
 * - {@link PMUI.draw.Canvas#event-selectelement Select element event}
 * - {@link PMUI.draw.Canvas#event-rightclick Right click event}
 *
 * This method also initializes jQueryUI's droppable plugin (instantiated as `this.dropBehavior`)
 * @chainable
 */
PMCanvas.prototype.attachListeners = function () {
    var $canvas,
        $canvasContainer;
    if (this.attachedListeners === false) {
        $canvas = $(this.html);
        if (!this.readOnly) {
            $canvas.click(this.onClick(this)),
            $canvasContainer = $canvas.parent();
            $canvas.dblclick(this.onDblClick(this));
            $canvas.mousedown(this.onMouseDown(this));
            $canvasContainer.scroll(this.onScroll(this, $canvasContainer));
            $canvas.mousemove(this.onMouseMove(this));
            $canvas.mouseup(this.onMouseUp(this));
            $canvas.on("rightclick", this.onRightClick(this));
        }
        $canvas.on("createelement", this.onCreateElement(this));
        $canvas.on("removeelement", this.onRemoveElement(this));
        $canvas.on("changeelement", this.onChangeElement(this));
        $canvas.on("selectelement", this.onSelectElement(this));
        $canvas.on("contextmenu", function (e) {
            e.preventDefault();
        });
        this.updateBehaviors();
        this.attachedListeners = true;
    }
    return this;
};
/**
 * enpty current selection extended
 * @returns {PMCanvas}
 */
PMCanvas.prototype.emptyCurrentSelection = function () {
    this.hideAllCoronas();
    PMUI.draw.Canvas.prototype.emptyCurrentSelection.call(this);
    return this;
};
/**
 * mouse move custom behavior
 * @param canvas
 * @returns {Function}
 */
PMCanvas.prototype.onMouseMove = function (canvas) {
    return function (e, ui) {
        if (canvas.lassoEnabled && canvas.isMouseDown && !canvas.rightClick) {
            canvas.isMouseDownAndMove = true;
            var x = e.pageX - canvas.getX() + canvas.getLeftScroll() - canvas.getAbsoluteX(),
                y = e.pageY - canvas.getY() + canvas.getTopScroll() - canvas.getAbsoluteY(),
                topLeftX,
                topLeftY,
                bottomRightX,
                bottomRightY;
            topLeftX = Math.min(x, canvas.multipleSelectionHelper.oldX);
            topLeftY = Math.min(y, canvas.multipleSelectionHelper.oldY);
            bottomRightX = Math.max(x, canvas.multipleSelectionHelper.oldX);
            bottomRightY = Math.max(y, canvas.multipleSelectionHelper.oldY);
            canvas.multipleSelectionHelper.setPosition(
                topLeftX / canvas.zoomFactor,
                topLeftY / canvas.zoomFactor
            );
            canvas.multipleSelectionHelper.setDimension(
                (bottomRightX - topLeftX) / canvas.zoomFactor,
                (bottomRightY - topLeftY) / canvas.zoomFactor
            );

        } else if (canvas.canConnect) {
            canvas.connectHelper(e)
            canvas.connectStartShape.corona.hide();
            canvas.hideAllFocusedLabels();
        } else if (canvas.canCreateShape) {
            canvas.createShapeHelper(e);
        }

    };
};
/**
 * on mouse up behavior
 * @param canvas
 * @returns {Function}
 */
PMCanvas.prototype.onMouseUp = function (canvas) {
    return function (e, ui) {
        var realPoint,
            x,
            y;
        e.preventDefault();
        if (canvas.canCreateShape) {
            canvas.manualCreateShape(canvas, e);
            canvas.canCreateShape = false;
            return true;
        }
        if (canvas.isMouseDownAndMove) {
            realPoint = canvas.relativePoint(e);
            x = realPoint.x;
            y = realPoint.y;
            canvas.multipleSelectionHelper.setPosition(
                Math.min(x, canvas.multipleSelectionHelper.zoomX) / canvas.zoomFactor,
                Math.min(y, canvas.multipleSelectionHelper.zoomY) / canvas.zoomFactor
            );
            if (canvas.multipleSelectionHelper) {
                canvas.multipleSelectionHelper.wrapElements();
                canvas.IncreaseAllConnectionZIndex();
            }
        } else {

            if (!canvas.multipleSelectionHelper.wasDragged) {
                canvas.multipleSelectionHelper.reset().setVisible(false);
            }
            if (canvas.isMouseDown) {
                canvas.onClickHandler(canvas, x, y);
            }
        }
        canvas.isMouseDown = false;
        canvas.isMouseDownAndMove = false;
        canvas.rightClick = false;
        //hide lasso tool
        $('.mafe-toolbar-lasso').css('background-color', 'rgb(233, 233, 233)');
        canvas.lassoEnabled = false;
    };
};
/**
 * Increacess z.Index to all connections
 * @constructor
 */
PMCanvas.prototype.IncreaseAllConnectionZIndex = function () {
    var i,
        connection;
    for (i = 0; i < this.sharedConnections.getSize(); i += 1) {
        connection = this.sharedConnections.get(i);
        connection.increaseZIndex();
    }
};
PMCanvas.prototype.createShapeHelper = function (e) {
    var realPoint = this.relativePoint(e);
    if (this.shapeHelper) {
        //remove the connection segment in order to create another one
        $(this.shapeHelper.html).remove();
    }
    this.shapeHelper = new CreateShapeHelper({
        x: realPoint.x * this.zoomFactor - this.getX(),
        y: realPoint.y * this.zoomFactor - this.getY(),
        parent: this,
        zOrder: 999,
        className: this.canCreateShapeClass
    });
    this.shapeHelper.paint();
};

PMCanvas.prototype.connectHelper = function (e) {
    var endPoint = {},
        realPoint,
        diff;

    if (this.canConnect) {
        if (this.connectionSegment) {
            //remove the connection segment in order to create another one
            $(this.connectionSegment.getHTML()).remove();
        }
        //start point
        this.startConnectionPoint = {
            x: this.connectStartShape.getAbsoluteX() + this.connectStartShape.xMidPoints[1],
            y: this.connectStartShape.getAbsoluteY() + this.connectStartShape.yMidPoints[1]
        };

        //Determine the point where the mouse currently is
        realPoint = this.relativePoint(e);

        endPoint.x = realPoint.x * this.zoomFactor - this.getX();
        endPoint.y = realPoint.y * this.zoomFactor - this.getY();

        endPoint.x += (endPoint.x - this.startConnectionPoint.x > 0) ? -5 : 5;
        endPoint.y += (endPoint.y - this.startConnectionPoint.y > 0) ? -5 : 5;

        //creates a new segment from where the helper was created to the
        // currently mouse location
        this.connectionSegment = new PMUI.draw.Segment({
            startPoint: this.startConnectionPoint,
            endPoint: endPoint,
            parent: this,
            zOrder: 9
        });
        this.connectionSegment.paint();
    }
};

PMCanvas.prototype.connectProcedure = function (customShape, e) {
    var endPoint,
        tempPoint,
        initPoint,
        i,
        endPort,
        sourcePort,
        distance = 99999999,
        connection,
        endPoint2,
        validationResult;

    if (customShape.canvas.connectionSegment) {
        //remove the connection segment left
        $(customShape.canvas.connectionSegment.getHTML()).remove();

    }
    customShape.canvas.canConnect = false;
    $('body').css('cursor', 'default');

    validationResult = PMDesigner.connectValidator.isValid(customShape.canvas.connectStartShape, customShape);

    if (!validationResult.result) {
        //show invalid message
        PMDesigner.msgFlash(validationResult.msg, document.body, 'info', 3000, 5);
        return false;
    }

    sourcePort = new PMUI.draw.Port({
        width: 10,
        height: 10
    });
    endPort = new PMUI.draw.Port({
        width: 10,
        height: 10
    });
    endPoint = new PMUI.util.Point(
        e.pageX - customShape.canvas.getX() - customShape.getAbsoluteX() + customShape.canvas.getLeftScroll(),
        e.pageY - customShape.canvas.getY() - customShape.getAbsoluteY() + customShape.canvas.getTopScroll()
    );
    endPoint2 = new PMUI.util.Point(
        e.pageX - customShape.canvas.getX() + customShape.canvas.getLeftScroll(),
        e.pageY - customShape.canvas.getY() + customShape.canvas.getTopScroll()
    );

    for (i = 0; i < customShape.canvas.connectStartShape.xMidPoints.length; i += 1) {
        tempPoint = new PMUI.util.Point(
            customShape.canvas.connectStartShape.getAbsoluteX() + customShape.canvas.connectStartShape.xMidPoints[i],
            customShape.canvas.connectStartShape.getAbsoluteY() + customShape.canvas.connectStartShape.yMidPoints[i]
        );
        if (distance > tempPoint.getSquaredDistance(endPoint2)) {
            distance = tempPoint.getSquaredDistance(endPoint2);
            initPoint = new PMUI.util.Point(
                customShape.canvas.connectStartShape.xMidPoints[i],
                customShape.canvas.connectStartShape.yMidPoints[i]
            )
        }
    }

    customShape.canvas.connectStartShape.addPort(sourcePort, initPoint.x, initPoint.y);
    customShape.addPort(endPort, endPoint.x, endPoint.y, false, sourcePort);

    //add ports to the canvas array for regularShapes
    //create the connection
    connection = new PMFlow({
        srcPort: sourcePort,
        destPort: endPort,
        segmentColor: new PMUI.util.Color(0, 0, 0),
        name: " ",
        canvas: customShape.canvas,
        segmentStyle: customShape.connectionType.segmentStyle,
        flo_type: customShape.connectionType.type
    });

    connection.setSrcDecorator(new PMUI.draw.ConnectionDecorator({
        width: 11,
        height: 11,
        canvas: customShape.canvas,
        decoratorPrefix: (typeof customShape.connectionType.srcDecorator !== 'undefined'
        && customShape.connectionType.srcDecorator !== null) ?
            customShape.connectionType.srcDecorator : "mafe-decorator",
        decoratorType: "source",
        parent: connection
    }));

    connection.setDestDecorator(new PMUI.draw.ConnectionDecorator({
        width: 11,
        height: 11,
        canvas: customShape.canvas,
        decoratorPrefix: (typeof customShape.connectionType.destDecorator !== 'undefined'
        && customShape.connectionType.destDecorator !== null) ?
            customShape.connectionType.destDecorator : "mafe-decorator",
        decoratorType: "target",
        parent: connection
    }));
    connection.canvas.commandStack.add(new PMUI.command.CommandConnect(connection));

    //connect the two ports
    connection.connect();
    connection.setSegmentMoveHandlers();

    //add the connection to the canvas, that means insert its html to
    // the DOM and adding it to the connections array
    customShape.canvas.addConnection(connection);

    // Filling PMFlow fields
    connection.setTargetShape(endPort.parent);
    connection.setOriginShape(sourcePort.parent);

    // now that the connection was drawn try to create the intersections
    connection.checkAndCreateIntersectionsWithAll();

    //attaching port listeners
    sourcePort.attachListeners(sourcePort);
    endPort.attachListeners(endPort);

    // finally trigger createEvent
    customShape.canvas.triggerCreateEvent(connection, []);

};
/**
 * hides all corona shape
 */
PMCanvas.prototype.hideAllCoronas = function () {
    var i,
        shape;
    for (i = 0; i < this.currentSelection.getSize(); i += 1) {
        shape = this.currentSelection.get(i);
        if (shape.corona) {
            shape.corona.hide();
        }
    }
    return this;
};
/**
 * cancel connection action
 */
PMCanvas.prototype.cancelConnect = function () {
    if (this.connectionSegment) {
        $(this.connectionSegment.getHTML()).remove();
    }
    this.canConnect = false;
    $('body').css('cursor', 'default');
};
/**
 * doble click mouse behavior
 * @param canvas
 * @returns {Function}
 */
PMCanvas.prototype.onDblClick = function (canvas) {
    return function (e, ui) {
        var currentLabel = canvas.currentLabel, figure, realPoint, realPoint, oldConnection;
        e.stopPropagation();
        e.preventDefault();
        realPoint = canvas.relativePoint(e);
        realPoint.x = realPoint.x * canvas.zoomFactor - canvas.getX();
        realPoint.y = realPoint.y * canvas.zoomFactor - canvas.getY();
        figure = canvas.getBestConnecion(realPoint);
        if (figure !== null) {
            canvas.emptyCurrentSelection();
            figure.label.getFocus();

        }
    };
};

PMCanvas.prototype.hideAllFocusedLabels = function () {
    if (this.currentLabel != null)
        this.currentLabel.loseFocus();
    return true;
};

/**
 * @event mousedown
 * MouseDown Handler of the canvas. It does the following:
 *
 * - Trigger the {@link PMUI.draw.Canvas#event-rightclick Right Click event} if it detects a right click
 * - Empties `canvas.currentSelection`
 * - Hides `canvas.currentConnection` if there's one
 * - Resets the position of `canvas.multipleSelectionContainer` making it visible and setting its
 *      `[x, y]` to the point where the user did mouse down in the `canvas`.
 *
 * @param {PMUI.draw.Canvas} canvas
 */
PMCanvas.prototype.onMouseDown = function (canvas) {
    return function (e, ui) {
        var x = e.pageX - canvas.getX() + canvas.getLeftScroll() - canvas.getAbsoluteX(),
            y = e.pageY - canvas.getY() + canvas.getTopScroll() - canvas.getAbsoluteY();

        if (canvas.canConnect) {
            canvas.cancelConnect();
        }
        //hide corona
        if (canvas.coronaShape) {
            canvas.hideAllCoronas();
        }
        e.preventDefault();
        if (e.which === 3) {
            canvas.rightClick = true;
            $(canvas.html).trigger("rightclick", [e, canvas]);
        }
        canvas.isMouseDown = true;
        canvas.isMouseDownAndMove = false;
        // do not create the rectangle selection if a segment handler
        // is being dragged
        if (canvas.draggingASegmentHandler) {
            return;
        }
        // clear old selection
        canvas.emptyCurrentSelection();
        //verify lasso is enabled
        if (canvas.lassoEnabled) {
            // hide the currentConnection if there's one
            canvas.hideCurrentConnection();
            canvas.multipleSelectionHelper.reset();
            canvas.multipleSelectionHelper.setPosition(x / canvas.zoomFactor,
                y / canvas.zoomFactor);
            canvas.multipleSelectionHelper.oldX = x;
            canvas.multipleSelectionHelper.oldY = y;
            canvas.multipleSelectionHelper.setVisible(true);
            canvas.multipleSelectionHelper.changeOpacity(0.2);
        }
    };
};

PMCanvas.prototype.onClick = function (canvas) {
    return function (e, ui) {
        var currentLabel = canvas.currentLabel, figure, realPoint, realPoint, oldConnection;
        if (currentLabel) {
            currentLabel.loseFocus();
            $(currentLabel.textField).focusout();
        }
        realPoint = canvas.relativePoint(e);
        realPoint.x = realPoint.x * canvas.zoomFactor - canvas.getX();
        realPoint.y = realPoint.y * canvas.zoomFactor - canvas.getY();
        figure = canvas.getBestConnecion(realPoint);
        canvas.hideDropConnectHandlers();
        if (figure !== null && !canvas.isMouseDown) {
            oldConnection = canvas.currentConnection;
            canvas.emptyCurrentSelection();
            if (oldConnection) {
                oldConnection.hidePortsAndHandlers();
            }
            figure.showPortsAndHandlers();
            canvas.currentConnection = figure;
        }

    };
};

PMCanvas.prototype.onMouseLeave = function (canvas) {
    return function (e, ui) {
        if (parseInt(e.screenX + 10, 10) >= parseInt(document.body.clientWidth, 10)) {
            window.scrollBy(1, 0);
        }

        if (parseInt(e.screenY - 75, 10) >= parseInt(document.body.clientHeight, 10)) {
            window.scrollBy(0, 1);
        }
    };
};
PMCanvas.prototype.manualCreateShape = function (parent, e) {
    var customShape = this.shapeFactory(this.canCreateShapeType),
        command,
        endPoint = {},
        realPoint = this.relativePoint(e);

    endPoint.x = realPoint.x * this.zoomFactor - parent.getAbsoluteX();
    endPoint.y = realPoint.y * this.zoomFactor - parent.getAbsoluteY();
    endPoint.y -= this.getY();

    parent.addElement(customShape, endPoint.x, endPoint.y, false);
    this.updatedElement = customShape;
    customShape.canvas.emptyCurrentSelection();
    this.addToList(customShape);
    customShape.showOrHideResizeHandlers(false);
    if (customShape.getParent() instanceof PMLane) {
        command = new PMCommandCreateInLane(customShape);
    } else {
        command = new PMUI.command.CommandCreate(customShape);
    }
    this.commandStack.add(command);
    command.execute();

    this.addToSelection(customShape);
    customShape.corona.show();

    e.pageY += customShape.getZoomHeight() / 2;
    this.connectProcedure(customShape, e);
    this.canCreateShape = false;

    this.connectStartShape.corona.hide();
    if (this.shapeHelper) {
        //remove the connection segment in order to create another one
        $(this.shapeHelper.html).remove();
    }

    if (customShape.getType() === 'PMGateway'
        || customShape.getType() === 'PMEvent') {

        customShape.manualCreateMenu(e);
        customShape.canvas.hideAllCoronas();
    }

};

/**
 * Parses `options` creating shapes and connections and placing them in this canvas.
 * It does the following:
 *
 * - Creates each shape (in the same order as it is in the array `options.shapes`)
 * - Creates each    connection (in the same order as it is in the array `options.connections`)
 * - Creates the an instance of {@link PMUI.command.CommandPaste} (if possible)
 *
 * @param {Object} options
 * @param {Array} [options.shapes=[]] The config options of each shape to be placed in this canvas.
 * @param {Array} [options.connections=[]] The config options of each connection to be placed in this canvas.
 * @param {boolean} [options.uniqueID=false] If set to true, it'll assign a unique ID to each shape created.
 * @param {boolean} [options.selectAfterFinish=false] If set to true, it'll add the shapes that are
 * direct children of this canvas to `this.currentSelection` arrayList.
 * @param {string} [options.prependMessage=""] The message to be prepended to each shape's label.
 * @param {boolean}  [options.createCommand=true] If set to true it'll create a command for each creation
 * of a shape and connection (see {@link PMUI.command.CommandCreate},
 {@link PMUI.command.CommandConnect}) and save them in
 * a {@link PMUI.command.CommandPaste} (for undo-redo purposes).
 * @param {number} [options.diffX=0] The number of pixels on the x-coordinate to move the shape on creation
 * @param {number} [options.diffY=0] The number of pixels on the y-coordinate to move the shape on creation
 * @chainable
 */
PMCanvas.prototype.parse = function (options) {
    var defaults = {
            shapes: [],
            connections: [],
            uniqueID: false,
            selectAfterFinish: false,
            prependMessage: "",
            createCommand: true,
            diffX: 0,
            diffY: 0
        },
        i,
        j,
        id,
        oldID,
        shape,
        points,
        shapeOptions,
        connection,
        connectionOptions,
        sourcePort,
        sourcePortOptions,
        sourceShape,
        sourceBorder,
        destPort,
        destPortOptions,
        destShape,
        destBorder,
        command,
        diffX,
        diffY,
        stackCommandCreate = [],
        stackCommandConnect = [],
        canvasID = this.getID(),
        mapOldId = {},
        map = {};
    $.extend(true, defaults, options);
    // set the differentials (if the shapes are pasted in the canvas)
    diffX = defaults.diffX;
    diffY = defaults.diffY;
    // map the canvas
    map[canvasID] = this;
    mapOldId[canvasID] = canvasID;
    // empty the current selection and sharedConnections as a consequence
    // (so that the copy is selected after)
    if (defaults.selectAfterFinish) {
        this.emptyCurrentSelection();
    }
    for (i = 0; i < defaults.shapes.length; i += 1) {
        shapeOptions = {};
        $.extend(true, shapeOptions, defaults.shapes[i]);

        // set the canvas of <shape>
        shapeOptions.canvas = this;

        // create a map of the current id with a new id
        oldID = shapeOptions.id;

        // generate a unique id on user request
        if (defaults.uniqueID) {
            shapeOptions.id = PMUI.generateUniqueId();
        }
        mapOldId[oldID] = shapeOptions.id;

        // change labels' messages (using prependMessage)
        if (shapeOptions.labels) {
            for (j = 0; j < shapeOptions.labels.length; j += 1) {
                shapeOptions.labels[j].message = defaults.prependMessage +
                    shapeOptions.labels[j].message;
            }
        }

        // create an instance of the shape based on its type
        shape = this.shapeFactory(shapeOptions.extendedType, shapeOptions);

        // map the instance with its id
        map[shapeOptions.id] = shape;

        // if the shapes don't have a valid parent then set the parent
        // to be equal to the canvas
        // TODO: ADD shapeOptions.topLeftOnCreation TO EACH SHAPE
        if (!mapOldId[shapeOptions.parent]) {
            this.addElement(shape,
                shapeOptions.x + diffX, shapeOptions.y + diffY, true);
        } else if (shapeOptions.parent !== canvasID) {
            // get the parent of this shape
            map[mapOldId[shapeOptions.parent]].addElement(shape, shapeOptions.x,
                shapeOptions.y, true);
        } else {
            // move the shapes a little (so it can be seen that
            // they were duplicated)
            map[mapOldId[shapeOptions.parent]].addElement(shape,
                shapeOptions.x + diffX, shapeOptions.y + diffY, true);
        }

        // perform some extra actions defined for each shape
        shape.parseHook();
        shape.attachListeners();
        // execute command create but don't add it to the canvas.commandStack
        command = new PMUI.command.CommandCreate(shape);
        command.execute();
        stackCommandCreate.push(command);
    }
    for (i = 0; i < defaults.connections.length; i += 1) {
        connectionOptions = {};
        $.extend(true, connectionOptions, defaults.connections[i]);
        // state of the connection
        points = connectionOptions.state || [];
        // determine the shapes
        sourcePortOptions = connectionOptions.srcPort;
        sourceShape = map[mapOldId[sourcePortOptions.parent]];
        sourceBorder = sourceShape.getBorderConsideringLayers();

        destPortOptions = connectionOptions.destPort;
        destShape = map[mapOldId[destPortOptions.parent]];
        destBorder = destShape.getBorderConsideringLayers();

        // populate points if points has no info (backwards compatibility,
        // e.g. the flow state is null)
        if (points.length === 0) {
            points.push({
                x: sourcePortOptions.x + sourceShape.getAbsoluteX(),
                y: sourcePortOptions.y + sourceShape.getAbsoluteY()
            });
            points.push({
                x: destPortOptions.x + destShape.getAbsoluteX(),
                y: destPortOptions.y + destShape.getAbsoluteY()
            });
        }

        //create the ports
        sourcePort = new PMUI.draw.Port({
            width: 8,
            height: 8
        });
        destPort = new PMUI.draw.Port({
            width: 8,
            height: 8
        });
        // add the ports to the shapes
        // LOGIC: points is an array of points relative to the canvas.
        // CustomShape.addPort() requires that the point passed as an argument
        // is respect to the shape, so transform the point's coordinates (also
        // consider the border)
        sourceShape.addPort(
            sourcePort,
            points[0].x + diffX + sourceBorder -
            sourceShape.getAbsoluteX(),
            points[0].y + diffX + sourceBorder -
            sourceShape.getAbsoluteY()
        );
        destShape.addPort(
            destPort,
            points[points.length - 1].x + diffX + destBorder -
            destShape.getAbsoluteX(),
            points[points.length - 1].y + diffY + destBorder -
            destShape.getAbsoluteY(),
            false,
            sourcePort
        );

        connection = this.connectionFactory(
            connectionOptions.type,
            {
                srcPort: sourcePort,
                destPort: destPort,
                segmentColor: new PMUI.util.Color(92, 156, 204),
                canvas: this,
                segmentStyle: connectionOptions.segmentStyle
            }
        );
        connection.id = connectionOptions.id || PMUI.generateUniqueId();
        if (defaults.uniqueID) {
            connection.id = PMUI.generateUniqueId();
        }
        //set its decorators
        connection.setSrcDecorator(new PMUI.draw.ConnectionDecorator({
            width: 1,
            height: 1,
            canvas: this,
            decoratorPrefix: connectionOptions.srcDecoratorPrefix,
            decoratorType: "source",
            parent: connection
        }));
        connection.setDestDecorator(new PMUI.draw.ConnectionDecorator({
            width: 1,
            height: 1,
            canvas: this,
            decoratorPrefix: connectionOptions.destDecoratorPrefix,
            decoratorType: "target",
            parent: connection
        }));
        command = new PMUI.command.CommandConnect(connection);
        stackCommandConnect.push(command);
        // connect the two ports
        if (points.length >= 3) {
            connection.connect({
                algorithm: 'user',
                points: connectionOptions.state,
                dx: defaults.diffX,
                dy: defaults.diffY
            });
        } else {
            connection.connect();
        }
        connection.setSegmentMoveHandlers();
        // add the connection to the canvas, that means insert its html to
        // the DOM and adding it to the connections array
        this.addConnection(connection);
        // now that the connection was drawn try to create the intersections
        connection.checkAndCreateIntersectionsWithAll();
        //attaching port listeners
        sourcePort.attachListeners(sourcePort);
        destPort.attachListeners(destPort);

        this.triggerCreateEvent(connection, []);
    }

    // finally add to currentSelection each shape if possible (this method is
    // down here because of the zIndex problem with connections)
    if (defaults.selectAfterFinish) {
        for (id in map) {
            if (map.hasOwnProperty(id)) {
                if (map[id].family !== 'Canvas') {
                    this.addToSelection(map[id]);
                }
            }
        }
    }
    // create command if possible
    if (defaults.createCommand) {
        this.commandStack.add(new PMUI.command.CommandPaste(this, {
            stackCommandCreate: stackCommandCreate,
            stackCommandConnect: stackCommandConnect
        }));
    }
    return this;
};

/**
 * Fires the {@link PMUI.draw.Canvas#event-removeelement} event,
 and elaborates the structure of the object that will
 * be passed to the handlers.
 * @param {PMUI.draw.CustomShape} shape The shape created
 * @param {Array} relatedElements The array with the other elements created
 * @chainable
 */
PMCanvas.prototype.triggerRemoveEvent = function (shape, relatedElements) {
    if (relatedElements.length === 0) {
        if (shape) {
            relatedElements.push(shape);
        }
    }
    this.updatedElement = {
        id: (shape && shape.id) || null,
        type: (shape && shape.type) || null,
        relatedObject: shape,
        relatedElements: relatedElements
    };
    this.canvas.hideDragConnectHandlers();
    if (shape && shape.corona && shape.getType() !== 'Connection') {
        shape.corona.hide();
    }
    if (shape && shape.validatorMarker) {
        shape.validatorMarker.removeBoxMarker();
    }
    $(this.html).trigger('removeelement');
    return this;
};

PMCanvas.prototype.createConnectHandlers = function (resizableStyle, nonResizableStyle) {
    var i,
        number = 20,
        connectHandler;
    //add the rest to the mid list
    for (i = 0; i < number; i += 1) {
        connectHandler = new PMConnectHandler({
            parent: this,
            zOrder: PMUI.util.Style.MAX_ZINDEX + 4,
            representation: new PMUI.draw.Rectangle(),
            resizableStyle: resizableStyle,
            nonResizableStyle: nonResizableStyle
        });
        this.dragConnectHandlers.insert(
            connectHandler
        );
        if (!this.html) {
            return;
        }
        this.html.appendChild(connectHandler.getHTML());
        connectHandler.setPosition(100, 100);
        connectHandler.setCategory("dragConnectHandler");
        connectHandler.attachListeners();
        connectHandler.paint();
    }

    for (i = 0; i < number; i += 1) {
        connectHandler = new PMConnectHandler({
            parent: this,
            zOrder: PMUI.util.Style.MAX_ZINDEX + 1,
            representation: new PMUI.draw.Rectangle(),
            resizableStyle: resizableStyle,
            nonResizableStyle: nonResizableStyle
        });
        this.dropConnectHandlers.insert(
            connectHandler
        );
        if (!this.html) {
            return;
        }
        this.html.appendChild(connectHandler.getHTML());
        connectHandler.setPosition(400, 100);
        connectHandler.setCategory("dropConnectHandler");
        connectHandler.attachListeners();
        connectHandler.paint();
    }
    return this;
};

PMCanvas.prototype.hideDragConnectHandlers = function () {
    var connectHandler,
        i;
    for (i = 0; i < this.dragConnectHandlers.getSize(); i += 1) {
        connectHandler = this.dragConnectHandlers.get(i);
        connectHandler.setVisible(false);
    }

    return this;
};

PMCanvas.prototype.hideDropConnectHandlers = function () {
    var connectHandler,
        i;
    for (i = 0; i < this.dropConnectHandlers.getSize(); i += 1) {
        connectHandler = this.dropConnectHandlers.get(i);
        connectHandler.setVisible(false);
    }
    return this;
};

PMCanvas.prototype.applyZoom = function (scale) {
    this.hideDragConnectHandlers();
    this.hideDropConnectHandlers();
    PMUI.draw.Canvas.prototype.applyZoom.call(this, scale);
    return this;
};
PMCanvas.prototype.existThatName = function (element, name) {
    var i,
        shape,
        result = false;
    for (i = 0; i < this.customShapes.getSize(); i += 1) {
        shape = this.customShapes.get(i);
        if (shape.getID() !== element.getID() && shape.getName() === element.getName()) {
            result = true;
            break;
        }
    }
    return result;
};

PMCanvas.prototype.validateName = function (element, newText, oldText) {
    var result = true;
    if ((typeof newText === "string") && (newText.trim() === "")) {
        result = false;
        PMDesigner.msgFlash("Task/sub-process name can't be empty".translate(), document.body, 'error', 3000, 5);
    } else if (this.existThatName(element.parent, newText)) {
        result = false;
        PMDesigner.msgFlash('This name already exists.'.translate(), document.body, 'error', 3000, 5);
    }
    return result;
};

PMCanvas.prototype.addConnection = function (conn) {
    var shapeElement;
    PMUI.draw.Canvas.prototype.addConnection.call(this, conn);
    if (conn.flo_state) {
        conn.algorithm = 'user';
        conn.disconnect(true).connect({
            algorithm: 'user',
            points: conn.flo_state
        });
        conn.setSegmentMoveHandlers();
    }
    conn.updateIncomingAndOutgoingConnections("create");
    shapeElement = conn.destPort.getParent();
    if (shapeElement instanceof PMGateway) {
        shapeElement.evaluateGatewayDirection();
    }
    shapeElement = conn.srcPort.getParent();
    if (shapeElement instanceof PMGateway) {
        shapeElement.evaluateGatewayDirection();
    }
};
/**
 * This method hide all flows into a container (shape);
 * @param {BPMNShape} shape
 */
PMCanvas.prototype.hideFlowRecursively = function (shape) {
    var i,
        child,
        j,
        flow;
    for (i = 0; i < shape.getChildren().getSize(); i += 1) {
        child = shape.getChildren().get(i);
        for (j = 0; j < child.getPorts().getSize(); j += 1) {
            flow = child.getPorts().get(j).connection;
            flow.disconnect();
        }
        if (child.getChildren().getSize() > 0) {
            this.hideFlowRecursively(child);
        }
    }
};

/**
 * Remove all selected elements, it destroy the shapes and all references to them.
 * @chainable
 */
PMCanvas.prototype.removeElements = function () {
    var that = this,
        dialogConfirm;
    if (!that.canCreateShape && !that.isDragging) {
        // Delete shape with a modal Dialog Confirm.
        dialogConfirm = new FormDesigner.main.DialogConfirm(null, "warning", "Are you sure you want to delete this element?".translate());
        dialogConfirm.onAccept = function () {
            that.executeCommandDelete();
        };
        dialogConfirm.onClose = function () {
            PMUI.isDelete = false;
            return that;
        };
    }
    return that;
};

/**
 * Calls to Delete command and executes the action
 * @param {PMCanvas} canvas
 */
PMCanvas.prototype.executeCommandDelete = function () {
    // destroy the shapes (also destroy all the references to them)
    var command = new PMCommandDelete(this);
    this.commandStack.add(command);
    command.execute();
    return this;
};

PMCanvas.prototype.triggerTaskTypeChangeEvent = function (element) {
    this.updatedElement = [{
        id: element.id,
        type: element.type,
        fields: [
            {
                field: "act_task_type",
                oldVal: '',
                newVal: this.act_task_type
            },
            {
                field: "act_task_type",
                oldVal: '',
                newVal: this.act_task_type
            }
        ],
        relatedObject: element
    }];
    $(this.html).trigger('changeelement');
    return this;
};

PMCanvas.prototype.buildDiagram = function (diagram) {
    var that = this;
    this.buildingDiagram = true;
    jQuery.each(diagram.laneset, function (index, val) {
        laneset = diagram.laneset[index];
        if (that.propertiesReview("laneset", laneset)) {
            that.loadShape('POOL', laneset, true);
        }
    });

    jQuery.each(diagram.lanes, function (index, val) {
        lanes = diagram.lanes[index];
        if (that.propertiesReview("lanes", lanes)) {
            that.loadShape('LANE', lanes, true);

        }
    });

    jQuery.each(diagram.activities, function (index, val) {
        activities = diagram.activities[index];
        if (that.propertiesReview("activities", activities)) {
            that.loadShape(activities.act_type, activities, true);
        }
    });
    jQuery.each(diagram.events, function (index, val) {
        events = diagram.events[index];
        if (that.propertiesReview("events", events)) {
            that.loadShape(events.evn_type, events, true);
        }
    });
    jQuery.each(diagram.gateways, function (index, val) {
        gateways = diagram.gateways[index];
        if (that.propertiesReview("gateways", gateways)) {
            that.loadShape(gateways.gat_type, gateways, true);
        }
    });
    jQuery.each(diagram.artifacts, function (index, val) {
        artifacts = diagram.artifacts[index];
        if (that.propertiesReview("artifacts", artifacts)) {
            that.loadShape(artifacts.art_type, artifacts, true);
        }
    });
    jQuery.each(diagram.data, function (index, val) {
        data = diagram.data[index];
        if (that.propertiesReview("data", data)) {
            that.loadShape(data.dat_type, data, true);
        }
    });
    jQuery.each(diagram.participants, function (index, val) {
        participants = diagram.participants[index];
        if (that.propertiesReview("participants", participants)) {
            that.loadShape('PARTICIPANT', participants, true);
        }
    });
    jQuery.each(diagram.flows, function (index, val) {
        connections = diagram.flows[index];
        if (that.propertiesReview("flows", connections)) {
            that.loadFlow(connections, true);
        }
    });

    this.buildingDiagram = false;
};
/**
 * Adds a start event as a defaul init canvas
 */
PMCanvas.prototype.setDefaultStartEvent = function () {
    var customShape = this.shapeFactory('START'),
        command;
    this.addElement(customShape, 100, 100, customShape.topLeftOnCreation);
    this.updatedElement = customShape;
    command = new PMUI.command.CommandCreate(customShape);
    this.commandStack.add(command);
    command.execute();

    this.addToSelection(customShape);
    customShape.corona.show();

};

PMCanvas.prototype.propertiesReview = function (type, currenShape) {
    var passed = true, shape, i;

    shape = {
        laneset: [],
        lanes: [],
        activities: [
            "act_uid",
            "act_name",
            "act_type"
        ],
        events: [
            "evn_uid",
            "evn_name",
            "evn_type"
        ],
        gateways: [
            "gat_uid",
            "gat_name",
            "gat_type"
        ],
        flows: [
            "flo_uid",
            "flo_type",
            "flo_element_dest",
            "flo_element_origin",
            "flo_x1",
            "flo_x2",
            "flo_y1",
            "flo_y2"
        ],
        artifacts: [],
        startMessageEvent: [
            "evn_uid",
            "evn_name",
            "evn_type"
        ],
        startTimerEvent: [
            "evn_uid",
            "evn_name",
            "evn_type"
        ]

    };

    if (shape[type]) {
        for (i = 0; i < shape[type].length; i += 1) {
            if (currenShape[shape[type][i]]) {
                if (currenShape[shape[type][i]] === null && currenShape[shape[type][i]] === "") {
                    currenShape[shape[type][i]] = " ";
                }
            }
        }
    }
    return true;
};
/**
 * Loads the shape provided by the shape factory.
 * @param type
 * @param shape
 * @param fireTrigger
 * @param businessObject
 */

PMCanvas.prototype.loadShape = function (type, shape, fireTrigger, businessObject) {
    var customShape,
        command,
        transformShape,
        container;

    transformShape = this.setShapeValues(type, shape);
    customShape = this.shapeFactory(type, transformShape);

    if (customShape) {
        //to import .bpmn diagram
        if (businessObject) {
            customShape.businessObject = businessObject;
        }
        customShape.extendedType = type;
        if (shape.bou_container === 'bpmnDiagram') {
            this.addElement(customShape, parseInt(shape.bou_x, 10), parseInt(shape.bou_y, 10), true);
        } else {
            container = this.customShapes.find('id', shape.bou_element);
            container.addElement(customShape, parseInt(shape.bou_x, 10), parseInt(shape.bou_y, 10), true);
        }
        this.updatedElement = customShape;
        this.addToList(customShape);
        customShape.showOrHideResizeHandlers(false);
        if (fireTrigger) {
            this.triggerCreateEvent(customShape, []);
        }
    }
};

PMCanvas.prototype.setShapeValues = function (type, options) {
    var newShape;
    switch (type) {
        case "TASK":
        case "SUB_PROCESS":
            options.width = parseInt(options.bou_width, 10);
            options.height = parseInt(options.bou_height, 10);
            options.id = options.act_uid;
            options.labels = [
                {
                    message: options.act_name
                }
            ];
            break;
        case "START":
        case "END":
        case "INTERMEDIATE":
        case "BOUNDARY":
            options.id = options.evn_uid;
            options.labels = [
                {
                    message: options.evn_name
                }
            ];
            break;

        case "TEXT_ANNOTATION":
        case "GROUP":
            options.width = parseInt(options.bou_width, 10);
            options.height = parseInt(options.bou_height, 10);
            options.id = options.art_uid;
            options.labels = [
                {
                    message: options.art_name
                }
            ];
            break;
        case "COMPLEX":
        case "EXCLUSIVE":
        case "PARALLEL":
        case "INCLUSIVE":
        case "EVENTBASED":
            options.id = options.gat_uid;
            options.labels = [
                {
                    message: options.gat_name
                }
            ];
            break;
        case "DATAOBJECT":
        case "DATASTORE":
        case "DATAINPUT":
        case "DATAOUTPUT":
            options.id = options.dat_uid;
            options.labels = [
                {
                    message: options.dat_name
                }
            ];
            break;
        case "PARTICIPANT":
            options.id = options.par_uid;
            options.width = parseInt(options.bou_width, 10);
            options.height = parseInt(options.bou_height, 10);
            options.labels = [
                {
                    message: options.par_name
                }
            ];
            break;
        case "POOL":
            options.id = options.lns_uid;
            options.width = parseInt(options.bou_width, 10);
            options.height = parseInt(options.bou_height, 10);
            options.labels = [
                {
                    message: options.lns_name
                }
            ];
            break;
        case "LANE":
            options.id = options.lan_uid;
            options.relPosition = parseInt(options.bou_rel_position, 10);
            options.width = parseInt(options.bou_width, 10);
            options.height = parseInt(options.bou_height, 10);
            options.labels = [
                {
                    message: options.lan_name
                }
            ];
            break;
    }
    return options;
};

PMCanvas.prototype.loadFlow = function (conn, trigger) {
    var sourceObj,
        targetObj,
        startPoint,
        endPoint,
        sourcePort,
        targetPort,
        connection,
        segmentMap = {
            'SEQUENCE': 'regular',
            'MESSAGE': 'segmented',
            'DATAASSOCIATION': 'dotted',
            'ASSOCIATION': 'dotted',
            'DEFAULT': 'regular',
            'CONDITIONAL': 'regular'
        },
        srcDecorator = {
            'SEQUENCE': 'mafe-decorator',
            'MESSAGE': 'mafe-message',
            'DATAASSOCIATION': 'mafe-association',
            'ASSOCIATION': 'mafe-decorator',
            'DEFAULT': 'mafe-default',
            'CONDITIONAL': 'mafe-decorator_conditional'
        },
        destDecorator = {
            'SEQUENCE': 'mafe-sequence',
            'MESSAGE': 'mafe-message',
            'DATAASSOCIATION': 'mafe-association',
            'ASSOCIATION': 'mafe-decorator_association',
            'DEFAULT': 'mafe-sequence',
            'CONDITIONAL': 'mafe-sequence'
        },
        positionSourceX,
        positionSourceY,
        positionTargetX,
        positionTargetY;

    sourceObj = this.getElementByUid(conn.flo_element_origin);
    targetObj = this.getElementByUid(conn.flo_element_dest);

    if (typeof sourceObj === "object" && typeof targetObj === "object") {
        startPoint = new PMUI.util.Point(conn.flo_x1, conn.flo_y1);
        endPoint = new PMUI.util.Point(conn.flo_x2, conn.flo_y2);

        sourcePort = new PMUI.draw.Port({
            width: 10,
            height: 10
        });

        targetPort = new PMUI.draw.Port({
            width: 10,
            height: 10
        });

        positionSourceX = startPoint.x - sourceObj.absoluteX + this.canvas.absoluteX;
        positionSourceY = startPoint.y - sourceObj.absoluteY + this.canvas.absoluteY;

        positionTargetX = endPoint.x - targetObj.absoluteX + this.canvas.absoluteX;
        positionTargetY = endPoint.y - targetObj.absoluteY + this.canvas.absoluteY;

        sourceObj.addPort(sourcePort, positionSourceX, positionSourceY);
        targetObj.addPort(targetPort, positionTargetX, positionTargetY, false, sourcePort);

        if (!conn.flo_name) {
            conn.flo_name = ' ';
        }
        connection = new PMFlow({
            id: conn.flo_uid,
            srcPort: sourcePort,
            destPort: targetPort,
            canvas: this.canvas,
            segmentStyle: segmentMap[conn.flo_type],
            segmentColor: new PMUI.util.Color(0, 0, 0),
            flo_type: conn.flo_type,
            name: conn.flo_name,
            flo_condition: conn.flo_condition,
            flo_state: conn.flo_state,
            flo_uid: (conn.flo_uid) ? conn.flo_uid : null
        });

        connection.setSrcDecorator(new PMUI.draw.ConnectionDecorator({
            decoratorPrefix: srcDecorator[conn.flo_type],
            decoratorType: "source",
            style: {
                cssClasses: []
            },
            width: 11,
            height: 11,
            canvas: this.canvas,
            parent: connection
        }));

        connection.setDestDecorator(new PMUI.draw.ConnectionDecorator({
            decoratorPrefix: destDecorator[conn.flo_type],
            decoratorType: "target",
            style: {
                cssClasses: []
            },
            width: 11,
            height: 11,
            canvas: this.canvas,
            parent: connection
        }));
        connection.setSegmentMoveHandlers();
        //add the connection to the canvas, that means insert its html to
        // the DOM and adding it to the connections array
        this.addConnection(connection);
        // Filling mafeFlow fields
        connection.setTargetShape(targetPort.parent);
        connection.setOriginShape(sourcePort.parent);
        // now that the connection was drawn try to create the intersections
        connection.checkAndCreateIntersectionsWithAll();
        //attaching port listeners
        sourcePort.attachListeners(sourcePort);
        targetPort.attachListeners(targetPort);
        this.updatedElement = connection;
        if (trigger) {
            this.triggerCreateEvent(connection, []);
        }
    } else {
        throw new Error("No elements found to connect.".translate());
    }
};

PMCanvas.prototype.getElementByUid = function (uid) {
    var element;
    element = this.items.find('id', uid);
    if (!element) {
        element = this.getCustomShapes().find('id', uid);
    }
    return element.relatedObject;
};

PMCanvas.prototype.createBPMNDiagram = function () {
    var bpmnDia = PMDesigner.moddle.create('bpmndi:BPMNDiagram', {id: 'dia_' + PMUI.generateUniqueId()});
    var bpmnPlane = PMDesigner.moddle.create('bpmndi:BPMNPlane', {
        'bpmnElement': this.businessObject.elem,
        id: 'plane_' + PMUI.generateUniqueId()
    });
    bpmnDia.plane = bpmnPlane;
    this.businessObject.diagram = bpmnDia;
    PMDesigner.businessObject.get('diagrams').push(bpmnDia);
    this.businessObject.di = bpmnPlane;
};

PMCanvas.prototype.createBusinesObject = function (createProcess) {
    this.businessObject.elem = {};
    var bpmnProcess = PMDesigner.moddle.create('bpmn:Process', {id: 'pmui-' + PMUI.generateUniqueId()});
    PMDesigner.businessObject.get('rootElements').push(bpmnProcess);
    this.businessObject.elem = bpmnProcess;
    if (this.businessObject.di
        && (typeof this.businessObject.di.bpmnElement === 'undefined'
        || !this.businessObject.di.bpmnElement)) {
        this.businessObject.di.bpmnElement = this.businessObject.elem;
    }
};

PMCanvas.prototype.updateCanvasProcess = function () {
    var process,
        children;

    if (this.businessObject.elem && (_.findWhere(PMDesigner.businessObject.get('rootElements'), {
            $type: "bpmn:Process",
            id: this.businessObject.elem.id
        }))) {
        process = _.findWhere(PMDesigner.businessObject.get('rootElements'), {
            $type: "bpmn:Process",
            id: this.businessObject.elem.id
        });
        if (process.flowElements.length === 1) {
            children = PMDesigner.businessObject.get('rootElements');
            CollectionRemove(children, process);
            this.businessObject.elem = null;
        }
        if (this.businessObject.di && this.businessObject.di.planeElement.length <= 1) {
            this.removeBPMNDiagram();
        }
    }
};
PMCanvas.prototype.removeBPMNDiagram = function () {
    var dia,
        children;

    dia = _.findWhere(PMDesigner.businessObject.get('diagrams'), {
        $type: "bpmndi:BPMNDiagram",
        id: this.businessObject.diagram.id
    });
    children = PMDesigner.businessObject.get('diagrams');

    CollectionRemove(children, dia);
    this.businessObject.di = null;
};
PMCanvas.prototype.toogleGridLine = function () {
    if (this.isGridLine === true) {
        this.disableGridLine();
    } else {
        this.enableGridLine();
    }
    //force to update the designer
    PMDesigner.project.updateElement([]);
    return this.isGridLine;
};
/**
 * Disable grid lines, removing the class pmui-pmcanvas
 * @returns {PMCanvas}
 */
PMCanvas.prototype.disableGridLine = function () {
    this.html.classList.remove("pmui-pmcanvas");
    this.isGridLine = false;

    return this;
};

/**
 * Enable grid lines, adding the class pmui-pmcanvas
 * @returns {PMCanvas}
 */
PMCanvas.prototype.enableGridLine = function () {
    this.html.classList.add("pmui-pmcanvas");
    this.isGridLine = true;
    return this;
};


/**
 * Return GridLine boolean property
 * @returns {PMCanvas}
 */
PMCanvas.prototype.getGridLine = function () {
    return this.isGridLine;
};

/**
 * Override method "fixSnapData" of PUI.draw.Canvas
 */
PMCanvas.prototype.fixSnapData = function () {
    //TODO complete fixSnapData function
};
/**
 * Sets and calculate the limits to work with the lasso tool.
 * calculate this.lassoLimits to validate if the selected shapes can be dragged surpasses the limits of the canvas.
 * @returns {PMCanvas}
 */
PMCanvas.prototype.setLassoLimits = function () {
    var minXObj = {
            "limit": 99999,
            "shape": null
        },
        minYObj = {
            "limit": 99999,
            "shape": null
        },
        shape,
        i;
    for (i = 0; i < this.currentSelection.getSize(); i += 1) {
        shape = this.currentSelection.get(i);
        if (shape.getX() < minXObj.limit) {
            minXObj = {
                "limit": shape.getX(),
                "shape": shape
            }
        }
        if (shape.getY() < minYObj.limit) {
            minYObj = {
                "limit": shape.getY(),
                "shape": shape
            }
        }
    }
    this.lassoLimits = {
        "x": minXObj,
        "y": minYObj
    };
    return this;
};

/**
 * @class PMEvent
 * @param {Object} options
 */
var PMEvent = function (options) {
    PMShape.call(this, options);
    /**
     * Defines the alphanumeric unique code
     * @type {String}
     */
    this.evn_uid = null;
    /**
     * Defines the name
     * @type {String}
     */
    this.evn_name = null;
    /**
     * Defines the event type
     * @type {String}
     */
    this.evn_type = null;
    /**
     * Defines the event marker supported
     * @type {String}
     */
    this.evn_marker = null;
    /**
     * Defines id the event interrups or not the execution
     * @type {Boolean}
     */
    this.evn_is_interrupting = true;
    /**
     * Defines the activity attachec when the event is a boundary element
     * @type {String}
     */
    this.evn_attached_to = null;
    /**
     * Defines if the event can cancel the activity attached to
     * @type {Boolean}
     */
    this.evn_cancel_activity = false;
    /**
     * Define the activity related when event is playing as transactional event
     * @type {String}
     */
    this.evn_activity_ref = null;
    /**
     * Defines if the event needs to wait for completation status
     * @type {Boolean}
     */
    this.evn_wait_for_completion = false;
    /**
     * Defines the error name when event is playing like an error event
     * @type {String}
     */
    this.evn_error_name = null;
    /**
     * Defines the error code when event is playing like an error event
     * @type {String}
     */
    this.evn_error_code = null;
    /**
     * Defines the escalation name when event is playing like
     * an escalation event
     * @type {String}
     */
    this.evn_escalation_name = null;
    /**
     * Defines the escalation name when event is playing like
     * an escalation event
     * @type {String}
     */
    this.evn_escalation_code = null;
    /**
     * Defines the condition on the event
     * @type {String}
     */
    this.evn_condition = null;
    /**
     * Defines the message association
     * @type {String}
     */
    this.evn_message = null;
    /**
     * Defines the operation tom be executed when event is used like
     * a transactional event
     * @type {String}
     */
    this.evn_operation_name = null;
    /**
     * XXXX
     * @type {String}
     */
    this.evn_operation_implementation_ref = null;
    /**
     * Defines the date to be executed a timer event
     * @type {String}
     */
    this.evn_time_date = null;
    /**
     * Defines the time cycle to be executed a timer event
     * @type {String}
     */
    this.evn_time_cycle = null;
    /**
     * Defines the duration of the timer event
     * @type {String}
     */
    this.evn_time_duration = null;
    /**
     * Define the behavior of the event. Valid values are: CATCH, THROW
     * @type {String}
     */
    this.evn_behavior = null;

    /**
     * Defines the order of the boundary event when is attached to an activity
     * @type {Number}
     */
    this.numberRelativeToActivity = 0;
    this.businessObject = {};

    PMEvent.prototype.init.call(this, options);
};

PMEvent.prototype = new PMShape();
/**
 * Defines the object type
 * @type {String}
 */
PMEvent.prototype.type = 'PMEvent';
/**
 * Initialize the object with default values
 * @param {Object} options
 */
PMEvent.prototype.mapBpmnType = {
    'START': {
        'MESSAGECATCH': 'bpmn:MessageEventDefinition',
        'TIMER': 'bpmn:TimerEventDefinition',
        'CONDITIONAL': 'bpmn:ConditionalEventDefinition',
        'SIGNALCATCH': 'bpmn:SignalEventDefinition'
    },
    'INTERMEDIATE': {
        'MESSAGETHROW': 'bpmn:MessageEventDefinition',
        'EMAIL': 'bpmn:MessageEventDefinition',
        'MESSAGECATCH': 'bpmn:MessageEventDefinition',
        'TIMER': 'bpmn:TimerEventDefinition',
        'CONDITIONAL': 'bpmn:ConditionalEventDefinition',
        'LINKCATCH': 'bpmn:LinkEventDefinition',
        'SIGNALCATCH': 'bpmn:SignalEventDefinition',
        'LINKTHROW': 'bpmn:LinkEventDefinition',
        'COMPENSATIONTHROW': 'bpmn:CompensateEventDefinition',
        'SIGNALTHROW': 'bpmn:SignalEventDefinition'

    },
    'BOUNDARY': {
        'MESSAGETHROW': 'bpmn:MessageEventDefinition',
        'MESSAGECATCH': 'bpmn:MessageEventDefinition',
        'TIMER': 'bpmn:TimerEventDefinition',
        'CONDITIONAL': 'bpmn:ConditionalEventDefinition',
        'LINKCATCH': 'bpmn:LinkEventDefinition',
        'SIGNALCATCH': 'bpmn:SignalEventDefinition',
        'LINKTHROW': 'bpmn:LinkEventDefinition',
        'COMPENSATIONTHROW': 'bpmn:CompensateEventDefinition',
        'SIGNALTHROW': 'bpmn:SignalEventDefinition',
        'ERRORCATCH': 'bpmn:ErrorEventDefinition'
    },
    'END': {
        'MESSAGETHROW': 'bpmn:MessageEventDefinition',
        'EMAIL': 'bpmn:MessageEventDefinition',
        'SIGNALTHROW': 'bpmn:SignalEventDefinition',
        'ERRORTHROW': 'bpmn:ErrorEventDefinition',
        'CANCELHROW': 'bpmn:EscalationEventDefinition',
        'COMPENSATIONTHROW': 'bpmn:CompensateEventDefinition',
        'TERMINATETHROW': 'bpmn:TerminateEventDefinition',
        'CANCELTHROW': 'bpmn:CancelEventDefinition'
    }
};
PMEvent.prototype.supportedList = {
    'START': {
        'EMPTY': true,
        'MESSAGECATCH': true,
        'TIMER': true,
        'CONDITIONAL': true,
        'SIGNALCATCH': true
    },
    'INTERMEDIATE': {
        'MESSAGETHROW': true,
        'MESSAGECATCH': true,
        'TIMER': true,
        'CONDITIONAL': true,
        'SIGNALCATCH': true,
        'SIGNALTHROW': true,
        'EMAIL': true
    },
    'BOUNDARY': {
        'MESSAGETHROW': false,
        'MESSAGECATCH': false,
        'TIMER': false,
        'CONDITIONAL': false,
        'LINKCATCH': false,
        'SIGNALCATCH': false,
        'LINKTHROW': false,
        'COMPENSATIONTHROW': false,
        'SIGNALTHROW': false,
        'ERRORCATCH': false
    },
    'END': {
        'EMPTY': true,
        'MESSAGETHROW': true,
        'SIGNALTHROW': true,
        'ERRORTHROW': true,
        'CANCELHROW': false,
        'COMPENSATIONTHROW': false,
        'TERMINATETHROW': true,
        'CANCELTHROW': false,
        'EMAIL': true
    }
};

PMEvent.prototype.init = function (options) {
    var defaults = {
        evn_uid: '',
        evn_is_interrupting: true,
        evn_message: '',
        evn_name: '',
        evn_marker: 'EMPTY',
        evn_type: 'START',
        evn_behavior: 'CATCH'
    };
    jQuery.extend(true, defaults, options);
    this.setEventUid(defaults.evn_uid)
        .setEventType(defaults.evn_type)
        .setEventMarker(defaults.evn_marker)
        .setEventMessage(defaults.evn_message)
        .setBehavior(defaults.evn_behavior)
        .setCondition(defaults.evn_condition)
        .setAttachedTo(defaults.evn_attached_to)
        .setIsInterrupting(defaults.evn_is_interrupting);
    if (defaults.evn_name) {
        this.setName(defaults.evn_name);
    }
    this.setOnBeforeContextMenu(this.beforeContextMenu);
};

/**
 * Sets the label element
 * @param {String} value
 * @return {*}
 */
PMEvent.prototype.setName = function (name) {
    if (typeof name !== 'undefined') {
        this.evn_name = name;
        if (this.label) {
            this.label.setMessage(name);
        }
    }
    return this;
};

PMEvent.prototype.getDataObject = function () {
    var container,
        element_id,
        name = this.getName();
    switch (this.parent.type) {
        case 'PMCanvas':
            container = 'bpmnDiagram';
            element_id = this.canvas.id;
            break;
        case 'PMPool':
            container = 'bpmnPool';
            element_id = this.parent.id;
            break;
        case 'PMLane':
            container = 'bpmnLane';
            element_id = this.parent.id;
            break;
        case 'PMActivity':
            container = 'bpmnActivity';
            element_id = this.parent.id;
            break;
        default:
            container = 'bpmnDiagram';
            element_id = this.canvas.id;
            break;
    }

    return {
        evn_uid: this.id,
        evn_name: name,
        evn_type: this.evn_type,
        evn_marker: this.evn_marker,
        evn_is_interrupting: this.evn_is_interrupting,
        evn_attached_to: this.evn_attached_to,
        evn_cancel_activity: this.evn_cancel_activity,
        evn_activity_ref: this.evn_activity_ref,
        evn_wait_for_completion: this.evn_wait_for_completion,
        evn_error_name: this.evn_error_name,
        evn_error_code: this.evn_error_code,
        evn_escalation_name: this.evn_escalation_name,
        evn_escalation_code: this.evn_escalation_code,
        evn_condition: this.evn_condition,
        evn_message: this.evn_message,
        evn_operation_name: this.evn_operation_name,
        evn_operation_implementation_ref: this.evn_operation_implementation_ref,
        evn_time_date: this.evn_time_date,
        evn_time_cycle: this.evn_time_cycle,
        evn_time_duration: this.evn_time_duration,
        evn_behavior: this.evn_behavior,
        bou_x: this.x,
        bou_y: this.y,
        bou_width: this.width,
        bou_height: this.height,
        bou_container: container,
        bou_element: element_id,
        _extended: this.getExtendedObject()
    };
};

/**
 * Sets the event uid property
 * @param {String} id
 * @return {*}
 */
PMEvent.prototype.setEventUid = function (id) {
    this.evn_uid = id;
    return this;
};

/**
 * Sets the event type property
 * @param {String} type
 * @return {*}
 */
PMEvent.prototype.setEventType = function (type) {
    type = type.toLowerCase();
    var defaultTypes = {
        start: 'START',
        end: 'END',
        intermediate: 'INTERMEDIATE',
        boundary: 'BOUNDARY'
    };
    if (defaultTypes[type]) {
        this.evn_type = defaultTypes[type];
    }
    return this;
};
/**
 * Sets the event marker property
 * @param {String} marker
 * @return {*}
 */
PMEvent.prototype.setEventMarker = function (marker) {
    this.evn_marker = marker;
    return this;
};
/**
 * Sets if the event interrups the execution or not
 * @param {Boolean} value
 * @return {*}
 */
PMEvent.prototype.setIsInterrupting = function (value) {
    if (typeof value === "boolean") {
        this.evn_is_interrupting = value;
    }
    return this;
};
/**
 * Sets the event behavior property
 * @param {String} behavior
 * @return {*}
 */
PMEvent.prototype.setBehavior = function (behavior) {
    behavior = behavior.toLowerCase();
    var defaultBehaviors = {
        "catch": 'CATCH',
        "throw": 'THROW'
    };
    if (defaultBehaviors[behavior]) {
        this.evn_behavior = defaultBehaviors[behavior];
    }
    return this;
};
/**
 * Sets the activity id where the event is attached to
 * @param {String} value
 * @param {Boolean} [cancel]
 * @return {*}
 */
PMEvent.prototype.setAttachedTo = function (value, cancel) {
    if (typeof cancel !== 'undefined') {
        if (typeof cancel === "boolean") {
            this.evn_cancel_activity = cancel;
        }
    } else {
        this.evn_cancel_activity = this.evn_cancel_activity || false;
    }
    this.evn_attached_to = value;
    return this;
};
/**
 * Destroy a event
 * @returns {PMEvent}
 */
PMEvent.prototype.destroy = function () {
    if (this.getType() === 'PMEvent' && this.getEventType() === 'BOUNDARY') {
        if (this.parent.boundaryPlaces && this.numberRelativeToActivity !== null) {
            this.parent.boundaryPlaces
                .get(this.numberRelativeToActivity)
                .available = true;
            this.parent.boundaryArray.remove(this);
        }
    }
    return this;
};
/**
 * Sets the event message
 * @param {String} msg
 * @return {*}
 */
PMEvent.prototype.setEventMessage = function (msg) {
    this.evn_message = msg;
    return this;
};
/**
 * Sets the event condition property
 * @param {String} value
 * @return {*}
 */
PMEvent.prototype.setCondition = function (value) {
    this.evn_condition = value;
    return this;
};
/**
 * Set the compensation properties
 * @param {String} activity
 * @param {Boolean} wait
 * @return {*}
 */
PMEvent.prototype.setCompensationActivity = function (activity, wait) {
    if (typeof wait !== 'undefined') {
        if (typeof wait === "boolean") {
            this.evn_wait_for_completion = wait;
        }
    } else {
        this.evn_wait_for_completion = this.evn_wait_for_completion || false;
    }
    this.evn_activity_ref = activity;
    return this;
};
/**
 * Sets the error properties
 * @param {String} name  Error Name
 * @param {String} code  Error Code
 * @return {*}
 */
PMEvent.prototype.setEventError = function (name, code) {
    this.evn_error_name = name;
    this.evn_error_code = code;
    return this;
};

/**
 * Sets the escalation properties
 * @param {String} name Escalation Name
 * @param {String} code Escalation Code
 * @return {*}
 */
PMEvent.prototype.setEventEscalation = function (name, code) {
    this.evn_escalation_name = name;
    this.evn_escalation_code = code;
    return this;
};
/**
 * Sets the event operation properties
 * @param {String} name
 * @param {String} implementation
 * @return {*}
 */
PMEvent.prototype.setEventOperation = function (name, implementation) {
    this.evn_operation_name = name;
    this.evn_operation_implementation_ref = implementation;
    return this;
};
/**
 * Sets the event timer properties
 * @param {String} date
 * @param {String} cycle
 * @param {String} duration
 * @return {*}
 */
PMEvent.prototype.setEventTimer = function (date, cycle, duration) {
    this.evn_time_date = date;
    this.evn_time_cycle = cycle;
    this.evn_time_duration = duration;
    return this;
};
/**
 * Sets te default_flow property
 * @param value
 * @return {*}
 */
PMEvent.prototype.setDefaultFlow = function (value) {
    PMShape.prototype.setDefaultFlow.call(this, value);
    this.evn_default_flow = value;
    return this;
};
/**
 * Attach the event to an activity
 * @return {*}
 */
PMEvent.prototype.attachToActivity = function () {
    var numBou = this.parent.getAvailableBoundaryPlace();
    if (numBou !== false) {
        this.parent.setBoundary(this, numBou);
        this.setAttachedTo(this.parent.getID());
        this.setNumber(numBou);
    } else {
        this.destroy();
        this.saveAndDestroy();
    }
    return this;
};

/**
 * Sets the number/order of the current event when is attached to an activity
 * @param {Number} num
 * @return {*}
 */
PMEvent.prototype.setNumber = function (num) {
    this.numberRelativeToActivity = num;
    return this;
};

PMEvent.prototype.getEventType = function () {
    return this.evn_type;
};

PMEvent.prototype.getEventMarker = function () {
    return this.evn_marker;
};

PMEvent.prototype.getEventMessage = function () {
    return this.evn_message;
};
/**
 * Validates if an even has an message connection
 * @returns {boolean}
 */
PMEvent.prototype.isAllowed = function () {
    var result = true,
        i,
        connection;
    for (i = 0; i < this.getPorts().getSize(); i += 1) {
        connection = this.getPorts().get(i).connection;
        if (connection.flo_type === 'MESSAGE') {
            result = false;
            break;
        }
    }
    return result;
};
/**
 * Change an event marker
 * @return {Object}
 */
PMEvent.prototype.changeMarkerTo = function (type, message) {
    var command,
        msg;
    if (this.isAllowed()) {
        command = new CommandChangeEventMarker(this, type);
        this.canvas.commandStack.add(command);
        command.execute();
    } else {
        msg = 'Invalid operation: Delete message flow before converting it to '.translate();
        PMDesigner.msgFlash(msg + message + ' Event'.translate(), document.body, 'error', 3000, 5);
    }
    return this;
};

PMEvent.prototype.manualCreateMenu = function (e) {
    var endMarker = null;
    switch (this.getEventType()) {
        case 'END':
            endMarker = {
                text: "End Event Type".translate(),
                icon: "mafe-menu-properties-action",
                id: "result",
                items: [
                    {
                        id: "endempty",
                        text: "Empty".translate(),
                        onClick: function (menuOption) {
                            var targetElement = menuOption
                                .getMenuTargetElement();
                            targetElement.changeMarkerTo('EMPTY');
                            PMDesigner.project.updateElement([]);
                        },
                        disabled: true
                    },
                    {
                        id: "endemail",
                        text: "Email Message".translate(),
                        onClick: function (menuOption) {
                            var targetElement = menuOption
                                .getMenuTargetElement();
                            targetElement.changeMarkerTo('EMAIL');
                            PMDesigner.project.updateElement([]);
                        }
                    },
                    {
                        id: "endmessagethrow",
                        text: "Message".translate(),
                        onClick: function (menuOption) {
                            var targetElement = menuOption
                                .getMenuTargetElement();
                            targetElement.changeMarkerTo('MESSAGETHROW');
                            PMDesigner.project.updateElement([]);
                        }
                    },
                    {
                        id: "enderrorthrow",
                        text: "Error".translate(),
                        onClick: function (menuOption) {
                            var targetElement = menuOption
                                .getMenuTargetElement();
                            targetElement.changeMarkerTo('ERRORTHROW');
                            PMDesigner.project.updateElement([]);
                        }
                    },
                    {
                        id: "endsignalthrow",
                        text: "Signal".translate(),
                        onClick: function (menuOption) {
                            var targetElement = menuOption
                                .getMenuTargetElement();
                            targetElement.changeMarkerTo('SIGNALTHROW');
                            PMDesigner.project.updateElement([]);
                        }
                    },
                    {
                        id: "endterminatethrow",
                        text: "Terminate".translate(),
                        onClick: function (menuOption) {
                            var targetElement = menuOption
                                .getMenuTargetElement();
                            targetElement.changeMarkerTo('TERMINATETHROW');
                            PMDesigner.project.updateElement([]);
                        }
                    }
                ]
            };
            break;
        case 'INTERMEDIATE':
            endMarker = intermediateThrowMarker = {
                text: "Intermediate Event Type".translate(),
                icon: "mafe-menu-properties-action",
                id: "result",
                items: [
                    {
                        id: "intermediateemail",
                        text: "Email Message".translate(),
                        onClick: function (menuOption) {
                            var targetElement = menuOption
                                .getMenuTargetElement();
                            targetElement.changeMarkerTo('EMAIL');
                            PMDesigner.project.updateElement([]);
                        },
                        disabled: true
                    },
                    {
                        id: "intermediatemessagethrow",
                        text: "Send Message".translate(),
                        onClick: function (menuOption) {
                            var targetElement = menuOption
                                .getMenuTargetElement();
                            targetElement.changeMarkerTo('MESSAGETHROW');
                            PMDesigner.project.updateElement([]);
                        }
                    },
                    {
                        id: "intermediatesignalthrow",
                        text: "Signal".translate(),
                        onClick: function (menuOption) {
                            var targetElement = menuOption
                                .getMenuTargetElement();
                            targetElement.changeMarkerTo('SIGNALTHROW');
                            PMDesigner.project.updateElement([]);
                        }
                    }

                ]
            };
            break;
    }
    if (endMarker) {
        var tempMenu = new PMUI.menu.Menu(endMarker);
        tempMenu.setTargetElement(this);
        tempMenu.show(e.pageX, e.pageY + this.getZoomHeight() / 2 + 4);
    }
};
PMEvent.prototype.beforeContextMenu = function () {
    var items, i,
        menuItem,
        hasMarker = false;
    this.canvas.hideAllCoronas();
    if (this.canvas.readOnly) {
      return;
    }
    switch (this.getEventType()) {
        case 'END':
            items = this.menu.items.find('id', 'result').childMenu.items;
            break;
        case 'INTERMEDIATE':
        case 'START':
            if (this.evn_behavior === 'CATCH') {
                items = this.menu.items.find('id', 'trigger').childMenu.items;
            } else {
                items = this.menu.items.find('id', 'result').childMenu.items;
            }
            break;
        default:
            items = new PMUI.util.ArrayList();
            break;
    }
    for (i = 0; i < items.getSize(); i += 1) {
        menuItem = items.get(i);
        if (menuItem.id === this.getEventType().toLowerCase() +
            this.getEventMarker().toLowerCase()) {
            menuItem.disable();
            hasMarker = true;
        } else {
            menuItem.enable();
        }
    }
};
/**
 * Stringifies the PMEvent object
 * @return {Object}
 */
PMEvent.prototype.stringify = function () {
    var inheritedJSON = PMShape.prototype.stringify.call(this),
        thisJSON = {
            evn_marker: this.getEventMarker(),
            evn_message: this.getEventMessage(),
            evn_condition: this.evn_condition,
            evn_attached_to: this.evn_attached_to,
            evn_is_interrupting: this.evn_is_interrupting,
            evn_behavior: this.evn_behavior
        };
    jQuery.extend(true, inheritedJSON, thisJSON);
    return inheritedJSON;
};

PMEvent.prototype.createBpmn = function (type) {
    if (!this.businessObject.elem && !(this instanceof PMUI.draw.MultipleSelectionContainer)) {
        this.createWithBpmn(type, 'businessObject');
    }
    this.updateBounds(this.businessObject.di);
    if (this.parent.getType() === 'PMCanvas' && !this.parent.businessObject.di) {
        this.canvas.createBPMNDiagram();
    }
    if (this.parent.businessObject.elem) {
        if (this.getEventType() === 'BOUNDARY') {
            if (this.parent.parent.getType() === 'PMLane') {
                this.updateShapeParent(this.businessObject, this.parent.parent.businessObject);
            } else if (this.parent.parent.getType() === 'PMPool') {
                this.updateShapeParent(this.businessObject, this.parent.parent.businessObject);
            }
        } else {
            this.updateShapeParent(this.businessObject, this.parent.businessObject);
        }
    } else {
        this.parent.createBusinesObject();
        this.updateShapeParent(this.businessObject, this.parent.businessObject);
    }
};

PMEvent.prototype.updateBpmn = function () {
    this.updateBounds(this.businessObject.di);
    if (!this.parent.businessObject.elem) {
        this.parent.createBusinesObject();
    }
    if (this.getEventType() === 'BOUNDARY') {
        this.updateShapeParent(this.businessObject, this.parent.parent.businessObject);
    } else {
        this.updateShapeParent(this.businessObject, this.parent.businessObject);
    }

};
/**
 * create bpmn object and attach to businessObject event
 */
PMEvent.prototype.createWithBpmn = function (bpmnElementType) {
    PMShape.prototype.createWithBpmn.call(this, bpmnElementType, 'businessObject');
    this.businessObject.elem.eventDefinitions = [];
    if (this.getEventType() === 'BOUNDARY') {
        this.businessObject.elem.attachedToRef = this.parent.businessObject.elem;
    }
    this.createEventDefinition();
};

PMEvent.prototype.createEventDefinition = function () {
    var def, type;
    if (this.getEventMarker() !== 'EMPTY'
        && this.getEventMarker() !== 'MULTIPLECATCH'
        && this.getEventMarker() !== 'PARALLELCATCH') {
        type = this.mapBpmnType[this.getEventType()][this.getEventMarker()];
        def = PMDesigner.bpmnFactory.create(type, {id: 'def_' + PMUI.generateUniqueId()});
        this.businessObject.elem.eventDefinitions.push(def);
    }
};

PMEvent.prototype.updateBpmEventMarker = function (newBpmnType) {
    this.businessObject.elem.eventDefinitions = [];
    this.createEventDefinition();
};

PMEvent.prototype.isSupported = function () {
    var isSupported = false;
    if (this.supportedList[this.evn_type][this.getEventMarker()] == true) {
        isSupported = true;
    }
    return isSupported;
};
/**
 * PMEvent Properties
 */
PMEvent.prototype.eventProperties = function () {
    var typeEventMarker = this.getEventType() + "_" + this.getEventMarker(),
        windowMessage,
        that = this;
    switch (typeEventMarker) {
        case "START_TIMER":
        case "INTERMEDIATE_TIMER":
            PMDesigner.timerEventProperties(that);
            break;
        case "INTERMEDIATE_EMAIL":
        case "END_EMAIL":
            PMDesigner.emailEventProperties(that);
            break;
        case "START_MESSAGECATCH":
        case "INTERMEDIATE_MESSAGETHROW":
        case "INTERMEDIATE_MESSAGECATCH":
        case "END_MESSAGETHROW":
            windowMessage = new MessageEventDefinition(that);
            break;
    }
};
/**
 * @class PMActivity
 * @param {Object} options
 */
var PMActivity = function (options) {
    PMShape.call(this, options);

    /**
     * Activity Alphanumeric unique identifier
     * @type {String}
     */
    this.act_uid = null;
    /**
     * Activity name
     * @type {String}
     */
    this.act_name = null;
    /**
     * Activity Type
     * @type {String}
     */
    this.act_type = null;
    /**
     * Define if the task is for compensation (BPMN)
     * @type {Boolean}
     */
    this.act_is_for_compensation = null;
    /**
     * Define the quantity needed to start the activity
     * @type {Number}
     */
    this.act_start_quantity = null;
    /**
     * Define the quantity needed to complete the activity
     * @type {Number}
     */
    this.act_completion_quantity = null;
    /**
     * Define the task type.
     * @type {String}
     */
    this.act_task_type = null;
    /**
     * Define the task loop type.
     * @type {String}
     */
    this.act_loop_type = null;
    /**
     * Define the activity implementation
     * @type {String}
     */
    this.act_implementation = null;
    /**
     * Define the instatiation status
     * @type {Boolean}
     */
    this.act_instantiate = null;
    /**
     * Define the script type supported
     * @type {String}
     */
    this.act_script_type = null;
    /**
     * Define the script
     * @type {String}
     */
    this.act_script = null;
    /**
     * Defines the loop type accepted
     * @type {String}
     */
    this.act_loop_type = null;
    /**
     * Define if the test to complete the loop would be executed before o later
     * @type {Boolean}
     */
    this.act_test_before = null;
    /**
     * Defines the maximum value of loops allowed
     * @type {Number}
     */
    this.act_loop_maximum = null;
    /**
     * Defines the loop condition
     * @type {String}
     */
    this.act_loop_condition = null;
    /**
     * Defines the loop cardinality
     * @type {String}
     */
    this.act_loop_cardinality = null;
    /**
     * Defines the loop behavior
     * @type {String}
     */
    this.act_loop_behavior = null;
    /**
     * Define if the activity has an adhoc behavior
     * @type {Boolean}
     */
    this.act_is_adhoc = null;
    /**
     * Defines if the activity is collapsed
     * @type {Boolean}
     */
    this.act_is_collapsed = null;
    /**
     * Defines the condition needed to complete the activity
     * @type {String}
     */
    this.act_completion_condition = null;
    /**
     * Define the order to be executed when exists several task in parallel mode
     * @type {Number}
     */
    this.act_ordering = null;
    /**
     * Defines if into a loop all instances would be cancelled
     * @type {Boolean}
     */
    this.act_cancel_remaining_instances = null;
    /**
     * Defines the protocol used for the transaction activities
     * @type {String}
     */
    this.act_protocol = null;
    /**
     * Define the method to be used when activity consume/execute a web service
     * @type {String}
     */
    this.act_method = null;
    /**
     * Define the scope of the activity
     * @type {Boolean}
     */
    this.act_is_global = null;
    /**
     * Define the referer to another object (Process, Participant or Another Activity)
     * @type {String}
     */
    this.act_referer = null;
    /**
     * Defines the default flow when activity is related to two or more flows
     * @type {String}
     */
    this.act_default_flow = null;
    /**
     * Defines the diagram related when activity plays as subprocess
     * @type {String}
     */
    this.act_master_diagram = null;
    /**
     * Array of Boundary places created to receive boundary events
     * @type {Array}
     */
    this.boundaryPlaces = new PMUI.util.ArrayList();
    /**
     * Array of Boundary events attached to this activity
     * @type {Array}
     */
    this.boundaryArray = new PMUI.util.ArrayList();
    this.isValidDropArea = true;

    PMActivity.prototype.init.call(this, options);
};

/**
 * Point the prototype to the PMShape Object
 * @type {PMShape}
 */
PMActivity.prototype = new PMShape();
/**
 * Defines the object type
 * @type {String}
 */
PMActivity.prototype.type = 'PMActivity';
/**
 * Points to container behavior object
 * @type {Object}
 */
PMActivity.prototype.activityContainerBehavior = null;
/**
 * Points to the resize behavior object
 * @type {Object}
 */
PMActivity.prototype.activityResizeBehavior = null;

PMActivity.prototype.mapBpmnType = {
    'EMPTY': 'bpmn:Task',
    'SENDTASK': 'bpmn:SendTask',
    'RECEIVETASK': 'bpmn:ReceiveTask',
    'USERTASK': 'bpmn:UserTask',
    'SERVICETASK': 'bpmn:ServiceTask',
    'SCRIPTTASK': 'bpmn:ScriptTask',
    'MANUALTASK': 'bpmn:ManualTask',
    'BUSINESSRULE': 'bpmn:BusinessRuleTask'
};
PMActivity.prototype.supportedArray = [
    'EMPTY',
    'COLLAPSED',
    'SENDTASK',
    'RECEIVETASK',
    'USERTASK',
    'SERVICETASK',
    'SCRIPTTASK',
    'MANUALTASK',
    'BUSINESSRULE'
];

PMActivity.prototype.supportedLoopArray = [
    'EMPTY',
    'NONE',
    'LOOP',
    'PARALLEL',
    'SEQUENTIAL'
];

PMActivity.prototype.mapLoopTypes = {
    'LOOP': 'bpmn:StandardLoopCharacteristics',
    'PARALLEL': 'bpmn:MultiInstanceLoopCharacteristics',
    'SEQUENTIAL': 'bpmn:MultiInstanceLoopCharacteristics'
};
/**
 * Initialize object with default values
 * @param options
 */
PMActivity.prototype.init = function (options) {
    var defaults = {
        act_type: 'TASK',
        act_name: 'Task',
        act_loop_type: 'NONE',
        act_is_for_compensation: false,
        act_task_type: 'EMPTY',
        act_is_collapsed: false,
        act_is_global: false,
        act_loop_cardinality: 0,
        act_loop_maximum: 0,
        act_start_quantity: 1,
        act_is_adhoc: false,
        act_cancel_remaining_instances: true,
        act_instantiate: false,
        act_completion_quantity: 0,
        act_implementation: '',
        act_script: '',
        act_script_type: '',
        act_default_flow: 0,
        minHeight: 50,
        minWidth: 100,
        maxHeight: 500,
        maxWidth: 600
    };
    jQuery.extend(true, defaults, options);
    this.setActivityUid(defaults.act_uid)
        .setActName(defaults.act_name)
        .setActivityType(defaults.act_type)
        .setLoopType(defaults.act_loop_type)
        .setIsForCompensation(defaults.act_is_for_compensation)
        .setTaskType(defaults.act_task_type)
        .setIsCollapsed(defaults.act_is_collapsed)
        .setIsGlobal(defaults.act_is_global)
        .setLoopCardinality(defaults.act_loop_cardinality)
        .setLoopMaximun(defaults.act_loop_maximum)
        .setStartQuantity(defaults.act_start_quantity)
        .setIsAdhoc(defaults.act_is_adhoc)
        .setCancelRemainingInstances(defaults.act_cancel_remaining_instances)
        .setInstantiate(defaults.act_instantiate)
        .setImplementation(defaults.act_implementation)
        .setCompletionQuantity(defaults.act_completion_quantity)
        .setScript(defaults.act_script)
        .setScriptType(defaults.act_script_type)
        .setDefaultFlow(defaults.act_default_flow)
        .setMinHeight(defaults.minHeight)
        .setMinWidth(defaults.minWidth)
        .setMaxHeight(defaults.maxHeight)
        .setMaxWidth(defaults.maxWidth);
    this.setOnBeforeContextMenu(this.beforeContextMenu);
};
/**
 * Sets the label element
 * @param {String} value
 * @return {*}
 */
PMActivity.prototype.setName = function (name) {
    if (typeof name !== 'undefined') {
        this.act_name = name;
        if (this.label) {
            this.label.setMessage(name);
        }
    }
    return this;
};
/**
 * Returns the activity type property
 * @return {String}
 */
PMActivity.prototype.getActivityType = function () {
    return this.act_type;
};

/**
 * Returns the is for compensation property
 * @return {Boolean}
 */
PMActivity.prototype.getIsForCompensation = function () {
    return this.act_is_for_compensation;
};

/**
 * Returns if the activity cancel remaining instances when is cancelled
 * @return {Boolean}
 */
PMActivity.prototype.getCancelRemainingInstances = function () {
    return this.act_cancel_remaining_instances;
};

/**
 * Returns the quantity needed to complete an activity
 * @return {Number}
 */
PMActivity.prototype.getCompletionQuantity = function () {
    return this.act_completion_quantity;
};

/**
 * Set is the activity is global (scope)
 * @param {Boolean} value
 * @return {*}
 */
PMActivity.prototype.getIsGlobal = function () {
    return this.act_is_global;
};

/**
 * Returns the start quantity needed to start an activity
 * @return  {Number}
 */
PMActivity.prototype.getStartQuantity = function () {
    return this.act_start_quantity;
};

/**
 * Returns if the instance is active
 * @return {Boolean}
 */
PMActivity.prototype.getInstantiate = function () {
    return this.act_instantiate;
};

/**
 * Returns the implementation property
 * @return {String}
 */
PMActivity.prototype.getImplementation = function () {
    return this.act_implementation;
};

/**
 * Return the Script property
 * @param {Number} value
 * @return {*}
 */
PMActivity.prototype.getScript = function () {
    return this.act_script;
};

/**
 * Return the Script Type property
 * @param {Number} value
 * @return {*}
 */
PMActivity.prototype.getScriptType = function () {
    return this.act_script_type;
};

/**
 * Return the minimun height of an activity
 * @return {*}
 */
PMActivity.prototype.getMinHeight = function () {
    return this.minHeight;
};

/**
 * Return the minimun width of an activity
 * @return {*}
 */
PMActivity.prototype.getMinWidth = function () {
    return this.minWidth;
};
/**
 * Return the maximun height of an activity
 * @return {*}
 */
PMActivity.prototype.getMaxHeight = function () {
    return this.maxHeight;
};

/**
 * Return the maximun width of an activity
 * @return {*}
 */
PMActivity.prototype.getMaxWidth = function () {
    return this.maxWidth;
};
/**
 * Sets the act_uid property
 * @param {String} value
 * @return {*}
 */
PMActivity.prototype.setActivityUid = function (value) {
    this.act_uid = value;
    return this;
};

/**
 * Sets the activity type property
 * @param {String} type
 * @return {*}
 */
PMActivity.prototype.setActivityType = function (type) {
    this.act_type = type;
    return this;
};

/**
 * Sets the implementation property
 * @param {String} type
 * @return {*}
 */
PMActivity.prototype.setImplementation = function (type) {
    this.act_implementation = type;
    return this;
};

/**
 * Set the loop type property
 * @param {String} type
 * @return {*}
 */
PMActivity.prototype.setLoopType = function (type) {
    this.act_loop_type = type;
    return this;
};

/**
 * Sets the collapsed property
 * @param {Boolean} value
 * @return {*}
 */
PMActivity.prototype.setIsCollapsed = function (value) {
    if (typeof value === "boolean") {
        this.act_is_collapsed = value;
    }
    return this;
};

/**
 * Sets the is for compensation property
 * @param {Boolean} value
 * @return {*}
 */
PMActivity.prototype.setIsForCompensation = function (value) {
    if (typeof value === "boolean") {
        this.act_is_for_compensation = value;
    }
    return this;
};

/**
 * Sets the activity task type
 * @param {String} type
 * @return {*}
 */
PMActivity.prototype.setTaskType = function (type) {
    this.act_task_type = type;
    return this;
};

/**
 * Sets the activity task type
 * @param {String} type
 * @return {*}
 */
PMActivity.prototype.setLoopType = function (type) {
    this.act_loop_type = type;
    return this;
};

/**
 * Set is the activity is global (scope)
 * @param {Boolean} value
 * @return {*}
 */
PMActivity.prototype.setIsGlobal = function (value) {
    if (typeof value === "boolean") {
        this.act_is_global = value;
    }
    return this;
};

/**
 * Set the loop cardinality of the activity
 * @param {String} value
 * @return {*}
 */
PMActivity.prototype.setLoopCardinality = function (value) {
    this.act_loop_cardinality = value;
    return this;
};

/**
 * Sets the loop maximun value
 * @param {Number} value
 * @return {*}
 */
PMActivity.prototype.setLoopMaximun = function (value) {
    this.act_loop_maximum = value;
    return this;
};

/**
 * Sets the start quantity needed to start an activity
 * @param  {Number} value
 * @return {*}
 */
PMActivity.prototype.setStartQuantity = function (value) {
    this.act_start_quantity = value;
    return this;
};

/**
 * Sets if the activity has an adhoc behavior
 * @param {Boolean} value
 * @return {*}
 */
PMActivity.prototype.setIsAdhoc = function (value) {
    if (typeof value === "boolean") {
        this.act_is_adhoc = value;
    }
    return this;
};

/**
 * Sets if the activity cancel remaining instances when is cancelled
 * @param {Boolean} value
 * @return {*}
 */
PMActivity.prototype.setCancelRemainingInstances = function (value) {
    if (typeof value === "boolean") {
        this.act_cancel_remaining_instances = value;
    }
    return this;
};

/**
 * Sets if the instance is active
 * @param {Boolean} value
 * @return {*}
 */
PMActivity.prototype.setInstantiate = function (value) {
    if (typeof value === "boolean") {
        this.act_instantiate = value;
    }
    return this;
};

/**
 * Sets the quantity needed to complete an activity
 * @param {Number} value
 * @return {*}
 */
PMActivity.prototype.setCompletionQuantity = function (value) {
    this.act_completion_quantity = value;
    return this;
};

/**
 * Sets the Script property
 * @param {Number} value
 * @return {*}
 */
PMActivity.prototype.setScript = function (value) {
    this.act_script = value;
    return this;
};

/**
 * Sets the Script Type property
 * @param {Number} value
 * @return {*}
 */
PMActivity.prototype.setScriptType = function (value) {
    this.act_script_type = value;

    return this;
};

/**
 * Sets te default_flow property
 * @param value
 * @return {*}
 */
PMActivity.prototype.setDefaultFlow = function (value) {
    if (this.html) {
        PMShape.prototype.setDefaultFlow.call(this, value);
        this.canvas.triggerCommandAdam(this, ['act_default_flow'], [this.act_default_flow], [value]);
    }
    this.act_default_flow = value;
    return this;
};
/**
 * Sets the minimun height
 * @param {Number} value
 * @return {*}
 */
PMActivity.prototype.setMinHeight = function (value) {
    this.minHeight = value;
    return this;
};

/**
 * Sets the minimun with
 * @param {Number} value
 * @return {*}
 */
PMActivity.prototype.setMinWidth = function (value) {
    this.minWidth = value;

    return this;
};
/**
 * Sets the maximun height
 * @param {Number} value
 * @return {*}
 */
PMActivity.prototype.setMaxHeight = function (value) {
    this.maxHeight = value;
    return this;
};

/**
 * Sets the maximun with
 * @param {Number} value
 * @return {*}
 */
PMActivity.prototype.setMaxWidth = function (value) {
    this.maxWidth = value;

    return this;
};

PMActivity.prototype.setActType = function (type) {
    this.act_type = type;
    return this;
};

PMActivity.prototype.setActName = function (name) {
    this.act_name = name;
    return this;
};
/**
 * Factory of activity behaviors. It uses lazy instantiation to create
 * instances of the different container behaviors
 * @param {String} type An string that specifies the container behavior we want
 * an instance to have, it can be regular or nocontainer
 * @return {ContainerBehavior}
 */
PMActivity.prototype.containerBehaviorFactory = function (type) {
    if (type === 'activity') {
        if (!this.activityContainerBehavior) {
            this.activityContainerBehavior = new ActivityContainerBehavior();
        }
        return this.activityContainerBehavior;
    } else {
        return PMShape.prototype.containerBehaviorFactory.call(this, type);
    }
};

PMActivity.prototype.dropBehaviorFactory = function (type, selectors) {
    if (type === 'pmconnection') {
        if (!this.pmConnectionDropBehavior) {
            this.pmConnectionDropBehavior = new PMConnectionDropBehavior(selectors);
        }
        return this.pmConnectionDropBehavior;
    } else if (type === 'pmcontainer') {
        if (!this.pmContainerDropBehavior) {
            this.pmContainerDropBehavior = new PMContainerDropBehavior(selectors);
        }
        return this.pmContainerDropBehavior;
    } else if (type === 'pmactivitydrop') {
        if (!this.pmContainerDropBehavior) {
            this.pmContainerDropBehavior = new PMActivityDropBehavior(selectors);
        }
        return this.pmContainerDropBehavior;
    } else {
        return PMUI.draw.CustomShape.prototype.dropBehaviorFactory.call(this, type, selectors);
    }
};

PMActivity.prototype.getDataObject = function () {
    var name = this.getName(),
        container,
        element_id;
    switch (this.parent.type) {
        case 'PMCanvas':
            container = 'bpmnDiagram';
            element_id = this.canvas.id;
            break;
        case 'PMPool':
            container = 'bpmnPool';
            element_id = this.parent.id;
            break;
        case 'PMLane':
            container = 'bpmnLane';
            element_id = this.parent.id;
            break;
        default:
            container = 'bpmnDiagram';
            element_id = this.canvas.id;
            break;
    }
    return {
        act_uid: this.id,
        act_name: name,
        act_type: this.act_type,
        act_task_type: this.act_task_type,
        act_loop_type: this.act_loop_type,
        act_is_for_compensation: this.act_is_for_compensation,
        act_start_quantity: this.act_start_quantity,
        act_completion_quantity: this.act_completion_quantity,
        act_implementation: this.act_implementation,
        act_instantiate: this.act_instantiate,
        act_script_type: this.act_script_type,
        act_script: this.act_script,
        act_loop_type: this.act_loop_type,
        act_test_before: this.act_test_before,
        act_loop_maximum: this.act_loop_maximum,
        act_loop_condition: this.act_loop_condition,
        act_loop_cardinality: this.act_loop_cardinality,
        act_loop_behavior: this.act_loop_behavior,
        act_is_adhoc: this.act_is_adhoc,
        act_is_collapsed: this.act_is_collapsed,
        act_completion_condition: this.act_completion_condition,
        act_ordering: this.act_ordering,
        act_cancel_remaining_instances: this.act_cancel_remaining_instances,
        act_protocol: this.act_protocol,
        act_method: this.act_method,
        act_is_global: this.act_is_global,
        act_referer: this.act_referer,
        act_default_flow: this.act_default_flow,
        act_master_diagram: this.act_master_diagram,
        bou_x: this.x,
        bou_y: this.y,
        bou_width: this.width,
        bou_height: this.height,
        bou_container: container,
        bou_element: element_id,
        _extended: this.getExtendedObject()
    };
};
/**
 * Create/Initialize the boundary places array
 * @return {*}
 */
PMActivity.prototype.makeBoundaryPlaces = function () {
    var bouX,
        bouY,
        factor = 3,
        space,
        number = 0,
        shape = this.boundaryArray.getFirst(),
        numBottom = 0,
        numLeft = 0,
        numTop = 0,
        numRight = 0;

    //BOTTON
    bouY = shape.parent.getHeight() - shape.getHeight() / 2;
    bouX = shape.parent.getWidth() - (numBottom + 1) * (shape.getWidth() + factor);
    while (bouX + shape.getWidth() / 2 > 0) {
        space = {};
        space.x = bouX;
        space.y = bouY;
        space.available = true;
        space.number = number;
        space.location = 'BOTTOM';
        shape.parent.boundaryPlaces.insert(space);
        number += 1;
        numBottom += 1;
        bouX = shape.parent.getWidth() - (numBottom + 1) * (shape.getWidth() + factor);
    }
    //LEFT
    bouY = shape.parent.getHeight() - (numLeft + 1) * (shape.getHeight() + factor);
    bouX = -shape.getHeight() / 2;
    while (bouY + shape.getHeight() / 2 > 0) {
        space = {};
        space.x = bouX;
        space.y = bouY;
        space.available = true;
        space.number = number;
        space.location = 'LEFT';
        shape.parent.boundaryPlaces.insert(space);
        number += 1;
        numLeft += 1;
        bouY = shape.parent.getHeight() - (numLeft + 1) * (shape.getHeight() + factor);
    }

    //TOP
    bouY = -shape.getWidth() / 2;
    bouX = numTop * (shape.getWidth() + factor);
    while (bouX + shape.getWidth() / 2 < shape.parent.getWidth()) {
        space = {};
        space.x = bouX;
        space.y = bouY;
        space.available = true;
        space.number = number;
        space.location = 'TOP';
        shape.parent.boundaryPlaces.insert(space);
        number += 1;
        numTop += 1;
        bouX = numTop * (shape.getWidth() + factor);
    }
    //RIGHT
    bouY = numRight * (shape.getHeight() + factor);
    bouX = shape.parent.getWidth() - shape.getWidth() / 2;
    while (bouY + shape.getHeight() / 2 < shape.parent.getHeight()) {
        space = {};
        space.x = bouX;
        space.y = bouY;
        space.available = true;
        space.number = number;
        space.location = 'RIGHT';
        shape.parent.boundaryPlaces.insert(space);
        number += 1;
        numRight += 1;
        bouY = numRight * (shape.getHeight() + factor);
    }
    return this;
};

/**
 * Sets the boundary element to a selected boundary place
 * @param {PMEvent} shape
 * @param {Number} number
 * @return {*}
 */
PMActivity.prototype.setBoundary = function (shape, number) {
    var bouPlace = this.boundaryPlaces.get(number);
    bouPlace.available = false;
    shape.setPosition(bouPlace.x, bouPlace.y);
    return this;
};

/**
 * Returns the current place available to attach boundary events.
 * Retuns false if there's not place available
 * @return {Number/Boolean}
 */
PMActivity.prototype.getAvailableBoundaryPlace = function () {
    var place = 0,
        bouPlace,
        sw = true,
        i;
    for (i = 0; i < this.boundaryPlaces.getSize(); i += 1) {
        bouPlace = this.boundaryPlaces.get(i);
        if (bouPlace.available && sw) {
            place = bouPlace.number;
            sw = false;
        }
    }
    if (sw) {
        place = false;
    }
    return place;
};

/**
 * Update Boundary Places Array
 * @return {*}
 */
PMActivity.prototype.updateBoundaryPlaces = function () {
    var i,
        aux,
        k = 0;
    aux = new PMUI.util.ArrayList();
    for (i = 0; i < this.boundaryPlaces.getSize(); i += 1) {
        aux.insert(this.boundaryPlaces.get(i));
    }

    this.boundaryPlaces.clear();
    this.makeBoundaryPlaces();

    for (i = 0; i < this.boundaryPlaces.getSize(); i += 1) {
        if (k < aux.getSize()) {
            this.boundaryPlaces.get(i).available = aux.get(k).available;
            k += 1;
        }
    }
    return this;
};

/**
 * Returns the number of boundary events attached to this activity
 * @return {Number}
 */
PMActivity.prototype.getNumberOfBoundaries = function () {
    var child,
        i,
        bouNum = 0;

    for (i = 0; i < this.getChildren().getSize(); i += 1) {
        child = this.getChildren().get(i);
        if (child.getType() === 'PMEvent' && child.evn_type === 'BOUNDARY') {
            bouNum = bouNum + 1;
        }
    }
    return bouNum;
};
/**
 * Updates boundary positions when exists a change into the boundary array
 * @param {Boolean} createIntersections
 */
PMActivity.prototype.updateBoundaryPositions = function (createIntersections) {
    var child,
        port,
        i,
        j;

    if (this.getNumberOfBoundaries() > 0) {
        this.updateBoundaryPlaces();
        for (i = 0; i < this.getChildren().getSize(); i += 1) {
            child = this.getChildren().get(i);
            if (child.getType() === 'PMEvent'
                && child.evn_type === 'BOUNDARY') {
                child.setPosition(this.boundaryPlaces.get(child.numberRelativeToActivity).x,
                    this.boundaryPlaces.get(child.numberRelativeToActivity).y
                );
                for (j = 0; j < child.ports.getSize(); j += 1) {
                    port = child.ports.get(j);
                    port.setPosition(port.x, port.y);
                    port.connection.disconnect().connect();
                    if (createIntersections) {
                        port.connection.setSegmentMoveHandlers();
                        port.connection.checkAndCreateIntersectionsWithAll();
                    }
                }
            }
        }
    }
};

PMActivity.prototype.getActivityType = function () {
    return this.act_type;
};

PMActivity.prototype.getContextMenu = function () {
};
PMActivity.prototype.getTaskType = function () {
    return this.act_task_type;
};

PMActivity.prototype.getLoopType = function () {
    return this.act_loop_type;
};

PMActivity.prototype.updateDefaultFlow = function (destID) {
    this.act_default_flow = destID;
    return this;
};

PMActivity.prototype.updateTaskType = function (newType) {
    return this;
};

PMActivity.prototype.updateScriptType = function (newType) {
    return this;
};

PMActivity.prototype.changeColor = function (newTheme) {
    switch (newTheme) {
        case 'red':
            newClass = 'mafe-activity-task-' + newTheme;
            break;
        case 'green':
            newClass = 'mafe-activity-task-' + newTheme;
            break;
        case 'orange':
            newClass = 'mafe-activity-task-' + newTheme;
            break;
        case 'silver':
            newClass = 'mafe-activity-task-' + newTheme;
            break;
        default:
            newClass = 'mafe-activity-task';
            break;

    }
    var firstLayer = this.getLayers().asArray()[0];
    //remove custom clasess
    firstLayer.style.removeClasses(['mafe-activity-task', 'mafe-activity-task-red', 'mafe-activity-task-green', 'mafe-activity-task-orange', 'mafe-activity-task-silver']);
    //add the new class
    firstLayer.style.addClasses([newClass]);
    return this;
};

PMActivity.prototype.setResizeBehavior = function (behavior) {
    var factory = new PMUI.behavior.BehaviorFactory({
        products: {
            "regularresize": PMUI.behavior.RegularResizeBehavior,
            "Resize": PMUI.behavior.RegularResizeBehavior,
            "yes": PMUI.behavior.RegularResizeBehavior,
            "resize": PMUI.behavior.RegularResizeBehavior,
            "noresize": PMUI.behavior.NoResizeBehavior,
            "NoResize": PMUI.behavior.NoResizeBehavior,
            "no": PMUI.behavior.NoResizeBehavior,
            "activityResize": PMActivityResizeBehavior
        },
        defaultProduct: "noresize"
    });
    this.resizeBehavior = factory.make(behavior);
    if (this.html) {
        this.resize.init(this);
    }
    return this;
};
/**
 * Change task type
 * @param {String} type
 * @returns {*}
 */
PMActivity.prototype.switchTaskType = function (type) {
    var marker = this.markersArray.get(0),
        lowerType = type.toLowerCase();
    marker.removeAllClasses();
    marker.setMarkerZoomClasses([
        "mafe-" + lowerType + "-marker-10",
        "mafe-" + lowerType + "-marker-15",
        "mafe-" + lowerType + "-marker-21",
        "mafe-" + lowerType + "-marker-26",
        "mafe-" + lowerType + "-marker-31"
    ]);
    marker.paint(true);
    this.setTaskType(type);
    this.paint();
    this.updateBpmnTaskType(this.mapBpmnType[this.getTaskType()]);
    PMDesigner.connectValidator.bpmnValidator();
    return this;
};

/**
 * Change subprocess type
 * @param {String} type
 * @returns {*}
 */
PMActivity.prototype.switchSubProcessType = function (type) {
    var marker = this.markersArray.get(0),
        lowerType = type.toLowerCase();
    marker.removeAllClasses();
    marker.setMarkerZoomClasses([
        "mafe-" + lowerType + "-marker-10",
        "mafe-" + lowerType + "-marker-15",
        "mafe-" + lowerType + "-marker-21",
        "mafe-" + lowerType + "-marker-26",
        "mafe-" + lowerType + "-marker-31"
    ]);
    marker.paint(true);
    this.setTaskType(type);
    this.paint();
    return this;
};

PMActivity.prototype.executeLoopType = function (type) {
    var marker = this.markersArray.get(1),
        lowerType = type.toLowerCase();
    marker.removeAllClasses();
    marker.setMarkerZoomClasses([
        "mafe-" + lowerType + "-marker-10",
        "mafe-" + lowerType + "-marker-15",
        "mafe-" + lowerType + "-marker-21",
        "mafe-" + lowerType + "-marker-26",
        "mafe-" + lowerType + "-marker-31"
    ]);
    marker.paint(true);
    this.setLoopType(type);
    this.paint();
    this.updateBpmnTaskType(this.mapBpmnType[this.getTaskType()], this.getLoopType());
    PMDesigner.connectValidator.bpmnValidator();
    return this;
};
/**
 * this method admin thw activity loop types
 * @param type
 * @param shape
 */
PMActivity.prototype.switchLoopType = function (type) {
    var data = {
            act_uid: this.getID()
        },
        self = this,
        url = '/project/' + PMDesigner.project.id + '/activity/validate-active-cases';

    PMDesigner.restApi.execute({
        data: JSON.stringify(data),
        method: "update",
        url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + url,
        success: function (data, textStatus, xhr) {
            if (data.result) {
                self.executeLoopType(type);
            } else {
                PMDesigner.msgFlash(data.message, document.body, 'error', 3000, 5);
            }
        },
        error: function (xhr, textStatus, errorThrown) {
            PMDesigner.msgFlash('There are problems updating the Loop Marker'.translate(), document.body, 'error', 3000, 5);
        }
    });

};

PMActivity.prototype.beforeContextMenu = function () {
    var items, i,
        menuItem,
        hasMarker = false;
    this.canvas.hideAllCoronas();
    if (this.canvas.readOnly) {
        return;
    }
    if (this.getActivityType() === 'TASK') {
        items = this.menu.items.find('id', 'taskType').childMenu.items;
        for (i = 0; i < items.getSize(); i += 1) {
            menuItem = items.get(i);
            if (menuItem.id === this.getTaskType().toLowerCase()) {
                menuItem.disable();
                hasMarker = true;
            } else {
                menuItem.enable();
            }
        }

        items = this.menu.items.find('id', 'loopType').childMenu.items;
        for (i = 0; i < items.getSize(); i += 1) {
            menuItem = items.get(i);
            if (menuItem.id === this.getLoopType().toLowerCase()) {
                menuItem.disable();
                hasMarker = true;
            } else {
                menuItem.enable();
            }
        }
    }

};
/**
 * updates XML task type tag, removes the current tag and create a new updated
 * @param newBpmnType
 * @param loopType
 */
PMActivity.prototype.updateBpmnTaskType = function (newBpmnType, loopType) {
    var tempID = this.businessObject.elem.id,
        outgoing,
        incoming;

    outgoing = (this.businessObject.elem && this.businessObject.elem.outgoing) ?
        this.businessObject.elem.outgoing : null;
    incoming = (this.businessObject.elem && this.businessObject.elem.incoming) ?
        this.businessObject.elem.incoming : null;
    this.removeBpmn();

    this.businessObject.elem = null;
    this.createBpmn(newBpmnType);
    this.businessObject.elem.id = tempID;
    this.businessObject.elem.incoming = (incoming) ? incoming : null;
    this.businessObject.elem.outgoing = (outgoing) ? outgoing : null;
    if (loopType && typeof loopType !== 'undefined' && loopType !== 'EMPTY') {
        this.createLoopCharacteristics(this.businessObject.elem, loopType);
    }


};

PMActivity.prototype.getBpmnElementType = function () {
    if (this.extendedType === 'SUB_PROCESS') {
        return 'bpmn:SubProcess';
    } else {
        return this.mapBpmnType[this.getTaskType()];
    }

};
PMActivity.prototype.isSupported = function () {
    var isSupported = false;
    if (this.supportedArray.indexOf(this.getTaskType()) !== -1) {
        isSupported = true;
        if (this.getTaskType() != "COLLAPSED") {
            if (this.supportedLoopArray.indexOf(this.getLoopType()) !== -1) {
                isSupported = true;
            } else {
                isSupported = false;
            }
        }
    }
    return isSupported;
};
/**
 * Creates XML Loop task tag to export.
 * @param element
 * @param loopType
 */
PMActivity.prototype.createLoopCharacteristics = function (element, loopType) {
    var loopTypeA,
        loopChar = {
            isSequential: false,
            behavior: 'All'
        };
    loopTypeA = this.mapLoopTypes[loopType];
    element['loopCharacteristics'] = PMDesigner.bpmnFactory.create(loopTypeA, loopChar);
    if (loopType == "SEQUENTIAL") {
        element['loopCharacteristics'].set('isSequential', true);
    } else {
        element['loopCharacteristics'].set('isSequential', false);
    }
};

PMActivity.prototype.createBpmn = function (type) {

    if (!this.businessObject.elem && !(this instanceof PMUI.draw.MultipleSelectionContainer)) {
        this.createWithBpmn(type, 'businessObject');
    }
    this.updateBounds(this.businessObject.di);
    if (this.parent.getType() === 'PMCanvas' && !this.parent.businessObject.di) {
        this.canvas.createBPMNDiagram();
    }
    //LOOP characteristics
    if (this.act_loop_type && this.act_loop_type !== "EMPTY") {
        this.createLoopCharacteristics(this.businessObject.elem, this.act_loop_type);
    }

    if (this.parent.businessObject.elem) {
        this.updateShapeParent(this.businessObject, this.parent.businessObject);
    } else {
        //Here create busines object to new process
        this.parent.createBusinesObject();

        this.updateShapeParent(this.businessObject, this.parent.businessObject);
    }
};

/**
 * @class PMGateway
 * @param {Object} options
 */
var PMGateway = function (options) {
    PMShape.call(this, options);
    /**
     * Gateway id
     * @type {String}
     */
    this.gat_uid = null;
    /**
     * Gateway name
     * @type {String}
     */
    this.gat_name = null;
    /**
     * Gateway type, accept only: 'exclusive' and 'parallel' values
     * @type {String}
     */
    this.gat_type = null;
    /**
     * Gateway Direction, accept only 'unspecified', 'converging', 'diverging',
     * 'mixed'
     * @type {String}
     */
    this.gat_direction = null;
    /**
     * Instantiate property
     * @type {String}
     */
    this.gat_instantiate = null;
    /**
     * Event Gatewat Type property
     * @type {String}
     */
    this.gat_event_gateway_type = null;
    /**
     * Activation Count property
     * @type {Number}
     */
    this.gat_activation_count = null;
    /**
     * WaitingForStart property
     * @type {Boolean}
     */
    this.gat_waiting_for_start = null;
    /**
     * Default Flow property
     * @type {null}
     */
    this.gat_default_flow = null;
    this.defaltFlowMenuItem = null;
    this.businessObject = {};
    PMGateway.prototype.init.call(this, options);
};

PMGateway.prototype = new PMShape();
/**
 * Defines the object type
 * @type {String}
 */
PMGateway.prototype.type = 'PMGateway';
/**
 * Get data about object
 *
 */

PMGateway.prototype.mapBpmnType = {
    'EXCLUSIVE': 'bpmn:ExclusiveGateway',
    'INCLUSIVE': 'bpmn:InclusiveGateway',
    'PARALLEL': 'bpmn:ParallelGateway',
    'COMPLEX': 'bpmn:ComplexGateway',
    'EVENTBASED': 'bpmn:EventBasedGateway'
};
PMGateway.prototype.supportedArray = [
    'EXCLUSIVE',
    'INCLUSIVE',
    'PARALLEL'
];

PMGateway.prototype.getDataObject = function () {
    var name = this.getName(),
        container,
        element_id;
    switch (this.parent.type) {
        case 'PMCanvas':
            container = 'bpmnDiagram';
            element_id = this.canvas.id;
            break;
        case 'PMPool':
            container = 'bpmnPool';
            element_id = this.parent.id;
            break;
        case 'PMLane':
            container = 'bpmnLane';
            element_id = this.parent.id;
            break;
        default:
            container = 'bpmnDiagram';
            element_id = this.canvas.id;
            break;
    }
    return {
        gat_uid: this.getID(),
        gat_name: name,
        gat_type: this.gat_type,
        gat_direction: this.gat_direction,
        gat_instantiate: this.gat_instantiate,
        gat_event_gateway_type: this.gat_event_gateway_type,
        gat_activation_count: this.gat_activation_count,
        gat_waiting_for_start: this.gat_waiting_for_start,
        gat_default_flow: this.gat_default_flow,
        bou_x: this.x,
        bou_y: this.y,
        bou_width: this.width,
        bou_height: this.height,
        bou_container: container,
        bou_element: element_id,
        _extended: this.getExtendedObject()
    };
};
/**
 * Initialize the PMGateway object
 * @param options
 */
PMGateway.prototype.init = function (options) {
    var defaults = {
        gat_direction: 'DIVERGING',
        gat_instantiate: false,
        gat_event_gateway_type: 'NONE',
        gat_activation_count: 0,
        gat_waiting_for_start: true,
        gat_type: 'COMPLEX',
        gat_name: "Gateway",
        gat_default_flow: 0
    };
    jQuery.extend(true, defaults, options);
    this.setGatewayUid(defaults.gat_uid)
        .setGatewayType(defaults.gat_type)
        .setDirection(defaults.gat_direction)
        .setInstantiate(defaults.gat_instantiate)
        .setEventGatewayType(defaults.gat_event_gateway_type)
        .setActivationCount(defaults.gat_activation_count)
        .setWaitingForStart(defaults.gat_waiting_for_start)
        .setDefaultFlow(defaults.gat_default_flow);
    if (defaults.gat_name) {
        this.setName(defaults.gat_name);
    }
    this.setOnBeforeContextMenu(this.beforeContextMenu);
};

/**
 * Sets the Gateway ID
 * @param id
 * @return {*}
 */
PMGateway.prototype.setGatewayUid = function (id) {
    this.gat_uid = id;
    return this;
};
/**
 * Sets the label element
 * @param {String} value
 * @return {*}
 */
PMGateway.prototype.setName = function (name) {
    if (typeof name !== 'undefined') {
        this.gat_name = name;
        if (this.label) {
            this.label.setMessage(name);
        }
    }
    return this;
};
/**
 * Sets the gateway type
 * @param type
 * @return {*}
 */
PMGateway.prototype.setGatewayType = function (type) {
    type = type.toUpperCase();
    var defaultTypes = {
        COMPLEX: 'COMPLEX',
        EXCLUSIVE: 'EXCLUSIVE',
        PARALLEL: 'PARALLEL',
        INCLUSIVE: 'INCLUSIVE',
        EVENTBASED: 'EVENTBASED',
        EXCLUSIVEEVENTBASED: 'EXCLUSIVEEVENTBASED',
        PARALLELEVENTBASED: 'PARALLELEVENTBASED'

    };
    if (defaultTypes[type]) {
        this.gat_type = defaultTypes[type];
    }
    return this;
};
/**
 * Sets the Gateway direction
 * @param direction
 * @return {*}
 */
PMGateway.prototype.setDirection = function (direction) {
    if (typeof direction !== 'undefined') {
        direction = direction.toLowerCase();
        var defaultDir = {
            unspecified: 'UNSPECIFIED',
            diverging: 'DIVERGING',
            converging: 'CONVERGING',
            mixed: 'MIXED'
        };
        if (defaultDir[direction]) {
            this.gat_direction = defaultDir[direction];
        }
    }
    return this;
};
/**
 * Sets the instantiate property
 * @param value
 * @return {*}
 */
PMGateway.prototype.setInstantiate = function (value) {
    this.gat_instantiate = value;
    return this;
};
/**
 * Sets the event_gateway_type property
 * @param value
 * @return {*}
 */
PMGateway.prototype.setEventGatewayType = function (value) {
    this.gat_event_gateway_type = value;
    return this;
};
/**
 * Sets the activation_count property
 * @param value
 * @return {*}
 */
PMGateway.prototype.setActivationCount = function (value) {
    this.gat_activation_count = value;
    return this;
};
/**
 * Sets the waiting_for_start property
 * @param value
 * @return {*}
 */
PMGateway.prototype.setWaitingForStart = function (value) {
    this.gat_waiting_for_start = value;
    return this;
};
/**
 * Sets te default_flow property
 * @param value
 * @return {*}
 */
PMGateway.prototype.setDefaultFlow = function (value) {
    if (this.html) {
        PMShape.prototype.setDefaultFlow.call(this, value);
    }
    this.gat_default_flow = value;
    return this;
};

PMGateway.prototype.getGatewayType = function () {
    return this.gat_type;
};


PMGateway.prototype.createConfigureAction = function () {

};

PMGateway.prototype.cleanFlowConditions = function () {
    var i, port, connection, oldValues;
    for (i = 0; i < this.getPorts().getSize(); i += 1) {
        port = this.getPorts().get(i);
        connection = port.connection;
        if (connection.srcPort.parent.getID() === this.getID()) {
            oldValues = {
                condition: connection.getFlowCondition(),
                type: connection.getFlowType()
            };
            connection.setFlowCondition('');
            connection.canvas
                .triggerFlowConditionChangeEvent(connection, oldValues);
        }
    }
};
PMGateway.prototype.updateGatewayType = function (newType) {

};

PMGateway.prototype.updateDirection = function (newDirection) {

};

PMGateway.prototype.updateDefaultFlow = function (destID) {

};

PMGateway.prototype.changeTypeTo = function (type) {
    var command = new CommandChangeGatewayType(this, type);
    this.canvas.commandStack.add(command);
    command.execute();
    return this;
};

/**
 * Sets te default_flow property
 * @param value
 * @return {*}
 */
PMGateway.prototype.setDefaultFlow = function (value) {
    if (this.html) {
        PMShape.prototype.setDefaultFlow.call(this, value);
    }
    this.gat_default_flow = value;
    return this;
};

PMGateway.prototype.manualCreateMenu = function (e) {
    var tempMenu = new PMUI.menu.Menu({
        text: "Gateway Type".translate(),
        icon: "mafe-menu-properties-action",
        id: "gatewaytype",
        items: [
            {
                id: "gatewayexclusive",
                text: "Exclusive (XOR) Gateway".translate(),
                onClick: function (menuOption) {
                    var targetElement = menuOption
                        .getMenuTargetElement();
                    targetElement.changeTypeTo('EXCLUSIVE');
                    PMDesigner.project.updateElement([]);
                },
                disabled: true
            },
            {
                id: "gatewayparallel",
                text: "Parallel (AND) Gateway".translate(),
                onClick: function (menuOption) {
                    var targetElement = menuOption
                        .getMenuTargetElement();
                    targetElement.changeTypeTo('PARALLEL');
                    PMDesigner.project.updateElement([]);
                }
            },
            {
                id: "gatewayinclusive",
                text: "Inclusive (OR) Gateway".translate(),
                onClick: function (menuOption) {
                    var targetElement = menuOption
                        .getMenuTargetElement();
                    targetElement.changeTypeTo('INCLUSIVE');
                    PMDesigner.project.updateElement([]);
                }
            }
        ]
    });
    tempMenu.setTargetElement(this);
    tempMenu.show(e.pageX, e.pageY + this.getZoomHeight() / 2 + 4);
};

PMGateway.prototype.beforeContextMenu = function () {
    var i,
        port,
        connection,
        shape,
        defaultflowItems = [],
        items,
        name,
        target,
        menuItem,
        hasMarker;
    this.canvas.hideAllCoronas();
    if (this.canvas.readOnly) {
        return;
    }
    items = this.menu.items.find('id', 'gatewaytype').childMenu.items;
    for (i = 0; i < items.getSize(); i += 1) {
        menuItem = items.get(i);
        if (menuItem.id === 'gateway' +
            this.getGatewayType().toLowerCase()) {
            menuItem.disable();
            hasMarker = true;
        } else {
            menuItem.enable();
        }
    }

    defaultflowItems.push({
        text: 'None'.translate(),
        id: 'emptyFlow',
        disabled: (0 === parseInt(this.gat_default_flow)) ? true : false,
        onClick: function (menuOption) {
            target = menuOption.getMenuTargetElement();
            var cmd = new CommandDefaultFlow(target, 0);
            cmd.execute();
            target.canvas.commandStack.add(cmd);
        }
    });

    for (i = 0; i < this.getPorts().getSize(); i += 1) {
        port = this.getPorts().get(i);
        connection = port.connection;
        if (connection.srcPort.parent.getID() === this.getID()) {
            shape = connection.destPort.parent;
            switch (shape.getType()) {
                case 'PMActivity':
                    name = (shape.getName() !== '') ? shape.getName() : 'Task'
                        .translate();
                    break;
                case 'PMEvent':
                    name = (shape.getName() !== '') ? shape.getName() : 'Event'
                        .translate();
                    break;
                case 'PMGateway':
                    name = (shape.getName() !== '') ? shape.getName() : 'Gateway'
                        .translate();
                    break;
            }
            defaultflowItems.push(
                {
                    text: name,
                    id: connection.getID(),
                    disabled: (connection.getID() === this.gat_default_flow),
                    onClick: function (menuOption) {
                        target = menuOption.getMenuTargetElement();
                        //target.setDefaultFlow(menuOption.id);
                        var cmd = new CommandDefaultFlow(target, menuOption.id);
                        cmd.execute();
                        target.canvas.commandStack.add(cmd);
                    }
                });
        }
    }
    if (this.defaltFlowMenuItem) {
        this.menu.removeItem('defaultflowMenu');
    }
    if ((defaultflowItems.length > 0) && (this.gat_type !== "PARALLEL")) {
        this.defaltFlowMenuItem = {
            id: 'defaultflowMenu',
            text: "Default Flow".translate(),
            items: defaultflowItems
        };
        var last = this.menu.items.getLast();
        this.menu.addItem(this.defaltFlowMenuItem);
        this.menu.addItem(last);
    }
};

PMGateway.prototype.updateBpmGatewayType = function (newBpmnType) {
    var outgoing,
        incoming;

    outgoing = ( this.businessObject.elem && this.businessObject.elem.outgoing) ?
        this.businessObject.elem.outgoing : null;
    incoming = ( this.businessObject.elem && this.businessObject.elem.incoming) ?
        this.businessObject.elem.incoming : null;

    this.removeBpmn();
    this.businessObject.elem = null;
    this.createBpmn(newBpmnType);
    this.businessObject.elem.incoming = (incoming) ? incoming : null;
    this.businessObject.elem.outgoing = (outgoing) ? outgoing : null;
};

PMGateway.prototype.isSupported = function () {
    var isSupported = false;
    if (this.supportedArray.indexOf(this.getGatewayType()) !== -1) {
        isSupported = true;
    }
    return isSupported;
};
/**
 * Evaluates the gateway address, according to the amount of input and output connections, by default use Diverging.
 * Converging multiples inputs and only an output.
 * Diverging an input and an output.
 * Diverging multiples inputs output and multiples outputs.
 * @returns {PMGateway}
 */
PMGateway.prototype.evaluateGatewayDirection = function () {
    var incomings = this.getIncomingConnections('SEQUENCE', 'DEFAULT') || [],
        outgoings = this.getOutgoingConnections('SEQUENCE', 'DEFAULT') || [],
        direction;
    if (incomings.length > 1 && outgoings.length === 1) {
        direction = "CONVERGING";
    } else {
        direction = "DIVERGING";
    }
    this.setDirection(direction);
    return this;
};

/**
 * @inheritdoc
 **/
PMGateway.prototype.setIncomingConnections = function() {
    PMShape.prototype.setIncomingConnections.apply(this, arguments);
    return this.evaluateGatewayDirection();
};
/**
 * @inheritdoc
 **/
PMGateway.prototype.addIncomingConnection = function() {
    PMShape.prototype.addIncomingConnection.apply(this, arguments);
    return this.evaluateGatewayDirection();
};
/**
 * @inheritdoc
 **/
PMGateway.prototype.removeIncomingConnection = function() {
    PMShape.prototype.removeIncomingConnection.apply(this, arguments);
    return this.evaluateGatewayDirection();
};
/**
 * @inheritdoc
 **/
PMGateway.prototype.setOutgoingConnections = function() {
    PMShape.prototype.setOutgoingConnections.apply(this, arguments);
    return this.evaluateGatewayDirection();
};
/**
 * @inheritdoc
 **/
PMGateway.prototype.addOutgoingConnection = function() {
    PMShape.prototype.addOutgoingConnection.apply(this, arguments);
    return this.evaluateGatewayDirection();
};
/**
 * @inheritdoc
 **/
PMGateway.prototype.removeOutgoingConnection = function() {
    PMShape.prototype.removeOutgoingConnection.apply(this, arguments);
    return this.evaluateGatewayDirection();
};

var PMLine = function (options) {
    PMUI.draw.RegularShape.call(this, options);
    /**
     * Defines the type artifact
     * @type {String}
     */
    this.art_type = null;
    /**
     * Defines the unique identifier
     * @type {String}
     */
    this.art_uid = null;
    this.art_orientation = null;
    this.label = null;
    this.box = null;
    this.extended = null;
    this.extendedType = null;
    this.relationship = null;
    PMLine.prototype.init.call(this, options);
};
/**
 * Constant that represents that a drag behavior for moving the shape should be
 * used
 * @property {Number}
 */
PMLine.prototype = new PMUI.draw.RegularShape();

PMLine.prototype.type = "PMArtifact";
PMLine.prototype.family = "RegularShape";

PMLine.prototype.init = function (options) {
    var defaults = {
        art_orientation: "vertical",
        label: "",
        art_type: 'PMArtifact',
        art_name: "",
        art_uid: PMUI.generateUniqueId()
    };
    jQuery.extend(true, defaults, options);
    this.setOrientation(defaults.art_orientation)
        .setLabel(defaults.label)
        .setArtifactUid(defaults.art_uid)
        .setArtifactType(defaults.art_type)
        .setName(defaults.art_name);
};

PMLine.prototype.setOrientation = function (orientation) {
    var availableOpt, option;
    orientation = orientation.toLowerCase();
    availableOpt = {
        "vertical": "vertical",
        "horizontal": "horizontal"
    };
    option = (availableOpt[orientation]) ? orientation : "vertical";
    this.art_orientation = option;
    this.style.addClasses(["mafe-line-" + this.art_orientation]);
    return this;
};
PMLine.prototype.setLabel = function (label) {

    return this;
};
/**
 * Sets the label element
 * @param {String} value
 * @return {*}
 */
PMLine.prototype.setName = function (value) {
    if (this.label) {
        this.label.setMessage(value);
    }
    return this;
};
/**
 * Returns the label text
 * @return {String}
 */
PMLine.prototype.getName = function () {
    var text = "";
    if (this.label) {
        text = this.label.getMessage();
    }
    return text;
};
PMLine.prototype.setExtendedType = function (type) {
    this.extendedType = type;
    return this;
};

PMLine.prototype.setRelationship = function (relationship) {
    this.relationship = relationship;
    return this;
};
PMLine.prototype.addRelationship = function (object) {
    if (typeof object === "object") {
        jQuery.extend(true, this.relationship, object);
    }
    return this;
};
PMLine.prototype.setExtended = function (extended) {
    var ext;
    ext = (typeof extended === 'object') ? extended : {};
    this.extended = ext;
    return this;
};

/**
 * Sets the artifact type property
 * @param {String} type
 * @return {*}
 */
PMLine.prototype.setArtifactType = function (type) {
    this.art_type = type;
    return this;
};
/**
 * Sets the artifact unique identifier
 * @param {String} value
 * @return {*}
 */
PMLine.prototype.setArtifactUid = function (value) {
    this.art_uid = value;
    return this;
};
PMLine.prototype.getExtendedObject = function () {
    this.extended = {
        extendedType: this.extendedType
    };
    return this.extended;
};
/**
 * Returns the clean object to be sent to the backend
 * @return {Object}
 */
PMLine.prototype.getDataObject = function () {
    var name = this.getName();
    return {
        art_uid: this.art_uid,
        art_name: name,
        art_type: this.art_type,
        bou_x: this.x,
        bou_y: this.y,
        bou_width: this.width,
        bou_height: this.height,
        bou_container: 'bpmnDiagram',
        _extended: this.getExtendedObject()
    };
};

PMLine.prototype.getArtifactType = function () {
    return this.art_type;
};

/**
 * @event mousedown
 * Moused down callback fired when the user mouse downs on the `shape`
 * @param {PMUI.draw.Shape} shape
 */
PMLine.prototype.onMouseDown = function (shape) {
    return function (e, ui) {
        e.stopPropagation();
    };
};

PMLine.prototype.createHTML = function () {
    var width = this.width || 20000,
        height = this.height || 20000;
    PMUI.draw.RegularShape.prototype.createHTML.call(this);
    this.style.removeAllProperties();
    this.style.addProperties({
        position: "absolute",
        cursor: "move"
    });
    if (this.art_orientation === "vertical") {
        this.height = height;
        this.width = 5;
        this.style.addProperties({
            width: "5px",
            height: height + "px",
            top: -parseInt(height / 3, 10)
        });
    } else {
        this.width = width;
        this.height = 5;
        this.style.addProperties({
            width: width + "px",
            height: "5px",
            left: -parseInt(width / 3, 10)
        });
    }
    return this.html;
};
var CustomLayer = function (options) {
    PMUI.draw.Layer.call(this, options);
    this.tooltip = null;
    this.listeners = {
        click: function () {
        }
    };

    CustomLayer.prototype.init.call(this, options);
};
CustomLayer.prototype = new PMUI.draw.Layer();

CustomLayer.prototype.type = "customLayer";

CustomLayer.prototype.init = function (options) {
    var defaults = {
        tooltip: "",
        listeners: {
            click: function (event, layer, shape) {
            }
        }
    };
    jQuery.extend(true, defaults, options);

    this.setTooltipMessage(defaults.tooltip)
        .setListeners(defaults.listeners);
};
CustomLayer.prototype.setTooltipMessage = function (message) {
    if (typeof message === "string") {
        this.tooltip = message;
    }
    if (this.html) {
        jQuery(this.html).attr("title", "");
        jQuery(this.html).tooltip({content: this.tooltip, tooltipClass: "mafe-action-tooltip"});
    }
    return this;
};
CustomLayer.prototype.setListeners = function (events) {
    if (typeof events === "object") {
        this.listeners.click = events.click;
    }
    return this;
};
CustomLayer.prototype.createHTML = function (modifying) {
    this.setProperties();
    PMUI.draw.Layer.prototype.createHTML.call(this, modifying);
    this.setTooltipMessage();
    this.defineEvents();
    return this.html;
};
CustomLayer.prototype.defineEvents = function () {
    var that = this;

    jQuery(that.html).on("click", function (event) {
        that.listeners.click(event, that, that.parent);
        event.stopPropagation();
    });
};
/**
 * @class PMArtifact
 * Handle BPMN Annotations
 *
 *
 * @constructor
 * Creates a new instance of the class
 * @param {Object} options
 */
var PMArtifact = function (options) {
    PMShape.call(this, options);
    /**
     * Defines the type artifact
     * @type {String}
     */
    this.art_type = null;
    /**
     * Defines the unique identifier
     * @type {String}
     */
    this.art_uid = null;

    PMArtifact.prototype.initObject.call(this, options);
};
PMArtifact.prototype = new PMShape();

/**
 * Defines the object type
 * @type {String}
 */
PMArtifact.prototype.type = "PMArtifact";
PMArtifact.prototype.PMArtifactResizeBehavior = null;

/**
 * Initialize the object with the default values
 * @param {Object} options
 */
PMArtifact.prototype.initObject = function (options) {
    var defaults = {
        art_type: 'TEXT_ANNOTATION',
        art_name: ""
    };

    jQuery.extend(true, defaults, options);
    this.setArtifactUid(defaults.art_uid);
    this.setArtifactType(defaults.art_type);
    this.setName(defaults.art_name);

};

/**
 * Sets the artifact type property
 * @param {String} type
 * @return {*}
 */
PMArtifact.prototype.setArtifactType = function (type) {
    this.art_type = type;
    return this;
};
/**
 * Sets the artifact unique identifier
 * @param {String} value
 * @return {*}
 */
PMArtifact.prototype.setArtifactUid = function (value) {
    this.art_uid = value;
    return this;
};
/**
 * Returns the clean object to be sent to the backend
 * @return {Object}
 */
PMArtifact.prototype.getDataObject = function () {
    var name = this.getName(),
        container,
        element_id;
    switch (this.parent.type) {
        case 'PMCanvas':
            container = 'bpmnDiagram';
            element_id = this.canvas.id;
            break;
        case 'PMPool':
            container = 'bpmnPool';
            element_id = this.parent.id;
            break;
        case 'PMLane':
            container = 'bpmnLane';
            element_id = this.parent.id;
            break;
        case 'PMActivity':
            container = 'bpmnActivity';
            element_id = this.parent.id;
            break;
        default:
            container = 'bpmnDiagram';
            element_id = this.canvas.id;
            break;
    }
    return {
        art_uid: this.id,
        art_name: name,
        art_type: this.art_type,
        bou_x: this.x,
        bou_y: this.y,
        bou_width: this.width,
        bou_height: this.height,
        bou_container: container,
        bou_element: element_id,
        _extended: this.getExtendedObject()
    };
};

PMArtifact.prototype.getArtifactType = function () {
    return this.art_type;
};

PMArtifact.prototype.updateHTML = function () {
    var height, width;
    height = this.height;
    width = this.width;
    PMShape.prototype.updateHTML.call(this);
    this.setDimension(width, height);
    return this;
};

/**
 * Extends the paint method to draw text annotation lines
 */
PMArtifact.prototype.paint = function () {
    if (this.getArtifactType() === 'GROUP') {
        PMShape.prototype.paint.call(this);
    } else {
        if (!this.graphics || this.graphics === null) {
            this.graphics = new JSGraphics(this.id);
        } else {
            this.graphics.clear();
        }
        this.graphics.setStroke(1);
        this.graphics.drawLine(0, 0, 0, this.getZoomHeight());
        this.graphics.drawLine(0, 0, Math.round(this.getZoomWidth() * 0.25), 0);
        this.graphics.drawLine(0, this.getZoomHeight(), Math.round(this.getZoomWidth() * 0.25), this.getZoomHeight());
        this.graphics.paint();
        for (i = 0; i < this.labels.getSize(); i += 1) {
            label = this.labels.get(i);
            label.paint();
        }
        if (this.corona) {
            this.corona.paint();
            this.corona.hide();
        }
    }
};

PMArtifact.prototype.setResizeBehavior = function (behavior) {
    var factory = new PMUI.behavior.BehaviorFactory({
        products: {
            "regularresize": PMUI.behavior.RegularResizeBehavior,
            "Resize": PMUI.behavior.RegularResizeBehavior,
            "yes": PMUI.behavior.RegularResizeBehavior,
            "resize": PMUI.behavior.RegularResizeBehavior,
            "noresize": PMUI.behavior.NoResizeBehavior,
            "NoResize": PMUI.behavior.NoResizeBehavior,
            "no": PMUI.behavior.NoResizeBehavior,
            "annotationResize": PMArtifactResizeBehavior
        },
        defaultProduct: "noresize"
    });
    this.resizeBehavior = factory.make(behavior);
    if (this.html) {
        this.resize.init(this);
    }
    return this;
};

PMArtifact.prototype.createWithBpmn = function () {
    var businessObject = {};
    var bpmnElementType = this.getBpmnElementType();

    businessObject.elem = PMDesigner.bpmnFactory.create(bpmnElementType, {
        id: this.id,
        text: this.getName() ? PMDesigner.escapeXMLCharacters(this.getName()) : ""
    });

    if (!businessObject.di) {
        if (this.type === 'Connection') {
            businessObject.di = PMDesigner.bpmnFactory.createDiEdge(businessObject.elem, [], {
                id: businessObject.id + '_di'
            });
        } else {
            businessObject.di = PMDesigner.bpmnFactory.createDiShape(businessObject.elem, {}, {
                id: businessObject.id + '_di'
            });
        }
    }
    this.businessObject = businessObject;

};
/**
 * Return an incremental name based of the type of the shape
 * @param {Object} pmCanvas The current canvas
 */
var IncrementNameCanvas = function (pmCanvas) {
    var random,
        elementsName = {
            TASK: "Task".translate(),
            SUB_PROCESS: "Sub-process".translate(),
            START: "Start Event".translate(),
            START_MESSAGE: "Start Message Event".translate(),
            START_TIMER: "Start Timer Event".translate(),
            END: "End Event".translate(),
            END_MESSAGE: "End Message Event".translate(),
            SELECTION: "Selection".translate(),
            EVALUATION: "Evaluation".translate(),
            PARALLEL: "Parallel Gateway".translate(),
            INCLUSIVE: "Inclusive Gateway".translate(),
            EXCLUSIVE: "Exclusive Gateway".translate(),
            PARALLEL_EVALUATION: "Parallel by Evaluation".translate(),
            PARALLEL_JOIN: "Parallel Join".translate(),
            TEXT_ANNOTATION: "Annotation".translate(),
            VERTICAL_LINE: "Vertical line".translate(),
            HORIZONTAL_LINE: "Horizontal line".translate(),
            H_LABEL: "Horizontal Text".translate(),
            V_LABEL: "Vertical Text".translate(),
            DATASTORE: "Data Store".translate(),
            DATAOBJECT: "Data Object".translate(),
            PARTICIPANT: "Black Box Pool".translate(),
            POOL: "Pool".translate(),
            INTERMEDIATE_SENDMESSAGE: "Intermediate Send Message Event".translate(),
            INTERMEDIATE_RECEIVEMESSAGE: "Intermediate Receive Message Event".translate(),
            LANE: "Lane".translate(),
            GROUP: "Group".translate(),
            BOUNDARY_EVENT: ' ',
            END_EMAIL: "End Email Event".translate(),
            INTERMEDIATE_EMAIL: "Intermediate Email Event".translate()
        },
        random = false;
    return {
        id: Math.random(),
        get: function (type) {
            var i,
                j,
                k = pmCanvas.getCustomShapes().getSize(),
                exists,
                index = 1;
            for (i = 0; i < k; i += 1) {
                exists = false;
                for (j = 0; j < k; j += 1) {
                    if (pmCanvas.getCustomShapes().get(j).getName() === elementsName[type] + " " + (i + 1)) {
                        exists = true;
                        break;
                    }
                }
                if (!exists) {
                    break;
                }
            }
            return elementsName[type] + " " + (i + 1);
        }
    };
};
/**
 * It is required overwriting the method, since there custom functionality
 * that should not affect the core of PMUI.
 */
PMUI.ui.Window.prototype.open = function () {
    var the_window, that = this;
    if (this.isOpen) {
        return this;
    }
    the_window = this.getHTML();
    if (this.modal) {
        this.modalObject.appendChild(the_window);
        document.body.appendChild(this.modalObject);
        jQuery(the_window).draggable({
            handle: $(this.header),
            containment: '#' + this.modalObject.id,
            scroll: false});
        if (!$.stackModal) {
            $.stackModal = [];
        }
        $.stackModal.push(the_window);
        $(the_window).on("keydown", function (event) {
            if (event.keyCode !== $.ui.keyCode.TAB) {
                return;
            }
            var tabbables = $(':tabbable', this),
                first = tabbables.filter(':first'),
                last = tabbables.filter(':last');
            if (event.target === last[0] && !event.shiftKey) {
                if (first && first.focus) {
                    first.focus(1);
                }
                return false;
            } else if (event.target === first[0] && event.shiftKey) {
                if (last && last.focus) {
                    last.focus(1);
                }
                return false;
            }
            if (event.which === PMDesigner.keyCodeF5) {
                this.blur();
                event.preventDefault();
                window.location.reload(true);
            }
        });
    } else {
        document.body.appendChild(the_window);
        jQuery(this.getHTML()).draggable();
    }
    if (typeof this.onOpen === 'function') {
        this.onOpen(this);
    }
    this.isOpen = true;
    this.updateDimensionsAndPosition();
    this.setVisible(true);
    this.defineEvents();
    if (document.body && this.modal) {
        document.body.style.overflow = "hidden"
    }
    $(the_window).find("*").on("keydown", function (e) {
        if (e.which === PMDesigner.keyCodeF5) {
            this.blur();
            e.preventDefault();
            window.location.reload(true);
        }
    });
    return this;
};
/**
 * It is required overwriting the method, since there custom functionality
 * that should not affect the core of PMUI.
 */
PMUI.ui.Window.prototype.close = function () {
    jQuery(this.modalObject).detach();
    jQuery(this.html).detach();
    jQuery(this.closeButton).detach();
    if (typeof this.onClose === 'function') {
        this.onClose(this);
    }
    this.isOpen = false;
    if (document.body && this.modal) {
        document.body.style.overflow = "auto";
    }
    if ($.stackModal) {
        $.stackModal.pop();
        var the_window = $.stackModal[$.stackModal.length - 1];
    }
    return this;
};

var autoResizeScreen = function () {
    var myWidth = 0, myHeight = 0;
    if (typeof(window.innerWidth) === 'number') {
        myWidth = window.innerWidth;
        myHeight = window.innerHeight;
    } else if (document.documentElement &&
        (document.documentElement.clientWidth || document.documentElement.clientHeight)) {
        myWidth = document.documentElement.clientWidth;
        myHeight = document.documentElement.clientHeight;
    } else if (document.body &&
        (document.body.clientWidth || document.body.clientHeight)) {
        myWidth = document.body.clientWidth;
        myHeight = document.body.clientHeight;
    }
    return myWidth;
};

var showUID = function (id) {
    var messageWindow = new PMUI.ui.MessageWindow({
        width: 490,
        bodyHeight: 'auto',
        id: 'showMessageWindowUID',
        windowMessageType: 'info',
        message: id,
        footerItems: [
            {
                text: 'Ok'.translate(),
                handler: function () {
                    messageWindow.close();
                },
                buttonType: "success"
            }
        ]
    });
    messageWindow.setTitle("ID".translate());
    messageWindow.open();
    messageWindow.showFooter();
    $(messageWindow.dom.icon).removeClass();
};

var applyStyleWindowForm = function (win) {
    $(win.body).removeClass("pmui-background");
    win.footer.html.style.textAlign = 'right';
    (function searchForm(items) {
        var i;
        for (i = 0; i < items.length; i += 1) {
            if (items[i].footer && items[i].footer.setVisible) {
                $(win.body).addClass("pmui-background");
                items[i].footer.setVisible(false);
            }
            searchForm(items[i].getItems ? items[i].getItems() :
                (items[i].getPanel ? items[i].getPanel().getItems() : []));
        }
    }(win.getItems()));
};

var QuickMessageWindow = function (html, message) {
    if (html === undefined) {
        return;
    }
    QuickMessageWindow.prototype.show.call(this, html, message);
};

QuickMessageWindow.prototype.show = function (html, message) {
    var that = this,
        factorX = 25,
        factorY = 20;
    if ($('#tooltipmessagecustom')[0]) {
        $('#tooltipmessagecustom').css({
            'top': $(html).offset().top + factorY,
            'left': $(html).offset().left + factorX
        });
        $('#tooltipmessagecustombody').html(message);
    } else {
        var button = $('<div id="header"></div>')
            .append($("<a style='font-size: 14px'></a>")
                .html('X')
                .on('click', function () {
                    $('#tooltipmessagecustom').remove();
                }));
        $('body').append($('<div></div>')
            .append(button)
            .append($('<div></div>')
                .attr('id', 'tooltipmessagecustombody')
                .css({'float': 'left'})
                .html(message))
            .addClass('pmui pmui-pmtooltipmessage')
            .attr('id', 'tooltipmessagecustom')
            .css({
                'box-sizing': 'border-box', 'position': 'absolute',
                'z-index': '100', 'font-size': '10',
                'top': $(html).offset().top + factorY,
                'left': $(html).offset().left + factorX
            })).on('mousedown', function (evt) {
            that.closeEvent(evt);
        }).on('click', function (evt) {
            that.closeEvent(evt);
        }).on('mouseup', function (evt) {
            that.closeEvent(evt);
        });
        $(window).scroll(function () {
            that.close();
        });
    }
};

QuickMessageWindow.prototype.close = function () {
    $('#tooltipmessagecustom').remove();
};

QuickMessageWindow.prototype.closeEvent = function (evt) {
    var element = evt.target || evt.srcElement;
    if ($('#tooltipmessagecustom')[0] && element !== $('#tooltipmessagecustom')[0] && element !== $('#tooltipmessagecustom')[0].children[1]) {
        $('#tooltipmessagecustom').remove();
    }
};

var ButtonFormPanel = function (options) {
    options.labelVisible = false;
    this.onclick = options.onclick;
    this.parameter = options.parameter;
    PMUI.field.TextField.call(this, options);
    ButtonFormPanel.prototype.init.call(this, options);
};

ButtonFormPanel.prototype = new PMUI.field.TextField();

ButtonFormPanel.prototype.init = function (options) {
    var defaults = {};
    jQuery.extend(true, defaults, options);
};

ButtonFormPanel.prototype.createHTML = function () {
    var that = this, button;
    PMUI.field.TextField.prototype.createHTML.call(this);
    button = new PMUI.ui.Button({
        id: this.id,
        text: this.getLabel(),
        handler: function () {
            that.onclick(that);
        }
    });
    this.dom.controlContainer.appendChild(button.getHTML());
    this.dom.controlContainer.getElementsByTagName('input')[0].style.display = 'none';
    button.defineEvents();
    this.button = button;
    return this.html;
};

var LabelFormPanel = function (options) {
    PMUI.field.TextField.call(this, options);
    LabelFormPanel.prototype.init.call(this, options);
};
LabelFormPanel.prototype = new PMUI.field.TextField();

LabelFormPanel.prototype.init = function (options) {
    var defaults = {};
    jQuery.extend(true, defaults, options);
};

LabelFormPanel.prototype.createHTML = function () {
    PMUI.field.TextField.prototype.createHTML.call(this);
    this.dom.controlContainer.getElementsByTagName('input')[0].style.display = 'none';
    return this.html;
};

var messagePageGrid = function (currentPage, pageSize, numberItems, criteria, filter) {
    var msg;
    if (numberItems === 0) {
        return '';
    }
    msg = 'Page'.translate() + ' ' + (currentPage + 1) + ' ' + 'of'.translate() + ' ' + Math.ceil(numberItems / pageSize);
    return msg;
};

/*
 * Function: validateKeysField
 * valid characteres for file name:
 * http://support.microsoft.com/kb/177506/es
 * 
 * (A-z)letter
 * (0-9)number
 *  ^   Accent circumflex (caret)
 *  &   Ampersand
 *  '   Apostrophe (single quotation mark)
 *  @   At sign
 *  {   Brace left
 *  }   Brace right
 *  [   Bracket opening
 *  ]   Bracket closing
 *  ,   Comma
 *  $   Dollar sign
 *  =   Equal sign
 *  !   Exclamation point
 *  -   Hyphen
 *  #   Number sign
 *  (   Parenthesis opening
 *  )   Parenthesis closing
 *  %   Percent
 *  .   Period
 *  +   Plus
 *  ~   Tilde
 *  _   Underscore
 *  
 *  Example: only backspace, number and letter.
 *  validateKeysField(objectHtmlInput, ['isbackspace', 'isnumber', 'isletter']);
 *  
 *  Aditional support:
 *  :   Colon
 *  
 */
var validateKeysField = function (object, validates) {
    object.onkeypress = function (e) {
        var key = document.all ? e.keyCode : e.which,
            isbackspace = key === 8,
            isnumber = key > 47 && key < 58,
            isletter = (key > 96 && key < 123) || (key > 64 && key < 91),
            isaccentcircumflex = key === 94,
            isampersand = key === 41,
            isapostrophe = key === 145,
            isatsign = key === 64,
            isbraceleft = key === 123,
            isbraceright = key === 125,
            isbracketopening = key === 91,
            isbracketclosing = key === 93,
            iscomma = key === 130,
            isdollarsign = key === 36,
            isequalsign = key === 61,
            isexclamationpoint = key === 33,
            ishyphen = key === 45,
            isnumbersign = key === 35,
            isparenthesisopening = key === 40,
            isparenthesisclosing = key === 41,
            ispercent = key === 37,
            isperiod = key === 46,
            isplus = key === 43,
            istilde = key === 126,
            isunderscore = key === 95,
            iscolon = key === 58,
            sw = eval(validates[0]);

        if (key === 0) {
            return true;
        }
        sw = eval(validates[0]);
        for (var i = 1; i < validates.length; i++) {
            sw = sw || eval(validates[i]);
        }
        return sw;
    };
};

var applyStyleTreePanel = function (treePanel, fontStyle) {
    if (fontStyle !== false) {
        $(treePanel.getHTML()).find('a').css('font-weight', 'bold');
        $(treePanel.getHTML()).find('a').css('color', 'black');
        $(treePanel.getHTML()).find('ul li ul li>a').css('font-weight', 'normal');
        $(treePanel.getHTML()).find('ul li ul li>a').css('color', 'black');
    }
};

/*
 * Convert time format HH:MM:SS to decimal value.
 */
var timeToDecimal = function (value) {
    var s = value.toString().replace(/\s/g, '').split(':'), hour, min, sec;
    hour = parseInt(s[0]) || 0;
    min = parseInt(s[1]) || 1;
    sec = parseInt(s[2]) || 1;
    return (hour + min / 60 + sec / 3600);
};

/*
 * Convert decimal to time format HH:MM:SS.
 */
var decimalToTime = function (value, second) {
    var num = typeof value === 'number' ? value : 1, hour, min, sec;

    hour = parseInt(num);
    num = num - parseInt(num);
    num = num.toFixed(13);
    num = num * 60;

    min = parseInt(num);
    num = num - parseInt(num);
    num = num.toFixed(13);
    num = num * 60;

    sec = parseInt(num);

    hour = hour.toString().length === 1 ? '0' + hour : hour;
    min = min.toString().length === 1 ? '0' + min : min;
    sec = sec.toString().length === 1 ? '0' + sec : sec;

    return second === true ? hour + ':' + min + ':' + sec : hour + ':' + min;
};

var Mafe = {};
/**
 *
 * @param {type} settings
 * @returns {undefined}
 */
Mafe.Window = function (settings) {
    this.views = [];
    PMUI.ui.Window.call(this, settings);
    Mafe.Window.prototype.init.call(this, settings);
};

Mafe.Window.prototype = new PMUI.ui.Window();

Mafe.Window.prototype.init = function (settings) {
    this.setHeight(DEFAULT_WINDOW_HEIGHT);
    this.setWidth(DEFAULT_WINDOW_WIDTH);
    this.hideFooter();
    this.setButtonPanelPosition('bottom');
};

Mafe.Window.prototype.createHTML = function () {
    PMUI.ui.Window.prototype.createHTML.call(this);
    this.footer.html.style.textAlign = 'right';
    return this.html;
};

Mafe.Window.prototype.resetView = function () {
    var items;
    this.hideFooter();
    items = this.items.asArray();
    for (var i = 0; i < items.length; i++) {
        if (items[i].setVisible) {
            items[i].setVisible(false);
        }
        if (items[i].reset) {
            items[i].reset();
        }
    }
};

Mafe.Window.prototype.setButtons = function (buttons) {
    this.clearFooterItems();
    this.setFooterItems(buttons);
    this.showFooter();
};

/**
 *
 * @param {type} settings
 * @returns {undefined}
 */
Mafe.Grid = function (settings) {
    var defaults = {
        pageSize: 10,
        width: '96%',
        filterPlaceholder: 'Search ...'.translate(),
        emptyMessage: 'No records found'.translate(),
        nextLabel: 'Next'.translate(),
        previousLabel: 'Previous'.translate(),
        style: {cssClasses: ['mafe-gridPanel']},
        customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
            return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
        }
    };
    jQuery.extend(true, defaults, settings);
    PMUI.grid.GridPanel.call(this, defaults);
    Mafe.Grid.prototype.init.call(this, defaults);
};

Mafe.Grid.prototype = new PMUI.grid.GridPanel();

Mafe.Grid.prototype.init = function (settings) {
    var defaults = {};
    jQuery.extend(true, defaults, settings);
};

Mafe.Grid.prototype.createHTML = function () {
    PMUI.grid.GridPanel.prototype.createHTML.call(this);
    $(this.html).find('.pmui-textcontrol').css({'margin-top': '5px', width: '250px'});
    return this.html;
};

/**
 *
 * @param {type} settings
 * @returns {undefined}
 */
Mafe.Form = function (settings) {
    var defaults;
    this.onYesConfirmCancellation = new Function();
    defaults = {
        width: DEFAULT_WINDOW_WIDTH - 3,
        height: 'auto'
    };
    jQuery.extend(true, defaults, settings);
    PMUI.form.Form.call(this, defaults);
    Mafe.Form.prototype.init.call(this);
};
Mafe.Form.prototype = new PMUI.form.Form();

Mafe.Form.prototype.init = function (settings) {
    var defaults = {};
    jQuery.extend(true, defaults, settings);
};

Mafe.Form.prototype.getConfirmCancellationToLoseChanges = function () {
    return this.confirmCancellationToLoseChanges;
};

Mafe.Form.prototype.loseChanges = function (options) {
    var that = this, confirmCancellation;
    if (that.isDirty()) {
        confirmCancellation = new Mafe.ConfirmCancellation(options);
        confirmCancellation.onYes = function () {
            that.onYesConfirmCancellation();
        };
    } else {
        that.onYesConfirmCancellation();
    }
};

Mafe.ConfirmDeletion = function () {
    var that = this, defaults;
    that.onDelete = new Function();
    that.onCancel = new Function();
    defaults = {
        id: 'idConfirmDeletion',
        width: 490,
        bodyHeight: 'auto',
        windowMessageType: 'warning',
        message: 'Do you want to delete this Element?'.translate(),
        footerItems: [{
            id: 'idCancelConfirmDeletion',
            text: 'No'.translate(),
            visible: true,
            handler: function () {
                that.onCancel();
                that.close();
            },
            buttonType: "error"
        }, {
            id: 'idDeleteConfirmDeletion',
            text: 'Yes'.translate(),
            visible: true,
            handler: function () {
                that.onDelete();
                that.close();
            },
            buttonType: "success"
        }
        ]
    };
    PMUI.ui.MessageWindow.call(this, defaults);
    Mafe.ConfirmDeletion.prototype.init.call(this);
};

Mafe.ConfirmDeletion.prototype = new PMUI.ui.MessageWindow();

Mafe.ConfirmDeletion.prototype.init = function () {
    this.open();
    this.showFooter();
};

/**
 *
 * @returns {undefined}
 */
Mafe.ConfirmCancellation = function (options) {
    var that = this, defaults;
    that.onYes = new Function();
    that.onNo = new Function();
    defaults = {
        id: 'idConfirmCancellation',
        title: options["title"] || 'Confirm'.translate(),
        width: 490,
        bodyHeight: 'auto',
        windowMessageType: 'warning',
        message: 'Are you sure you want to discard your changes?'.translate(),
        footerItems: [
            {
                id: 'idCancelConfirmCancellation',
                text: 'No'.translate(),
                visible: true,
                handler: function () {
                    that.onNo();
                    that.close();
                },
                buttonType: "error"
            }, {
                id: 'idDeleteConfirmCancellation',
                text: 'Yes'.translate(),
                visible: true,
                handler: function () {
                    that.onYes();
                    that.close();
                },
                buttonType: "success"
            }
        ]
    };
    PMUI.ui.MessageWindow.call(this, defaults);
    Mafe.ConfirmCancellation.prototype.init.call(this);
};
Mafe.ConfirmCancellation.prototype = new PMUI.ui.MessageWindow();

Mafe.ConfirmCancellation.prototype.init = function () {
    this.open();
    this.showFooter();
};

/**
 *
 * @param {type} settings
 * @returns {undefined}
 */
Mafe.Tree = function (settings) {
    var defaults;
    if (settings && settings.width) {
        this._width = settings.width;
    } else {
        this._width = 210;
    }
    defaults = {
        id: 'idMafeTree',
        filterable: true,
        filterPlaceholder: 'Search ...'.translate(),
        emptyMessage: 'No records found'.translate(),
        autoBind: true,
        nodeDefaultSettings: {
            autoBind: true,
            collapsed: false,
            labelDataBind: 'labelDataBind',
            itemsDataBind: 'itemsDataBind',
            childrenDefaultSettings: {
                labelDataBind: 'labelDataBind',
                autoBind: true
            }
        }
    };
    jQuery.extend(true, defaults, settings);
    PMUI.panel.TreePanel.call(this, defaults);
    Mafe.Tree.prototype.init.call(this, defaults);
};
Mafe.Tree.prototype = new PMUI.panel.TreePanel();

Mafe.Tree.prototype.init = function (defaults) {
    var that = this;
    that.style.addProperties({overflow: 'auto'});
};

Mafe.Tree.prototype.createHTML = function () {
    PMUI.panel.TreePanel.prototype.createHTML.call(this);
    this.setWidth(this._width);
    return this.html;
};

/**
 *
 * @param {type} settings
 * @returns {undefined}
 */
Mafe.Accordion = function (settings) {
    var defaults;
    if (settings && settings.width) {
        this._width = settings.width;
    }
    defaults = {
        id: 'idAccordion',
        hiddenTitle: true,
        heightItem: 'auto',
        title: '',
        multipleSelection: true,
        listeners: {
            select: function (accordionItem, event) {
            }
        }
    };
    jQuery.extend(true, defaults, settings);
    PMUI.panel.AccordionPanel.call(this, defaults);
    Mafe.Accordion.prototype.init.call(this);
};
Mafe.Accordion.prototype = new PMUI.panel.AccordionPanel();

Mafe.Accordion.prototype.init = function () {
    var that = this;
    that.style.addProperties({'vertical-align': 'top'});
};

Mafe.Accordion.prototype.createHTML = function () {
    PMUI.panel.AccordionPanel.prototype.createHTML.call(this);
    this.setWidth(this._width);
    return this.html;
};

/**
 * Failsafe remove an element from a collection
 *
 * @param  {Array<Object>} [collection]
 * @param  {Object} [element]
 *
 * @return {Object} the element that got removed or undefined
 */
var CollectionRemove = function (collection, element) {
    var idx;
    if (!collection || !element) {
        return;
    }
    idx = collection.indexOf(element);
    if (idx === -1) {
        return;
    }
    collection.splice(idx, 1);
    return element;
};

function setEncoded(link, name, data) {
    var encodedData = encodeURIComponent(data);
    if (window.navigator.msSaveBlob) {
        window.navigator.msSaveBlob(new Blob([data], {type: "application/octet-stream"}), name);

    } else {
        if (data) {
            link.addClass('active').attr({
                'href': 'data:application/bpmn20-xml;charset=UTF-8,' + encodedData,
                'download': name
            });
        } else {
            link.removeClass('active');
        }
    }

}

function convertDatetimeToIso8601(datetime) {
    var separate, date, time, timeDifference, signed, hours, minutes;
    separate = datetime.split(' ');
    date = separate[0].split('-');
    time = (separate[1] || '00:00:00').split(':');
    date = new Date(date[0] || 0, (date[1] || 1) - 1, date[2] || 0, time[0] || 0, time[1] || 0, time[2] || 0);
    timeDifference = date.getTimezoneOffset();
    signed = '+';
    if (timeDifference > 0) {
        signed = '-';
    }
    timeDifference = Math.abs(timeDifference);
    hours = Math.floor(timeDifference / 60).toString();
    minutes = Math.floor(timeDifference % 60).toString();
    return datetime.replace(/\s/g, 'T') + signed +
        ('0' + hours).slice(-2) + ':' + ('0' + minutes).slice(-2);
}
/**
 * FullScreen class
 */
var FullScreen = function (options) {
    this.element = null;
    this.onReadyScreen = null;
    this.onCancelScreen = null;
    this.fullscreemed = false;
    this.fireEvent = false;
    FullScreen.prototype.init.call(this, options);
};
/**
 * [init description]
 * @param  {Object} options Config options
 */
FullScreen.prototype.init = function (options) {
    var defaults = {
        element: document.documentElement,
        onReadyScreen: function () {
        },
        onCancelScreen: function () {
        }
    };
    jQuery.extend(true, defaults, options);
    this.element = defaults.element;
    this.action = null;
    this.onReadyScreen = defaults.onReadyScreen;
    this.onCancelScreen = defaults.onCancelScreen;
    this.attachListeners();
};
FullScreen.prototype.cancel = function () {
    var requestMethod, fnCancelScreen, wscript, el;
    if (parent.document.documentElement === document.documentElement) {
        el = document;
    } else {
        el = parent.document;
    }
    requestMethod = el.cancelFullScreen ||
        el.webkitCancelFullScreen ||
        el.mozCancelFullScreen ||
        el.exitFullscreen;
    if (requestMethod) {
        requestMethod.call(el);
        try {
            fnCancelScreen = this.onCancelScreen;
            fnCancelScreen(el);
        } catch (e) {
            throw new Error(e);
        }
    } else if (typeof window.ActiveXObject !== "undefined") { // Older IE.
        wscript = new ActiveXObject("WScript.Shell");
        if (wscript !== null) {
            wscript.SendKeys("{F11}");
        }
    }
};

FullScreen.prototype.applyZoom = function () {
    var requestMethod, wscript, fnReadyScreen, el = this.element;
    requestMethod = el.requestFullScreen ||
        el.webkitRequestFullScreen ||
        el.mozRequestFullScreen ||
        el.msRequestFullScreen;

    if (requestMethod) {
        requestMethod.call(el);
        try {
            fnReadyScreen = this.onReadyScreen;
            fnReadyScreen(el);
        } catch (e) {
            throw new Error(e);
        }
    } else if (typeof window.ActiveXObject !== "undefined") {
        wscript = new ActiveXObject("WScript.Shell");
        if (wscript !== null) {
            wscript.SendKeys("{F11}");
        }
    }
    return false
};

FullScreen.prototype.toggle = function (action) {
    var el,
        isInFullScreen;
    this.action = action;
    if (parent.document.documentElement === document.documentElement) {
        el = document;
    } else {
        el = parent.document;
    }

    isInFullScreen = (el.fullScreenElement && el.fullScreenElement !== null) || (el.mozFullScreen || el.webkitIsFullScreen);
    if (isInFullScreen) {
        action.setTooltip('Full Screen'.translate());
        this.cancel();
    } else {

        this.applyZoom();
        this.fullscreemed = true;
        action.setTooltip('Exit full screen'.translate());
    }
    return false;
};
FullScreen.prototype.attachListeners = function () {
    var el, self = this;
    if (parent.document.documentElement === document.documentElement) {
        el = document;
    } else {
        el = parent.document;
    }

    el.addEventListener("fullscreenchange", function () {
        self.fireFullScreen();
    }, false);

    el.addEventListener("mozfullscreenchange", function () {
        self.fireFullScreen();
        //to fix: in firefox the tooltip is always visible
        if (self.action) {
            self.action.setTooltip('Full Screen'.translate());
            $(self.action.selector).tooltip("close");
        }

    }, false);

    el.addEventListener("webkitfullscreenchange", function (e, a) {
        self.fireFullScreen();
    }, false);

    el.addEventListener("msfullscreenchange", function () {
        self.fireFullScreen();
    }, false);
};
FullScreen.prototype.fireFullScreen = function () {
    if (this.fullscreemed && this.fireEvent) {
        this.fireEvent = false;
    } else {
        this.fireEvent = true;
    }
    return true;
};
var PMIframe = function (settings) {
    PMUI.core.Element.call(this, settings);

    this.src = null;
    this.name = null;
    this.scrolling = null;
    this.frameborder = null;
    this.errorMessage = null;
    this.data = null;

    PMIframe.prototype.init.call(this, settings);
};

PMIframe.prototype = new PMUI.core.Element();

PMIframe.prototype.type = "PMPMIframe";

PMIframe.prototype.family = 'PMPMIframe';

PMIframe.prototype.init = function (settings) {
    var defaults = {
        src: "",
        name: "",
        width: 770,
        height: 305,
        scrolling: 'no',
        frameborder: "0"
    };

    jQuery.extend(true, defaults, settings);

    this.setSrc(defaults.src)
        .setName(defaults.name)
        .setWidth(defaults.width)
        .setHeight(defaults.height)
        .setScrolling(defaults.scrolling)
        .setFrameborder(defaults.frameborder);
};

PMIframe.prototype.setSrc = function (src) {
    this.src = src;
    return this;
};
PMIframe.prototype.setName = function (name) {
    this.name = name;
    return this;
};
PMIframe.prototype.setScrolling = function (scrolling) {
    this.scrolling = scrolling;
    return this;
};
PMIframe.prototype.setFrameborder = function (frameborder) {
    this.frameborder = frameborder;
    return this;
};

PMIframe.prototype.createHTML = function () {
    var input;

    if (this.html) {
        return this.html;
    }

    input = PMUI.createHTMLElement("iframe");
    input.className = "PMIframeWin";
    input.id = this.id;
    input.name = "PMIframeWindow";
    input.src = this.src;
    input.frameBorder = this.frameborder;

    this.html = input;
    this.applyStyle();
    return this.html;
};


var PMTiny = function (options) {
    PMUI.control.HTMLControl.call(this, options);
    this.theme = null;
    this.plugins = null;
    this.mode = null;
    this.editorSelector = null;
    this.widthTiny = null;
    this.heightTiny = null;
    this.directionality = null;
    this.verifyHtml = null;
    this.themeAdvancedButtons1 = null;
    this.themeAdvancedButtons2 = null;
    this.popupCss = null;
    this.skin = null;
    this.skinVariant = null;
    this.processID = null;
    this.domainURL = null;
    this.baseURL = null;
    this.contentCss = null;
    this.themeAdvancedFonts = null;
    PMTiny.prototype.init.call(this, options);
};

PMTiny.prototype = new PMUI.control.HTMLControl();

PMTiny.prototype.type = "PMTiny";
PMTiny.prototype.family = 'PMCodeMirrorControl';

PMTiny.prototype.init = function (options) {
    var defaults = {
        theme: "advanced",
        plugins: "advhr,advimage,advlink,advlist,autolink,autoresize,contextmenu,directionality,emotions,example,example_dependency,fullpage,fullscreen,iespell,inlinepopups,insertdatetime,layer,legacyoutput,lists,media,nonbreaking,noneditable,pagebreak,paste,preview,print,save,searchreplace,style,tabfocus,table,template,visualblocks,visualchars,wordcount,xhtmlxtras,pmSimpleUploader,pmVariablePicker,pmGrids,style",
        mode: "specific_textareas",
        editorSelector: "tmceEditor",
        widthTiny: DEFAULT_WINDOW_WIDTH - 60,
        heightTiny: DEFAULT_WINDOW_HEIGHT - 100,
        directionality: 'ltr',
        verifyHtml: false,
        themeAdvancedButtons1: "pmSimpleUploader,|,pmVariablePicker,|,pmGrids,|,bold,italic,underline,|,justifyleft,justifycenter,justifyright,justifyfull,|,fontselect,fontsizeselect,|,cut,copy,paste,|,bullist,numlist,|,outdent,indent,blockquote",
        themeAdvancedButtons2: "tablecontrols,|,undo,redo,|,link,unlink,image,|,forecolor,backcolor,styleprops,|,hr,removeformat,visualaid,|,sub,sup,|,ltr,rtl,|,code",
        popupCss: "/js/tinymce/jscripts/tiny_mce/themes/advanced/skins/default/dialog.css",
        skin: "o2k7",
        skinVariant: "silver",
        processID: null,
        domainURL: "/sys" + WORKSPACE + "/" + LANG + "/" + SKIN + "/",
        baseURL: "/js/tinymce/jscripts/tiny_mce",
        contentCss: "",
        themeAdvancedFonts: "Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats;"
    };

    jQuery.extend(true, defaults, options);

    this.setTheme(defaults.theme)
        .setPlugins(defaults.plugins)
        .setMode(defaults.mode)
        .setEditorSelector(defaults.editorSelector)
        .setDirectionality(defaults.directionality)
        .setVerifyHtml(defaults.verifyHtml)
        .setThemeAdvancedButtons1(defaults.themeAdvancedButtons1)
        .setThemeAdvancedButtons2(defaults.themeAdvancedButtons2)
        .setPopupCss(defaults.popupCss)
        .setSkin(defaults.skin)
        .setSkinVariant(defaults.skinVariant)
        .setProcessID(defaults.processID)
        .setDomainURL(defaults.domainURL)
        .setBaseURL(defaults.baseURL)
        .setHeightTiny(defaults.heightTiny)
        .setWidthTiny(defaults.widthTiny)
        .setContentCss(defaults.contentCss)
        .setThemeAdvancedFonts(defaults.themeAdvancedFonts);
};

PMTiny.prototype.setTheme = function (theme) {
    this.theme = theme;
    return this;
};
PMTiny.prototype.setPlugins = function (plugins) {
    this.plugins = plugins;
    return this;
};
PMTiny.prototype.setMode = function (mode) {
    this.mode = mode;
    return this;
};
PMTiny.prototype.setEditorSelector = function (editorSelector) {
    this.editorSelector = editorSelector;
    return this;
};
PMTiny.prototype.setDirectionality = function (directionality) {
    this.directionality = directionality;
    return this;
};
PMTiny.prototype.setVerifyHtml = function (verifyHtml) {
    this.verifyHtml = verifyHtml;
    return this;
};
PMTiny.prototype.setThemeAdvancedButtons1 = function (themeAdvancedButtons1) {
    this.themeAdvancedButtons1 = themeAdvancedButtons1;
    return this;
};
PMTiny.prototype.setThemeAdvancedButtons2 = function (themeAdvancedButtons2) {
    this.themeAdvancedButtons2 = themeAdvancedButtons2;
    return this;
};
PMTiny.prototype.setPopupCss = function (popupCss) {
    this.popupCss = popupCss;
    return this;
};
PMTiny.prototype.setSkin = function (skin) {
    this.skin = skin;
    return this;
};
PMTiny.prototype.setSkinVariant = function (skinVariant) {
    this.skinVariant = skinVariant;
    return this;
};
PMTiny.prototype.setProcessID = function (processID) {
    this.processID = processID;
    return this;
};
PMTiny.prototype.setDomainURL = function (domainURL) {
    this.domainURL = domainURL;
    return this;
};
PMTiny.prototype.setBaseURL = function (baseURL) {
    this.baseURL = baseURL;
    return this;
};
PMTiny.prototype.setWidthTiny = function (widthTiny) {
    this.widthTiny = widthTiny;
    return this;
};
PMTiny.prototype.setHeightTiny = function (heightTiny) {
    this.heightTiny = heightTiny;
    return this;
};
/**
 * Set CSS used in the content editor
 *
 * @param string contentCss
 * @returns {PMTiny}
 */
PMTiny.prototype.setContentCss = function (contentCss) {
    this.contentCss = contentCss;
    return this;
};
/**
 * Set fonts list for the advanced theme
 *
 * @param string themeAdvancedFonts
 * @returns {PMTiny}
 */
PMTiny.prototype.setThemeAdvancedFonts = function (themeAdvancedFonts) {
    this.themeAdvancedFonts = themeAdvancedFonts;
    return this;
};


PMTiny.prototype.setParameterTiny = function () {
    var that = this, domainURL;
    tinyMCE.baseURL = this.baseURL;
    domainURL = this.domainURL;
    tinyMCE.init({
        theme: this.theme,
        plugins: this.plugins,
        mode: this.mode,
        editor_selector: this.editorSelector,
        width: this.widthTiny,
        height: this.heightTiny,
        directionality: this.directionality,
        verify_html: this.verifyHtml,
        theme_advanced_buttons1: this.themeAdvancedButtons1,
        theme_advanced_buttons2: this.themeAdvancedButtons2,
        popup_css: this.popupCss,
        skin: this.skin,
        skin_variant: this.skinVariant,
        relative_urls: false,
        remove_script_host: false,
        convert_urls: this.convert_urls,
        content_css: this.contentCss,
        theme_advanced_fonts: this.themeAdvancedFonts,
        oninit: function () {
            tinyMCE.activeEditor.processID = PMDesigner.project.id;
            tinyMCE.activeEditor.domainURL = domainURL;
            //added the tinyeditor reference to the PMUI control
            that.controlEditor = tinyMCE.activeEditor;
            tinyMCE.execCommand('mceFocus', false, 'tinyeditor');
        },
        onchange_callback: function (inst) {
            that.onChangeHandler();
            if (inst.isDirty()) {
                inst.save();
            }
            return true;
        },
        handle_event_callback: function (e) {
            if (this.isDirty()) {
                this.save();
            }
            return true;
        }
    });
};

PMTiny.prototype.createHTML = function () {
    var input;

    if (this.html) {
        return this.html;
    }

    input = PMUI.createHTMLElement("textArea");
    input.className = "tmceEditor";
    input.id = "tinyeditor";
    input.name = "tinyeditor";
    input.width = this.width;
    input.height = this.height;

    this.html = input;
    return this.html;
};

PMTiny.prototype.setValueTiny = function (value) {
    if (this.html) {
        if (this.html.id) {
            $('#' + this.html.id + '_ifr').height('100%');
        }
        if (this.controlEditor) {
            this.controlEditor.setContent(value);
        }
    }
    return this;
};

PMTiny.prototype.getValue = function (value) {
    if (this.html) {
        if (this.controlEditor) {
            return this.controlEditor.getContent(value);
        }
    }
    return '';
};

PMTiny.prototype.setVisible = function (visible) {
    visible = !!visible;
    this.visible = visible;

    if (this.html) {
        if (this.html.id) {
            if (visible) {
                $('#' + this.html.id + '_tbl').css("display", "block");
            } else {
                $('#' + this.html.id + '_tbl').css("display", "none");
            }
        }
    }

    return this;
};
var PMTinyField = function (settings) {
    PMUI.form.Field.call(this, settings);
    PMTinyField.prototype.init.call(this, settings);
};

PMTinyField.prototype = new PMUI.form.Field();

PMTinyField.prototype.type = "PMTinyField";

PMTinyField.prototype.family = 'PMTinyField';

PMTinyField.prototype.init = function (settings) {
    var defaults = {
        theme: "advanced",
        plugins: "advhr,advimage,advlink,advlist,autolink,autoresize,contextmenu,directionality,emotions,example,example_dependency,fullpage,fullscreen,iespell,inlinepopups,insertdatetime,layer,legacyoutput,lists,media,nonbreaking,noneditable,pagebreak,paste,preview,print,save,searchreplace,style,tabfocus,table,template,visualblocks,visualchars,wordcount,xhtmlxtras,pmSimpleUploader,pmVariablePicker,pmGrids,style",
        mode: "specific_textareas",
        editorSelector: "tmceEditor",
        widthTiny: DEFAULT_WINDOW_WIDTH - 60,
        heightTiny: DEFAULT_WINDOW_HEIGHT - 100,
        directionality: 'rtl',
        verifyHtml: false,
        themeAdvancedButtons1: "pmSimpleUploader,|,pmVariablePicker,|,pmGrids,|,bold,italic,underline,|,justifyleft,justifycenter,justifyright,justifyfull,|,fontselect,fontsizeselect,|,cut,copy,paste,|,bullist,numlist,|,outdent,indent,blockquote",
        themeAdvancedButtons2: "tablecontrols,|,undo,redo,|,link,unlink,image,|,forecolor,backcolor,styleprops,|,hr,removeformat,visualaid,|,sub,sup,|,ltr,rtl,|,code",
        popupCss: "/js/tinymce/jscripts/tiny_mce/themes/advanced/skins/default/dialog.css",
        skin: "o2k7",
        skinVariant: "silver",
        processID: null,
        domainURL: "/sys" + WORKSPACE + "/" + LANG + "/" + SKIN + "/",
        baseURL: "/js/tinymce/jscripts/tiny_mce",
        contentCss: "",
        themeAdvancedFonts: "Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats;"
    };

    jQuery.extend(true, defaults, settings);

    this.setTheme(defaults.theme)
        .setPlugins(defaults.plugins)
        .setMode(defaults.mode)
        .setEditorSelector(defaults.editorSelector)
        .setDirectionality(defaults.directionality)
        .setVerifyHtml(defaults.verifyHtml)
        .setThemeAdvancedButtons1(defaults.themeAdvancedButtons1)
        .setThemeAdvancedButtons2(defaults.themeAdvancedButtons2)
        .setPopupCss(defaults.popupCss)
        .setSkin(defaults.skin)
        .setSkinVariant(defaults.skinVariant)
        .setProcessID(defaults.processID)
        .setDomainURL(defaults.domainURL)
        .setBaseURL(defaults.baseURL)
        .setHeightTiny(defaults.heightTiny)
        .setWidthTiny(defaults.widthTiny)
        .setContentCss(defaults.contentCss)
        .setThemeAdvancedFonts(defaults.themeAdvancedFonts)
        .hideLabel(true);
};

PMTinyField.prototype.setTheme = function (theme) {
    this.controls[0].setTheme(theme);
    return this;
};
PMTinyField.prototype.setPlugins = function (plugins) {
    this.controls[0].setPlugins(plugins);
    return this;
};
PMTinyField.prototype.setMode = function (mode) {
    this.controls[0].setMode(mode);
    return this;
};
PMTinyField.prototype.setEditorSelector = function (editorSelector) {
    this.controls[0].setEditorSelector(editorSelector);
    return this;
};
PMTinyField.prototype.setDirectionality = function (directionality) {
    this.controls[0].setDirectionality(directionality);
    return this;
};
PMTinyField.prototype.setVerifyHtml = function (verifyHtml) {
    this.controls[0].setVerifyHtml(verifyHtml);
    return this;
};
PMTinyField.prototype.setThemeAdvancedButtons1 = function (themeAdvancedButtons1) {
    this.controls[0].setThemeAdvancedButtons1(themeAdvancedButtons1);
    return this;
};
PMTinyField.prototype.setThemeAdvancedButtons2 = function (themeAdvancedButtons2) {
    this.controls[0].setThemeAdvancedButtons2(themeAdvancedButtons2);
    return this;
};
PMTinyField.prototype.setPopupCss = function (popupCss) {
    this.controls[0].setPopupCss(popupCss);
    return this;
};
PMTinyField.prototype.setSkin = function (skin) {
    this.skin = skin;
    this.controls[0].setSkin(skin);
    return this;
};
PMTinyField.prototype.setSkinVariant = function (skinVariant) {
    this.controls[0].setSkinVariant(skinVariant);
    return this;
};
PMTinyField.prototype.setProcessID = function (processID) {
    this.controls[0].setProcessID(processID);
    return this;
};
PMTinyField.prototype.setDomainURL = function (domainURL) {
    this.controls[0].setDomainURL(domainURL);
    return this;
};
PMTinyField.prototype.setBaseURL = function (baseURL) {
    this.controls[0].setBaseURL(baseURL);
    return this;
};
PMTinyField.prototype.setWidthTiny = function (widthTiny) {
    this.controls[0].setWidthTiny(widthTiny);
    return this;
};
PMTinyField.prototype.setHeightTiny = function (heightTiny) {
    this.controls[0].setHeightTiny(heightTiny);
    return this;
};
/**
 * Set CSS used in the content editor
 *
 * @param string contentCss
 * @returns {PMTinyField}
 */
PMTinyField.prototype.setContentCss = function (contentCss) {
    this.controls[0].setContentCss(contentCss);
    return this;
};
/**
 * Set fonts list for the advanced theme
 *
 * @param string themeAdvancedFonts
 * @returns {PMTinyField}
 */
PMTinyField.prototype.setThemeAdvancedFonts = function (themeAdvancedFonts) {
    this.controls[0].setThemeAdvancedFonts(themeAdvancedFonts);
    return this;
};
PMTinyField.prototype.hideLabel = function (value) {
    jQuery(this.dom.labelTextContainer).hide();
    this.labelVisible = !value;
    return this;
}
PMTinyField.prototype.setParameterTiny = function () {
    this.controls[0].setParameterTiny();
    return this;
}
PMTinyField.prototype.setValueTiny = function (value) {
    this.controls[0].setValueTiny(value);
    return this;
}

PMTinyField.prototype.setControls = function () {
    if (this.controls.length) {
        return this;
    }
    this.controls.push(new PMTiny());
    return this;
};




var PMRestClient = function (options) {
    this.endpoint = null;
    this.typeRequest = null;
    this.data = null;
    this.functionSuccess = null;
    this.functionFailure = null;
    this.functionComplete = null;
    this.apiVersion = null;
    this.restProxy = null;
    this.pmProcess = null;
    this.messageError = null;
    this.messageSuccess = null;
    this.flashContainer = null;
    this.flashDuration = null;
    this.flashSeverity = null;
    this.multipart = false;
    this.needStringify = null;
    this.hostName = HTTP_SERVER_HOSTNAME;

    PMRestClient.prototype.init.call(this, options);
};

PMRestClient.prototype.type = "PMRestClient";

PMRestClient.prototype.init = function (options) {
    var defaults = {
        endpoint: '',
        typeRequest: 'get',
        data: null,
        functionSuccess: function (resp, data) {
        },
        functionFailure: function (resp, data) {
        },
        functionComplete: function (resp, data) {
        },
        apiVersion: '1.0',
        restProxy: '',
        processID: '',
        messageError: false,
        messageSuccess: false,
        flashContainer: document.body,
        flashDuration: 3000,
        flashSeverity: 'success',
        multipart: false,
        needStringify: true

    }

    jQuery.extend(true, defaults, options);

    this.setProcessID()
        .setApiVersion(defaults.apiVersion)
        .setEndpoint(defaults.endpoint)
        .setTypeRequest(defaults.typeRequest)
        .setData(defaults.data)
        .setFunctionSuccess(defaults.functionSuccess)
        .setFunctionFailure(defaults.functionFailure)
        .setFunctionComplete(defaults.functionComplete)
        .setMessageSuccess(defaults.messageSuccess)
        .setMessageError(defaults.messageError)
        .setFlashContainer(defaults.flashContainer)
        .setFlashDuration(defaults.flashDuration)
        .setFlashSeverity(defaults.flashSeverity)
        .setMultipart(defaults.multipart)
        .setNeedStringify(defaults.needStringify);
};

PMRestClient.prototype.setProcessID = function () {
    this.processID = PMDesigner.project.id;
    return this;
};

PMRestClient.prototype.setEndpoint = function (endpoint) {
    this.endpoint = "/api/" + this.apiVersion + "/" + WORKSPACE + "/project/" + this.processID + "/" + endpoint;
    return this;
};

PMRestClient.prototype.setBaseEndPoint = function (endpoint) {
    this.endpoint = "/api/" + this.apiVersion + "/" + WORKSPACE + "/" + endpoint;
    return this;
};

PMRestClient.prototype.setTypeRequest = function (typeRequest) {
    this.typeRequest = typeRequest;
    return this;
};

PMRestClient.prototype.setData = function (data) {
    this.data = data;
    return this;
};

PMRestClient.prototype.setFunctionSuccess = function (functionSuccess) {
    this.functionSuccess = functionSuccess;
    return this;
};

PMRestClient.prototype.setFunctionComplete = function (functionComplete) {
    this.functionComplete = functionComplete;
    return this;
};

PMRestClient.prototype.setFunctionFailure = function (functionFailure) {
    this.functionFailure = functionFailure;
    return this;
};

PMRestClient.prototype.setMessageSuccess = function (messageSuccess) {
    this.messageSuccess = (messageSuccess) ? messageSuccess : false;
    return this;
};

PMRestClient.prototype.setMessageError = function (messageError) {
    this.messageError = (messageError) ? messageError : false;
    return this;
};

PMRestClient.prototype.setApiVersion = function (apiVersion) {
    this.apiVersion = apiVersion;
    return this;
};

PMRestClient.prototype.setRestProxy = function () {
    this.restProxy = new PMUI.proxy.RestProxy();
    return this;
};

PMRestClient.prototype.setToken = function () {
    var keys;
    if (this.pmProcess === '' || this.pmProcess === null) {
        this.setProcessID();
    }
    if (this.restProxy === '' || this.restProxy === null) {
        this.setRestProxy();
    }
    keys = PMDesigner.project.getKeysClient();
    this.restProxy.setDataType("json");
    this.restProxy.setAuthorizationType('oauth2', PMDesigner.project.tokens);
    if (this.multipart) {
        this.enableMultipart();
    }
    return this;
};

/**
 * Enables Multipart header
 * @returns {PMRestClient}
 */
PMRestClient.prototype.enableMultipart = function () {
    this.restProxy.rc.setHeader('X-Requested-With', 'MULTIPART');
    return this;
};

PMRestClient.prototype.setFlashContainer = function (flashContainer) {
    this.flashContainer = flashContainer;
    return this;
};

PMRestClient.prototype.setFlashDuration = function (flashDuration) {
    this.flashDuration = flashDuration;
    return this;
};

PMRestClient.prototype.setFlashSeverity = function (flashSeverity) {
    this.flashSeverity = flashSeverity;
    return this;
};

PMRestClient.prototype.setMultipart = function (multipart) {
    this.multipart = multipart;
    return this;
};

PMRestClient.prototype.setNeedStringify = function (value) {
    this.needStringify = value;
    return this;
};

PMRestClient.prototype.setHeader = function (name, value) {
    if (this.restProxy === '' || this.restProxy === null) {
        this.setRestProxy();
    }
    this.restProxy.rc.setHeader(name, value);
    return this;
};

/**
 * Validates Multipart responses
 * @param response
 */
PMRestClient.prototype.validateMesssage = function (response) {
    var HTTP_SUCCESS = ["200", "201", "202", "204", "207"],
        showMessage = false,
        messageMultipart = [],
        i,
        that = this;
    for (i = 0; i < response.length; i += 1) {
        if (HTTP_SUCCESS.indexOf(String(response[i].status)) !== -1) {
            if (that.messageSuccess[i] !== null) {
                showMessage = true;
                messageMultipart.push(that.messageSuccess[i]);
            }
        } else {
            if (that.messageError[i] !== null) {
                showMessage = true;
                messageMultipart.push(that.messageError[i]);
                that.setFlashSeverity('error');
            }
        }
    }
};

/**
 * Executes extended Rest Client
 *
 */
PMRestClient.prototype.executeRestClient = function () {
    if (this.restProxy === '' || this.restProxy === null) {
        this.setRestProxy();
    }
    this.setToken();
    this.restProxy.rc.setHeader("Accept-Language", LANG);
    switch (this.typeRequest) {
        case 'get':
            this.get();
            break;
        case 'post':
            this.post();
            break;
        case 'put':
            this.put();
            break;
        case 'update':
            this.update();
            break;
        case 'remove':
            this.remove();
            break;
    }
};

/**
 * Get method
 */
PMRestClient.prototype.get = function () {
    var that = this;
    this.restProxy.get({
        url: this.hostName + this.endpoint,
        authorizationOAuth: true,
        data: this.data,
        success: function (xhr, response) {
            if (that.multipart) {
                that.validateMesssage(response);
            }
            if (that.messageSuccess) {
                PMDesigner.msgFlash(
                    that.messageSuccess,
                    that.flashContainer,
                    that.flashSeverity,
                    that.flashDuration
                );
            }
            that.functionSuccess(xhr, response);
        },
        failure: function (xhr, response) {
            that.failureResponse(that, xhr, response);
        },
        complete: function (xhr, response) {
            that.functionComplete(xhr, response);
        }
    });
};
/**
 * Post method
 */
PMRestClient.prototype.post = function () {
    var that = this;
    this.restProxy.post({
        url: this.hostName + this.endpoint,
        authorizationOAuth: true,
        data: this.data,
        success: function (xhr, response) {
            if (that.multipart) {
                that.validateMesssage(response);
            }
            if (that.messageSuccess) {
                PMDesigner.msgFlash(
                    that.messageSuccess,
                    that.flashContainer,
                    that.flashSeverity,
                    that.flashDuration
                );
            }
            that.functionSuccess(xhr, response);
        },
        failure: function (xhr, response) {
            that.failureResponse(that, xhr, response);
        },
        complete: function (xhr, response) {
            that.functionComplete(xhr, response);
        }
    });
};
/**
 * Put Method
 */
PMRestClient.prototype.put = function () {
    var that = this;
    this.restProxy.put({
        url: this.hostName + this.endpoint,
        authorizationOAuth: true,
        data: this.data,
        success: function (xhr, response) {
            if (that.multipart) {
                that.validateMesssage(response);
            }
            if (that.messageSuccess) {
                PMDesigner.msgFlash(
                    that.messageSuccess,
                    that.flashContainer,
                    that.flashSeverity,
                    that.flashDuration
                );
            }
            that.functionSuccess(xhr, response);
        },
        failure: function (xhr, response) {
            that.failureResponse(that, xhr, response);
        },
        complete: function (xhr, response) {
            that.functionComplete(xhr, response);
        }
    });
};
/**
 * Delete method
 */
PMRestClient.prototype.remove = function () {
    var that = this;
    this.restProxy.remove({
        url: this.hostName + this.endpoint,
        authorizationOAuth: true,
        data: this.data,
        success: function (xhr, response) {
            if (that.multipart) {
                that.validateMesssage(response);
            }
            if (that.messageSuccess) {
                PMDesigner.msgFlash(
                    that.messageSuccess,
                    that.flashContainer,
                    that.flashSeverity,
                    that.flashDuration
                );
            }
            that.functionSuccess(xhr, response);
        },
        failure: function (xhr, response) {
            that.failureResponse(that, xhr, response);
        },
        complete: function (xhr, response) {
            that.functionComplete(xhr, response);
        }
    });
};
/**
 * Update Method
 */
PMRestClient.prototype.update = function () {
    var that = this;
    this.restProxy.update({
        url: this.hostName + this.endpoint,
        authorizationOAuth: true,
        data: this.data,
        success: function (xhr, response) {
            if (that.multipart) {
                that.validateMesssage(response);
            }
            if (that.messageSuccess) {
                PMDesigner.msgFlash(
                    that.messageSuccess,
                    that.flashContainer,
                    that.flashSeverity,
                    that.flashDuration
                );
            }
            that.functionSuccess(xhr, response);
        },
        failure: function (xhr, response) {
            that.failureResponse(that, xhr, response);
        },
        complete: function (xhr, response) {
            that.functionComplete(xhr, response);
        }
    });
};
/**
 * Failure response manager
 * @param root
 * @param xhr
 * @param response
 */
PMRestClient.prototype.failureResponse = function (root, xhr, response) {
    if (xhr.status === 401 && typeof(response.error) != "undefined") {
        root.refreshAccesToken(root);
        return;
    } else {
        if (xhr.status === 400) {
            root.functionFailure(xhr, response);

            return;
        }

        if (root.messageError) {
            PMDesigner.msgWinError(root.messageError);
        }
    }
    root.functionFailure(xhr, response);
};
/**
 * Gets new token using the refresh token key
 * @param root
 */
PMRestClient.prototype.refreshAccesToken = function (root) {
    var newRestClient = new PMUI.proxy.RestProxy();
    newRestClient.post({
        url: this.hostName + "/api/" + root.apiVersion + "/" + WORKSPACE + "/token",
        data: {
            grant_type: "refresh_token",
            client_id: PMDesigner.project.tokens.client_id,
            client_secret: PMDesigner.project.tokens.client_secret,
            refresh_token: PMDesigner.project.tokens.refresh_token
        },
        success: function (xhr, response) {
            PMDesigner.project.tokens.access_token = response.access_token;
            PMDesigner.project.tokens.expires_in = response.expires_in;
            PMDesigner.project.tokens.token_type = response.token_type;
            PMDesigner.project.tokens.scope = response.scope;
            PMDesigner.project.tokens.refresh_token = response.refresh_token;

            root.executeRestClient();
        },
        failure: function (xhr, response) {
            PMDesigner.msgWinError('An error occurred while retrieving the access token'.translate());
        }
    });
};
var PMCodeMirror = function (settings) {
    PMUI.control.TextAreaControl.call(this, settings);

    this.lineNumbers = null;
    this.matchBrackets = null;
    this.mode = null;
    this.indentUnit = null;
    this.indentWithTabs = null;
    this.enterMode = null;
    this.tabMode = null;
    this.lineWrapping = null;
    this.showCursorWhenSelecting = null;
    this.autofocus = null;
    this.height = null;
    this.dom = {};
    this.cm = null;

    PMCodeMirror.prototype.init.call(this, settings);
};

PMCodeMirror.prototype = new PMUI.control.TextAreaControl();

PMCodeMirror.prototype.type = "PMCodeMirrorControl";

PMCodeMirror.prototype.family = 'PMCodeMirrorControl';

PMCodeMirror.prototype.init = function (settings) {
    var defaults = {
        lineNumbers: true,
        matchBrackets: true,
        mode: "application/x-httpd-php-open",
        indentUnit: 4,
        indentWithTabs: true,
        enterMode: "keep",
        tabMode: "shift",
        lineWrapping: true,
        showCursorWhenSelecting: true,
        autofocus: "on",
        height: 120
    };

    jQuery.extend(true, defaults, settings);

    this.setLineNumbers(defaults.lineNumbers)
        .setMatchBrackets(defaults.matchBrackets)
        .setMode(defaults.mode)
        .setIndentUnit(defaults.indentUnit)
        .setIndentWithTabs(defaults.indentWithTabs)
        .setEnterMode(defaults.enterMode)
        .setTabMode(defaults.tabMode)
        .setLineWrapping(defaults.lineWrapping)
        .setShowCursorWhenSelecting(defaults.showCursorWhenSelecting)
        .setAutofocus(defaults.autofocus)
        .setHeight(defaults.height);
};

PMCodeMirror.prototype.setHeight = function (height) {
    this.height = height;
    return this;
};

PMCodeMirror.prototype.setLineNumbers = function (lineNumbers) {
    this.lineNumbers = lineNumbers;
    return this;
};
PMCodeMirror.prototype.setMatchBrackets = function (matchBrackets) {
    this.matchBrackets = matchBrackets;
    return this;
};
PMCodeMirror.prototype.setMode = function (mode) {
    this.mode = mode;
    return this;
};

PMCodeMirror.prototype.setIndentUnit = function (indentUnit) {
    this.indentUnit = indentUnit;
    return this;
};
PMCodeMirror.prototype.setIndentWithTabs = function (indentWithTabs) {
    this.indentWithTabs = indentWithTabs;
    return this;
};
PMCodeMirror.prototype.setEnterMode = function (enterMode) {
    this.enterMode = enterMode;
    return this;
};
PMCodeMirror.prototype.setTabMode = function (tabMode) {
    this.tabMode = tabMode;
    return this;
};
PMCodeMirror.prototype.setLineWrapping = function (lineWrapping) {
    this.lineWrapping = lineWrapping;
    return this;
};
PMCodeMirror.prototype.setShowCursorWhenSelecting = function (showCursorWhenSelecting) {
    this.showCursorWhenSelecting = showCursorWhenSelecting;
    return this;
};
PMCodeMirror.prototype.setAutofocus = function (autofocus) {
    this.autofocus = autofocus;
    return this;
};


PMCodeMirror.prototype.setParameterCodeMirror = function () {
    if (!this.html) {
        return this;
    }

    this.cm = CodeMirror.fromTextArea(
        this.dom.textArea,
        {
            height: this.height,
            lineNumbers: this.lineNumbers,
            matchBrackets: this.matchBrackets,
            mode: this.mode,
            indentUnit: this.indentUnit,
            indentWithTabs: this.indentWithTabs,
            enterMode: this.enterMode,
            tabMode: this.tabMode,
            lineWrapping: this.lineWrapping,
            showCursorWhenSelecting: this.showCursorWhenSelecting,
            autofocus: this.autofocus,
            extraKeys: {"Ctrl-Space": "autocomplete"}
        }
    );
    return this;
};

PMCodeMirror.prototype.getValueFromRawElement = function () {
    return (this.cm && this.cm.getValue()) || "";
};

PMCodeMirror.prototype.defineEvents = function () {
    var that = this;
    if (!this.eventsDefined) {
        if (this.cm) {
            this.cm.on("change", function () {
                that.onChangeHandler();
            });
        }
    }

    return this;
};

PMCodeMirror.prototype.createHTML = function () {
    var containerCode;

    if (this.html) {
        return this.html;
    }

    PMUI.control.TextAreaControl.prototype.createHTML.call(this);
    containerCode = PMUI.createHTMLElement("div");
    containerCode.appendChild(this.html);
    this.dom.textArea = this.html;
    this.html = containerCode;
    this.applyStyle();
    this.setParameterCodeMirror();
    return this.html;
};

var PMCodeMirrorField = function (settings) {
    PMUI.form.Field.call(this, settings);

    PMCodeMirrorField.prototype.init.call(this, settings);
};

PMCodeMirrorField.prototype = new PMUI.form.Field();

PMCodeMirrorField.prototype.type = "PMCodeMirrorField";

PMCodeMirrorField.prototype.family = 'PMCodeMirrorField';

PMCodeMirrorField.prototype.init = function (settings) {
    var defaults = {
        lineNumbers: true,
        matchBrackets: true,
        mode: "application/x-httpd-php-open",
        indentUnit: 4,
        indentWithTabs: true,
        enterMode: "keep",
        tabMode: "shift",
        lineWrapping: true,
        showCursorWhenSelecting: true,
        autofocus: "on"
    };

    jQuery.extend(true, defaults, settings);

    this.setLineNumbers(defaults.lineNumbers)
        .setMatchBrackets(defaults.matchBrackets)
        .setMode(defaults.mode)
        .setIndentUnit(defaults.indentUnit)
        .setIndentWithTabs(defaults.indentWithTabs)
        .setEnterMode(defaults.enterMode)
        .setTabMode(defaults.tabMode)
        .setLineWrapping(defaults.lineWrapping)
        .setShowCursorWhenSelecting(defaults.showCursorWhenSelecting)
        .setAutofocus(defaults.autofocus);
};

PMCodeMirrorField.prototype.setLineNumbers = function (lineNumbers) {
    this.controls[0].setLineNumbers(lineNumbers);
    return this;
};
PMCodeMirrorField.prototype.setMatchBrackets = function (matchBrackets) {
    this.controls[0].setMatchBrackets(matchBrackets);
    return this;
};
PMCodeMirrorField.prototype.setMode = function (mode) {
    this.controls[0].setMode(mode);
    return this;
};
PMCodeMirrorField.prototype.setIndentUnit = function (indentUnit) {
    this.controls[0].setIndentUnit(indentUnit);
    return this;
};
PMCodeMirrorField.prototype.setIndentWithTabs = function (indentWithTabs) {
    this.controls[0].setIndentWithTabs(indentWithTabs);
    return this;
};
PMCodeMirrorField.prototype.setEnterMode = function (enterMode) {
    this.controls[0].setEnterMode(enterMode);
    return this;
};
PMCodeMirrorField.prototype.setTabMode = function (tabMode) {
    this.controls[0].setTabMode(tabMode);
    return this;
};
PMCodeMirrorField.prototype.setLineWrapping = function (lineWrapping) {
    this.controls[0].setLineWrapping(lineWrapping);
    return this;
};
PMCodeMirrorField.prototype.setShowCursorWhenSelecting = function (showCursorWhenSelecting) {
    this.controls[0].setShowCursorWhenSelecting(showCursorWhenSelecting);
    return this;
};
PMCodeMirrorField.prototype.setAutofocus = function (autofocus) {
    this.controls[0].setAutofocus(autofocus);
    return this;
};

PMCodeMirrorField.prototype.setControls = function () {
    if (this.controls.length) {
        return this;
    }
    this.controls.push(new PMCodeMirror());
    return this;
};

var CriteriaField = function (options) {
    this.renderType = (options && options.renderType) || "text";
    PMUI.field.TextField.call(this, options);
    this.process = null;
    this.workspace = null;
    this.buttonHTML = null;
    this.rows = options.rows;
    this.options = options;
    CriteriaField.prototype.init.call(this, options);
};

CriteriaField.prototype = new PMUI.field.TextField();

CriteriaField.prototype.setProcess = function (process) {
    this.process = process;
    return this;
};

CriteriaField.prototype.setWorkspace = function (workspace) {
    this.workspace = workspace;
    return this;
};

CriteriaField.prototype.init = function (options) {
    var defaults = {
        process: PMDesigner.project.projectId,
        workspace: WORKSPACE
    };
    jQuery.extend(true, defaults, options);
    this.setProcess(defaults.process)
        .setWorkspace(defaults.workspace);
};

CriteriaField.prototype.createVariablePicker = function () {
    var vp = new VariablePicker({
        relatedField: this,
        processId: this.process
    });
    return vp;
};

CriteriaField.prototype.setControls = function () {
    if (this.controls.length) {
        return this;
    }
    if (this.renderType === 'text') {
        this.controls.push(new PMUI.control.TextControl());
    } else {
        this.controls.push(new PMUI.control.TextAreaControl({style: {cssProperties: {resize: 'vertical'}}}));
    }
    return this;
};

/**
 * Update the property disable
 * @param {boolean} value 
 */
CriteriaField.prototype.updateDisabled = function (value) {
    this.setDisabled(value);
    this.buttonHTML.setDisabled(value);
};

CriteriaField.prototype.createCallBack = function () {
    var that = this,
        newValue,
        init = 0,
        index = 0;
    return {
        success: function (variable) {
            var prevText,
                lastText,
                htmlControl = that.controls[index].html;
            init = htmlControl.selectionStart;
            prevText = htmlControl.value.substr(index, init);
            lastText = htmlControl.value.substr(htmlControl.selectionEnd, htmlControl.value.length);
            newValue = prevText + variable + lastText;
            that.setValue(newValue);
            that.isValid();
            htmlControl.selectionEnd = init + variable.length;
        }
    };
};

CriteriaField.prototype.createHTML = function () {
    var button, that = this, variablePicker;
    PMUI.field.TextField.prototype.createHTML.call(this);
    button = new PMUI.ui.Button({
        id: 'buttonCriteriaField',
        text: '@@',
        handler: function () {
            if (that.process != "") {
                variablePicker = that.createVariablePicker();
                variablePicker.open(that.createCallBack());
            } else {
                return;
            }
        },
        style: {
            cssProperties: {
                background: '#1E91D1',
                fontSize: 18,
                padding: '5px',
                borderRadius: '4px',
                verticalAlign: 'top'
            }
        }
    });

    this.buttonHTML = button;
    $(this.helper.html).before(button.getHTML());
    this.buttonHTML.style.addProperties({"margin-left": "10px"});
    this.buttonHTML.html.tabIndex = -1;
    if (typeof this.options.disabled === 'boolean') {
        this.buttonHTML.setDisabled(this.options.disabled);
    }

    if (this.rows != null)
        this.controls[0].setHeight(this.rows);
    button.defineEvents();

    return this.html;
};

// Overwrite original init function for FormItemFactory
PMUI.form.FormItemFactory.prototype.init = function () {
    var defaults = {
        products: {
            "criteria": CriteriaField,
            "field": PMUI.form.Field,
            "panel": PMUI.form.FormPanel,
            "text": PMUI.field.TextField,
            "password": PMUI.field.PasswordField,
            "dropdown": PMUI.field.DropDownListField,
            "radio": PMUI.field.RadioButtonGroupField,
            "checkbox": PMUI.field.CheckBoxGroupField,
            "textarea": PMUI.field.TextAreaField,
            "datetime": PMUI.field.DateTimeField,
            "optionsSelector": PMUI.field.OptionsSelectorField,
            "buttonField": PMUI.field.ButtonField,
            "annotation": PMUI.field.TextAnnotationField
        },
        defaultProduct: "panel"
    };
    this.setProducts(defaults.products)
        .setDefaultProduct(defaults.defaultProduct);
};

var SwitchField = function (options) {
    this.renderType = (options && options.renderType) || "text";
    PMUI.field.CheckBoxGroupField.call(this, options);
    this.process = null;
    this.workspace = null;
    this.rows = options.rows;
    this.options = options;
    SwitchField.prototype.init.call(this, options);
};

SwitchField.prototype = new PMUI.field.CheckBoxGroupField();

SwitchField.prototype.setProcess = function (process) {
    this.process = process;
    return this;
};

SwitchField.prototype.setWorkspace = function (workspace) {
    this.workspace = workspace;
    return this;
};

SwitchField.prototype.init = function (options) {
    var defaults = {
        process: PMDesigner.project.projectId,
        workspace: WORKSPACE
    };
    jQuery.extend(true, defaults, options);
    this.setProcess(defaults.process)
        .setWorkspace(defaults.workspace);
};

SwitchField.prototype.createCallBack = function () {
    var that = this,
        newValue,
        init = 0,
        index = 0;
    return {
        success: function (variable) {
            var prevText,
                lastText,
                htmlControl = that.controls[index].html;
            init = htmlControl.selectionStart;
            prevText = htmlControl.value.substr(index, init);
            lastText = htmlControl.value.substr(htmlControl.selectionEnd, htmlControl.value.length);
            newValue = prevText + variable + lastText;
            that.setValue(newValue);
            that.isValid();
            htmlControl.selectionEnd = init + variable.length;
        }
    };
};

SwitchField.prototype.setPlaceholder = function (placeholder) {}

SwitchField.prototype.setMaxLength = function (placeholder) {}

SwitchField.prototype.setReadOnly = function (placeholder) {}

SwitchField.prototype.createHTML = function () {
    PMUI.field.CheckBoxGroupField.prototype.createHTML.call(this);
    this.setSwitchStyle();
    return this.html;
};

/**
 * Set style type switch to checkbox
 */
SwitchField.prototype.setSwitchStyle = function () {
    var table,
        span,
        label;
    if (this.html) {
        table = this.html.getElementsByTagName("table")[0];
        table.setAttribute('style', 'padding: 0px; border:0px');
        table.setAttribute('class', '');
        span = table.getElementsByTagName("span")[0];
        span.setAttribute('class', 'slider round');
        label = table.getElementsByTagName("label")[0];
        label.setAttribute('class', 'switch');
    }
};

// Overwrite original init function for FormItemFactory
PMUI.form.FormItemFactory.prototype.init = function () {
    var defaults = {
        products: {
            "criteria": CriteriaField,
            "switch": SwitchField,
            "field": PMUI.form.Field,
            "panel": PMUI.form.FormPanel,
            "text": PMUI.field.TextField,
            "password": PMUI.field.PasswordField,
            "dropdown": PMUI.field.DropDownListField,
            "radio": PMUI.field.RadioButtonGroupField,
            "checkbox": PMUI.field.CheckBoxGroupField,
            "textarea": PMUI.field.TextAreaField,
            "datetime": PMUI.field.DateTimeField,
            "optionsSelector": PMUI.field.OptionsSelectorField,
            "buttonField": PMUI.field.ButtonField,
            "annotation": PMUI.field.TextAnnotationField
        },
        defaultProduct: "panel"
    };
    this.setProducts(defaults.products)
        .setDefaultProduct(defaults.defaultProduct);
};

var PMTooltipMessage = function (options) {
    PMUI.ui.Window.call(this, options);
    this.container = null;
    this.message = options.message;
    PMTooltipMessage.prototype.init.call(this, options);
};

PMTooltipMessage.prototype = new PMUI.ui.Window();
PMTooltipMessage.prototype.type = "PMTooltipMessage";


PMTooltipMessage.prototype.createHTML = function () {
    if (this.html) {
        return this.html;
    }
    PMUI.ui.Window.prototype.createHTML.call(this);
    this.closeButton.style.removeAllClasses();
    this.closeButton.setText("x");
    this.closeButton.style.addClasses(['mafe-tooltip-close']);
    this.header.appendChild(this.closeButton.getHTML());
    this.container = PMUI.createHTMLElement('div');
    this.container.innerHTML = this.message;
    this.body.appendChild(this.container);
    return this.html;
};

PMTooltipMessage.prototype.open = function (x, y) {
    PMUI.ui.Window.prototype.open.call(this);
    this.setVisible(false);
    this.setX(x);
    this.setY(y);
    this.header.className = "mafe-tooltip-header";
    this.body.className = "mafe-tooltip-body";
    this.style.addClasses(['mafe-tooltip']);
    this.setTitle("");
    $("#" + this.id).show("drop", "fast");
    this.closeButton.defineEvents();
};

PMTooltipMessage.prototype.setMessage = function (message) {
    this.message = message;
    if (this.html)
        this.container.innerHTML = message;
};
var VariablePicker = function (options) {
    this.relatedField = null;
    this.processId = null;
    this.workspace = null;
    this.window = null;
    this.currentVariable = null;
    this.pageSize = 10;
    VariablePicker.prototype.init.call(this, options);
};

VariablePicker.prototype.type = 'VariablePicker';

VariablePicker.prototype.family = 'VariablePicker';

VariablePicker.prototype.init = function (options) {
    var defaults = {
        relatedField: null,
        processId: PMDesigner.project.projectId,
        workspace: WORKSPACE
    };

    jQuery.extend(true, defaults, options);

    this.setRelatedField(defaults.relatedField)
        .setProcessId(defaults.processId)
        .setWorkspace(defaults.workspace);
};

VariablePicker.prototype.setRelatedField = function (field) {
    if (field instanceof PMUI.form.Field) {
        this.relatedField = field;
    }
    return this;
};

VariablePicker.prototype.setProcessId = function (process) {
    this.processId = process;
    return this;
};

VariablePicker.prototype.setWorkspace = function (workspace) {
    this.workspace = workspace;
    return this;
};

VariablePicker.prototype.getURL = function () {
    var url = '/api/1.0/' + this.workspace + '/project/' + this.processId + '/variables';
    return url;
};

VariablePicker.prototype.open = function (callback) {
    var w, rc, fieldC, dataGrid, panel, textField, that = this;

    button = new PMUI.ui.Button({
        id: 'insertVariable',
        text: 'Insert Variable'.translate(),
        handler: function () {
            if (callback && callback.success && typeof callback.success === 'function') {
                that.currentVariable = fieldC.getValue() + that.currentVariable;
                callback.success.call(that, that.currentVariable);
            }
            that.returnFocus();
            that.close();
        },
        disabled: true
    });

    textField = new PMUI.field.TextField({
        id: 'textFieldSearch',
        label: '',
        placeholder: 'Text to Search'.translate()
    });

    w = new PMUI.ui.Window({
        id: 'processVariables',
        title: 'Process Variables'.translate(),
        width: 480,
        height: 475,
        closable: true,
        modal: true,
        buttons: [
            button
        ],
        buttonsPosition: 'center'
    });

    fieldC = new PMUI.field.DropDownListField({
        id: 'prefixDropDownListField',
        label: 'Prefix'.translate(),
        helper: '@@ string, @# float, @% integer, @= original type, @& object.'.translate(),
        options: [
            {
                id: 'prefixDropDownListField1',
                label: '@@',
                value: '@@'
            },
            {
                id: 'prefixDropDownListField2',
                label: '@#',
                value: '@#'
            },
            {
                id: 'prefixDropDownListField3',
                label: '@%',
                value: '@%'
            },
            {
                id: 'prefixDropDownListField6',
                label: '@=',
                value: '@='
            },
            {
                id: 'prefixDropDownListField7',
                label: '@&',
                value: '@&'
            }
        ],
        onChange: function (newValue, oldValue) {
        }
    });

    textField = new PMUI.field.TextField({
        id: 'textFieldSearch',
        label: '',
        placeholder: 'Text to search'.translate(),
        width: 150
    });

    dataGrid = new PMUI.grid.GridPanel({
        id: 'gridPanel',
        selectable: true,
        pageSize: this.pageSize,
        nextLabel: 'Next'.translate(),
        previousLabel: 'Previous'.translate(),
        tableContainerHeight: 280,
        customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
            return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
        },
        columns: [
            {
                id: 'gridPanelVariable',
                title: 'Variable'.translate(),
                columnData: 'var_name',
                width: 150,
                sortable: true,
                alignmentCell: 'left'
            },
            {
                id: 'gridPanelLabel',
                title: 'Type'.translate(),
                columnData: 'var_label',
                width: 230,
                sortable: false,
                alignmentCell: 'left'
            }
        ],
        onRowClick: function (row, data) {
            button.enable();
            that.currentVariable = data.var_name;
        }

    });

    panelFilter = new PMUI.core.Panel({
        id: 'panelFilter',
        layout: 'hbox',
        items: [fieldC, textField]
    });

    panel = new PMUI.core.Panel({
        id: 'paneldataGrid',
        layout: 'vbox',
        items: [panelFilter, dataGrid]
    });

    rc = new PMRestClient({
        typeRequest: 'get',
        functionSuccess: function (xhr, response) {
            that.window = w;
            dataGrid.setDataItems(response);
            w.open();
            w.showFooter();
            w.addItem(panel);
            panelFilter.setWidth(430);
            fieldC.setControlsWidth(70);
            textField.controls[0].onKeyUp = function () {
                dataGrid.filter(textField.controls[0].html.value);
            };
            dataGrid.dom.toolbar.style.display = 'none';
            $(dataGrid.dom.footer).css("margin-top", "0px");
            $(dataGrid.dom.footer).css("position", "static");
            $(dataGrid.dom.footer).css("padding-left", "10px");
            $(dataGrid.dom.footer).css("padding-right", "10px");

            textField.dom.labelTextContainer.innerHTML = '';
            textField.dom.labelTextContainer.style.marginTop = 5;
            fieldC.dom.labelTextContainer.style.paddingLeft = 20;
            panel.style.addProperties({'padding-left': 20});
            fieldC.dom.labelTextContainer.style.width = 60;
            textField.dom.labelTextContainer.style.display = 'none';
            textField.controls[0].setWidth(200);
            $(dataGrid.html).find(".pmui-gridpanel-footer").css("position", "static");
            $(dataGrid.html).find(".pmui-gridpanel-footer").css("margin-top", "0px");
        }
    });
    rc.setBaseEndPoint('projects/' + this.processId + '/variables').executeRestClient();
};

VariablePicker.prototype.close = function () {
    if (this.window) {
        this.window.close();
        this.window = null;
    }
};

/**
 * Set focus to Input of the Variable Picker
 */
VariablePicker.prototype.returnFocus = function () {
    var that = this;
    if (that.relatedField && that.relatedField.html) {
        jQuery(that.relatedField.html).find(":input").focus();
    }
};

var PMAction = function (options) {

    this.name = null;
    this.action = null;
    this.selector = null;
    this.tooltip = null;
    this.execute = null;
    this.label = null;
    this.before = null;
    this.after = null;
    this.handler = null;
    this.eventsDefined = false;
    PMAction.prototype.init.call(this, options);
};

PMAction.prototype.type = "action";

PMAction.prototype.events = [
    "click", "click"
];

PMAction.prototype.init = function (options) {
    var defaults = {
        action: "click",
        selector: "",
        tooltip: "",
        execute: false,
        label: {
            selector: "",
            text: "",
            value: ""
        },
        before: function (event) {
            event.preventDefault();
            PMUI.removeCurrentMenu();
        },
        after: function (event) {
            event.stopPropagation();
        },
        handler: function (event) {
        }
    };
    jQuery.extend(true, defaults, options);

    this.setAction(defaults.action)
        .setSelector(defaults.selector)
        .setExecute(defaults.execute)
        .setLabel(defaults.label)
        .setBefore(defaults.before)
        .setAfter(defaults.after)
        .setHandler(defaults.handler)
        .setText(defaults.label.text)
        .setValue(defaults.label.value)
        .setTooltip(defaults.tooltip)
        .addEventListener();

};
PMAction.prototype.setAction = function (action) {
    this.action = action;
    return this;
};
PMAction.prototype.setSelector = function (selector) {
    this.selector = selector;
    return this;
};
PMAction.prototype.setExecute = function (option) {
    this.execute = option;
    return this;
};
PMAction.prototype.setLabel = function (label) {
    this.label = label;
    if (!label.selector) {
        this.label.selector = this.selector;
    }
    return this;
};
PMAction.prototype.setBefore = function (action) {
    this.before = action;
    return this;
};
PMAction.prototype.setAfter = function (action) {
    this.after = action;
    return this;
};
PMAction.prototype.setHandler = function (action) {
    this.handler = action;
    return this;
};
PMAction.prototype.setText = function (text) {
    if (typeof text === "string" && text.length > 0) {
        this.label.text = text;
        jQuery(this.label.selector).text(this.label.text);
    }
    return this;
};
PMAction.prototype.setValue = function (value) {
    if (typeof value === "string" && value.length > 0) {
        this.label.value = value;
        jQuery(this.label.selector).val(this.label.value);
    }
    return this;
};
PMAction.prototype.setTooltip = function (message) {
    var that = this;
    if (typeof message === "string") {
        this.tooltip = message;
        jQuery(this.selector).attr("title", "");
        jQuery(this.selector).tooltip({ content: that.tooltip, tooltipClass: "mafe-action-tooltip" });
    }
    return this;
};
PMAction.prototype.addEventListener = function () {
    var that = this;
    if (this.execute === true && this.eventsDefined === false) {

        jQuery(this.selector).on(this.action, function (event) {
            try {
                that.before(event);
            } catch (e) {
                throw new Error('Before action '.translate() + e.message);
            }
            try {
                that.handler(event);
            } catch (e) {
                throw new Error('Handler '.translate() + e.message);
            }
            try {
                that.after(event);
            } catch (e) {
                throw new Error('After action '.translate() + e.message);
            }
        });
        if (this.label.text !== "") {
            jQuery(this.label.selector).text(this.label.text);
        }

        this.eventsDefined = true;
    }
    return this;
};
PMAction.prototype.defineEvents = function () {
    this.setExecute(true);
    this.addEventListener();
};

var PMSeparatorLine = function (settings) {
    PMUI.control.HTMLControl.call(this, settings);
    this.width = null;
    this.height = null;
    this.color = null;
    this.marginLeft = null;
    PMSeparatorLine.prototype.init.call(this, settings);
};

PMSeparatorLine.prototype = new PMUI.control.HTMLControl();

PMSeparatorLine.prototype.type = "PMSeparatorLine";

PMSeparatorLine.prototype.family = 'PMSeparatorLine';

PMSeparatorLine.prototype.init = function (settings) {
    var defaults = {
        width: "90%",
        height: '3px',
        color: "#C0C0C0",
        marginLeft: "0%"
    };
    jQuery.extend(true, defaults, settings);
    this.setWidth(defaults.width);
    this.setColor(defaults.color);
    this.setHeight(defaults.height);
    this.setMarginLeft(defaults.marginLeft);
};

PMSeparatorLine.prototype.setHeight = function (height) {
    this.height = height;
    if (this.html) {
        this.html.style.height = this.height;
    }
    return this;
};

PMSeparatorLine.prototype.setWidth = function (width) {
    this.width = width;
    if (this.html) {
        this.html.style.width = this.width;
    }
    return this;
};
PMSeparatorLine.prototype.setColor = function (color) {
    this.color = color;
    if (this.html) {
        this.html.style.background = this.color;
    }
    return this;
};
PMSeparatorLine.prototype.setMarginLeft = function (marginLeft) {
    this.marginLeft = marginLeft;
    if (this.html) {
        this.html.style.marginLeft = this.marginLeft;
    }
    return this;
};

PMSeparatorLine.prototype.createHTML = function () {
    var input;
    if (this.html) {
        return this.html;
    }
    input = PMUI.createHTMLElement("hr");
    input.className = "PMSeparatorLine";
    input.id = this.id;
    input.name = "PMSeparatorLine";
    this.html = input;
    this.setColor(this.color);
    this.setMarginLeft(this.marginLeft);
    this.applyStyle();
    return this.html;
};
var PMSeparatorLineField = function (settings) {
    PMUI.form.Field.call(this, settings);
    PMSeparatorLineField.prototype.init.call(this, settings);
};

PMSeparatorLineField.prototype = new PMUI.form.Field();

PMSeparatorLineField.prototype.type = "PMSeparatorLineField";

PMSeparatorLineField.prototype.family = 'PMSeparatorLineField';

PMSeparatorLineField.prototype.init = function (settings) {
    var defaults = {
        controlHeight: 1,
        controlColor: "#CDCDCD",
        widthControl: "90%",
        marginLeft: "5%"
    };
    jQuery.extend(true, defaults, settings);
    this.setControlHeight(defaults.controlHeight)
        .setControlColor(defaults.controlColor)
        .setMarginLeft(defaults.marginLeft);
};

PMSeparatorLineField.prototype.setControlHeight = function (size) {
    this.controls[0].setHeight(size);
    return this;
};
PMSeparatorLineField.prototype.setControlColor = function (color) {
    this.controls[0].setColor(color);
    return this;
};
PMSeparatorLineField.prototype.setMarginLeft = function (marginLeft) {
    this.controls[0].setMarginLeft(marginLeft);
    return this;
};
PMSeparatorLineField.prototype.setControls = function () {
    if (this.controls.length) {
        return this;
    }
    this.controls.push(new PMSeparatorLine());

    return this;
};

PMSeparatorLineField.prototype.getValue = function () {
    return null;
};

PMSeparatorLineField.prototype.createHTML = function () {

    PMUI.form.Field.prototype.createHTML.call(this);

    this.hideLabel();
    this.setLabelWidth('0%');
    return this.html;
};
var PMLabelField = function (settings) {
    PMUI.form.Field.call(this, settings);

    PMLabelField.prototype.init.call(this, settings);
};

PMLabelField.prototype = new PMUI.form.Field();

PMLabelField.prototype.type = "PMLabelField";

PMLabelField.prototype.family = 'PMLabelField';

PMLabelField.prototype.init = function (settings) {
    var defaults = {
        text: "Mafe Label0",
        textMode: "plain"
    };

    jQuery.extend(true, defaults, settings);

    this.setText(defaults.text);
    this.setTextMode(defaults.textMode);
};

PMLabelField.prototype.setText = function (text) {
    this.controls[0].setText(text);
    return this;
};
PMLabelField.prototype.setTextMode = function (textMode) {
    this.controls[0].setTextMode(textMode);
    return this;
};

PMLabelField.prototype.setControls = function () {
    if (this.controls.length) {
        return this;
    }
    this.controls.push(new PMLabelControl());

    return this;
};

PMLabelField.prototype.getValue = function () {
    return null;
};

PMLabelField.prototype.createHTML = function () {

    PMUI.form.Field.prototype.createHTML.call(this);
    this.hideLabel();
    this.setLabelWidth('0%');
    return this.html;
};
   

var PMLabelControl = function (settings) {
    PMUI.control.Control.call(this, settings);
    this.label = null;
    PMLabelControl.prototype.init.call(this, settings);
};

PMLabelControl.prototype = new PMUI.control.Control();

PMLabelControl.prototype.type = "PMLabelControl";

PMLabelControl.prototype.family = 'PMLabelControl';

PMLabelControl.prototype.init = function (settings) {
    this.label = new PMUI.ui.TextLabel();
};

PMLabelControl.prototype.setText = function (text) {
    if (this.label) {
        this.label.setText(text);
    }
    return this;
};

PMLabelControl.prototype.setTextMode = function (textMode) {
    if (this.label) {
        this.label.setTextMode(textMode);
    }
    return this;
};

PMLabelControl.prototype.createHTML = function () {
    PMUI.control.Control.prototype.createHTML.call(this);
    this.html = this.label.getHTML();
    this.setPositionMode('relative');
    return this.html;
};

/*global jCore*/
var PMArtifactResizeBehavior = function () {
};

PMArtifactResizeBehavior.prototype = new PMUI.behavior.RegularResizeBehavior();
PMArtifactResizeBehavior.prototype.type = "PMArtifactResizeBehavior";

/**
 * Sets a shape's container to a given container
 * @param container
 * @param shape
 */
PMArtifactResizeBehavior.prototype.onResizeStart = function (shape) {
    return PMUI.behavior.RegularResizeBehavior
        .prototype.onResizeStart.call(this, shape);
};
/**
 * Removes shape from its current container
 * @param shape
 */
PMArtifactResizeBehavior.prototype.onResize = function (shape) {
    return function (e, ui) {
        PMUI.behavior.RegularResizeBehavior
            .prototype.onResize.call(this, shape)(e, ui);
        if (shape.graphics) {
            shape.paint();
        }
    };
};

var ConnectValidator = function () {

};
ConnectValidator.prototype.type = "ConnectValidator";

ConnectValidator.prototype.initRules = {
    'START': {
        'START': 'sequencialRules',
        'INTERMEDIATE': 'sequencialRules',
        'INTERMEDIATEEMAIL': 'sequencialRules',
        'INTERMEDIATEMESSAGETHROW': 'sequencialRules',
        'INTERMEDIATEMESSAGECATCH': 'sequencialRules',
        'TASK': 'sequencialRules',
        'SUB_PROCESS': 'sequencialRules',
        'GATEWAY': 'sequencialRules',
        'END': 'sequencialRules',
        'ENDEMAIL': 'sequencialRules',
        'ENDMESSAGETHROW': 'sequencialRules',
        'DATA': 'associationRules',
        'PARTICIPANT': 'messageRules',
        'TEXT_ANNOTATION': 'annotationRules'
    },
    'STARTMESSAGECATCH': {
        'START': 'sequencialRules',
        'INTERMEDIATE': 'sequencialRules',
        'INTERMEDIATEEMAIL': 'sequencialRules',
        'INTERMEDIATEMESSAGETHROW': 'sequencialRules',
        'INTERMEDIATEMESSAGECATCH': 'sequencialRules',
        'TASK': 'sequencialRules',
        'SUB_PROCESS': 'sequencialRules',
        'GATEWAY': 'sequencialRules',
        'END': 'sequencialRules',
        'ENDEMAIL': 'sequencialRules',
        'ENDMESSAGETHROW': 'sequencialRules',
        'DATA': 'associationRules',
        'PARTICIPANT': 'messageRules',
        'TEXT_ANNOTATION': 'annotationRules'
    },
    'INTERMEDIATE': {
        'START': 'sequencialRules',
        'INTERMEDIATE': 'sequencialRules',
        'INTERMEDIATEEMAIL': 'sequencialRules',
        'INTERMEDIATEMESSAGETHROW': 'sequencialRules',
        'INTERMEDIATEMESSAGECATCH': 'sequencialRules',
        'TASK': 'sequencialRules',
        'SUB_PROCESS': 'sequencialRules',
        'GATEWAY': 'sequencialRules',
        'END': 'sequencialRules',
        'ENDEMAIL': 'sequencialRules',
        'ENDMESSAGETHROW': 'sequencialRules',
        'DATA': 'associationRules',
        'PARTICIPANT': 'messageRules',
        'TEXT_ANNOTATION': 'annotationRules'
    },
    'INTERMEDIATEEMAIL': {
        'START': 'sequencialRules',
        'INTERMEDIATE': 'sequencialRules',
        'INTERMEDIATEEMAIL': 'sequencialRules',
        'INTERMEDIATEMESSAGETHROW': 'sequencialRules',
        'TASK': 'sequencialRules',
        'SUB_PROCESS': 'sequencialRules',
        'GATEWAY': 'sequencialRules',
        'END': 'sequencialRules',
        'ENDEMAIL': 'sequencialRules',
        'ENDMESSAGETHROW': 'sequencialRules',
        'DATA': 'associationRules',
        'PARTICIPANT': 'messageRules',
        'TEXT_ANNOTATION': 'annotationRules'
    },
    'INTERMEDIATEMESSAGETHROW': {
        'START': 'sequencialRules',
        'INTERMEDIATE': 'sequencialRules',
        'INTERMEDIATEEMAIL': 'sequencialRules',
        'INTERMEDIATEMESSAGETHROW': 'sequencialRules',
        'INTERMEDIATEMESSAGECATCH': 'sequencialRules',
        'TASK': 'sequencialRules',
        'SUB_PROCESS': 'sequencialRules',
        'GATEWAY': 'sequencialRules',
        'END': 'sequencialRules',
        'ENDEMAIL': 'sequencialRules',
        'ENDMESSAGETHROW': 'sequencialRules',
        'DATA': 'associationRules',
        'PARTICIPANT': 'messageRules',
        'TEXT_ANNOTATION': 'annotationRules'
    },
    'INTERMEDIATEMESSAGECATCH': {
        'START': 'sequencialRules',
        'INTERMEDIATE': 'sequencialRules',
        'INTERMEDIATEEMAIL': 'sequencialRules',
        'INTERMEDIATEMESSAGETHROW': 'sequencialRules',
        'INTERMEDIATEMESSAGECATCH': 'sequencialRules',
        'TASK': 'sequencialRules',
        'SUB_PROCESS': 'sequencialRules',
        'GATEWAY': 'sequencialRules',
        'END': 'sequencialRules',
        'ENDEMAIL': 'sequencialRules',
        'ENDMESSAGETHROW': 'sequencialRules',
        'DATA': 'associationRules',
        'PARTICIPANT': 'messageRules',
        'TEXT_ANNOTATION': 'annotationRules'
    },
    'END': {
        'START': 'sequencialRules',
        'INTERMEDIATE': 'sequencialRules',
        'INTERMEDIATEEMAIL': 'sequencialRules',
        'INTERMEDIATEMESSAGETHROW': 'sequencialRules',
        'INTERMEDIATEMESSAGECATCH': 'sequencialRules',
        'TASK': 'sequencialRules',
        'SUB_PROCESS': 'sequencialRules',
        'GATEWAY': 'sequencialRules',
        'END': 'sequencialRules',
        'ENDEMAIL': 'sequencialRules',
        'ENDMESSAGETHROW': 'sequencialRules',
        'DATA': 'associationRules',
        'PARTICIPANT': 'messageRules',
        'TEXT_ANNOTATION': 'annotationRules'
    },
    'ENDEMAIL': {
        'START': 'sequencialRules',
        'INTERMEDIATE': 'sequencialRules',
        'INTERMEDIATEEMAIL': 'sequencialRules',
        'INTERMEDIATEMESSAGETHROW': 'sequencialRules',
        'INTERMEDIATEMESSAGECATCH': 'sequencialRules',
        'TASK': 'sequencialRules',
        'SUB_PROCESS': 'sequencialRules',
        'GATEWAY': 'sequencialRules',
        'END': 'sequencialRules',
        'ENDEMAIL': 'sequencialRules',
        'ENDMESSAGETHROW': 'sequencialRules',
        'PARTICIPANT': 'messageRules',
        'TEXT_ANNOTATION': 'annotationRules'
    },
    'ENDMESSAGETHROW': {
        'START': 'sequencialRules',
        'INTERMEDIATE': 'sequencialRules',
        'INTERMEDIATEEMAIL': 'sequencialRules',
        'INTERMEDIATEMESSAGETHROW': 'sequencialRules',
        'INTERMEDIATEMESSAGECATCH': 'sequencialRules',
        'TASK': 'sequencialRules',
        'SUB_PROCESS': 'sequencialRules',
        'GATEWAY': 'sequencialRules',
        'END': 'sequencialRules',
        'ENDEMAIL': 'sequencialRules',
        'ENDMESSAGETHROW': 'sequencialRules',
        'PARTICIPANT': 'messageRules',
        'TEXT_ANNOTATION': 'annotationRules'

    },
    'TASK': {
        'START': 'sequencialRules',
        'INTERMEDIATE': 'sequencialRules',
        'INTERMEDIATEEMAIL': 'sequencialRules',
        'INTERMEDIATEMESSAGETHROW': 'sequencialRules',
        'INTERMEDIATEMESSAGECATCH': 'sequencialRules',
        'TASK': 'sequencialRules',
        'SUB_PROCESS': 'sequencialRules',
        'GATEWAY': 'sequencialRules',
        'END': 'sequencialRules',
        'ENDEMAIL': 'sequencialRules',
        'ENDMESSAGETHROW': 'sequencialRules',
        'DATA': 'associationRules',
        'PARTICIPANT': 'messageRules',
        'TEXT_ANNOTATION': 'annotationRules'
    },
    'SUB_PROCESS': {
        'START': 'sequencialRules',
        'INTERMEDIATE': 'sequencialRules',
        'INTERMEDIATEEMAIL': 'sequencialRules',
        'INTERMEDIATEMESSAGETHROW': 'sequencialRules',
        'INTERMEDIATEMESSAGECATCH': 'sequencialRules',
        'TASK': 'sequencialRules',
        'SUB_PROCESS': 'sequencialRules',
        'GATEWAY': 'sequencialRules',
        'END': 'sequencialRules',
        'ENDEMAIL': 'sequencialRules',
        'ENDMESSAGETHROW': 'sequencialRules',
        'DATA': 'associationRules',
        'PARTICIPANT': 'messageRules',
        'TEXT_ANNOTATION': 'annotationRules'
    },
    'GATEWAY': {
        'START': 'sequencialRules',
        'INTERMEDIATE': 'sequencialRules',
        'INTERMEDIATEEMAIL': 'sequencialRules',
        'INTERMEDIATEMESSAGETHROW': 'sequencialRules',
        'INTERMEDIATEMESSAGECATCH': 'sequencialRules',
        'TASK': 'sequencialRules',
        'SUB_PROCESS': 'sequencialRules',
        'GATEWAY': 'sequencialRules',
        'END': 'sequencialRules',
        'ENDEMAIL': 'sequencialRules',
        'ENDMESSAGETHROW': 'sequencialRules',
        'DATA': 'associationRules',
        'PARTICIPANT': 'messageRules',
        'TEXT_ANNOTATION': 'annotationRules'
    },
    'DATA': {
        'START': 'associationRules',
        'INTERMEDIATE': 'associationRules',
        'INTERMEDIATEEMAIL': 'associationRules',
        'INTERMEDIATEMESSAGETHROW': 'associationRules',
        'INTERMEDIATEMESSAGECATCH': 'associationRules',
        'TASK': 'associationRules',
        'SUB_PROCESS': 'associationRules',
        'GATEWAY': 'associationRules',
        'END': 'associationRules',
        'DATA': 'associationRules',
        'PARTICIPANT': 'messageRules',
        'TEXT_ANNOTATION': 'annotationRules'
    },
    'PARTICIPANT': {
        'START': 'messageRules',
        'STARTMESSAGECATCH': 'messageRules',
        'TASK': 'messageRules',
        'SUB_PROCESS': 'messageRules',
        'GATEWAY': 'messageRules',
        'END': 'messageRules',
        'ENDEMAIL': 'messageRules',
        'ENDMESSAGETHROW': 'messageRules',
        'DATA': 'messageRules',
        'PARTICIPANT': 'messageRules',
        'TEXT_ANNOTATION': 'annotationRules',
        'INTERMEDIATEMESSAGECATCH': 'messageRules'
    },
    'TEXT_ANNOTATION': {
        'START': 'annotationRules',
        'INTERMEDIATE': 'annotationRules',
        'INTERMEDIATEEMAIL': 'annotationRules',
        'INTERMEDIATEMESSAGETHROW': 'annotationRules',
        'INTERMEDIATEMESSAGECATCH': 'annotationRules',
        'TASK': 'annotationRules',
        'SUB_PROCESS': 'annotationRules',
        'GATEWAY': 'annotationRules',
        'END': 'annotationRules',
        'ENDEMAIL': 'annotationRules',
        'ENDMESSAGETHROW': 'annotationRules',
        'DATA': 'annotationRules',
        'PARTICIPANT': 'annotationRules',
        'TEXT_ANNOTATION': 'annotationRules'
    }
};

ConnectValidator.prototype.sequencialRules = {
    'START': {
        'START': false,
        'INTERMEDIATE': true,
        'INTERMEDIATEEMAIL': true,
        'INTERMEDIATEMESSAGETHROW': true,
        'INTERMEDIATEMESSAGECATCH': true,
        'TASK': true,
        'SUB_PROCESS': true,
        'GATEWAY': false,
        'END': false
    },
    'STARTMESSAGECATCH': {
        'START': false,
        'INTERMEDIATE': true,
        'INTERMEDIATEEMAIL': true,
        'INTERMEDIATEMESSAGETHROW': true,
        'INTERMEDIATEMESSAGECATCH': true,
        'TASK': true,
        'SUB_PROCESS': true,
        'GATEWAY': false,
        'END': false
    },
    'INTERMEDIATE': {
        'START': false,
        'INTERMEDIATE': true,
        'INTERMEDIATEEMAIL': true,
        'INTERMEDIATEMESSAGETHROW': true,
        'INTERMEDIATEMESSAGECATCH': true,
        'TASK': true,
        'SUB_PROCESS': true,
        'GATEWAY': true,
        'END': true,
        'ENDEMAIL': true,
        'ENDMESSAGETHROW': true
    },
    'INTERMEDIATEEMAIL': {
        'START': false,
        'INTERMEDIATE': true,
        'INTERMEDIATEEMAIL': true,
        'INTERMEDIATEMESSAGETHROW': true,
        'INTERMEDIATEMESSAGECATCH': true,
        'TASK': true,
        'SUB_PROCESS': true,
        'GATEWAY': true,
        'END': true,
        'ENDEMAIL': true,
        'ENDMESSAGETHROW': true
    },
    'INTERMEDIATEMESSAGETHROW': {
        'START': false,
        'INTERMEDIATE': true,
        'INTERMEDIATEEMAIL': true,
        'INTERMEDIATEMESSAGETHROW': true,
        'INTERMEDIATEMESSAGECATCH': true,
        'TASK': true,
        'SUB_PROCESS': true,
        'GATEWAY': true,
        'END': true,
        'ENDEMAIL': true,
        'ENDMESSAGETHROW': true
    },
    'INTERMEDIATEMESSAGECATCH': {
        'START': false,
        'INTERMEDIATE': true,
        'INTERMEDIATEEMAIL': true,
        'INTERMEDIATEMESSAGETHROW': true,
        'INTERMEDIATEMESSAGECATCH': true,
        'TASK': true,
        'SUB_PROCESS': true,
        'GATEWAY': true,
        'END': true,
        'ENDEMAIL': true,
        'ENDMESSAGETHROW': true
    },
    'END': {
        'START': false,
        'TASK': false,
        'SUB_PROCESS': false,
        'GATEWAY': false,
        'END': false,
        'INTERMEDIATE': false,
        'INTERMEDIATEEMAIL': false,
        'INTERMEDIATEMESSAGETHROW': false,
        'INTERMEDIATEMESSAGECATCH': false
    },
    'ENDEMAIL': {
        'START': false,
        'TASK': false,
        'SUB_PROCESS': false,
        'GATEWAY': false,
        'END': false,
        'INTERMEDIATE': false,
        'INTERMEDIATEEMAIL': false,
        'INTERMEDIATEMESSAGETHROW': false,
        'INTERMEDIATEMESSAGECATCH': false
    },
    'ENDMESSAGETHROW': {
        'START': false,
        'TASK': false,
        'SUB_PROCESS': false,
        'GATEWAY': false,
        'END': false,
        'INTERMEDIATE': false,
        'INTERMEDIATEEMAIL': false,
        'INTERMEDIATEMESSAGETHROW': false,
        'INTERMEDIATEMESSAGECATCH': false
    },
    'TASK': {
        'START': false,
        'TASK': true,
        'SUB_PROCESS': true,
        'GATEWAY': true,
        'END': true,
        'ENDEMAIL': true,
        'ENDMESSAGETHROW': true,
        'INTERMEDIATE': true,
        'INTERMEDIATEEMAIL': true,
        'INTERMEDIATEMESSAGETHROW': true,
        'INTERMEDIATEMESSAGECATCH': true
    },
    'SUB_PROCESS': {
        'START': false,
        'TASK': true,
        'SUB_PROCESS': true,
        'GATEWAY': true,
        'END': true,
        'ENDEMAIL': true,
        'ENDMESSAGETHROW': true,
        'INTERMEDIATE': true,
        'INTERMEDIATEEMAIL': true,
        'INTERMEDIATEMESSAGETHROW': true,
        'INTERMEDIATEMESSAGECATCH': true
    },
    'GATEWAY': {
        'START': false,
        'TASK': true,
        'SUB_PROCESS': true,
        'GATEWAY': true,
        'END': true,
        'ENDEMAIL': true,
        'ENDMESSAGETHROW': true,
        'INTERMEDIATE': true,
        'INTERMEDIATEEMAIL': true,
        'INTERMEDIATEMESSAGETHROW': true,
        'INTERMEDIATEMESSAGECATCH': true
    },
    'DATAOBJECT': {
        'TASK': true,
        'INTERMEDIATE': true,
        'INTERMEDIATEEMAIL': true,
        'INTERMEDIATEMESSAGETHROW': true,
        'INTERMEDIATEMESSAGECATCH': true
    }
};

ConnectValidator.prototype.messageRules = {
    'PARTICIPANT': {
        'PARTICIPANT': true,
        'TASK': true,
        'SUBPROCESS': true,
        'START': false,
        'STARTMESSAGECATCH': true,
        'END': false,
        'INTERMEDIATE': true,
        'INTERMEDIATEMESSAGECATCH': true,
        'GATEWAY': false
    },
    'START': {
        'PARTICIPANT': false
    },
    'STARTMESSAGECATCH': {
        'PARTICIPANT': false
    },
    'INTERMEDIATE': {
        'PARTICIPANT': false
    },
    'INTERMEDIATEMESSAGETHROW': {
        'PARTICIPANT': true
    },
    'INTERMEDIATEMESSAGECATCH': {
        'PARTICIPANT': false
    },
    'INTERMEDIATEEMAIL': {
        'PARTICIPANT': true
    },
    'TASK': {
        'PARTICIPANT': true
    },
    'SUB_PROCESS': {
        'PARTICIPANT': true
    },
    'END': {
        'PARTICIPANT': false
    },
    'GATEWAY': {
        'PARTICIPANT': false
    },
    'DATA': {
        'PARTICIPANT': false
    },
    'ENDMESSAGETHROW': {
        'PARTICIPANT': true
    },
    'ENDEMAIL': {
        'PARTICIPANT': true
    }

};

ConnectValidator.prototype.associationRules = {
    'PARTICIPANT': {
        'TASK': false,
        'START': false,
        'INTERMEDIATE': false,
        'SUB_PROCESS': false,
        'GATEWAY': false,
        'DATA': false,
        'END': false,
        'PARTICIPANT': false
    },
    'START': {
        'TASK': false,
        'START': false,
        'INTERMEDIATE': false,
        'SUB_PROCESS': false,
        'GATEWAY': false,
        'DATA': false,
        'END': false,
        'PARTICIPANT': false
    },
    'INTERMEDIATE': {
        'TASK': false,
        'START': false,
        'INTERMEDIATE': false,
        'SUB_PROCESS': false,
        'GATEWAY': false,
        'DATA': false,
        'END': false,
        'PARTICIPANT': false
    },
    'END': {
        'TASK': false,
        'START': false,
        'INTERMEDIATE': false,
        'SUB_PROCESS': false,
        'GATEWAY': false,
        'DATA': false,
        'END': false,
        'PARTICIPANT': false
    },
    'TASK': {
        'TASK': false,
        'START': false,
        'INTERMEDIATE': false,
        'SUB_PROCESS': false,
        'GATEWAY': false,
        'DATA': true,
        'END': false,
        'PARTICIPANT': false
    },
    'SUB_PROCESS': {
        'TASK': false,
        'START': false,
        'INTERMEDIATE': false,
        'SUB_PROCESS': false,
        'GATEWAY': false,
        'DATA': true,
        'END': false,
        'PARTICIPANT': false
    },
    'GATEWAY': {
        'TASK': false,
        'START': false,
        'INTERMEDIATE': false,
        'SUB_PROCESS': false,
        'GATEWAY': false,
        'DATAOBJECT': false,
        'END': false
    },
    'DATA': {
        'TASK': true,
        'START': false,
        'INTERMEDIATE': false,
        'SUB_PROCESS': true,
        'GATEWAY': false,
        'DATA': false,
        'END': false,
        'PARTICIPANT': false
    }
};

ConnectValidator.prototype.proMessageRules = {
    'TASK': {
        'START': false,
        'INTERMEDIATE': false,
        'TASK': true,
        'SUB_PROCESS': true,
        'GATEWAY': false,
        'END': false
    }
};

ConnectValidator.prototype.isValid = function (sourceShape, targetShape, connection) {
    var result = {
            result: false,
            msg: 'Invalid Connections'.translate()
        },
        connectionConf,
        rules,
        i,
        parentSource;
    //type of shapes
    if (this.getTypeToValidate(sourceShape) && this.getTypeToValidate(targetShape)) {
        result.msg = 'Invalid Connection';
        //validate if there is an current connection between same elements
        if ((!connection || (connection.getSrcPort().getParent() !== sourceShape || connection.getDestPort() !== targetShape)) && PMFlow.existsConnection(sourceShape, targetShape, false)) {
            return {
                result: false,
                msg: 'There is already a connection between these elements'.translate()
            };
        }
        //validate loop connections
        if (sourceShape.getID() === targetShape.getID()) {
            return result;
        }

        if (sourceShape.participantObject) {
            parentSource = sourceShape.participantObject.elem.$parent.id;
        } else {
            if (sourceShape.businessObject.elem.$type !== "bpmn:BoundaryEvent") {
                parentSource = sourceShape.businessObject.elem.$parent.id;
            } else {
                parentSource = sourceShape.businessObject.elem.attachedToRef.$parent.id;
            }
        }
        //verify if elenents are into the same process
        if (sourceShape.businessObject.elem
            && targetShape.businessObject.elem
            && targetShape.businessObject.elem.$parent
            && parentSource !== targetShape.businessObject.elem.$parent.id) {

            switch (sourceShape.type) {
                case 'PMActivity':
                    result.conf = {
                        type: 'MESSAGE',
                        segmentStyle: 'segmented',
                        destDecorator: 'mafe-message',
                        srcDecorator: 'mafe-message'
                    };
                    if (targetShape.type === 'PMActivity') {
                        result.result = true;
                        result.invalid = true;
                    } else if (targetShape.type === 'PMEvent' && targetShape.evn_marker === 'MESSAGECATCH') {
                        result.result = true;
                    } else if (targetShape.type === 'PMData') {
                        result.result = true;
                        result.conf = {
                            type: 'DATAASSOCIATION',
                            segmentStyle: 'dotted',
                            destDecorator: 'mafe-association'
                        };
                    } else if (targetShape.type === 'PMArtifact') {
                        result.result = true;
                        result.conf = {
                            type: 'ASSOCIATION',
                            segmentStyle: 'dotted',
                            destDecorator: 'mafe-default'
                        };
                    }
                    break;
                case 'PMEvent':
                    result.conf = {
                        type: 'MESSAGE',
                        segmentStyle: 'segmented',
                        destDecorator: 'mafe-message',
                        srcDecorator: 'mafe-message'
                    };
                    if (sourceShape.type === 'PMEvent'
                        && (sourceShape.evn_marker === 'MESSAGETHROW')) {
                        if (targetShape.type === 'PMActivity') {
                            result.result = true;
                        } else if (targetShape.type === 'PMEvent' && targetShape.evn_marker === 'MESSAGECATCH') {
                            result.result = true;
                        }
                    } else if (targetShape.type === 'PMArtifact') {
                        result.result = true;
                        result.conf = {
                            type: 'ASSOCIATION',
                            segmentStyle: 'dotted',
                            destDecorator: 'mafe-default'
                        };
                    }
                    break;
                case 'PMGateway':
                    if (targetShape.type === 'PMArtifact') {
                        result.result = true;
                        result.conf = {
                            type: 'ASSOCIATION',
                            segmentStyle: 'dotted',
                            destDecorator: 'mafe-default'
                        };
                    }
                    break;
                case 'PMData':
                    if (targetShape.type === 'PMActivity') {
                        result.result = true;
                        result.conf = {
                            type: 'DATAASSOCIATION',
                            segmentStyle: 'dotted',
                            destDecorator: 'mafe-association'
                        };
                    } else if (targetShape.type === 'PMArtifact') {
                        result.result = true;
                        result.conf = {
                            type: 'ASSOCIATION',
                            segmentStyle: 'dotted',
                            destDecorator: 'mafe-default'
                        };
                    }
                    break;
                case 'PMArtifact':
                    result.result = true;
                    result.conf = {
                        type: 'ASSOCIATION',
                        segmentStyle: 'dotted',
                        destDecorator: 'mafe-default'
                    };
                    break;
            }
            if (result.result) {
                // validate that the sourceShape allow the outgoing connection
                if ((!connection || (connection.getSrcPort().getParent() !== sourceShape)) && !this.canAcceptOutgoingConnection(sourceShape, result.conf.type)) {
                    return {
                        result: false,
                        msg: 'The source shape can not have more than one outgoing connection'.translate()
                    };
                }
            }
            if (result.result) {
                result.result = true;
                targetShape.setConnectionType(result.conf);
            }
        } else {
            if (this.initRules[this.getTypeToValidate(sourceShape)][this.getTypeToValidate(targetShape)]) {
                rules = this.initRules[this.getTypeToValidate(sourceShape)][this.getTypeToValidate(targetShape)];
                switch (rules) {
                    case 'sequencialRules':
                        if (this.sequencialRules[this.getTypeToValidate(sourceShape)][this.getTypeToValidate(targetShape)]) {
                            result.result = this.sequencialRules[this.getTypeToValidate(sourceShape)][this.getTypeToValidate(targetShape)];
                        }
                        connectionConf = {
                            type: 'SEQUENCE',
                            segmentStyle: 'regular',
                            destDecorator: 'mafe-sequence'
                        };
                        break;
                    case 'messageRules':
                        if (this.messageRules[this.getTypeToValidate(sourceShape)][this.getTypeToValidate(targetShape)]) {
                            result.result = this.messageRules[this.getTypeToValidate(sourceShape)][this.getTypeToValidate(targetShape)];
                            if (result.result) {
                                if (sourceShape.extendedType === 'START' && sourceShape.evn_marker === "MESSAGE") {
                                    result.result = false;
                                    result.msg = 'Start Event must not have any outgoing Message Flows'.translate();
                                }
                            }
                        }
                        connectionConf = {
                            type: 'MESSAGE',
                            segmentStyle: 'segmented',
                            destDecorator: 'mafe-message',
                            srcDecorator: 'mafe-message'
                        };
                        break;
                    case 'annotationRules':
                        result.result = true;
                        connectionConf = {
                            type: 'ASSOCIATION',
                            segmentStyle: 'dotted',
                            destDecorator: 'mafe-default'
                        };
                        break;
                    case 'associationRules':
                        if (this.associationRules[this.getTypeToValidate(sourceShape)][this.getTypeToValidate(targetShape)]) {
                            result.result = this.associationRules[this.getTypeToValidate(sourceShape)][this.getTypeToValidate(targetShape)];
                        }
                        connectionConf = {
                            type: 'DATAASSOCIATION',
                            segmentStyle: 'dotted',
                            destDecorator: 'mafe-association'
                        };
                        break;
                }
                if (result.result) {
                    // validate that the sourceShape allow the outgoing connection
                    if ((!connection || (connection.getSrcPort().getParent() !== sourceShape)) && !this.canAcceptOutgoingConnection(sourceShape, connectionConf.type)) {
                        return {
                            result: false,
                            msg: 'The source shape can not have more than one outgoing connection'.translate()
                        };
                    }
                }
                targetShape.setConnectionType(connectionConf);
            }
        }
    }
    return result;
};

ConnectValidator.prototype.getTypeToValidate = function (shape) {
    var type;
    switch (shape.extendedType) {
        case 'START':
            if (shape.getEventMarker() === 'MESSAGECATCH') {
                type = shape.extendedType + shape.getEventMarker();
            } else {
                type = shape.extendedType;
            }
            break;
        case 'END':
            if (shape.getEventMarker() === 'EMAIL'
                || shape.getEventMarker() === 'MESSAGETHROW') {
                type = shape.extendedType + shape.getEventMarker();
            } else {
                type = shape.extendedType;
            }
            break;
        case 'INTERMEDIATE':
            if (shape.getEventMarker() === 'EMAIL'
                || shape.getEventMarker() === 'MESSAGETHROW'
                || shape.getEventMarker() === 'MESSAGECATCH') {
                type = shape.extendedType + shape.getEventMarker();
            } else {
                type = shape.extendedType;
            }
            break;
        case 'TASK':
        case 'SUB_PROCESS':
        case 'PARTICIPANT':
        case 'TEXT_ANNOTATION':
            type = shape.extendedType;
            break;
        case 'EXCLUSIVE':
        case 'PARALLEL':
        case 'INCLUSIVE':
        case 'COMPLEX':
            type = 'GATEWAY';
            break;
        case 'DATAOBJECT':
        case 'DATASTORE':
        case 'DATAINPUT':
        case 'DATAOUTPUT':
            type = 'DATA';
            break;
        case 'GROUP':
            type = 'GROUP';
            break;
        case 'POOL':
            type = 'POOL';
            break;
        case 'LANE':
            type = 'LANE';
            break;
        default:
            type = 'TASK';
            break;
    }
    return type;
};
ConnectValidator.prototype.styleErrorMap = new PMUI.util.ArrayList();
ConnectValidator.prototype.hasNotMap = new PMUI.util.ArrayList();
/**
 * Validate All Shapes.
 * @returns {ConnectValidator}
 */
ConnectValidator.prototype.bpmnValidator = function () {
    var canvas,
        shape,
        i;
    canvas = PMUI.getActiveCanvas();
    PMDesigner.validTable.clear().draw();
    for (i = 0; i < canvas.customShapes.getSize(); i += 1) {
        shape = canvas.customShapes.get(i);
        this.bpmnValidatorShape(shape);
    }
    return this;
};
/**
 * Validate un update a bpmn element
 * @param {*} updatedElement 
 */
ConnectValidator.prototype.bpmnValidateOnUpdate = function (updatedElement) {
    for (i = 0; i < updatedElement.length; i += 1) {
        if(updatedElement[i].type === "Connection") {
            if (updatedElement[i].relatedObject.type === "Port") {
                this.bpmnValidatorShape(updatedElement[i].relatedObject.parent);
                this.bpmnValidatorShape(updatedElement[i].relatedObject.oldParent);
            } else {
                this.bpmnValidatorShape(updatedElement[i].relatedObject.destPort.parent);
                this.bpmnValidatorShape(updatedElement[i].relatedObject.srcPort.parent);
            }
            
        }
    }
    return this;
};

/**
 * Validate Shape
 * @param shape
 * @returns {ConnectValidator}
 */
ConnectValidator.prototype.bpmnValidatorShape = function (shape) {
    if (shape && shape.validatorMarker) {
        this.bpmnFlowValidator(shape);
        shape.validatorMarker.hide();
        if (shape.getNumErrors() > 0 && shape.validatorMarker) {
            if (shape.validateWarning(shape.getArrayErrors())) {
                shape.setTypeErrors("warning");
            } else {
                shape.setTypeErrors("error");
            }
            shape.validatorMarker.show();
        }
    }
    return this;
};
/**
 * Call to Sequence flow Validator
 * @param shape
 * @returns {ConnectValidator}
 */
ConnectValidator.prototype.bpmnFlowValidator = function (shape) {
    this.sequenceFlowValidator(shape);
    return this;
};
/**
 * Check the type bpmn Shape and get rules of validation
 * @param shape
 */
ConnectValidator.prototype.sequenceFlowValidator = function (shape) {
    var ref = shape.businessObject.elem,
        criteria,
        errorCandidates,
        i,
        error = {},
        type,
        max;
    shape.errors.clear();
    if (shape.getType() === 'PMParticipant') {
        ref = shape.participantObject.elem;
    }
    switch (ref.$type) {
        case 'bpmn:Task':
        case 'bpmn:SendTask':
        case 'bpmn:ReceiveTask':
        case 'bpmn:ServiceTask':
        case 'bpmn:UserTask':
        case 'bpmn:ScriptTask':
        case 'bpmn:ManualTask':
        case 'bpmn:BusinessRuleTask':
            type = 'bpmnActivity';
            break;
        case 'bpmn:SubProcess':
            type = 'bpmnSubProcess';
            break;
        case 'bpmn:IntermediateThrowEvent':
        case 'bpmn:IntermediateCatchEvent':
            type = 'bpmnIntermediateEvent';
            break;
        case 'bpmn:ExclusiveGateway':
        case 'bpmn:ParallelGateway':
        case 'bpmn:InclusiveGateway':
            type = 'bpmnGateway';
            break;
        case 'bpmn:DataObjectReference':
            type = 'bpmnDataObject';
            break;
        case 'bpmn:DataStoreReference':
            type = 'bpmnDataStore';
            break;
        default:
            type = ref.$type;
            break;
    }
    if (PMDesigner.modelRules.getStatus()) {
        errorCandidates = PMDesigner.modelRules.getCollectionType(type);
    }
    if (errorCandidates) {
        for (i = 0, max = errorCandidates.getSize(); i < max; i += 1) {
            error = errorCandidates.get(i);
            criteria = error.getCriteria();
            if (criteria && typeof criteria === "function") {
                criteria(shape, _.extend({}, error));
            }
        }
    }
};
/**
 * Verify if the supplied element can accept one outgoing connection more.
 * @param {PMShape} shape
 * @returns {boolean}
 */
ConnectValidator.prototype.canAcceptOutgoingConnection = function (shape, connectionType) {
    if (!(shape instanceof PMGateway) && connectionType === 'SEQUENCE') {
        return !shape.getOutgoingConnections(connectionType).length;
    }
    return true;
};
/**
 * Validate if PMActivity/PMEvent onDrop is correct
 * @param {PMUI.draw.Shape} shape
 * @param {PMUI.draw.Shape} customShape
 * @returns {boolean}
 */
ConnectValidator.prototype.onDropMovementIsAllowed = function (shape, customShape) {
    var result = true,
        i,
        connection,
        ports,
        len,
        sourceShape,
        targetShape,
        elemShape,
        flowType,
        oldParent = customShape.getOldParent().getParent();

    if (shape.getID() !== customShape.getParent().getID()) {
        ports = customShape.getPorts();
        len = ports.getSize();
        for (i = 0; i < len; i += 1) {
            //Get sourceShape and targetShape (PMevent/PMactivity)
            connection = ports.get(i).getConnection();
            sourceShape = connection.getSrcPort().getParent();
            targetShape = connection.getDestPort().getParent();
            elemShape = shape.businessObject.elem;
            elemShape = (elemShape && elemShape.$parent && elemShape.$parent.$parent) ? elemShape : false;
            flowType = (connection.getFlowType() === 'MESSAGE') ? true : false;

            if (flowType &&
                (sourceShape.getParent().getID() === shape.getID()
                    || targetShape.getParent().getID() === shape.getID())) {
                result = false;
                break;
            } else if (elemShape && flowType &&
                (elemShape.$parent.$parent.id === sourceShape.businessObject.elem.$parent.id ||
                    elemShape.$parent.$parent.id === targetShape.businessObject.elem.$parent.id)) {
                result = (oldParent) ? (oldParent.getID() !== shape.getParent().getID()) ? false : true : false;
                break;
            }
        }
    }
    return result;
};

var CommandChangeGatewayType = function (receiver, options) {
    PMUI.command.Command.call(this, receiver);
    this.before = null;
    this.after = null;
    CommandChangeGatewayType.prototype.initObject.call(this, options);
};

PMUI.inheritFrom('PMUI.command.Command', CommandChangeGatewayType);
/**
 * Type of the instances of this class
 * @property {String}
 */
CommandChangeGatewayType.prototype.type = "CommandChangeGatewayType";

/**
 * Initializes the command parameters
 * @param {PMUI.draw.Core} receiver The object that will perform the action
 */
CommandChangeGatewayType.prototype.initObject = function (options) {
    var parsedClass = options;
    this.layer = this.receiver.getLayers().get(0);
    this.before = {
        zoomSprite: this.layer.zoomSprites,
        type: this.receiver.extendedType,
        defaultFlow: this.receiver.canvas.items.find('id', this.receiver.gat_default_flow) ?
            this.receiver.canvas.items.find('id', this.receiver.gat_default_flow).relatedObject : null
    };
    this.after = {
        zoomSprite: [
            'mafe-gateway-' + parsedClass.toLowerCase() + '-20',
            'mafe-gateway-' + parsedClass.toLowerCase() + '-30',
            'mafe-gateway-' + parsedClass.toLowerCase() + '-41',
            'mafe-gateway-' + parsedClass.toLowerCase() + '-51',
            'mafe-gateway-' + parsedClass.toLowerCase() + '-61'
        ],
        type: options
    };
};

/**
 * Executes the command, changes the position of the element, and if necessary
 * updates the position of its children, and refreshes all connections
 */
CommandChangeGatewayType.prototype.execute = function () {
    var menuShape;
    this.layer.setZoomSprites(this.after.zoomSprite);
    this.receiver.setGatewayType(this.after.type);
    this.receiver.extendedType = this.after.type;
    this.receiver
        .updateBpmGatewayType(this.receiver.mapBpmnType[this.after.type]);
    PMDesigner.project.updateElement([]);
    this.receiver.paint();
    if (this.after.type === 'PARALLEL' && this.before.defaultFlow) {
        this.receiver.gat_default_flow = null;
        this.before.defaultFlow.changeFlowType('sequence');
        this.before.defaultFlow.setFlowType("SEQUENCE");
    }

    menuShape = PMDesigner.getMenuFactory(this.after.type);
    this.receiver.setContextMenu(menuShape);
};

/**
 * Returns to the state before the command was executed
 */
CommandChangeGatewayType.prototype.undo = function () {
    this.layer.setZoomSprites(this.before.zoomSprite);
    this.receiver.setGatewayType(this.before.type);
    this.receiver.extendedType = this.before.type;
    this.receiver
        .updateBpmGatewayType(this.receiver.mapBpmnType[this.before.type]);
    PMDesigner.project.setDirty(true);
    $(this.receiver.html).trigger('changeelement');
    this.receiver.paint();
    menuShape = PMDesigner.getMenuFactory(this.before.type);
    this.receiver.setContextMenu(menuShape);
    if (this.after.type === 'PARALLEL' && this.before.defaultFlow) {
        this.receiver.gat_default_flow = this.before.defaultFlow.getID();
        this.before.defaultFlow.setFlowCondition("");
        this.before.defaultFlow.changeFlowType('default');
        this.before.defaultFlow.setFlowType("DEFAULT");

    }
};

/**
 *  Executes the command again after an undo action has been done
 */
CommandChangeGatewayType.prototype.redo = function () {
    this.execute();
};


var CommandDefaultFlow = function (receiver, options) {
    PMUI.command.Command.call(this, receiver);
    this.before = null;
    this.after = null;
    CommandDefaultFlow.prototype.initObject.call(this, options);
};

PMUI.inheritFrom('PMUI.command.Command', CommandDefaultFlow);
/**
 * Type of the instances of this class
 * @property {String}
 */
CommandChangeGatewayType.prototype.type = "CommandDefaultFlow";

/**
 * Initializes the command parameters
 * @param {PMUI.draw.Core} receiver The object that will perform the action
 */
CommandDefaultFlow.prototype.initObject = function (options) {

    this.before = {
        id: this.receiver.gat_default_flow
    };
    this.after = {
        id: options
    };
};

/**
 * Executes the command, changes the position of the element, and if necessary
 * updates the position of its children, and refreshes all connections
 */
CommandDefaultFlow.prototype.execute = function () {
    this.receiver.setDefaultFlow(this.after.id);
    PMDesigner.project.setDirty(true);
};

/**
 * Returns to the state before the command was executed
 */
CommandDefaultFlow.prototype.undo = function () {
    this.receiver.setDefaultFlow(this.before.id);
};

/**
 *  Executes the command again after an undo action has been done
 */
CommandDefaultFlow.prototype.redo = function () {
    this.execute();
};
/**
 * @class PMArtifact
 * Handle BPMN Annotations
 *
 *
 * @constructor
 * Creates a new instance of the class
 * @param {Object} options
 */
var PMLabel = function (options) {
    PMShape.call(this, options);
    /**
     * Defines the type artifact
     * @type {String}
     */
    this.art_type = null;
    /**
     * Defines the unique identifier
     * @type {String}
     */
    this.art_uid = null;

    PMArtifact.prototype.initObject.call(this, options);
};
PMLabel.prototype = new PMShape();

/**
 * Defines the object type
 * @type {String}
 */
PMLabel.prototype.type = "PMArtifact";
PMLabel.prototype.PMArtifactResizeBehavior = null;

/**
 * Initialize the object with the default values
 * @param {Object} options
 */
PMArtifact.prototype.initObject = function (options) {
    var defaults = {
        art_type: 'PMArtifact',
        art_name: ""
    };

    jQuery.extend(true, defaults, options);
    this.setArtifactUid(defaults.art_uid);
    this.setArtifactType(defaults.art_type);
    this.setName(defaults.art_name);

};

/**
 * Sets the artifact type property
 * @param {String} type
 * @return {*}
 */
PMLabel.prototype.setArtifactType = function (type) {
    this.art_type = type;
    return this;
};
/**
 * Sets the artifact unique identifier
 * @param {String} value
 * @return {*}
 */
PMLabel.prototype.setArtifactUid = function (value) {
    this.art_uid = value;
    return this;
};
/**
 * Returns the clean object to be sent to the backend
 * @return {Object}
 */
PMLabel.prototype.getDataObject = function () {
    var name = this.getName();
    return {
        art_uid: this.id,
        art_name: name,
        art_type: this.art_type,
        bou_x: this.x,
        bou_y: this.y,
        bou_width: this.width,
        bou_height: this.height,
        bou_container: 'bpmnDiagram',
        _extended: this.getExtendedObject()
    };
};

PMLabel.prototype.getArtifactType = function () {
    return this.art_type;
};
PMLabel.prototype.createLayer = function (options) {

    var layer;
    options.parent = this;
    layer = new CustomLayer(options);
    this.addLayer(layer);
    return layer;
};
PMArtifact.prototype.updateHTML = function () {
    var height, width;
    height = this.height;
    width = this.width;
    PMShape.prototype.updateHTML.call(this);
    this.setDimension(width, height);
    return this;
};
/*global jCore, $ */
/**
 * @class AdamMarker
 * Handle Activity Markers
 *
 * @constructor
 * Creates a new instance of the class
 * @param {Object} options
 */
var PMMarker = function (options) {
    PMUI.draw.Shape.call(this, options);
    /**
     * Defines the positions of the markers
     * @type {Array}
     * @private
     */
    this.positions = ['left+2 top+2', 'center top+5', 'right top',
        'left+5 bottom-1', 'center bottom-2', 'right-5 bottom-1'];
    /**
     * Defines the offset of the markers
     * @type {Array}
     * @private
     */
    this.offset = ['5 5', '0 5', '0 5', '5 -1', '0 -1', '-5 -1'];
    /**
     * Define the marker type property
     * @type {null}
     */
    this.markerType = null;
    PMMarker.prototype.initObject.call(this, options);
};
PMMarker.prototype = new PMUI.draw.Shape();
/**
 * Defines the object type
 * @type {String}
 */
PMMarker.prototype.type = 'PMMarker';

/**
 * Initialize the object with the default values
 * @param {Object} options
 */
PMMarker.prototype.initObject = function (options) {
    var defaults = {
        canvas: null,
        parent: null,
        position: 0,
        width: 21,
        height: 21,
        markerZoomClasses: [],
        markerType: null
    };
    $.extend(true, defaults, options);
    this.setParent(defaults.parent)
        .setPosition(defaults.position)
        .setHeight(defaults.height)
        .setWidth(defaults.width)
        .setMarkerZoomClasses(defaults.markerZoomClasses)
        .setMarkerType(defaults.markerType);
};

/**
 * Applies zoom to the Marker
 * @return {*}
 */
PMMarker.prototype.applyZoom = function () {
    var newSprite;
    this.removeAllClasses();
    this.setProperties();
    newSprite = this.markerZoomClasses[this.parent.canvas.zoomPropertiesIndex];
    this.html.className = newSprite;
    this.currentZoomClass = newSprite;
    return this;
};

/**
 * Create the HTML for the marker
 * @return {*}
 */
PMMarker.prototype.createHTML = function () {
    PMUI.draw.Shape.prototype.createHTML.call(this);

    this.html.id = this.id;
    this.setProperties();
    this.html.className = this.markerZoomClasses[
        this.parent.canvas.getZoomPropertiesIndex()
        ];
    this.currentZoomClass = this.html.className;
    this.parent.html.appendChild(this.html);
    return this.html;
};

/**
 * Updates the painting of the marker
 * @param update
 */
PMMarker.prototype.paint = function (update) {
    if (this.getHTML() === null || update) {
        this.createHTML();
    }
    $(this.html).position({
        of: $(this.parent.html),
        my: this.positions[this.position],
        at: this.positions[this.position],
        collision: 'none'
    });
};

/**
 * Sets the marker type property
 * @param {String} newType
 * @return {*}
 */
PMMarker.prototype.setMarkerType = function (newType) {
    this.markerType = newType;
    return this;
};

/**
 * Sets the position of the marker
 * @param {Number} newPosition
 * @return {*}
 */
PMMarker.prototype.setPosition = function (newPosition) {
    if (newPosition !== null && typeof newPosition === 'number') {
        this.position = newPosition;
    }
    return this;
};

/**
 * Sets the parent of the marker
 * @param {AdamActivity} newParent
 * @return {*}
 */
PMMarker.prototype.setParent = function (newParent) {
    this.parent = newParent;
    return this;
};

/**
 * Sets the elements class
 * @param eClass
 * @return {*}
 */
PMMarker.prototype.setEClass = function (eClass) {
    this.currentZoomClass = eClass;
    return this;
};

/**
 * Sets the array of zoom classes
 * @param {Object} classes
 * @return {*}
 */
PMMarker.prototype.setMarkerZoomClasses = function (classes) {
    this.markerZoomClasses = classes;
    return this;
};

/**
 * Sets the marker HTML properties
 * @return {*}
 */
PMMarker.prototype.setProperties = function () {
    this.html.style.width = this.width * this.parent.getCanvas().getZoomFactor() + 'px';
    this.html.style.height = this.height * this.parent.getCanvas().getZoomFactor() + 'px';
    return this;
};

/**
 * Remove all classes of HTML
 * @return {*}
 */
PMMarker.prototype.removeAllClasses = function () {
    this.html.className = '';
    return this;
};

/*global jCore*/
var PMActivityResizeBehavior = function () {
};

PMActivityResizeBehavior.prototype = new PMUI.behavior.RegularResizeBehavior();
PMActivityResizeBehavior.prototype.type = "PMActivityResizeBehavior";

/**
 * Sets a shape's container to a given container
 * @param container
 * @param shape
 */
PMActivityResizeBehavior.prototype.onResizeStart = function (shape) {
    return PMUI.behavior.RegularResizeBehavior
        .prototype.onResizeStart.call(this, shape);
};
/**
 * Removes shape from its current container
 * @param shape
 */
PMActivityResizeBehavior.prototype.onResize = function (shape) {
    return function (e, ui) {
        PMUI.behavior.RegularResizeBehavior
            .prototype.onResize.call(this, shape)(e, ui);
        shape.paint();
        shape.updateBoundaryPositions(false);
    };
};

PMActivityResizeBehavior.prototype.onResizeEnd = function (shape) {
    return function (e, ui) {
        var i,
            label,
            command,
            margin = 10;
        shape.resizing = false;
        shape.canvas.isResizing = false;
        // last resize
        PMUI.behavior.RegularResizeBehavior.prototype.onResize.call(this, shape)(e, ui);

        // show the handlers again
        shape.showOrHideResizeHandlers(true);

        // update the dimensions of the parent if possible (a shape might
        // have been resized out of the dimensions of its parent)
        shape.parent.updateDimensions(margin);

        if (shape.ports) {
            shape.firePortsChange();
        }

        // TESTING COMMANDS
        command = new PMCommandResize(shape);
        shape.canvas.commandStack.add(command);
        command.execute();
        for (i = 0; i < shape.labels.getSize(); i += 1) {
            label = shape.labels.get(i);
            label.setLabelPosition(label.location, label.diffX, label.diffY);
        }

        return true;
    };
};

/**
 * @class PMGateway
 * @param {Object} options
 */
var PMData = function (options) {
    PMShape.call(this, options);
    this.dat_name = '';
    this.dat_type = 'DATAOBJECT';
    this.dat_is_collection = false;
    this.dat_is_global = false;
    this.dat_object_ref = '';
    this.dat_is_unlimited = false;
    this.dat_capacity = 0;

    this.businessObject = {};
    PMData.prototype.init.call(this, options);
};

PMData.prototype = new PMShape();
/**
 * Defines the object type
 * @type {String}
 */
PMData.prototype.type = 'PMData';

PMData.prototype.mapBpmnType = {
    'DATAOBJECT': 'bpmn:DataObject',
    'DATAINPUT': 'bpmn:DataInput',
    'DATAOUTPUT': 'bpmn:DataOutput'
};

PMData.prototype.getDataObject = function () {
    var name = this.getName(),
        container,
        element_id;
    switch (this.parent.type) {
        case 'PMCanvas':
            container = 'bpmnDiagram';
            element_id = this.canvas.id;
            break;
        case 'PMPool':
            container = 'bpmnPool';
            element_id = this.parent.id;
            break;
        case 'PMLane':
            container = 'bpmnLane';
            element_id = this.parent.id;
            break;
        default:
            container = 'bpmnDiagram';
            element_id = this.canvas.id;
            break;
    }
    return {
        dat_uid: this.dat_uid,
        dat_name: name,
        dat_type: this.dat_type,
        bou_x: this.x,
        bou_y: this.y,
        bou_width: this.width,
        bou_height: this.height,
        bou_container: container,
        bou_element: element_id,
        _extended: this.getExtendedObject()
    };
};
/**
 * Initialize the PMData object
 * @param options
 */
PMData.prototype.init = function (options) {
    var defaults = {
        dat_name: '',
        dat_type: '',
        dat_is_collection: false,
        dat_is_global: false,
        dat_object_ref: '',
        dat_is_unlimited: false,
        dat_capacity: 0
    };
    jQuery.extend(true, defaults, options);
    this.setDataUid(defaults.dat_uid)
        .setDataType(defaults.dat_type);
    if (defaults.dat_name) {
        this.setName(defaults.dat_name);
    }
    this.setOnBeforeContextMenu(this.beforeContextMenu);
};
/**
 * Gets BPMNShape type
 * @returns {string}
 */
PMData.prototype.getDataType = function () {
    return this.dat_type;
};

/**
 * Gets Data Name
 * @returns {string}
 */
PMData.prototype.getDataName = function () {
    return this.dat_name;
};

/**
 * Gets Data Capasity
 * @returns {*}
 */
PMData.prototype.getCapacity = function () {
    return this.dat_capacity;
};

/**
 * Gets Data global value
 * @returns {boolean}
 */
PMData.prototype.getIsGlobal = function () {
    return this.dat_is_global;
};

/**
 * Gets Data Unlimited value
 * @returns {boolean}
 */
PMData.prototype.getUnlimited = function () {
    return this.dat_is_unlimited;
};
/**
 * Gets Data collection status
 * @returns {boolean}
 */
PMData.prototype.getDataCollection = function () {
    return this.dat_is_collection;
};
/**
 * Gets Data type
 * @returns {string}
 */
PMData.prototype.getDataType = function () {
    return this.dat_type;
};
/**
 * Sets the act_uid property
 * @param {String} value
 * @return {*}
 */
PMData.prototype.setDataUid = function (value) {
    this.dat_uid = value;
    return this;
};
/**
 * Sets Data name
 * @param {string} newName
 * @chainable
 */
PMData.prototype.setName = function (name) {
    if (typeof name !== 'undefined') {
        this.act_name = name;
        if (this.label) {
            this.label.setMessage(name);
        }
    }
    return this;
};
/**
 * Sets BPMNData type
 * @param {string} newType
 * @chainable
 */
PMData.prototype.setDataType = function (newType) {
    if (!newType || typeof newType === 'undefined') {
        return;
    }
    this.dat_type = newType;
    return this;
};
/**
 * Sets Data global mode
 * @param {boolean} isGlobal
 * @returns {*}
 */
PMData.prototype.setIsGlobal = function (isGlobal) {
    if (typeof isGlobal !== 'undefined') {
        this.dat_is_global = isGlobal;
    }
    return this;
};
/**
 * Sets Data capasity
 * @param {string} capacity
 * @returns {*}
 */
PMData.prototype.setCapacity = function (capacity) {
    if (typeof capacity === 'undefined')
        return;
    this.dat_capacity = capacity;
    return this;
};
/**
 * Setd data unlimited mode
 * @param {boolean} unlimited
 * @returns {*}
 */
PMData.prototype.setIsUnlimited = function (unlimited) {
    if (typeof unlimited === 'undefined')
        return;
    this.dat_is_unlimited = unlimited;
    return this;
};
/**
 * Sets Data collection mode
 * @param {boolean} collection
 * @returns {*}
 */
PMData.prototype.setIsCollection = function (collection) {
    if (typeof collection === 'undefined')
        return;
    this.dat_is_collection = collection;
    return this;
};
/**
 * Change data type
 * @param {String} type
 * @returns {*}
 */
PMData.prototype.switchDataType = function (type) {
    var marker = this.markersArray.get(0),
        lowerType = type.toLowerCase();
    marker.removeAllClasses();
    marker.setMarkerZoomClasses([
        "mafe-" + lowerType + "-marker-10",
        "mafe-" + lowerType + "-marker-15",
        "mafe-" + lowerType + "-marker-21",
        "mafe-" + lowerType + "-marker-26",
        "mafe-" + lowerType + "-marker-31"
    ]);
    marker.paint(true);
    this.setDataType(type);
    this.updateBpmnDataType(this.mapBpmnType[this.getDataType()], type);
    PMDesigner.project.updateElement([]);
    return this;
};

PMData.prototype.updateBpmnDataType = function (newBpmnType, dataType) {
    this.verifyDataIOEspecification();
    this.removeBpmn();
    this.businessObject.elem = null;
    this.createBpmn(newBpmnType, dataType);
};

PMData.prototype.createWithBpmn = function (bpmnElementType, name) {
    var businessObject = {};
    if (this.extendedType === 'DATASTORE') {
        var ds = PMDesigner.bpmnFactory.create('bpmn:DataStore', {
            id: this.id,
            name: this.getName() ? PMDesigner.escapeXMLCharacters(this.getName()) : "",
        });
        PMDesigner.businessObject.get('rootElements').push(ds);
        businessObject.elem = PMDesigner.bpmnFactory.create('bpmn:DataStoreReference', {
            id: this.id + '_ref',
            name: this.getName() ? PMDesigner.escapeXMLCharacters(this.getName()) : "",
            dataStoreRef: ds
        });
        businessObject.elem.dsRef = ds;
    } else {

        if (bpmnElementType == 'bpmn:DataObject' || bpmnElementType === undefined) {
            var dObj = PMDesigner.bpmnFactory.create('bpmn:DataObject', {
                id: this.id,
                name: this.getName() ? PMDesigner.escapeXMLCharacters(this.getName()) : "",
            });
            businessObject.elem = PMDesigner.bpmnFactory.create('bpmn:DataObjectReference', {
                id: this.id + '_ref',
                name: this.getName() ? PMDesigner.escapeXMLCharacters(this.getName()) : "",
                dataObjectRef: dObj
            });
            // validate if container is a lane because data store is always into process tag
            if (this.parent.getType() === 'PMLane') {
                this.updateSemanticParent({elem: dObj}, this.parent.parent.businessObject);
            } else {
                this.updateSemanticParent({elem: dObj}, this.parent.businessObject);
            }
            businessObject.elem.doRef = dObj;
        }
        if (bpmnElementType == 'bpmn:DataInput' || bpmnElementType == 'bpmn:DataOutput') {
            businessObject = this.createDataIOBusinessObject(bpmnElementType);
        }
    }
    if (!businessObject.di) {
        businessObject.di = PMDesigner.bpmnFactory.createDiShape(businessObject.elem, {}, {
            id: businessObject.elem.id + '_di'
        });
    }
    this[name] = businessObject;
};

PMData.prototype.beforeContextMenu = function () {
    var items, i,
        menuItem,
        hasMarker = false;
    this.canvas.hideAllCoronas();
    if (this.canvas.readOnly) {
      return;
    }
    if (this.getDataType() === 'DATAOBJECT' || this.getDataType() === 'DATAOUTPUT' || this.getDataType() === 'DATAINPUT') {
        items = this.menu.items.find('id', 'dataType').childMenu.items;
        for (i = 0; i < items.getSize(); i += 1) {
            menuItem = items.get(i);
            if (menuItem.id === this.getDataType().toLowerCase()) {
                menuItem.disable();
                hasMarker = true;
            } else {
                menuItem.enable();
            }
        }
    }
};

PMData.prototype.getDataType = function () {
    return this.dat_type;
};

PMData.prototype.createDataIOEspecification = function (element) {
    var ioEspecification = PMDesigner.bpmnFactory.create('bpmn:InputOutputSpecification', {
        id: this.id + '_ioEspecification',
        name: (this.getName() ? PMDesigner.escapeXMLCharacters(this.getName()) : "") + '_ioEspecification'
    });
    ioEspecification['dataInputs'] = [];
    this.parent.businessObject.elem['ioSpecification'] = ioEspecification;
};

PMData.prototype.verifyDataIOEspecification = function () {
    if (!this.parent.businessObject.elem || !this.parent.businessObject.elem.get('ioSpecification')) {
        this.createDataIOEspecification(this.parent.businessObject);
    }
};

PMData.prototype.createDataBusinessObject = function () {
    return this;
};

PMData.prototype.createDataIOBusinessObject = function (bpmnElementType) {
    var dObj = PMDesigner.bpmnFactory.create(bpmnElementType, {
        id: this.id,
        name: this.getName() ? PMDesigner.escapeXMLCharacters(this.getName()) : ""
    });
    this.parent.businessObject.elem['ioSpecification'].dataInputs.push(dObj);
    return {elem: dObj};
};
var PMParticipant = function (options) {
    PMShape.call(this, options);
    /**
     *
     * @type {String}
     */
    this.name = '';
    /**
     *
     * @type {String}
     */
    this.proUid = '';
    /**
     *
     * @type {String}
     */
    this.childLaneset = '';
    /**
     *
     * @type {Number}
     */
    this.numParticipants = 0;
    this.graphic = null;
    this.headLineCoord = 40;
    this.orientation = 'HORIZONTAL';
    PMParticipant.prototype.initObject.call(this, options);
};

PMParticipant.prototype = new PMShape();
PMParticipant.prototype.type = 'PMParticipant';
PMParticipant.prototype.participantResizeBehavior = null;

PMParticipant.prototype.getDataObject = function () {
    var name = this.getName();
    return {
        par_uid: this.getID(),
        par_name: name,
        bou_x: this.x,
        bou_y: this.y,
        bou_width: this.width,
        bou_height: this.height,
        bou_container: 'bpmnDiagram',
        _extended: this.getExtendedObject()
    };
};

PMParticipant.prototype.initObject = function (options) {
    var defaultOptions = {
        name: 'Participant',
        numParticipants: 0,
        orientation: 'HORIZONTAL'
    };
    $.extend(true, defaultOptions, options);

    this.setName(defaultOptions.par_name)
        .setNumparticipants(defaultOptions.numParticipants)
        .setOrientation(defaultOptions.orientation);
};

PMParticipant.prototype.createHTML = function () {
    PMShape.prototype.createHTML.call(this);
    this.style.addClasses(['mafe_participant']);
    return this.html;
};
PMParticipant.prototype.decreaseZIndex = function () {
    this.fixZIndex(this, 1);
    return this;
};
PMParticipant.prototype.onUpdateLabel = function () {
    var label = this.getLabels().get(0);
    label.text.style["text-overflow"] = "ellipsis";
    label.text.style["overflow"] = "hidden";
    label.text.style["white-space"] = "nowrap";
    label.text.style["margin-left"] = "2%";
    label.text.setAttribute("title", label.getMessage());
    return this;
};
PMParticipant.prototype.paint = function () {
    var zoomFactor = this.canvas.zoomFactor;
    if (typeof this.graphic === 'undefined' || this.graphic === null) {
        this.graphic = new JSGraphics(this.id);
    } else {
        this.graphic.clear();
    }

    if (this.validatorMarker) {
        this.validatorMarker.paint();
        this.validatorMarker.hide();
    }
    this.graphic.setColor('#3b4753');
    this.graphic.setStroke(2);
    if (this.orientation === 'VERTICAL') {
        this.graphic.drawLine(0, this.headLineCoord * zoomFactor, this.zoomWidth,
            this.headLineCoord * zoomFactor);
        this.getLabels().get(0).setOrientation('horizontal');
        this.getLabels().get(0).setLabelPosition('top');
    } else {
        this.graphic.drawLine(this.headLineCoord * zoomFactor, 0,
            this.headLineCoord * zoomFactor,
            this.zoomHeight - 5);
        this.getLabels().get(0).setOrientation('vertical');
        this.getLabels().get(0).setLabelPosition('center-left', 20, 0);

    }
    this.onUpdateLabel();
    this.graphic.paint();
    if (this.corona) {
        this.corona.paint();
        this.corona.hide();
    }
};

/**
 * Change pool orientation dinamically
 * @param {String} orientation
 * @returns {BPMNPool}
 */
PMParticipant.prototype.changeOrientation = function (orientation) {
    var command = new BPMNCommandUpdateOrientation(this, {
        before: this.getOrientation(),
        after: orientation.toUpperCase()
    });
    command.execute();
    this.getCanvas().commandStack.add(command);
    return this;
};
/**
 * Set participant name
 * @param {String} name
 * @returns {BPMNLane}
 */
PMParticipant.prototype.setName = function (name) {
    if (typeof name !== 'undefined') {
        this.par_name = name;
        if (this.label) {
            this.label.setMessage(name);
        }
    }
    return this;
};
/**
 * Set participant name
 * @param {String} name
 * @returns {BPMNLane}
 */
PMParticipant.prototype.setType = function (newType) {
    this.type = newType;
    return this;
};
/**
 * Set process uid asociated to participant
 * @param {String} uid
 * @returns {BPMNLane}
 */
PMParticipant.prototype.setProUid = function (uid) {
    this.proUid = uid;
    return this;
};
/**
 * Set number of participants
 * @param {Number} uid
 * @returns {BPMNLane}
 */
PMParticipant.prototype.setNumparticipants = function (numParticipants) {
    this.numParticipants = numParticipants;
    return this;
};

/**
 * Set pool orientation
 * @param {String} orientation
 * @returns {BPMNLane}
 */
PMParticipant.prototype.setOrientation = function (orientation) {
    this.orientation = orientation;
    return this;
};
/**
 * Get the participant name
 * @returns {String}
 */
PMParticipant.prototype.getName = function () {
    return this.par_name;
};
/**
 * Get the participant process uid
 * @returns {String}
 */
PMParticipant.prototype.getProUid = function () {
    return this.proUid;
};
/**
 * Get the number of participants
 * @returns {Number}
 */
PMParticipant.prototype.getNumParticipants = function () {
    return this.numParticipants;
};
/**
 * Get the type
 * @returns {Number}
 */
PMParticipant.prototype.getType = function () {
    return this.type;
};
/**
 * Get orientation
 * @returns {Number}
 */
PMParticipant.prototype.getOrientation = function () {
    return this.orientation;
};

PMParticipant.prototype.setResizeBehavior = function (behavior) {
    var factory = new PMUI.behavior.BehaviorFactory({
        products: {
            "regularresize": PMUI.behavior.RegularResizeBehavior,
            "Resize": PMUI.behavior.RegularResizeBehavior,
            "yes": PMUI.behavior.RegularResizeBehavior,
            "resize": PMUI.behavior.RegularResizeBehavior,
            "noresize": PMUI.behavior.NoResizeBehavior,
            "NoResize": PMUI.behavior.NoResizeBehavior,
            "no": PMUI.behavior.NoResizeBehavior,
            "participantResize": PMParticipantResizeBehavior
        },
        defaultProduct: "noresize"
    });
    this.resizeBehavior = factory.make(behavior);
    if (this.html) {
        this.resize.init(this);
    }
    return this;
};

PMParticipant.prototype.createBpmn = function (type) {
    var bpmnCollaboration;
    if (!this.parent.businessObject.di) {
        this.parent.createBPMNDiagram();
    }
    if (!(_.findWhere(PMDesigner.businessObject.get('rootElements'), {$type: "bpmn:Collaboration"}))) {
        bpmnCollaboration = PMDesigner.moddle.create('bpmn:Collaboration', {id: 'pmui-' + PMUI.generateUniqueId()});
        PMDesigner.businessObject.get('rootElements').push(bpmnCollaboration);
        this.parent.businessObject.di.bpmnElement = bpmnCollaboration;
        bpmnCollaboration.participants = [];
        bpmnCollaboration.messageFlows = [];
    } else {
        bpmnCollaboration = _.findWhere(PMDesigner.businessObject.get('rootElements'), {$type: "bpmn:Collaboration"});
    }
    if (!this.businessObject.elem) {
        this.createWithBpmn(type, 'participantObject');
        this.updateBounds(this.participantObject.di);
        this.updateSemanticParent(this.participantObject, {elem: bpmnCollaboration});
        this.updateDiParent(this.participantObject.di, this.parent.businessObject.di);
    }
};
PMParticipant.prototype.updateSemanticParent = function (businessObject, newParent) {
    var children;
    if (businessObject.elem.$parent === newParent.elem) {
        return;
    }
    if (businessObject.elem.$parent) {
        children = businessObject.elem.$parent.get('participants');
        CollectionRemove(children, businessObject.elem);
    }
    if (!newParent.elem) {
        businessObject.elem.$parent = null;
    } else {
        children = newParent.elem.get('participants');
        children.push(businessObject.elem);
        businessObject.elem.$parent = newParent.elem;
    }
};

PMParticipant.prototype.updateDiParent = function (di, parentDi) {
    PMShape.prototype.updateDiParent.call(this, di, parentDi);
};

/**
 * create busines object to moodle bpmn export
 */
PMParticipant.prototype.createBusinesObject = function () {
    var participant = _.findWhere(this.participantObject.elem.$parent.get('participants'),
            {id: this.participantObject.elem.id}),
        bpmnProcess = PMDesigner.moddle.create('bpmn:Process',
            {id: 'pmui-' + PMUI.generateUniqueId()});

    PMDesigner.businessObject.get('rootElements').push(bpmnProcess);
    this.businessObject.elem = bpmnProcess;
    this.businessObject.di = this.canvas.businessObject.di;
    participant.processRef = bpmnProcess;
};

PMParticipant.prototype.removeBpmn = function () {
    var coll, children, pros;
    if ((_.findWhere(PMDesigner.businessObject.get('rootElements'), {$type: "bpmn:Collaboration"}))) {
        coll = _.findWhere(PMDesigner.businessObject.get('rootElements'), {$type: "bpmn:Collaboration"});
        if (coll.participants.length === 1) {
            children = PMDesigner.businessObject.get('rootElements');
            CollectionRemove(children, coll);
            //PMDesigner.businessObject.get
            if (this.parent.businessObject.di) {
                this.parent.businessObject.di.bpmnElement = this.parent.businessObject;
            }

        }
    }
    if (this.businessObject.elem && (_.findWhere(PMDesigner.businessObject.get('rootElements'), {
            $type: "bpmn:Process",
            id: this.businessObject.elem.id
        }))) {
        pros = _.findWhere(PMDesigner.businessObject.get('rootElements'), {
            $type: "bpmn:Process",
            id: this.businessObject.elem.id
        });
        children = PMDesigner.businessObject.get('rootElements');
        CollectionRemove(children, pros);
        this.businessObject.elem = null;
    }
    this.updateSemanticParent(this.participantObject, {elem: null});
    this.updateDiParent(this.participantObject.di);
    if (this.businessObject.di
        && this.businessObject.di.planeElement.length == 0) {
        this.parent.removeBPMNDiagram();
    }
};

PMParticipant.prototype.updateBpmn = function () {
    this.updateBounds(this.participantObject.di);

};
/*global jCore*/
var PMParticipantResizeBehavior = function () {
};

PMParticipantResizeBehavior.prototype = new PMUI.behavior.RegularResizeBehavior();
PMParticipantResizeBehavior.prototype.type = "PMParticipantResizeBehavior";
/**
 * Sets a shape's container to a given container
 * @param container
 * @param shape
 */
PMParticipantResizeBehavior.prototype.onResizeStart = function (shape) {
    return PMUI.behavior.RegularResizeBehavior
        .prototype.onResizeStart.call(this, shape);
};
/**
 * Removes shape from its current container
 * @param shape
 */
PMParticipantResizeBehavior.prototype.onResize = function (shape) {
    return function (e, ui) {
        PMUI.behavior.RegularResizeBehavior
            .prototype.onResize.call(this, shape)(e, ui);
        if (shape.graphic) {
            shape.paint();
        }

    };
};

var PMPoolResizeBehavior = function () {
    this.oldHeight = null;
    this.isTop = false;
};

PMPoolResizeBehavior.prototype = new PMUI.behavior.RegularResizeBehavior();
PMPoolResizeBehavior.prototype.type = "PMPoolResizeBehavior";
PMPoolResizeBehavior.prototype.init = function (shape) {
    PMUI.behavior.RegularResizeBehavior
        .prototype.init.call(this, shape);
    $shape = $(shape.getHTML());
    $shape.resizable();
    $shape.resizable('option', 'minWidth', 200 * shape.canvas.getZoomFactor());
    $shape.resizable('option', 'minHeight', 30 * shape.canvas.getZoomFactor());
};

/**
 * Sets a shape's container to a given container
 * @param container
 * @param shape
 */
PMPoolResizeBehavior.prototype.onResizeStart = function (shape) {
    return function (e, ui) {
        PMUI.behavior.RegularResizeBehavior
            .prototype.onResizeStart.call(this, shape)(e, ui);
        //shape.hideAllChilds();
        shape.hasMinimun = false;
        if (shape.bpmnLanes.getSize() > 0) {
            this.lastLaneHeight = shape.bpmnLanes.getLast().getHeight();
            this.firstLaneHeight = shape.bpmnLanes.getFirst().getHeight();
        }

    };
};
/**
 * Removes shape from its current container
 * @param shape
 */
PMPoolResizeBehavior.prototype.onResize = function (shape) {
    return function (e, ui) {
        var i,
            port,
            diffH,
            newWidth,
            lane,
            top = true,
            newY,
            canvas = shape.canvas;
        shape.setPosition(ui.position.left / canvas.zoomFactor,
            ui.position.top / canvas.zoomFactor);
        shape.setDimension(ui.size.width / canvas.zoomFactor,
            ui.size.height / canvas.zoomFactor);
        if (shape.graphic) {
            shape.paint();
        }
        //to resize last lane
        diffH = (ui.size.height / canvas.zoomFactor) - (ui.originalSize.height / canvas.zoomFactor);
        newWidth = shape.getWidth() - shape.headLineCoord - 1.2;

        if (shape.bpmnLanes.getSize() > 0) {
            if (ui.originalPosition.top === ui.position.top) {
                this.isTop = false;
                shape.setMinimunsResize();
                lane = shape.bpmnLanes.getLast();
                lane.setDimension(newWidth, this.lastLaneHeight + diffH);
                for (i = 0; i < shape.bpmnLanes.getSize() - 1; i += 1) {
                    lane = shape.bpmnLanes.get(i);
                    lane.setDimension(newWidth, lane.getHeight());
                }
            } else {
                this.isTop = true;
                shape.setMinimunsResize(true);

                lane = shape.bpmnLanes.getFirst();
                lane.setDimension(newWidth, this.firstLaneHeight + diffH);
                newY = this.firstLaneHeight + diffH;

                for (i = 1; i < shape.bpmnLanes.getSize(); i += 1) {
                    lane = shape.bpmnLanes.get(i);
                    lane.setPosition(lane.getX(), newY);
                    lane.setDimension(newWidth, lane.getHeight());
                    newY += lane.getHeight();
                }
            }

        }
    };
};
/**
 * Adds a shape to a given container
 * @param container
 * @param shape
 */
PMPoolResizeBehavior.prototype.onResizeEnd = function (shape) {
    return function (e, ui) {
        var i,
            size,
            label,
            delta;
        shape.resizing = false;
        shape.canvas.isResizing = false;
        // last resize
        PMUI.behavior.RegularResizeBehavior.prototype.onResize.call(this, shape)(e, ui);
        // show the handlers again
        shape.showOrHideResizeHandlers(true);
        // update the dimensions of the parent if possible (a shape might
        // have been resized out of the dimensions of its parent)
        shape.updateBpmnOnResize();
        for (i = 0, size = shape.labels.getSize(); i < size; i += 1) {
            label = shape.labels.get(i);
            label.setLabelPosition(label.location, label.diffX, label.diffY);
        }
        delta = {
            dx: shape.x - shape.oldX,
            dy: shape.y - shape.oldY
        };
        options = {
            isTop: this.isTop,
            beforeHeightsOpt: {
                lastLaneHeight: this.lastLaneHeight,
                firstLaneHeight: this.firstLaneHeight
            },
            delta: delta
        };
        var command = new PMCommandPoolResize(shape, options);
        shape.getCanvas().commandStack.add(command);
        shape.canvas.refreshArray.clear();
        shape.poolChildConnectionOnResize(true, true);
        shape.refreshAllPoolConnections(false, delta);
        //force to activate save button
        PMDesigner.project.updateElement([]);
    };
};

/**
 * Updates the minimum height and maximum height of the JQqueryUI's resizable plugin.
 * @param {PMUI.draw.Shape} shape
 * @chainable
 */
PMPoolResizeBehavior.prototype.updateResizeMinimums = function (shape) {
    var minW,
        minH,
        children = shape.getChildren(),
        limits = children.getDimensionLimit(),
        margin = 15,
        $shape = $(shape.getHTML());
    if (children.getSize() > 0) {
        minW = (limits[1] + margin) * shape.canvas.getZoomFactor();
        minH = (limits[2] + margin) * shape.canvas.getZoomFactor();
    } else {
        minW = 300 * shape.canvas.getZoomFactor();
        minH = 30 * shape.canvas.getZoomFactor();
    }
    // update jQueryUI's minWidth and minHeight
    $shape.resizable();
    $shape.resizable('option', 'minWidth', minW);
    $shape.resizable('option', 'minHeight', minH);
    return this;
};
/**
 * @class PMPoolDropBehavior
 * The {@link PMUI.behavior.DropBehavior DropBehavior} for PMPool class.
 * @extend PMUI.behavior.DropBehavior
 * @param {Object} [options] css selectors that the drop behavior
 * will accept
 * @constructor
 */
var PMPoolDropBehavior = function (options) {
    PMUI.behavior.DropBehavior.call(this, options);
};

PMPoolDropBehavior.prototype = new PMUI.behavior.DropBehavior();

PMPoolDropBehavior.prototype.constructor = PMPoolDropBehavior;
/**
 * Type for the instance
 * @property {string}
 */
PMPoolDropBehavior.prototype.type = 'PMPoolDropBehavior';
/**
 * @inheritDoc
 */
PMPoolDropBehavior.prototype.onDrop = function (shape) {
    return function (e, ui) {
        var canvas = shape.getCanvas(),
            id = ui.draggable.attr('id'),
            shapesAdded = [],
            selection,
            position,
            droppedShape,
            command;

        if (canvas.readOnly) {
            return false;
        }

        droppedShape = canvas.shapeFactory(id);

        if (!droppedShape) {
            droppedShape = canvas.customShapes.find("id", id);
            if (!droppedShape || !shape.dropBehavior.dropHook(shape, droppedShape, e, ui)) {
                PMDesigner.msgFlash('Invalid operation.'.translate(), document.body, 'error', 5000, 5);
                droppedShape.setPosition(droppedShape.getOldX(), droppedShape.getOldY());
                return false;
            }
            if (droppedShape.parent.id !== shape.id) {

                selection = canvas.currentSelection;
                selection.asArray().forEach(function (item) {
                    var coordinates = PMUI.getPointRelativeToPage(item);

                    coordinates = PMUI.pageCoordinatesToShapeCoordinates(shape, null,
                        coordinates.x, coordinates.y, droppedShape);
                    shapesAdded.push({
                        shape: item,
                        container: shape,
                        x: coordinates.x,
                        y: coordinates.y,
                        topLeft: false
                    });
                });
                if (shape.getType() === 'PMLane') {
                    command = new PMCommandSwitchToLaneContainer(shapesAdded);
                } else {
                    command = new PMUI.command.CommandSwitchContainer(shapesAdded);
                }
                command.execute();
                canvas.commandStack.add(command);
                canvas.multipleDrop = true;
            }

            canvas.hideAllFocusLabels();
            shape.updateDimensions(10);
            canvas.updatedElement = null;

            droppedShape.ports.asArray().forEach(function (item) {
                var connectionx = item.connection,
                    result = PMDesigner.connectValidator.isValid(
                        connectionx.getSrcPort().parent,
                        connectionx.getDestPort().parent,
                        connectionx);

                if (result.conf && result.conf.segmentStyle !== connectionx.originalSegmentStyle) {
                    PMDesigner.msgFlash('Invalid flow between elements. Please delete the flow and reconnect the elements.'.translate(), document.body, 'error', 5000, 5);
                }
            });

            return;
        }

        if (droppedShape instanceof PMLane && _.find(shape.children.asArray(), function (i) { return !(i instanceof PMLane); })) {
            return PMDesigner.msgFlash('The lane can be dropped only over an empty pool. Please empty the pool before dropping a lane.'.translate(), document.body, 'error', 3000, 5);
        }

        position = PMUI.pageCoordinatesToShapeCoordinates(shape, e, null, null, droppedShape);


        command = new PMCommandCreateLane({
            pool: shape,
            lane: droppedShape,
            x: position.getX(),
            y: position.getY()
        });

        if (command) {
            canvas.hideAllCoronas();
            canvas.updatedElement = shape;
            canvas.commandStack.add(command);
            command.execute();

            canvas.hideAllFocusLabels();
            if (droppedShape.label && droppedShape.focusLabel) {
                droppedShape.label.getFocus();
            }
        }
    };
};
/**
 * Hook if PMEvent onDrop is correct
 * @param {PMUI.draw.Shape} shape
 * @param {PMUI.draw.Shape} customShape
 * @param {Object} e jQuery object that contains the properties on the
 * drop event
 * @param {Object} ui jQuery object that contains the properties on the
 * drop event
 * @returns {boolean}
 */
PMPoolDropBehavior.prototype.dropHook = function (shape, customShape, e, ui) {
    var result = true,
        shapesTypes = ['PMEvent', 'PMActivity'];
    if ((shapesTypes.indexOf(customShape.getType()) > -1) &&
        !PMDesigner.connectValidator.onDropMovementIsAllowed(shape, customShape)) {
        result = false;
    }
    return result;
};
var PMPool = function (options) {
    PMShape.call(this, options);
    /**
     *
     * @type {String}
     */
    this.name = '';
    /**
     *
     * @type {String}
     */
    this.proUid = '';
    /**
     *
     * @type {String}
     */
    this.proType = '';
    /**
     *
     * @type {Boolean}
     */
    this.executable = false;
    /**
     *
     * @type {Boolean}
     */
    this.closed = false;

    /**
     *
     * @type {String}
     */
    this.parentLane = null;
    /**
     *
     * @type {Number}
     */
    this.relPosition = 0;
    /**
     *
     * @type {Boolean}
     */
    this.sizeIdentical = false;
    /**
     *
     * @type {Number}
     */
    this.participants = 0;
    this.graphic = null;
    this.headLineCoord = 40;
    this.orientation = 'HORIZONTAL';
    this.participantObject = null;
    this.hasMinimun = false;
    this.zoomPoolArray = [1, 5, 1, 2, 1];
    /**
     * Private property which indicates if a shape was just created inside the pool.
     * @type {boolean}
     */
    this.shapeJustCreated = false;
    this.bpmnLanes = new PMUI.util.ArrayList();

    PMPool.prototype.initObject.call(this, options);
};
PMPool.prototype = new PMShape();
PMPool.prototype.type = 'PMPool';

PMPool.prototype.poolContainerBehavior = null;
PMPool.prototype.poolResizeBehavior = null;

PMPool.prototype.getDataObject = function () {
    var name = this.getName();
    return {
        lns_uid: this.getID(),
        lns_name: name,
        bou_x: this.x,
        bou_y: this.y,
        bou_width: this.width,
        bou_height: this.height,
        bou_container: 'bpmnDiagram',
        _extended: this.getExtendedObject()
    };
};

/**
 * Object init method (internal)
 * @param {Object} options
 */
PMPool.prototype.initObject = function (options) {
    var defaultOptions = {
        lns_name: 'Pool',
        proUid: '',
        proType: '',
        executable: false,
        closed: false,
        parentLane: '',
        relPosition: 0,
        sizeIdentical: false,
        participants: 0,
        orientation: 'HORIZONTAL',
        resizing: false,
        parentLane: null,
        identicalSize: false
    };
    $.extend(true, defaultOptions, options);
    this.setName(defaultOptions.lns_name)
        .setProUid(defaultOptions.proUid)
        .setProType(defaultOptions.proType)
        .setExecutable(defaultOptions.executable)
        .setClosed(defaultOptions.closed)
        .setParentLane(defaultOptions.parentLane)
        .setRelPosition(defaultOptions.relPosition)
        .setSizeIdentical(defaultOptions.sizeIdentical)
        .setOrientation(defaultOptions.orientation)
        .setParentLane(defaultOptions.parentLane)
        .setIdenticalSize(defaultOptions.identicalSize);
};

/**
 * Creates the HTML representation of the layer
 * @returns {HTMLElement}
 */
PMPool.prototype.createHTML = function () {
    PMShape.prototype.createHTML.call(this);
    return this.html;
};

PMPool.prototype.decreaseZIndex = function () {
    this.fixZIndex(this, 3);
    return this;
};

PMPool.prototype.applyZoom = function () {
    PMShape.prototype.applyZoom.call(this);
    this.updateDimensions(10);
};

PMPool.prototype.onUpdateLabel = function () {
    var label = this.getLabels().get(0);
    label.text.style["text-overflow"] = "ellipsis";
    label.text.style["overflow"] = "hidden";
    label.text.style["white-space"] = "nowrap";
    label.text.style["margin-left"] = "2%";
    label.text.setAttribute("title", label.getMessage());
    return this;
};
/**
 * Paints the corresponding Pool, in this case adds the
 * corresponding css classes and margins
 * @chainable
 */
PMPool.prototype.paint = function () {
    var zoomFactor = this.canvas.zoomFactor,
        label = this.getLabels().get(0);
    if (typeof this.graphic === 'undefined' || this.graphic === null) {
        this.graphic = new JSGraphics(this.id);
    } else {
        this.graphic.clear();
    }
    this.graphic.setColor('#3b4753'); //change color
    this.graphic.setStroke(2);
    if (this.orientation === 'VERTICAL') {
        this.graphic.drawLine(0, this.headLineCoord * zoomFactor,
            this.zoomWidth, this.headLineCoord * zoomFactor);
        label.setOrientation('horizontal');
        label.setLabelPosition('top');
    } else {
        this.graphic.drawLine(this.headLineCoord * zoomFactor, 0,
            this.headLineCoord * zoomFactor, this.zoomHeight - 5);
    }
    this.onUpdateLabel();
    this.graphic.paint();
    if (this.corona) {
        this.corona.paint();
        this.corona.hide();
    }
};

/**
 * Factory of pool behaviors. It uses lazy instantiation to create
 * instances of the different container behaviors
 * @param {String} type An string that specifies the container behavior we want
 * an instance to have, it can be regular or nocontainer
 * @return {ContainerBehavior}
 */
PMPool.prototype.containerBehaviorFactory = function (type) {
    if (type === 'pool') {
        if (!this.poolContainerBehavior) {
            this.poolContainerBehavior = new PoolContainerBehavior();
        }
        return this.poolContainerBehavior;
    } else {

        return PMShape.prototype.containerBehaviorFactory.call(this, type);
    }
};

/**
 * Handler for the onmousedown event, changes the draggable properties
 * according to the drag behavior that is being applied
 * @param {CustomShape} CustomShape
 * @returns {Function}
 * TODO Implement Mouse Down handler
 */
PMPool.prototype.onMouseDown = function (shape) {
    if (shape.getParentLane()) {
        return function (e, ui) {
            e.stopPropagation();
        };
    } else {
        return PMShape.prototype.onMouseDown.call(this, shape);
    }
};

PMPool.prototype.setResizeBehavior = function (behavior) {
    var factory = new PMUI.behavior.BehaviorFactory({
        products: {
            "regularresize": PMUI.behavior.RegularResizeBehavior,
            "Resize": PMUI.behavior.RegularResizeBehavior,
            "yes": PMUI.behavior.RegularResizeBehavior,
            "resize": PMUI.behavior.RegularResizeBehavior,
            "noresize": PMUI.behavior.NoResizeBehavior,
            "NoResize": PMUI.behavior.NoResizeBehavior,
            "no": PMUI.behavior.NoResizeBehavior,
            "poolResize": PMPoolResizeBehavior
        },
        defaultProduct: "noresize"
    });
    this.resizeBehavior = factory.make(behavior);
    if (this.html) {
        this.resize.init(this);
    }
    return this;
};
/**
 * Add a lane and refactor all lane positions and dimensions
 * @param {BPMNLane} newLane
 * @returns {PMPool}
 */
PMPool.prototype.addLane = function (newLane) {
    this.bpmnLanes.insert(newLane);
    newLane.setRelPosition(this.bpmnLanes.getSize());
    newLane.setParentPool(this.getID());
    return this;
};

/**
 * remove a lane and refactor all lane positions and dimensions
 * @param {BPMNLane} lane
 * @chainable
 */
PMPool.prototype.removeLane = function (lane) {
    this.bpmnLanes.remove(lane);
    return this;
};

/**
 * Get number of lanes into a pool
 * @return {Number}
 */
PMPool.prototype.getAllChildLanesNum = function (num) {
    var i, lane, childPool;
    for (i = 0; i < this.bpmnLanes.getSize(); i += 1) {
        lane = this.bpmnLanes.get(i);
        childPool = this.canvas.customShapes.find('id', lane.getChildPool());
        if (childPool) {
            num += childPool.getAllChildLanesNum(0);
        } else {
            num += 1;
        }
    }
    return num;
};

/**
 * Destroy a pool and update lane parentLane property if is a child pool
 * @chainable
 */
PMPool.prototype.destroy = function () {
    var parentLane = this.canvas.customShapes.find('id', this.getParentLane());
    if (parentLane) {
        this.parent.labels.get(0).setVisible(true);
        parentLane.childPool = null;

    }
    return this;
};

/**
 * Comparison function for ordering layers according to priority
 * @param {BPMNLane} lane1
 * @param {BPMNLane} lane2
 * @returns {boolean}
 */
PMPool.prototype.comparisonFunction = function (lane1, lane2) {
    return lane1.relPosition > lane2.relPosition;
};

/**
 * Refactor recursively all lane position an dimensions
 * because lanes has a particular behavior
 * @chainable
 */
PMPool.prototype.setLanePositionAndDimension = function (lane) {
    var numLanes,
        oldX,
        oldY,
        newx,
        newy,
        oldWidth,
        oldHeight,
        newWidth,
        newHeight,
        oldRelPosition,
        lane,
        i,
        label,
        numLanes2,
        newHeight2,
        newWidth2,
        childPool;

    numLanes = this.bpmnLanes.getSize();
    newx = (this.orientation === 'HORIZONTAL') ? this.headLineCoord : -1;
    newy = (this.orientation === 'HORIZONTAL') ? 0 : this.headLineCoord;
    //new lane width to update
    newWidth = (this.orientation === 'HORIZONTAL') ?
    this.getWidth() - this.headLineCoord - 2 : this.getWidth() / numLanes;

    //new lane height to update
    newHeight = (this.orientation === 'HORIZONTAL') ?
    (this.getHeight() - 1) / numLanes : this.getHeight() - this.headLineCoord;
    // FOR IDENTICAL OPTION
    numLanes2 = this.getAllChildLanesNum(0);
    newWidth2 = (this.orientation === 'HORIZONTAL') ?
    this.getWidth() - this.headLineCoord - 2 : this.getWidth() / numLanes2;
    newHeight2 = (this.orientation === 'HORIZONTAL') ?
    this.getHeight() / numLanes2 : this.getHeight() - this.headLineCoord;
    if (numLanes > 1) {
        lane.style.addProperties({
            'border-top': '2px solid #3b4753'
        });
        lane.setDimension(newWidth, lane.getHeight());
        lane.setPosition(newx, newy + this.getHeight());
        lane.setRelPosition(numLanes);
        //TODO: We need remove this method to implement Vertical direction POOL

        this.setDimension(this.getWidth(), this.getHeight() + lane.getHeight());
        this.label.updateDimension(false);
        this.paint();
    } else {
        lane.style.removeProperties(['border-top', 'border-left']);
        lane.setDimension(newWidth, this.getHeight());
        lane.setPosition(newx, newy);
        lane.setRelPosition(numLanes);
    }
    lane.label.updateDimension(false);
    this.label.updateDimension(false);
    lane.paint();
    lane.updateBounds(lane.businessObject.di);
    this.updateBounds(this.participantObject.di);
    return this;
};

/**
 * Updates all child lanes when a lane has been removed
 * @param lane
 * @returns {PMPool}
 */
PMPool.prototype.updateOnRemoveLane = function (lane) {
    var i, diffH = 0, nextLane, tempLane = lane, tempX = lane.getX(), tempY = lane.getY(), newY = 0;
    for (i = lane.getRelPosition() - 1; i < this.bpmnLanes.getSize(); i += 1) {
        nextLane = this.bpmnLanes.get(i);
        if (i === 0) {
            nextLane.style.removeProperties(['border-top', 'border-left']);
        }
        if (i > 0) {
            newY = this.getNewPositionY(i);
        }
        nextLane.setPosition(lane.getX(), newY);
        nextLane.setRelPosition(nextLane.getRelPosition() - 1);
        nextLane.paint();
    }
    if (this.bpmnLanes.getSize() > 0) {
        this.setDimension(this.getWidth(), this.getHeight() - lane.getHeight());
        this.paint();
    }

    return this;
};

/**
+ * Get the new position in Y axis for a lane
+ * @param index
+ * @returns {integer}
+ */
PMPool.prototype.getNewPositionY = function (index) {
    var i,
        nextLane,
        newY = 0;
    for (i = 0; i < index; i += 1) {
        nextLane = this.bpmnLanes.get(i);
        newY += this.bpmnLanes.get(i).getHeight();
    }
    return newY;
};

/**
 * Updates all bpmn child lanes when resize event has been finished
 */
PMPool.prototype.updateBpmnOnResize = function () {
    var lane,
        i;
    this.updateBpmn();
    for (i = 0; i < this.bpmnLanes.getSize(); i += 1) {
        lane = this.bpmnLanes.get(i);
        lane.updateBpmn();
    }
};

PMPool.prototype.setMinimunsResize = function (top) {
    var $shape = $(this.getHTML()),
        lane,
        i,
        j,
        shape,
        limits,
        margin = 15,
        laneChildrens = new PMUI.util.ArrayList(),
        minH,
        hdiff,
        laneHeightMin = 0,
        minW = 200;
    if (!this.hasMinimun) {
        if (top) {
            lane = this.bpmnLanes.getFirst();
            minH = this.getHeight() - lane.getHeight() + (30 * this.canvas.getZoomFactor());
            for (i = this.bpmnLanes.getSize() - 1; i >= 0; i -= 1) {
                lane = this.bpmnLanes.get(i);
                for (j = 0; j < lane.getChildren().getSize(); j += 1) {
                    shape = lane.getChildren().get(j);
                    laneChildrens.insert(
                        {
                            x: shape.x,
                            y: shape.y + laneHeightMin,
                            width: shape.width,
                            height: shape.height
                        }
                    );
                }
                laneHeightMin += lane.height;
            }


        } else {
            lane = this.bpmnLanes.getLast();
            minH = lane.getY() + (30 * this.canvas.getZoomFactor());
            for (i = 0; i < this.bpmnLanes.getSize(); i += 1) {
                lane = this.bpmnLanes.get(i);
                for (j = 0; j < lane.getChildren().getSize(); j += 1) {
                    shape = lane.getChildren().get(j);
                    laneChildrens.insert(
                        {
                            x: shape.x,
                            y: shape.y + laneHeightMin,
                            width: shape.width,
                            height: shape.height
                        }
                    );
                }
                laneHeightMin += lane.height;
            }
        }
        if (laneChildrens.getSize() > 0) {
            laneChildrens.insert(
                {
                    x: 0,
                    y: 0,
                    width: 0,
                    height: minH
                }
            );
            limits = laneChildrens.getDimensionLimit();
            minW = (limits[1] + margin + this.headLineCoord) * this.canvas.getZoomFactor();
            minH = (limits[2] + margin) * this.canvas.getZoomFactor();
        }
        $shape.resizable('option', 'minWidth', minW);
        $shape.resizable('option', 'minHeight', minH);
        this.hasMinimun = true;
    }
};

/**
 * Updates all child lanes  connections when resize event has been finished
 */
PMPool.prototype.updateConnectionsChildrens = function () {
    var i,
        j,
        shapeLane,
        shape = this;
    for (i = 0; i < shape.children.asArray().length; i++) {
        if (shape.children.asArray()[i].type == "PMLane") {
            shapeLane = shape.children.asArray()[i];
            for (j = 0; j < shapeLane.children.asArray().length; j++) {
                shapeLane.children.asArray()[i].refreshConnections(false, true);
            }
        } else {
            shape.children.asArray()[i].refreshConnections(false, true);
        }
    }
};
/**
 * Updates dimesions and positions to pool lane childs
 */

PMPool.prototype.updateAllLaneDimension = function(avoidWeight) {
    var newWidth = 0,
        lane,
        newY = 0,
        i,
        parentHeight = 0,
        laneOldY,
        containerWidth,
        delta;
    for (i = 0; i < this.bpmnLanes.getSize(); i += 1) {
        lane = this.bpmnLanes.get(i);
        if (lane.getWidth() > newWidth) {
            newWidth = lane.getWidth();
        }
        laneOldY = lane.y;
        lane.setDimension(newWidth, lane.getHeight());
        lane.setRelPosition(i + 1);
        if (i > 0) {
            newY += this.bpmnLanes.get(i - 1).getHeight();
            lane.setPosition(lane.getX(), newY);
            lane.style.addProperties({
                'border-top': '2px solid #3b4753'
            });
        } else {
            lane.style.removeProperties(['border-top', 'border-left']);

        }
        parentHeight += lane.getHeight();
        lane.paint();
        lane.updateBounds(lane.businessObject.di);
        //updating connections into a lane
        lane.canvas.refreshArray.clear();
        delta = {
            dx: 0,
            dy: lane.y - laneOldY
        };
        if (delta.dx > 0 || delta.dy >0){
            lane.fixConnectionsOnResize(lane.resizing, true);
            lane.laneRefreshConnections(delta);
        }
    }
    newWidth = newWidth && !avoidWeight ? newWidth + this.headLineCoord + 2.1: this.getWidth();
    this.setDimension(newWidth, parentHeight);
    this.paint();
    this.updateBounds(this.participantObject.di);
};

/**
 * to multiple pool support
 * @returns {*}
 */
PMPool.prototype.getMasterPool = function () {
    if (this.parent.family !== 'Canvas') {
        return this.parent.parent.getMasterPool();
    } else {
        return this;
    }
};
/**
 * Set pool name
 * @param {String} name
 * @returns {PMPool}
 */
PMPool.prototype.setName = function (name) {
    if (typeof name !== 'undefined') {
        this.lns_name = name;
        if (this.label) {
            this.label.setMessage(name);
        }
    }
    return this;
};
/**
 * Set pool type
 * @param {String} name
 * @returns {PMPool}
 */
PMPool.prototype.setType = function (newType) {
    this.type = newType;
    return this;
};
/**
 * Set proUid attached to pool
 * @param {String} uid
 * @returns {PMPool}
 */
PMPool.prototype.setProUid = function (uid) {
    this.proUid = uid;
    return this;
};
/**
 * Set pool type
 * @param {String} proType
 * @returns {PMPool}
 */
PMPool.prototype.setProType = function (proType) {
    this.proType = proType;
    return this;
};
/**
 * Set executable mode to pool
 * @param {Boolean} executable
 * @returns {PMPool}
 */
PMPool.prototype.setExecutable = function (executable) {
    this.executable = executable;
    return this;
};
/**
 * Set closed mode to pool
 * @param {Boolean} closed
 * @returns {PMPool}
 */
PMPool.prototype.setClosed = function (closed) {
    this.closed = closed;
    return this;
};
/**
 * Set pool orientation
 * @param {String} orientation
 * @returns {PMPool}
 */
PMPool.prototype.setOrientation = function (orientation) {
    this.orientation = orientation;
    return this;
};
/**
 * Set pool resizing mode
 * @param {String} orientation
 * @returns {PMPool}
 */
PMPool.prototype.setResizing = function (resizing) {
    this.resizing = resizing;
    return this;
};
/**
 * Set parent Lane UID to pool
 * @param {String} parentLane
 * @returns {PMPool}
 */
PMPool.prototype.setParentLane = function (parentLane) {
    this.parentLane = parentLane;
    return this;
};
/**
 * Set pool rel position
 * @param {Number} relPosition
 * @returns {PMPool}
 */
PMPool.prototype.setRelPosition = function (relPosition) {
    this.relPosition = relPosition;
    return this;
};
/**
 * Set size identical mode if we want identical size of pool childs
 * @param {Number} relPosition
 * @returns {PMPool}
 */
PMPool.prototype.setSizeIdentical = function (sizeIdentical) {
    this.sizeIdentical = sizeIdentical;
    return this;
};
/**
 * Set number of participants into a pool
 * @param {Number} num
 * @returns {PMPool}
 */
PMPool.prototype.setParticipants = function (num) {
    this.participants = num;
    return this;
};

/**
 * Set identical Size to a pool
 * @param {Boolean} val
 * @returns {PMPool}
 */
PMPool.prototype.setIdenticalSize = function (val) {
    this.identicalSize = val;
    return this;
};
/**
 * Get the pool name
 * @returns {String}
 */
PMPool.prototype.getName = function () {
    return this.lns_name;
};
/**
 * Get the pool type
 * @returns {String}
 */
PMPool.prototype.getType = function () {
    return this.type;
};
/**
 * Get the process uid attached to pool
 * @returns {String}
 */
PMPool.prototype.getProUid = function () {
    return this.proUid;
};
/**
 * Get the process uid attached to pool
 * @returns {String}
 */
PMPool.getProcessType = function () {
    return this.proType;
};
/**
 * Get a boolean value if the pool is in executable mode
 * @returns {Boolean}
 */
PMPool.isExecutable = function () {
    return this.executable;
};
/**
 * Get a boolean value if the pool is closed
 * @returns {Boolean}
 */
PMPool.prototype.isClosed = function () {
    return this.closed;
};
/**
 * Get the pool orientation
 * @returns {Sring}
 */
PMPool.prototype.getOrientation = function () {
    return this.orientation;
};
/**
 * Get the pool resizing mode
 * @returns {String}
 */
PMPool.prototype.getResizing = function () {
    return this.resizing;
};
/**
 * Get the relative position of the pool
 * @returns {Number}
 */
PMPool.prototype.getRelPosition = function () {
    return this.relPosition;
};
/**
 * Get a boolean value if pool have identical size
 * @returns {Boolean}
 */
PMPool.prototype.isSizeIdentical = function () {
    return this.sizeIdentical;
};
/**
 * Get a parent lane
 * @returns {BPMNLane}
 */
PMPool.prototype.getParentLane = function () {
    return this.parentLane;
};
/**
 * Get a number of participants asociate to pool
 * @returns {Boolean}
 */
PMPool.prototype.getParticipants = function () {
    return this.participants;
};

/**
 * Get a identical size value
 * @returns {Boolean}
 */
PMPool.prototype.getIdenticalSize = function () {
    return this.identicalSize;
};
/**
 * Updates the dimensions and position of this shape (note: 'this' is a shape)
 * @param {Number} margin the margin for this element to consider towards the
 * shapes near its borders
 * @chainable
 */
PMPool.prototype.updateDimensions = function (margin) {
    // update its size (if an child grew out of the shape)
    // only if it's not the canvas
    if (this.family !== 'Canvas') {
        this.updateSize(margin);
        this.refreshConnections();
        this.resizeBehavior.updateResizeMinimums(this);
        this.updateDimensions.call(this.parent, margin);
    }
    return this;
};
/**
 * Updates the dimensions and position of this pool relative to lane childs (note: 'this' is a shape)
 * @chainable
 */
PMPool.prototype.updateDimensionsWithLanes = function (margin) {
    // update its size (if an child grew out of the shape)
    // only if it's not the canvas
    var lane,
        i,
        j,
        limits,
        minW,
        minH,
        childrens,
        margin = 15,
        shape,
        $shape = $(this.getHTML()),
        laneHeightMin = 0,
        laneHeightSum = 0,
        lastLane,
        laneChildrens = new PMUI.util.ArrayList();

    for (i = 0; i < this.bpmnLanes.getSize(); i += 1) {
        lane = this.bpmnLanes.get(i);
        childrens = lane.getChildren();
        for (j = 0; j < childrens.getSize(); j += 1) {
            shape = childrens.get(j);
            if (i < this.bpmnLanes.getSize() - 1) {
                laneHeightSum += shape.parent.height;
            } else {
                laneHeightSum = 0;
            }
            laneChildrens.insert(
                {
                    x: shape.x,
                    y: shape.y + laneHeightMin,
                    width: shape.width,
                    height: shape.height
                }
            );
        }
        laneHeightMin += lane.height;
    }
    if (laneChildrens.getSize() > 0) {
        limits = laneChildrens.getDimensionLimit();
        minW = (limits[1] + margin + this.headLineCoord) * this.canvas.getZoomFactor();
        minH = (limits[2] + margin) * this.canvas.getZoomFactor();
    } else {
        minW = 300 * this.canvas.getZoomFactor();
        lastLane = this.bpmnLanes.get(this.bpmnLanes.getSize() - 2);
        if (lastLane) {
            minH = lastLane.getY() + lastLane.getHeight() + (30 * this.canvas.getZoomFactor());
        }
    }
    $shape.resizable();
    $shape.resizable('option', 'minWidth', minW);
    $shape.resizable('option', 'minHeight', minH);
    return this;
};

PMPool.prototype.stringify = function () {
    var inheritedJSON = PMShape.prototype.stringify.call(this),
        thisJSON = {
            name: this.getName(),
            proUid: this.getProUid(),
            proType: this.proType,
            executable: this.executable,
            closed: this.isClosed(),
            parentLane: this.parentLane,
            relPosition: this.getRelPosition(),
            sizeIdentical: this.isSizeIdentical(),
            participants: this.getParticipants(),
            orientation: this.getOrientation(),
            parentLane: this.getParentLane()
        };
    $.extend(true, inheritedJSON, thisJSON);
    return inheritedJSON;
};
/**
 * Creates bussines model to export to standard bpmn file
 * @param type xml tag to export bpmn file
 */
PMPool.prototype.createBpmn = function (type) {
    var bpmnCollaboration;
    if (!this.parent.businessObject.di) {
        this.parent.createBPMNDiagram();
    }
    if (!(_.findWhere(PMDesigner.businessObject.get('rootElements'), {$type: "bpmn:Collaboration"}))) {
        bpmnCollaboration = PMDesigner.moddle.create('bpmn:Collaboration', {id: 'pmui-' + PMUI.generateUniqueId()});
        PMDesigner.businessObject.get('rootElements').unshift(bpmnCollaboration);
        this.parent.businessObject.di.bpmnElement = bpmnCollaboration;
        bpmnCollaboration.participants = [];
    } else {
        bpmnCollaboration = _.findWhere(PMDesigner.businessObject.get('rootElements'), {$type: "bpmn:Collaboration"});
    }
    if (!this.businessObject.elem) {
        this.createWithBpmn(type, 'participantObject');
        this.updateBounds(this.participantObject.di);
        this.updateSemanticParent(this.participantObject, {elem: bpmnCollaboration});
        this.updateDiParent(this.participantObject.di, this.parent.businessObject.di);
    }
};
/**
 * update participant parent .bpmn file
 * @param businessObject
 * @param newParent
 */
PMPool.prototype.updateSemanticParent = function (businessObject, newParent) {
    var children;
    if (businessObject.elem.$parent && businessObject.elem.$parent === newParent.elem) {
        return;
    }
    if (businessObject.elem.$parent) {
        // remove from old parent
        children = businessObject.elem.$parent.get('participants');
        CollectionRemove(children, businessObject.elem);
    }
    if (!newParent.elem) {
        businessObject.elem.$parent = null;
    } else {
        // add to new parent
        children = newParent.elem.get('participants');
        children.push(businessObject.elem);
        businessObject.elem.$parent = newParent.elem;
    }
};

PMPool.prototype.updateDiParent = function (di, parentDi) {
    PMShape.prototype.updateDiParent.call(this, di, parentDi);
};


/**
 * Overwrite the parent function to set the dimension
 * @param {Number} x
 * @param {Number} y
 * @return {*}
 */
PMPool.prototype.setDimension = function (x, y) {
    var factorArray = [10.5, 5, 1.1, 2, 1],  //factors used to fix browsers round problems
        zoomFactor = this.getCanvas().getZoomFactor(),
        i,
        lanesCount = this.bpmnLanes.getSize(),
        lane,
        containerWidth = x - this.headLineCoord - (factorArray[this.getCanvas().zoomPropertiesIndex] * zoomFactor);
    PMUI.draw.CustomShape.prototype.setDimension.call(this, x, y);
    if (this.label) {
        //validation for vertical labels case pool and lanes
        this.label.updateDimension(false);
    }

    for (i = 0; i < lanesCount; i += 1) {
        lane = this.bpmnLanes.get(i);
        lane.setDimension(containerWidth, lane.getHeight());
    }

    this.paint();
    return this;
};

/**
 * create busines object to moodle bpmn export
 */
PMPool.prototype.createBusinesObject = function () {
    var participant = _.findWhere(this.participantObject.elem.$parent.get('participants'),
            {id: this.participantObject.elem.id}),
        bpmnProcess = PMDesigner.moddle.create('bpmn:Process',
            {id: 'pmui-' + PMUI.generateUniqueId()});

    PMDesigner.businessObject.get('rootElements').push(bpmnProcess);
    this.businessObject.elem = bpmnProcess;
    this.businessObject.di = this.canvas.businessObject.di;
    participant.processRef = bpmnProcess;
};
/**
 * @inheritDoc
 */
PMPool.prototype.dropBehaviorFactory = function (type, selectors) {
    if (type === 'pmcontainer') {
        this.pmConnectionDropBehavior = this.pmConnectionDropBehavior || new PMPoolDropBehavior(selectors);
        return this.pmConnectionDropBehavior;
    }
    return PMShape.prototype.dropBehaviorFactory.apply(this, arguments);
};

PMPool.prototype.removeBpmn = function () {
    var coll, children, pros;

    if ((_.findWhere(PMDesigner.businessObject.get('rootElements'), {$type: "bpmn:Collaboration"}))) {
        coll = _.findWhere(PMDesigner.businessObject.get('rootElements'), {$type: "bpmn:Collaboration"});
        if (coll.participants.length === 1) {
            children = PMDesigner.businessObject.get('rootElements');
            CollectionRemove(children, coll);
            //PMDesigner.businessObject.get
            if (this.parent.businessObject.di) {
                this.parent.businessObject.di.bpmnElement = this.parent.businessObject;
            }
        }
    }
    if (this.businessObject.elem && (_.findWhere(PMDesigner.businessObject.get('rootElements'), {
            $type: "bpmn:Process",
            id: this.businessObject.elem.id
        }))) {
        pros = _.findWhere(PMDesigner.businessObject.get('rootElements'), {
            $type: "bpmn:Process",
            id: this.businessObject.elem.id
        });
        children = PMDesigner.businessObject.get('rootElements');
        CollectionRemove(children, pros);
        this.businessObject.elem = null;
    }
    this.updateSemanticParent(this.participantObject, {elem: null});
    this.updateDiParent(this.participantObject.di);
    if (this.businessObject.di
        && this.businessObject.di.planeElement
        && this.businessObject.di.planeElement.length === 0) {
        this.parent.removeBPMNDiagram();
    }
};

PMPool.prototype.updateBpmn = function () {
    this.updateBounds(this.participantObject.di);
};
/**
 * @inheritdoc
 */
PMPool.prototype.onMouseUp = function (customShape) {
    var that = this;
    return function (e, ui) {
        if (customShape.canvas.canCreateShape && customShape.canvas.connectStartShape.getID() !== customShape.getID()) {
            that.shapeJustCreated = true;
        }

        (PMShape.prototype.onMouseUp.call(that, customShape))(e, ui);
    };
};
/**
 * @inheritdoc
 */
PMPool.prototype.onClick = function (customShape) {
    var that = this;
    return function (e) {
        if (that.shapeJustCreated) {
            customShape.canvas.hideAllCoronas();
        } else {
            (PMShape.prototype.onClick.call(that, customShape))(e);
        }
        that.shapeJustCreated = false;
        if (this.helper) {
            $(this.helper.html).remove();
        }
        customShape.wasDragged = false;
        e.stopPropagation();
    };
};
var PMLane = function (options) {
    PMShape.call(this, options);
    /**
     *
     * @type {String}
     */
    this.name = '';
    /**
     *
     * @type {String}
     */
    this.proUid = '';
    /**
     *
     * @type {String}
     */
    this.childLaneset = '';
    /**
     *
     * @type {Number}
     */
    this.relPosition = 0;

    this.parentPool = null;

    this.childPool = null;
    /**
     * Private property which indicates if a shape was just created inside the lane.
     * @type {boolean}
     */
    this.shapeJustCreated = false;

    PMLane.prototype.initObject.call(this, options);
};

PMLane.prototype = new PMShape();
PMLane.prototype.type = 'PMLane';
PMLane.prototype.laneContainerBehavior = null;


PMLane.prototype.getDataObject = function () {
    var name = this.getName();
    return {
        lan_uid: this.id,
        lns_uid: this.parent.id,
        lan_name: name,
        bou_x: this.x,
        bou_y: this.y,
        bou_width: this.width,
        bou_height: this.height,
        bou_rel_position: this.getRelPosition(),
        element_id: this.parent.id,
        bou_container: 'bpmnPool',
        lan_parent_pool: this.parentPool,
        _extended: this.getExtendedObject()
    };
};

PMLane.prototype.initObject = function (options) {
    var defaultOptions = {
        lan_name: 'Lane',
        proUid: null,
        childLaneset: null,
        relPosition: 0,
        childPool: null,
        orientation: 'HORIZONTAL'
    };
    $.extend(true, defaultOptions, options);

    this.setName(defaultOptions.lan_name)
        .setProUid(defaultOptions.proUid)
        .setRelPosition(defaultOptions.relPosition)
        .setChildPool(defaultOptions.childPool)
        .setOrientation(defaultOptions.orientation);
};
PMLane.prototype.paint = function () {
    if (this.parent.orientation === 'VERTICAL') {
        this.getLabels().get(0).setOrientation('horizontal');
        this.getLabels().get(0).setLabelPosition('top');
    } else {
        this.getLabels().get(0).setOrientation('vertical');
        this.getLabels().get(0).setLabelPosition('center-left', 20, 0);

    }
    if (this.childPool) {
        this.getLabels().get(0).setVisible(false);
    }
    this.getLabels().get(0).setZOrder(1);
    if (this.corona) {
        this.corona.paint();
        this.corona.hide();
    }
};

/**
 * Factory of pool behaviors. It uses lazy instantiation to create
 * instances of the different container behaviors
 * @param {String} type An string that specifies the container behavior we want
 * an instance to have, it can be regular or nocontainer
 * @return {ContainerBehavior}
 */
PMLane.prototype.containerBehaviorFactory = function (type) {
    if (type === 'lane') {
        if (!this.laneContainerBehavior) {
            this.laneContainerBehavior = new LaneContainerBehavior();
        }
        return this.laneContainerBehavior;
    } else {

        return PMShape.prototype.containerBehaviorFactory.call(this, type);
    }
};
PMLane.prototype.setResizeBehavior = function (behavior) {
    var factory = new PMUI.behavior.BehaviorFactory({
        products: {
            "regularresize": PMUI.behavior.RegularResizeBehavior,
            "Resize": PMUI.behavior.RegularResizeBehavior,
            "yes": PMUI.behavior.RegularResizeBehavior,
            "resize": PMUI.behavior.RegularResizeBehavior,
            "noresize": PMUI.behavior.NoResizeBehavior,
            "NoResize": PMUI.behavior.NoResizeBehavior,
            "no": PMUI.behavior.NoResizeBehavior,
            "laneResize": PMLaneResizeBehavior
        },
        defaultProduct: "noresize"
    });
    this.resizeBehavior = factory.make(behavior);
    if (this.html) {
        this.resize.init(this);
    }
    return this;
};
/**
 * Convert a lane to Pool
 */
PMLane.prototype.transformToPool = function () {
    var customShape = new BPMNPool({
        width: this.getWidth() - 1,
        height: this.getHeight() - 1,
        "canvas": this.canvas,
        "connectAtMiddlePoints": false,
        container: 'pool',
        topLeft: true,
        drop: {
            type: 'bpmnconnectioncontainer',
            selectors: ["#BPMNLane",
                "#BPMNActivity", "#BPMNSubProcess", "#BPMNStartEvent",
                "#BPMNEndEvent", "#BPMNIntermediateEvent", "#BPMNGateway",
                "#BPMNDataObject", "#BPMNDataStore", "#BPMNGroup",
                "#BPMNAnnotation", ".bpmn_droppable", ".custom_shape"],
            overwrite: true
        },
        drag: 'noDrag',
        resizeBehavior: "no",
        resizeHandlers: {
            type: "Rectangle",
            total: 4,
            resizableStyle: {
                cssProperties: {
                    'background-color': "rgb(0, 255, 0)",
                    'border': '1px solid black'
                }
            },
            nonResizableStyle: {
                cssProperties: {
                    'background-color': "white",
                    'border': '1px solid black'
                }
            }
        },
        "style": {
            cssClasses: []
        },
        layers: [
            {
                layerName: "first-layer",
                priority: 2,
                visible: true,
                style: {
                    cssProperties: {}
                }
            }
        ],
        labels: [
            {
                message: this.getMessage(),
                width: 50,
                height: 10,
                orientation: 'vertical',
                position: {
                    location: 'center-left',
                    diffX: 20,
                    diffY: 0
                },
                updateParent: false
            }
        ],
        orientation: this.parent.getOrientation(),
        name: this.getName(),
        parentLane: this.getID()
    });
    this.labels.get(0).setVisible(false);
    this.setChildPool(customShape.getID());
    this.addElement(customShape, 1, 0, true);
    customShape.labels.get(0).setVisible(true);
    this.canvas.triggerSinglePropertyChangeEvent(this, 'childPool', this.getChildPool(), customShape.getID());
    customShape.attachListeners();
    this.canvas.updatedElement = customShape;
    var command = new jCore.CommandCreate(customShape);
    this.canvas.commandStack.add(command);
    command.execute();
};

PMLane.prototype.createHTML = function () {
    PMShape.prototype.createHTML.call(this);
    if (this.getRelPosition() > 1) {
        this.style.addProperties({
            'border-top': '2px solid #3b4753'
        });
    }
    return this.html;
};

PMLane.prototype.attachListeners = function () {
    var $lane = $(this.html);
    if (!this.html) {
        return this;
    }
    PMShape.prototype.attachListeners.call(this);
};

PMLane.prototype.onMouseDown = function (shape) {
    return function (e, ui) {
        if (shape.getType() === 'PMPool' || shape.getType() === 'PMLane') {
            shape.canvas.cancelConnect();
        }
        PMUI.draw.CustomShape.prototype.onMouseDown.call(this, shape)(e, ui);
        shape.setDragBehavior('nodrag');
    };
};

PMLane.prototype.destroy = function () {
    var parentPool = this.canvas.customShapes.find('id', this.parent.getID());
    if (parentPool) {
        parentPool.removeLane(this);
        parentPool.updateOnRemoveLane(this);

    }
    return this;
};
PMLane.prototype.setChildPool = function (pPool) {
    this.childPool = pPool;
    return this;
};
PMLane.prototype.setParentPool = function (pPool) {
    this.parentPool = pPool;
    return this;
};
/**
 * Set lane name
 * @param {String} name
 * @returns {BPMNLane}
 */
PMLane.prototype.setName = function (name) {
    if (typeof name !== 'undefined') {
        this.lan_name = name;
        if (this.label) {
            this.label.setMessage(name);
        }
    }
    return this;
};
/**
 * Set lane type
 * @param {String} newType
 * @returns {BPMNLane}
 */
PMLane.prototype.setType = function (newType) {
    this.type = newType;
    return this;
};
/**
 * Set process uid asociated to lane
 * @param {String} uid
 * @returns {BPMNLane}
 */
PMLane.prototype.setProUid = function (uid) {
    this.proUid = uid;
    return this;
};
/**
 * Set the relative position of lane
 * @param {Number} relPosition
 * @returns {BPMNLane}
 */
PMLane.prototype.setRelPosition = function (relPosition) {
    this.relPosition = relPosition;
    return this;
};

PMLane.prototype.setOrientation = function (orientation) {
    this.orientation = orientation;
    return this;
};
/**
 * Set ChildLaneset to lane
 * @param {BPMNPool} childLane
 * @returns {BPMNLane}
 */
PMLane.prototype.setChildLaneset = function (childLane) {
    this.childLaneset = childLane;
    return this;
};
/**
 * Get the lane name
 * @returns {String}
 */
PMLane.prototype.getName = function () {
    return this.lan_name;
};

PMLane.prototype.getType = function () {
    return this.type;
};
/**
 * Get pro uid asociated to lane
 * @returns {String}
 */
PMLane.prototype.getProUid = function () {
    return this.proUid;
};
/**
 * Get the relative position of lane
 * @returns {Number}
 */
PMLane.prototype.getRelPosition = function () {
    return this.relPosition;
};
PMLane.prototype.getOrientation = function () {
    return this.orientation;
};
/**
 * Get ChildLaneSet
 * @returns {BPMNPool}
 */
PMLane.prototype.getChildLaneset = function () {
    return this.childLaneset;
};
PMLane.prototype.getChildPool = function () {
    return this.childPool;
};
PMLane.prototype.applyZoom = function () {
    PMShape.prototype.applyZoom.call(this);
    this.updateDimensions();
};
PMLane.prototype.updateDimensions = function (margin) {
    var minW,
        minH,
        children = this.getChildren(),
        limits = children.getDimensionLimit(),
        margin = 30,
        $shape = $(this.getHTML());
    minW = (limits[1] + margin) * this.canvas.getZoomFactor();
    minH = (limits[2] + margin) * this.canvas.getZoomFactor();
    // update jQueryUI's minWidth and minHeight
    $shape.resizable();
    $shape.resizable('option', 'minWidth', minW);
    $shape.resizable('option', 'minHeight', minH);
    return this;
};

/**
 * That method shows or hides lane resize behaviors
 * @param visible
 * @returns {PMLane}
 */
PMLane.prototype.showOrHideResizeHandlers = function (visible) {
    var i;
    if (!visible) {
        visible = false;
    }
    for (i = 0; i < this.cornerResizeHandlers.getSize(); i += 1) {
        this.cornerResizeHandlers.get(i).setVisible(false);
    }

    for (i = 0; i < this.midResizeHandlers.getSize(); i += 1) {
        if (i === 2) {
            this.midResizeHandlers.get(i).setVisible(visible);
        } else {
            this.midResizeHandlers.get(i).setVisible(false);
        }
    }
    return this;
};

PMLane.prototype.updateAllRelatedDimensions = function (avoidWeight) {
    this.parent.updateAllLaneDimension(avoidWeight);
    this.parent.paint();
    return this;
};

PMLane.prototype.stringify = function () {
    var inheritedJSON = PMShape.prototype.stringify.call(this),
        thisJSON = {
            name: this.getName(),
            proUid: this.getProUid(),
            relPosition: this.getRelPosition(),
            childPool: this.getChildPool()
        };
    $.extend(true, inheritedJSON, thisJSON);
    return inheritedJSON;
};

PMLane.prototype.createBpmn = function (type) {
    var bpmnLaneset;
    if (!this.parent.businessObject.elem) {
        this.parent.createBusinesObject();
    }
    bpmnLaneset = this.createLaneset();
    this.createWithBpmn('bpmn:Lane', 'businessObject');
    this.updateBounds(this.businessObject.di);
    this.updateSemanticParent(this.businessObject, { elem: bpmnLaneset });
    this.updateDiParent(this.businessObject.di, this.parent.parent.businessObject.di);

};

PMLane.prototype.createLaneset = function () {
    var bpmnLaneset;
    if (!(_.findWhere(this.parent.businessObject.elem.get('flowElements'), {$type: "bpmn:LaneSet"}))) {
        bpmnLaneset = PMDesigner.moddle.create('bpmn:LaneSet');
        bpmnLaneset.$parent = this.parent.businessObject.elem;
        this.parent.businessObject.elem.get('flowElements').push(bpmnLaneset);
    } else {
        bpmnLaneset = _.findWhere(this.parent.businessObject.elem.get('flowElements'), {$type: "bpmn:LaneSet"});

    }
    return bpmnLaneset;
};

PMLane.prototype.updateSemanticParent = function (businessObject, newParent) {
    var children;
    if (businessObject.elem.$parent === newParent.elem) {
        return;
    }
    if (businessObject.elem.$parent) {
        // remove from old parent
        children = businessObject.elem.$parent.get('lanes');
        CollectionRemove(children, businessObject.elem);
    }

    if (!newParent.elem) {
        businessObject.elem.$parent = null;
    } else {
        // add to new parent
        children = newParent.elem.get('lanes');
        children.push(businessObject.elem);
        businessObject.elem.$parent = newParent.elem;
    }
};

PMLane.prototype.removeBpmn = function () {
    var bpmnLaneset;
    this.parent.updateBounds(this.parent.participantObject.di);
    PMShape.prototype.removeBpmn.call(this);
    if ((_.findWhere(this.parent.businessObject.elem.get('flowElements'), {$type: "bpmn:LaneSet"}))) {
        bpmnLaneset = _.findWhere(this.parent.businessObject.elem.get('flowElements'), {$type: "bpmn:LaneSet"});
        if (bpmnLaneset.lanes < 1) {
            CollectionRemove(this.parent.businessObject.elem.get('flowElements'), bpmnLaneset);
        }
    }
};

PMLane.prototype.updateBpmn = function () {
    this.updateBounds(this.businessObject.di);
};

PMLane.prototype.laneRefreshConnections = function (delta) {
    var i,
        max,
        srcElem,
        destElem,
        connection,
        delta;
    for (i = 0, max = this.canvas.refreshArray.getSize(); i < max; i += 1) {
        connection = this.canvas.refreshArray.get(i);
        srcElem = connection.getSrcPort().parent;
        destElem = connection.getDestPort().parent;
        if (this.isConnetionIntoLane(srcElem)
            && this.isConnetionIntoLane(destElem)) {
            connection.reconnectUser(delta, false);
            connection.setSegmentMoveHandlers();
            connection.checkAndCreateIntersectionsWithAll();
            this.canvas.triggerUserStateChangeEvent(connection);
        } else {
            connection.reconnectManhattah();
            connection.setSegmentMoveHandlers();
            connection.checkAndCreateIntersectionsWithAll();
            this.canvas.triggerConnectionStateChangeEvent(connection);
        }


    }
};

PMLane.prototype.isConnetionIntoLane = function (shape) {
    var i,
        max,
        child;
    for (i = 0, max = this.children.getSize(); i < max; i += 1) {
        child = this.children.get(i);
        if (shape.getID() === child.getID()) {
            return true;
        }
    }
    return false;
};
/**
 * @inheritdoc
 */
PMLane.prototype.onMouseUp = function (customShape) {
    var that = this;
    return function (e, ui) {
        if (customShape.canvas.canCreateShape && customShape.canvas.connectStartShape.getID() !== customShape.getID()) {
            that.shapeJustCreated = true;
        }

        (PMShape.prototype.onMouseUp.call(that, customShape))(e, ui);
    };
};
/**
 * @inheritdoc
 */
PMLane.prototype.onClick = function (customShape) {
    var that = this;
    return function (e) {
        if (that.shapeJustCreated) {
            customShape.canvas.hideAllCoronas();
        } else {
            (PMShape.prototype.onClick.call(that, customShape))(e);
        }
        that.shapeJustCreated = false;
        if (this.helper) {
            $(this.helper.html).remove();
        }
        customShape.wasDragged = false;
        e.stopPropagation();
    };
};

/**
 * Command create: command created when a lane is created (from the toolbar)
 * @class BPMNCommandCreateLane
 * @constructor
 */
var PMCommandCreateLane = function (settngs) {
    PMUI.command.Command.call(this, settngs);
};

PMCommandCreateLane.prototype = new PMUI.command.Command({});
/**
 * Type of command of this object
 * @type {String}
 */
PMCommandCreateLane.prototype.type = 'PMCommandCreateLane';

/**
 * Executes the command
 */
PMCommandCreateLane.prototype.execute = function () {
    var pool = this.receiver.pool,
        lane = this.receiver.lane;

    pool.addElement(lane, this.receiver.x, this.receiver.y, lane.topLeftOnCreation);
    lane.showOrHideResizeHandlers(false);
    lane.canvas.triggerCreateEvent(lane, []);
    if (lane instanceof PMLane) {
        pool.setLanePositionAndDimension(lane);
    }
};
/**
 * Inverse executes the command a.k.a. undo
 */
PMCommandCreateLane.prototype.undo = function () {
    var pool = this.receiver.pool,
        lane = this.receiver.lane;

    pool.removeElement(lane);
};
/**
 * Re-executes the command.
 * @returns {BPMNCommandCreateLane}
 */
PMCommandCreateLane.prototype.redo = function () {
    this.execute();
    return this;
};

var PMCommandAddToLane = function (receiver, childObject, coordinates) {
    PMCommandAddToLane.superclass.call(this, receiver);
    this._boundExceed = {};
    PMCommandAddToLane.prototype.initObject.call(this, receiver, childObject, coordinates);
};

PMUI.inheritFrom('PMUI.command.Command', PMCommandAddToLane);

PMCommandAddToLane.prototype.type = 'PMCommandAddToLane';

PMCommandAddToLane.prototype.initObject = function (receiver, childObject, coordinates) {
    this.childObject = childObject;

    this._boundExceed = {
        x: childObject.getWidth() - (receiver.getWidth() - coordinates.x),
        y: childObject.getHeight() - (receiver.getHeight() - coordinates.y)
    };
    this.before = {
        laneWidth: this.receiver.getWidth(),
        laneHeight: this.receiver.getHeight()
    };
    this.after = {
        childX: this.childObject.getX(),
        childY: this.childObject.getY()
    };
};

PMCommandAddToLane.prototype.execute = function () {
    if (this._boundExceed.x > 0 || this._boundExceed.y > 0) {
        this.receiver.setDimension(
            this.receiver.width + (this._boundExceed.x >= 0 ? this._boundExceed.x : 0),
            this.receiver.height + (this._boundExceed.y >= 0 ? this._boundExceed.y : 0)
        );
        this.receiver.updateAllRelatedDimensions();
    }

    if (!this.receiver.getChildren().contains(this.childObject)) {
        this.receiver.getChildren().insert(this.childObject);
    }

    this.after.laneWidth = this.receiver.getWidth();
    this.after.laneHeight = this.receiver.getHeight();

    this.receiver.html.appendChild(this.childObject.getHTML());
    this.childObject.canvas.addToList(this.childObject);
    this.childObject.showOrHideResizeHandlers(false);
    this.childObject.canvas.triggerCreateEvent(this.childObject, []);
    return this;
};

PMCommandAddToLane.prototype.undo = function () {
    this.receiver.getChildren().remove(this.childObject);

    if (this.after.laneWidth !== this.before.laneWidth || this.after.laneHeight !== this.before.laneHeight) {
        this.receiver.setDimension(this.before.laneWidth, this.before.laneHeight);
        this.receiver.updateAllRelatedDimensions();
    }

    this.childObject.saveAndDestroy();
    this.childObject.canvas.triggerRemoveEvent(this.childObject, []);
    return this;
};

PMCommandAddToLane.prototype.redo = function () {
    this.execute();
    return this;
};



/**
 * @class PMUI.behavior.ContainerDropBehavior
 * Encapsulates the drop behavior of a container
 * @extends PMUI.behavior.DropBehavior
 *
 * @constructor
 * Creates a new instance of the class
 * @param {Array} [selectors=[]] css selectors that this drop behavior will
 * accept
 */
var PMContainerDropBehavior = function (selectors) {
    PMUI.behavior.DropBehavior.call(this, selectors);
};
PMContainerDropBehavior.prototype = new PMUI.behavior.DropBehavior();
/**
 * Type of the instances
 * @property {String}
 */
PMContainerDropBehavior.prototype.type = "PMContainerDropBehavior";
/**
 * Default selectors for this drop behavior
 * @property {String}
 */
PMContainerDropBehavior.prototype.defaultSelector = "";

/**
 * On drop handler for this drop behavior, creates shapes when dropped from the
 * toolbar, or move shapes among containers
 * @param {PMUI.draw.Shape} shape
 * @return {Function}
 */
PMContainerDropBehavior.prototype.onDrop = function (shape) {
    return function (e, ui) {
        var customShape = null,
            canvas = shape.getCanvas(),
            selection,
            sibling,
            i,
            command,
            coordinates,
            id,
            shapesAdded = [];
        if (canvas.readOnly) {
            return false;
        }

        shape.entered = false;
        if (ui.helper && ui.helper.attr('id') === "drag-helper") {
            return false;
        }
        id = ui.draggable.attr('id');

        customShape = canvas.shapeFactory(id);
        if (customShape === null) {

            customShape = canvas.customShapes.find('id', id);
            if (!customShape || !shape.dropBehavior.dropHook(shape, customShape, e, ui)) {
                PMDesigner.msgFlash('Invalid operation.'.translate(), document.body, 'error', 5000, 5);
                customShape.setPosition(customShape.getOldX(), customShape.getOldY());
                return false;
            }
            if (customShape.getParent().getType() === 'PMLane'
                && shape.getType() === 'PMPool'
                && shape.bpmnLanes.getSize() > 0) {
                PMDesigner.msgFlash('Invalid operation.'.translate(), document.body, 'error', 5000, 5);
                customShape.setPosition(customShape.getOldX(), customShape.getOldY());
                return false;
            }

            if (!(customShape.parent &&
                customShape.parent.id === shape.id)) {

                selection = canvas.currentSelection;
                for (i = 0; i < selection.getSize(); i += 1) {
                    sibling = selection.get(i);
                    coordinates = PMUI.getPointRelativeToPage(sibling);
                    coordinates = PMUI.pageCoordinatesToShapeCoordinates(shape, null,
                        coordinates.x, coordinates.y, customShape);
                    shapesAdded.push({
                        shape: sibling,
                        container: shape,
                        x: coordinates.x,
                        y: coordinates.y,
                        topLeft: false
                    });
                }
                if (shape.getType() === 'PMLane') {
                    command = new PMCommandSwitchToLaneContainer(shapesAdded);
                } else {
                    command = new PMUI.command.CommandSwitchContainer(shapesAdded);
                }
                command.execute();
                canvas.commandStack.add(command);
                canvas.multipleDrop = true;

            }

            canvas.hideAllFocusLabels();
            shape.updateDimensions(10);
            canvas.updatedElement = null;
            var portx,
                connectionx,
                result;
            for (var i = 0; i < customShape.ports.asArray().length; i += 1) {
                portx = customShape.ports.asArray()[i];
                connectionx = portx.connection;

                result = PMDesigner.connectValidator.isValid(connectionx.getSrcPort().parent, connectionx.getDestPort().parent, connectionx);
                if (result.conf && result.conf.segmentStyle !== connectionx.originalSegmentStyle) {
                    PMDesigner.msgFlash('Invalid flow between elements. Please delete the flow and reconnect the elements.'.translate(), document.body, 'error', 5000, 5);
                }
            }
        } else {
            coordinates = PMUI.pageCoordinatesToShapeCoordinates(shape, e, null, null, customShape);
            var result = shape.addElement(customShape, coordinates.x, coordinates.y,
                customShape.topLeftOnCreation);
            if (shape.containerBehavior.addElementLane !== true) {
                //since it is a new element in the designer, we triggered the
                //custom on create element event
                canvas.hideAllCoronas();
                canvas.updatedElement = customShape;
                if (shape.getType() == 'PMLane') {
                    command = new PMCommandAddToLane(shape, customShape, coordinates);
                } else if (customShape.getType() === 'PMLane') {
                    command = new PMCommandCreateLane(customShape);
                } else {
                    // create the command for this new shape
                    command = new PMUI.command.CommandCreate(customShape);
                }
                canvas.commandStack.add(command);
                command.execute();
                canvas.hideAllFocusLabels();
                if (customShape.label && customShape.focusLabel) {
                    customShape.label.getFocus();
                }
            }
            canvas.hideAllFocusLabels();
            if (customShape.label && customShape.focusLabel) {
                customShape.label.getFocus();
            }
        }
    };
};

PMContainerDropBehavior.prototype.setSelectors = function (selectors, overwrite) {
    PMUI.behavior.DropBehavior.prototype
        .setSelectors.call(this, selectors, overwrite);
    this.selectors.push(".port");
    return this;
};
/**
 * Hook if PMEvent onDrop is correct
 * @param {PMUI.draw.Shape} shape
 * @param {PMUI.draw.Shape} customShape
 * @param {Object} e jQuery object that contains the properties on the
 * drop event
 * @param {Object} ui jQuery object that contains the properties on the
 * drop event
 * @returns {boolean}
 */
PMContainerDropBehavior.prototype.dropHook = function (shape, customShape, e, ui) {
    var result = true,
        shapesTypes = ['PMEvent', 'PMActivity'];
    if ((shapesTypes.indexOf(customShape.getType()) > -1) &&
        !PMDesigner.connectValidator.onDropMovementIsAllowed(shape, customShape)) {
        result = false;
    }
    return result;
};
var PoolContainerBehavior = function () {
    this.addElementLane = false;
};

PoolContainerBehavior.prototype = new PMUI.behavior.RegularContainerBehavior();
PoolContainerBehavior.prototype.type = "PoolContainerBehavior";

PoolContainerBehavior.prototype.addToContainer = function (container,
                                                           shape, x, y,
                                                           topLeftCorner) {
    var shapeLeft = 0,
        shapeTop = 0,
        shapeWidth,
        shapeHeight,
        canvas,
        topLeftFactor = (topLeftCorner === true) ? 0 : 1;
    this.addElementLane = false;
    if (shape.type === 'PMLane') {
        if (container.type === "Canvas") {
            canvas = container;
        } else {
            canvas = container.canvas;
        }
        // have not lanes childrens
        if (container.children.getSize() !== container.bpmnLanes.getSize()) {
            PMDesigner.msgFlash('The lane can be dropped only over an empty pool. Please empty the pool before dropping a lane.'.translate(), document.body, 'error', 3000, 5);
            this.addElementLane = true;
        } else {
            shapeWidth = container.getWidth() - container.headLineCoord;
            shapeHeight = container.getHeight();
            shapeLeft += x - (shapeWidth / 2) * topLeftFactor;
            shapeTop += y - (shapeHeight / 2) * topLeftFactor;
            shapeLeft /= canvas.zoomFactor;
            shapeTop /= canvas.zoomFactor;
            shape.setParent(container);
            container.getChildren().insert(shape);
            this.addShape(container, shape, shapeLeft, shapeTop);
            container.addLane(shape);
            shape.fixZIndex(shape, 0);
            // fix resize minWidth and minHeight and also fix the dimension
            // of this shape (if a child made it grow)
            //container.resizeBehavior.updateResizeMinimums(container);
            // adds the shape to either the customShape arrayList or the regularShapes
            // arrayList if possible
            canvas.addToList(shape);
        }
    } else {
        if (PMUI.draw.Geometry.pointInRectangle(new PMUI.util.Point(x, y),
                new PMUI.util.Point(container.headLineCoord, 0),
                new PMUI.util.Point(container.getZoomWidth(),
                    container.getZoomHeight())
            )) {
            if (container.getChildren().find('type', 'PMLane')) {
                this.addToCanvas(container, shape);

            } else {
                PMUI.behavior.RegularContainerBehavior.prototype.addToContainer.call(this, container,
                    shape, x, y, topLeftCorner);
                shape.absoluteX += 2;
                shape.absoluteY += 2;
            }
        } else {
            this.addToCanvas(container, shape);
        }
    }
    if (!this.addElementLane) {
        shape.canvas.triggerCreateEvent(shape, []);
    }
};
/**
 * @inheritDoc
 */
PoolContainerBehavior.prototype.removeFromContainer = function (shape) {
    var pool = shape.getParent();

    pool.getChildren().remove(shape);

    if (pool.isResizable()) {
        pool.resizeBehavior.updateResizeMinimums(shape.parent);
    }

    if (shape instanceof PMLane) {
        pool.removeLane(shape);
        pool.updateOnRemoveLane(shape);  
    }

    shape.saveAndDestroy();
    shape.canvas.triggerRemoveEvent(shape, []);
    shape.setParent(null);

    return this;
};
/**
 * Force to add a shape to canvas
 * @param container
 * @param shape
 */
PoolContainerBehavior.prototype.addToCanvas = function (container, shape) {
    shape.setParent(container.canvas);
    container.canvas.getChildren().insert(shape);
    this.addShape(container.canvas, shape, shape.getOldX(), shape.getOldY());
    shape.fixZIndex(shape, 0);
    // adds the shape to either the customShape arrayList or the regularShapes
    // arrayList if possible
    container.canvas.addToList(shape);
};

PoolContainerBehavior.prototype.addShape = function (container, shape, x, y) {
    shape.setPosition(x, y);
    if (shape instanceof PMArtifact && shape.art_type === 'GROUP') {
        $(container.getHTML()).prepend(shape.getHTML());
    } else {
        container.getHTML().appendChild(shape.getHTML());
    }
    shape.updateHTML();
    shape.paint();
    shape.applyBehaviors();
    shape.attachListeners();
    return this;
};

/**
 * Command resize: command resize when a poll is resized (mazimized or minimized)
 * @class BPMNCommandPoolResize
 * @constructor
 */
var PMCommandPoolResize = function (receiver, options) {
    this.isTop = options.isTop;
    this.beforeHeightsOpt = options.beforeHeightsOpt;
    this.delta = options.delta;
    if (receiver.bpmnLanes.getSize() > 0) {
        this.afterHeightsOpt = {
            lastLaneHeight: receiver.bpmnLanes.getLast().getHeight(),
            firstLaneHeight: receiver.bpmnLanes.getFirst().getHeight()
        };
    }
    PMUI.command.CommandResize.call(this, receiver);
};
/**
 * Type of command of this object
 * @type {String}
 */
PMUI.inheritFrom('PMUI.command.CommandResize', PMCommandPoolResize);
PMCommandPoolResize.prototype.type = 'PMCommandPoolResize';

/**
 * Executes the command
 */
PMCommandPoolResize.prototype.execute = function () {
    var i,
        pool = this.receiver,
        lane,
        newY,
        newWidth,
        delta;
    PMUI.command.CommandResize.prototype.execute.call(this);

    if (pool.bpmnLanes.getSize() > 0) {
        newWidth = pool.getWidth() - pool.headLineCoord - 1.2;
        if (this.isTop) {
            lane = pool.bpmnLanes.getFirst();
            lane.setDimension(newWidth, this.afterHeightsOpt.firstLaneHeight);
            newY = this.afterHeightsOpt.firstLaneHeight;
            for (i = 1; i < pool.bpmnLanes.getSize(); i += 1) {
                lane = pool.bpmnLanes.get(i);
                lane.setPosition(lane.getX(), newY);
                lane.setDimension(newWidth, lane.getHeight());
                newY += lane.getHeight();
            }
        } else {
            pool.setMinimunsResize();
            lane = pool.bpmnLanes.getLast();
            lane.setDimension(newWidth, this.afterHeightsOpt.lastLaneHeight);
            for (i = 0; i < pool.bpmnLanes.getSize() - 1; i += 1) {
                lane = pool.bpmnLanes.get(i);
                lane.setDimension(newWidth, lane.getHeight());
            }
        }
    }
    pool.canvas.refreshArray.clear();
    delta = {
        dx: this.delta.dx,
        dy: this.delta.dy
    };
    pool.poolChildConnectionOnResize(true, true);
    pool.refreshAllPoolConnections(false, delta);
};
/**
 * Inverse executes the command a.k.a. undo
 */
PMCommandPoolResize.prototype.undo = function () {
    var i,
        pool = this.receiver,
        lane,
        newWidth,
        newY,
        delta;
    this.receiver.oldHeight = this.receiver.getHeight();
    PMUI.command.CommandResize.prototype.undo.call(this);
    if (this.receiver.graphic) {
        this.receiver.paint();
    }
    if (pool.bpmnLanes.getSize() > 0) {
        newWidth = pool.getWidth() - pool.headLineCoord - 1.2;
        if (this.isTop) {
            lane = pool.bpmnLanes.getFirst();
            lane.setDimension(newWidth, this.beforeHeightsOpt.firstLaneHeight);
            newY = this.beforeHeightsOpt.firstLaneHeight;
            for (i = 1; i < pool.bpmnLanes.getSize(); i += 1) {
                lane = pool.bpmnLanes.get(i);
                lane.setPosition(lane.getX(), newY);
                lane.setDimension(newWidth, lane.getHeight());
                newY += lane.getHeight();
            }
        } else {
            lane = pool.bpmnLanes.getLast();
            lane.setDimension(newWidth, this.beforeHeightsOpt.lastLaneHeight);
            for (i = 0; i < pool.bpmnLanes.getSize() - 1; i += 1) {
                lane = pool.bpmnLanes.get(i);
                lane.setDimension(newWidth, lane.getHeight());
            }
        }
    }
    pool.canvas.refreshArray.clear();
    delta = {
        dx: this.delta.dx * -1,
        dy: this.delta.dy * -1
    };
    pool.poolChildConnectionOnResize(true, true);
    pool.refreshAllPoolConnections(false, delta);
};
/**
 * Executes the command a.k.a redo
 */
PMCommandPoolResize.prototype.redo = function () {
    this.execute();
};

/*global jCore*/
var PMLaneResizeBehavior = function () {
};

PMLaneResizeBehavior.prototype = new PMUI.behavior.RegularResizeBehavior();
PMLaneResizeBehavior.prototype.type = "PMLaneResizeBehavior";

PMLaneResizeBehavior.prototype.init = function (shape) {
    PMUI.behavior.RegularResizeBehavior
        .prototype.init.call(this, shape);
    $shape = $(shape.getHTML());
    $shape.resizable();
    $shape.resizable('option', 'minHeight', 30 * shape.canvas.getZoomFactor());
};
/**
 * Sets a shape's container to a given container
 * @param container
 * @param shape
 */
PMLaneResizeBehavior.prototype.onResizeStart = function (shape) {
    return PMUI.behavior.RegularResizeBehavior
        .prototype.onResizeStart.call(this, shape);
};
/**
 * Removes shape from its current container
 * @param shape
 */
PMLaneResizeBehavior.prototype.onResize = function (shape) {
    return function (e, ui) {
        PMUI.behavior.RegularResizeBehavior
            .prototype.onResize.call(this, shape)(e, ui);
    };
};

PMLaneResizeBehavior.prototype.onResizeEnd = function (shape) {
    return function (e, ui) {
        var i,
            j,
            label,
            command;
        shape.resizing = false;
        shape.canvas.isResizing = false;
        // last resize
        PMUI.behavior.RegularResizeBehavior.prototype.onResize.call(this, shape)(e, ui);
        // show the handlers again
        shape.showOrHideResizeHandlers(true);
        for (i = 0; i < shape.labels.getSize(); i += 1) {
            label = shape.labels.get(i);
            label.setLabelPosition(label.location, label.diffX, label.diffY);
        }
        // TESTING COMMANDS
        command = new PMCommandLaneResize(shape);
        command.execute();
        shape.canvas.commandStack.add(command);
        shape.parent.updateDimensionsWithLanes();
        return true;
    };
};

var LaneContainerBehavior = function () {
};

LaneContainerBehavior.prototype = new PMUI.behavior.RegularContainerBehavior();
LaneContainerBehavior.prototype.type = "LaneContainerBehavior";
/**
 * @inheritDoc
 */
LaneContainerBehavior.prototype.addToContainer = function (container, shape, x, y, topLeftCorner) {
    PMUI.behavior.RegularContainerBehavior.prototype.addToContainer.call(this, container, shape, x, y, topLeftCorner);
    shape.getCanvas().triggerCreateEvent(shape, []);
};

LaneContainerBehavior.prototype.addShape = function (container, shape, x, y) {
    shape.setPosition(x, y);
    //insert the shape HTML to the DOM
    if (shape instanceof PMArtifact && shape.art_type === 'GROUP') {
        $(container.getHTML()).prepend(shape.getHTML());
    } else {
        container.getHTML().appendChild(shape.getHTML());
    }
    shape.updateHTML();
    shape.paint();
    shape.applyBehaviors();
    shape.attachListeners();
    return this;
};
/**
 * @inheritDoc
 */
LaneContainerBehavior.prototype.removeFromContainer = function (shape) {
    shape.getCanvas().triggerRemoveEvent(shape, []);
    PMUI.behavior.RegularContainerBehavior.prototype.removeFromContainer.call(this, shape);
};
/**
 * @class CommandDelete
 * Class CommandDelete determines the actions executed when some shapes are deleted (redo) and the actions
 * executed when they're recreated (undo).
 *
 * Instances of this class are created in {@link Canvas#removeElements}.
 * @extends Command
 *
 * @constructor Creates an instance of the class CommandDelete
 * @param {Object} receiver The object that will execute the command
 */
PMCommandDelete = function (receiver) {
    PMUI.command.Command.call(this, receiver);
    /**
     * A stack of commandsConnect
     * @property {Array}
     */
    this.stackCommandConnect = [];
    /**
     * ArrayList that represents the selection that was active before deleting the elements
     * @property {ArrayList}
     */
    this.currentSelection = new PMUI.util.ArrayList();

    /**
     * Reference to the current connection in the canvas
     * @property {Connection}
     */
    this.currentConnection = null;
    /**
     * List of all the elements related to the commands
     * @property {Array}
     */
    this.relatedElements = [];
    this.beforeRelPositions = [];
    this.tempLanes = new PMUI.util.ArrayList();
    PMCommandDelete.prototype.initObject.call(this, receiver);
};

PMUI.inheritFrom('PMUI.command.Command', PMCommandDelete);

/**
 * Type of command
 * @property {String}
 */
PMCommandDelete.prototype.type = "PMCommandDelete";

/**
 * Instance initializer which uses options to extend the config options to initialize the instance
 * @param {Object} receiver The object that will execute the command
 * @private
 */
PMCommandDelete.prototype.initObject = function (receiver) {
    var i,
        shape;
    // move the current selection to this.currentSelection array
    for (i = 0; i < receiver.getCurrentSelection().getSize() > 0; i += 1) {
        shape = receiver.getCurrentSelection().get(i);
        this.currentSelection.insert(shape);
    }
    // save the currentConnection of the canvas if possible
    if (receiver.currentConnection) {
        this.currentConnection = receiver.currentConnection;
    }
};

/**
 * Saves and destroys connections and shapes
 * @private
 * @param {Object} shape
 * @param {boolean} root True if `shape` is a root element in the tree
 * @param {boolean} [fillArray] If set to true it'll fill `this.relatedElements` with the objects erased
 * @return {boolean}
 */
PMCommandDelete.prototype.saveAndDestroy = function (shape, root, fillArray) {
    var i,
        child,
        parent,
        children = null,
        connection,
        length,
        canvas = shape.canvas;

    if (shape.hasOwnProperty("children")) {
        children = shape.children;
    }
    // special function to be called as an afterwards
    // BIG NOTE: doesn't have to delete html
    if (shape.destroy) {
        shape.destroy();
    }
    length = children.getSize();
    for (i = 0; i < length; i += 1) {
        child = children.get(i);
        this.saveAndDestroy(child, false, fillArray);
    }
    while (shape.ports && shape.ports.getSize() > 0) {
        connection = shape.ports.getFirst().connection;
        if (fillArray) {
            this.relatedElements.push(connection);
        }
        this.stackCommandConnect.push(
            new PMUI.command.CommandConnect(connection)
        );
        connection.saveAndDestroy();
    }
    // remove from the children array of its parent
    if (root) {
        parent = shape.parent;
        parent.getChildren().remove(shape);
        if (parent.isResizable()) {
            parent.resizeBehavior.updateResizeMinimums(shape.parent);
        }
        if (parent.getType() === 'PMLane') {
            parent.updateDimensions();
        }
        // remove from the currentSelection and from either the customShapes
        // arrayList or the regularShapes arrayList
        // remove the html only from the root
        if (shape.corona) {
            shape.corona.hide();
        }
        shape.html = $(shape.html).detach()[0];
        if (shape.getType() === 'PMLane') {
            this.beforeRelPositions[shape.getID()] = shape.getRelPosition();
            shape.parent.bpmnLanes.remove(shape);
        }
    }
    if (fillArray) {
        this.relatedElements.push(shape);
    }
    // remove from the currentSelection and from either the customShapes
    // arrayList or the regularShapes arrayList
    canvas.removeFromList(shape);
    return true;
};
/**
 * Executes the command
 *
 * @chainable
 */
PMCommandDelete.prototype.execute = function () {
    var shape,
        i,
        canvas = this.receiver,
        currentConnection,
        fillArray = false,
        mainShape = null,
        data,
        url = '/project/' + PMDesigner.project.id + '/activity/validate-active-cases';
    if (this.relatedElements.length === 0) {
        fillArray = true;
    }
    canvas.emptyCurrentSelection();
    // copy from this.currentConnection
    if (this.currentSelection.getSize() === 1) {
        shape = this.currentSelection.get(0);
        if (shape.getType() === 'PMActivity') {
            data = {
                act_uid: shape.getID(),
                case_type: 'assigned'
            };
            PMDesigner.restApi.execute({
                data: JSON.stringify(data),
                method: "update",
                url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + url,
                async: false,
                success: function (data, textStatus, xhr) {
                    if (data.result) {
                        canvas.addToSelection(shape);
                    } else {
                        shape.corona.hide();
                        PMDesigner.msgFlash(data.message, document.body, 'error', 3000, 5);
                    }
                },
                error: function (xhr, textStatus, errorThrown) {
                    PMDesigner.msgFlash('There are problems removing task'.translate(), document.body, 'error', 3000, 5);
                }
            });
        } else {
            canvas.addToSelection(shape);
        }
    } else if (this.currentSelection.getSize() !== 0) {
        PMDesigner.msgFlash('Invalid operation, please delete elements individually'.translate(), document.body, 'error', 3000, 5);
    }

    if (canvas.currentSelection.getSize() === 1) {
        mainShape = shape;
    }
    // remove the elements in the canvas current selection
    if (shape && shape.getType() === 'PMPool') {
        for (i = 0; i < shape.bpmnLanes.getSize(); i += 1) {
            this.tempLanes.insert(shape.bpmnLanes.get(i));
        }
    }

    while (canvas.getCurrentSelection().getSize() > 0) {
        shape = canvas.getCurrentSelection().getFirst();
        this.saveAndDestroy(shape, true, fillArray);
    }
    if (shape && shape.getType() === 'PMLane') {
        for (i = 0; i < shape.parent.bpmnLanes.getSize(); i += 1) {
            shape.parent.bpmnLanes.get(i)
                .refreshChildrenPositions(true)
                .refreshConnections(false, false);
        }
    }
    // destroy the currentConnection
    canvas.currentConnection = this.currentConnection;
    currentConnection = canvas.currentConnection;
    if (currentConnection) {
        // add to relatedElements just in the case when only a connection is
        // selected and deleted
        this.relatedElements.push(currentConnection);

        this.stackCommandConnect.push(
            new PMUI.command.CommandConnect(currentConnection)
        );

        currentConnection.saveAndDestroy();
        currentConnection = null;
        mainShape = currentConnection;
    }
    canvas.triggerRemoveEvent(mainShape, this.relatedElements);
    return this;
};

/**
 * Inverse executes the command a.k.a. undo
 *
 * @chainable
 */
PMCommandDelete.prototype.undo = function () {
    // undo recreates the shapes
    var i,
        shape,
        mainShape = this.currentSelection.getFirst() || this.currentConnection,
        size,
        haveLanes = false,
        shapeBefore,
        j,
        element,
        length;
    this.currentSelection.sort(function (lane1, lane2) {
        return lane1.relPosition > lane2.relPosition;
    });

    length = this.currentSelection.getSize();
    for (i = 0; i < length; i += 1) {
        shape = this.currentSelection.get(i);
        shapeBefore = null;
        // add to the canvas array of regularShapes and customShapes
        shape.canvas.addToList(shape);
        shape.getChildren().asArray().forEach(function (e) {
            shape.canvas.addToList(e);
        });
        // add to the children of the parent
        shape.parent.getChildren().insert(shape);
        shape.showOrHideResizeHandlers(false);

        if (shape.getType() !== 'PMLane') {
            shape.parent.html.appendChild(shape.getHTML());
        } else {
            shapeBefore = shape.parent.bpmnLanes
                .get(this.beforeRelPositions[shape.getID()] - 1);
            if (shapeBefore) {
                shape.parent.html
                    .insertBefore(shape.getHTML(), shapeBefore.getHTML());
            } else {
                shape.parent.html.appendChild(shape.getHTML());
            }
            size = shape.parent.bpmnLanes.getSize();
            for (j = this.beforeRelPositions[shape.getID()] - 1; j < size; j += 1) {
                element = shape.parent.bpmnLanes.get(j);
                element.setRelPosition(j + 2);
            }
            shape.setRelPosition(this.beforeRelPositions[shape.getID()]);
            shape.parent.bpmnLanes.insert(shape);
            shape.parent.bpmnLanes.bubbleSort(shape.parent.comparisonFunction);
            shape.parent.updateAllLaneDimension();
        }
        if (shape.getType() === 'PMPool') {
            for (i = 0; i < this.tempLanes.getSize(); i += 1) {
                shape.bpmnLanes.insert(this.tempLanes.get(i));
            }
            if (shape.bpmnLanes.getSize() > 0) {
                shape.updateAllLaneDimension();
            }
        }
        if(shape.corona){
            shape.corona.hide();
        }
    }
    // reconnect using the stack of commandConnect
    for (i = this.stackCommandConnect.length - 1; i >= 0; i -= 1) {
        this.stackCommandConnect[i].buildConnection();
    }
    this.receiver.triggerCreateEvent(mainShape, this.relatedElements);
    if (haveLanes) {
        for (i = 0; i < mainShape.parent.bpmnLanes.getSize(); i += 1) {
            mainShape.parent.bpmnLanes.get(i)
                .refreshChildrenPositions(true)
                .refreshConnections(false);
        }
    }
    return this;
};

/**
 * Executes the command (a.k.a redo)
 * @chainable
 */
PMCommandDelete.prototype.redo = function () {
    this.execute();
    return this;
};

var PMCommandResize = function (receiver) {
    PMCommandResize.superclass.call(this, receiver);
    this._boundExceed = null;
    this._parent;
    PMCommandResize.prototype.initObject.call(this, receiver);
};

PMUI.inheritFrom('PMUI.command.CommandResize', PMCommandResize);

PMCommandResize.prototype.type = 'PMCommandResize';

PMCommandResize.prototype.initObject = function (receiver) {
    this._parent = this.receiver.getParent();

    if (this._parent.getType() === 'PMLane') {
        this.before.parentWidth = this.after.parentWidth = this._parent.getWidth();
        this.before.parentHeight = this.after.parentHeight = this._parent.getHeight();

        if (this._boundExceed === null) {
            this._boundExceed = {
                x: this.receiver.getWidth() - (this._parent.getWidth() - this.receiver.getX()),
                y: this.receiver.getHeight() - (this._parent.getHeight() - this.receiver.getY())
            };
        }

        if (this._boundExceed.x > 0) {
            this.after.parentWidth = this.before.parentWidth + this._boundExceed.x;
        }
        if (this._boundExceed.y > 0) {
            this.after.parentHeight = this.before.parentHeight + this._boundExceed.y;
        }
    }
};

PMCommandResize.prototype.execute = function () {
    PMCommandResize.superclass.prototype.execute.call(this);

    if (this._boundExceed !== null && (this._boundExceed.x > 0 || this._boundExceed.y > 0)) {
        this._parent.setDimension(
            this.after.parentWidth,
            this.after.parentHeight
        );
        this._parent.updateAllRelatedDimensions();
    }

    return this;
};

PMCommandResize.prototype.undo = function () {
    PMCommandResize.superclass.prototype.undo.call(this);

    if (this._boundExceed !== null && (this._boundExceed.x > 0 || this._boundExceed.y > 0)) {
        this._parent.setDimension(
            this.before.parentWidth,
            this.before.parentHeight
        );
        this._parent.updateAllRelatedDimensions();
    }

    return this;
};


/**
 * Command resize: command resize when a poll is resized (mazimized or minimized)
 * @class BPMNCommandPoolResize
 * @constructor
 */
var PMCommandLaneResize = function (receiver) {
    PMUI.command.CommandResize.call(this, receiver);
};
/**
 * Type of command of this object
 * @type {String}
 */
PMUI.inheritFrom('PMUI.command.CommandResize', PMCommandLaneResize);
PMCommandLaneResize.prototype.type = 'PMCommandLaneResize';
/**
 * Executes the command
 */
PMCommandLaneResize.prototype.execute = function () {
    PMUI.command.CommandResize.prototype.execute.call(this);
    this.receiver.updateAllRelatedDimensions(true);
};
/**
 * Inverse executes the command a.k.a. undo
 */
PMCommandLaneResize.prototype.undo = function () {
    this.receiver.oldWidth = this.receiver.getWidth();
    this.receiver.oldHeight = this.receiver.getHeight();
    PMUI.command.CommandResize.prototype.undo.call(this);
    this.receiver.updateAllRelatedDimensions(true);
};
/**
 * Executes the command a.k.a redo
 */
PMCommandLaneResize.prototype.redo = function () {
    this.execute();
};

var PMCommandMoveInLane = function (receiver) {
    PMCommandMoveInLane.superclass.call(this, receiver);
    this._parentLane = null;
    this._boundExceed = {
        x: 0,
        y: 0
    };
    PMCommandMoveInLane.prototype.initObject.call(this, receiver);
};

PMUI.inheritFrom('PMUI.command.CommandMove', PMCommandMoveInLane);

PMCommandMoveInLane.prototype.type = 'PMCommandMoveInLane';

PMCommandMoveInLane.prototype.initObject = function (receiver) {
    var i,
        size = this.receiver.getSize(),
        shape,
        boundExceed,
        laneWidth,
        laneHeight;

    if (this.receiver.getSize() > 0) {
        this._parentLane = receiver.get(0).getParent();
    }

    laneWidth = this._parentLane.getWidth();
    laneHeight = this._parentLane.getHeight();

    this.before.laneWidth = laneWidth;
    this.before.laneHeight = laneHeight;

    for (i = 0; i < size; i += 1) {
        shape = this.receiver.get(i);
        boundExceed = {
            x: shape.getWidth() - (laneWidth - shape.getX()),
            y: shape.getHeight() - (laneHeight - shape.getY())
        };
        if (boundExceed.x > 0 || boundExceed.y > 0) {
            if (this._boundExceed.x < boundExceed.x) {
                this._boundExceed.x = boundExceed.x;
            }
            if (this._boundExceed.y < boundExceed.y) {
                this._boundExceed.y = boundExceed.y;
            }
        }
    }
    this.after.laneWidth = laneWidth + this._boundExceed.x;
    this.after.laneHeight = laneHeight + this._boundExceed.y;
};

PMCommandMoveInLane.prototype.execute = function () {
    var res = PMCommandMoveInLane.superclass.prototype.execute.call(this);

    this._parentLane.setDimension(
        this.after.laneWidth,
        this.after.laneHeight
    );
    this._parentLane.updateAllRelatedDimensions(false);

    return res;
};

PMCommandMoveInLane.prototype.undo = function () {
    var res = PMCommandMoveInLane.superclass.prototype.undo.call(this);

    this._parentLane.setDimension(
        this.before.laneWidth,
        this.before.laneHeight
    );
    this._parentLane.updateAllRelatedDimensions();

    return res;
};
var CommandChangeEventMarker = function (receiver, options) {
    PMUI.command.Command.call(this, receiver);
    this.before = null;
    this.after = null;
    CommandChangeEventMarker.prototype.initObject.call(this, options);
};

PMUI.inheritFrom('PMUI.command.Command', CommandChangeEventMarker);
/**
 * Type of the instances of this class
 * @property {String}
 */
CommandChangeEventMarker.prototype.type = "CommandChangeEventMarker";

/**
 * Initializes the command parameters
 * @param {PMUI.draw.Core} receiver The object that will perform the action
 */
CommandChangeEventMarker.prototype.initObject = function (options) {
    var parsedClass = options;
    this.layer = this.receiver.getLayers().get(0);
    this.before = {
        zoomSprite: this.layer.zoomSprites,
        marker: this.receiver.evn_marker
    };
    this.after = {
        zoomSprite: [
            'mafe-event-' + this.receiver.getEventType().toLowerCase() + '-' +
            parsedClass.toLowerCase() + '-16',
            'mafe-event-' + this.receiver.getEventType().toLowerCase() + '-' +
            parsedClass.toLowerCase() + '-24',
            'mafe-event-' + this.receiver.getEventType().toLowerCase() + '-' +
            parsedClass.toLowerCase() + '-33',
            'mafe-event-' + this.receiver.getEventType().toLowerCase() + '-' +
            parsedClass.toLowerCase() + '-41',
            'mafe-event-' + this.receiver.getEventType().toLowerCase() + '-' +
            parsedClass.toLowerCase() + '-49'
        ],
        marker: options
    };
};

/**
 * Executes the command, changes the position of the element, and if necessary
 * updates the position of its children, and refreshes all connections
 */
CommandChangeEventMarker.prototype.execute = function () {
    var menuShape;
    this.layer.setZoomSprites(this.after.zoomSprite);
    this.receiver.setEventMarker(this.after.marker);
    this.receiver
        .updateBpmEventMarker(this.receiver.getBpmnElementType());
    PMDesigner.project.updateElement([]);
    menuShape = PMDesigner.getMenuFactory(this.receiver.getEventType());
    this.receiver.setContextMenu(menuShape);
    this.receiver.paint();
};

/**
 * Returns to the state before the command was executed
 */
CommandChangeEventMarker.prototype.undo = function () {
    var menuShape;
    this.layer.setZoomSprites(this.before.zoomSprite);
    this.receiver.setEventMarker(this.before.marker);
    this.receiver
        .updateBpmEventMarker(this.receiver.getBpmnElementType());
    this.receiver.extendedType = this.before.marker;
    PMDesigner.project.setDirty(true);
    $(this.receiver.html).trigger('changeelement');

    menuShape = PMDesigner.getMenuFactory(this.receiver.getEventType());
    this.receiver.setContextMenu(menuShape);
    this.receiver.paint();
};

/**
 *  Executes the command again after an undo action has been done
 */
CommandChangeEventMarker.prototype.redo = function () {
    this.execute();
};
var PMCommandSwitchToLaneContainer = function (receiver) {
    PMCommandSwitchToLaneContainer.superclass.call(this, receiver);
    this._parentLane = null;
    this._boundExceed = {
        x: 0,
        y: 0
    };
    PMCommandSwitchToLaneContainer.prototype.initObject.call(this, receiver);
};

PMUI.inheritFrom("PMUI.command.CommandSwitchContainer", PMCommandSwitchToLaneContainer);

PMCommandSwitchToLaneContainer.prototype.type = 'PMCommandSwitchToLaneContainer';

PMCommandSwitchToLaneContainer.prototype.initObject = function (receiver) {
    this._parentLane = this.after.shapes[0].parent;
};

PMCommandSwitchToLaneContainer.prototype.execute = function () {
    var i, shape, boundExceed, laneWidth, laneHeight;
    PMCommandSwitchToLaneContainer.superclass.prototype.execute.call(this);

    if (this.before.laneWidth === undefined ||this.before.laneHeight === undefined) {
        laneWidth = this._parentLane.getWidth();
        laneHeight = this._parentLane.getHeight();

        this.before.laneWidth = laneWidth;
        this.before.laneHeight = laneHeight;

        for (i = 0; i < this.relatedShapes.length; i += 1) {
            shape = this.relatedShapes[i];
            boundExceed = {
                x: shape.getWidth() - (laneWidth - shape.getX()),
                y: shape.getHeight() - (laneHeight - shape.getY())
            };
            if (boundExceed.x > 0 || boundExceed.y > 0) {
                if (this._boundExceed.x < boundExceed.x) {
                    this._boundExceed.x = boundExceed.x;
                }
                if (this._boundExceed.y < boundExceed.y) {
                    this._boundExceed.y = boundExceed.y;
                }
            }
        }

        this.after.laneWidth = laneWidth + this._boundExceed.x;
        this.after.laneHeight = laneHeight + this._boundExceed.y;
    }

    this._parentLane.setDimension(
        this.after.laneWidth,
        this.after.laneHeight
    );
    this._parentLane.updateAllRelatedDimensions();

    return this;
};

PMCommandSwitchToLaneContainer.prototype.undo = function () {
    PMCommandSwitchToLaneContainer.superclass.prototype.undo.call(this);

    this._parentLane.setDimension(
        this.before.laneWidth,
        this.before.laneHeight
    );
    this._parentLane.updateAllRelatedDimensions();

    return this;
};


var PMCommandCreateInLane = function (receiver) {
    PMCommandCreateInLane.superclass.call(this, receiver);
    this._parentLane = null;
    this._boundExceed = {};
    PMCommandCreateInLane.prototype.initObject.call(this, receiver);
};

PMUI.inheritFrom('PMUI.command.CommandCreate', PMCommandCreateInLane);

PMCommandCreateInLane.prototype.type = 'PMCommandCreateInLane';

PMCommandCreateInLane.prototype.initObject = function (receiver) {
    this._parentLane = receiver.getParent();

    if (!(this._parentLane instanceof PMLane)) {
        throw new Error('initObject(): The container element must be a PMLane instance.');
    }

    this._boundExceed = {
        x: receiver.getWidth() - (this._parentLane.getWidth() - receiver.getX()),
        y: receiver.getHeight() - (this._parentLane.getHeight() - receiver.getY())
    };

    this.before.laneWidth = this._parentLane.getWidth();
    this.before.laneHeight = this._parentLane.getHeight();

    this.after.laneWidth = this.before.laneWidth + (this._boundExceed.x > 0 ? this._boundExceed.x : 0);
    this.after.laneHeight = this.before.laneHeight + (this._boundExceed.y > 0 ? this._boundExceed.y : 0);
};

PMCommandCreateInLane.prototype.execute = function () {
    PMCommandCreateInLane.superclass.prototype.execute.call(this);

    if (this._boundExceed.x > 0 || this._boundExceed.y > 0) {
        this._parentLane.setDimension(
            this.after.laneWidth,
            this.after.laneHeight
        );
        this._parentLane.updateAllRelatedDimensions();
    }

    return this;
};

PMCommandCreateInLane.prototype.undo = function () {
    PMCommandCreateInLane.superclass.prototype.undo.call(this);

    if (this._boundExceed.x > 0 || this._boundExceed.y > 0) {
        this._parentLane.setDimension(
            this.before.laneWidth,
            this.before.laneHeight
        );
        this._parentLane.updateAllRelatedDimensions();
    }

    return this;
};
var CanvasContainerBehavior = function () {
};

CanvasContainerBehavior.prototype = new PMUI.behavior.RegularContainerBehavior();
CanvasContainerBehavior.prototype.type = "CanvasContainerBehavior";

/**
 * Adds a shape to a given container given its coordinates
 * @param {PMUI.draw.BehavioralElement} container container using this behavior
 * @param {PMUI.draw.Shape} shape shape to be added
 * @param {number} x x coordinate where the shape will be added
 * @param {number} y y coordinate where the shape will be added
 * @param {boolean} topLeftCorner Determines whether the x and y coordinates
 * will be considered from the top left corner or from the center
 */
CanvasContainerBehavior.prototype.addToContainer = function (container, shape, x, y, topLeftCorner) {
    var shapeLeft = 0,
        shapeTop = 0,
        shapeWidth,
        shapeHeight,
        canvas,
        topLeftFactor = (topLeftCorner === true) ? 0 : 1;

    if (container.family === "Canvas") {
        canvas = container;
    } else {
        canvas = container.canvas;
    }
    shapeWidth = shape.getZoomWidth();
    shapeHeight = shape.getZoomHeight();

    shapeLeft += x - (shapeWidth / 2) * topLeftFactor;
    shapeTop += y - (shapeHeight / 2) * topLeftFactor;

    shapeLeft /= canvas.zoomFactor;
    shapeTop /= canvas.zoomFactor;

    shape.setParent(container);
    container.getChildren().insert(shape);
    this.addShape(container, shape, shapeLeft, shapeTop);

    // fix the zIndex of this shape and it's children
    if (shape.getType() === 'PMPool' || shape.getType() === 'PMParticipant') {
        shape.fixZIndex(shape, 3);
    } else {
        shape.fixZIndex(shape, 2);
    }
    // fix resize minWidth and minHeight and also fix the dimension
    // of this shape (if a child made it grow)
    container.updateDimensions(10);
    // adds the shape to either the customShape arrayList or the regularShapes
    // arrayList if possible
    canvas.addToList(shape); 
    canvas.triggerCreateEvent(shape, []);
};
CanvasContainerBehavior.prototype.addShape = function (container, shape, x, y) {
    shape.setPosition(x, y);
    //insert the shape HTML to the DOM
    if (shape instanceof PMArtifact && shape.art_type === 'GROUP') {
        $(container.getHTML()).prepend(shape.getHTML());
    } else {
        container.getHTML().appendChild(shape.getHTML());
    }
    shape.updateHTML();
    shape.paint();
    shape.applyBehaviors();
    shape.attachListeners();
    return this;
};
/**
 * Removes shape from its current container
 * @param {PMUI.draw.Shape} shape shape to be removed
 * @template
 * @protected
 */
CanvasContainerBehavior.prototype.removeFromContainer = function (shape) {
    var canvas = shape.getCanvas();
    canvas.triggerRemoveEvent(shape, []);
    PMUI.behavior.RegularContainerBehavior.prototype.removeFromContainer.call(this, shape);
};

/**
 * @class ActivityContainerBehavior
 * Encapsulates the behavior of a regular container
 * @extends PMUI.behavior.RegularContainerBehavior
 *
 * @constructor
 * Creates a new instance of the class
 */
var ActivityContainerBehavior = function () {
};

ActivityContainerBehavior.prototype = new PMUI.behavior.RegularContainerBehavior();
/**
 * Type of the instances
 * @property {String}
 */
ActivityContainerBehavior.prototype.type = "ActivityContainerBehavior";
/**
 * Adds a shape to a given container given its coordinates
 * @param {PMUI.draw.BehavioralElement} container container using this behavior
 * @param {PMUI.draw.Shape} shape shape to be added
 * @param {number} x x coordinate where the shape will be added
 * @param {number} y y coordinate where the shape will be added
 * @param {boolean} topLeftCorner Determines whether the x and y coordinates
 * will be considered from the top left corner or from the center
 */
ActivityContainerBehavior.prototype.addToContainer = function (container, shape, x, y, topLeftCorner) {
    var shapeLeft = 0,
        shapeTop = 0,
        shapeWidth,
        shapeHeight,
        canvas,
        topLeftFactor = (topLeftCorner === true) ? 0 : 1;

    if (container.family === "Canvas") {
        canvas = container;
    } else {
        canvas = container.canvas;
    }
    shapeWidth = shape.getZoomWidth();
    shapeHeight = shape.getZoomHeight();

    shapeLeft += x - (shapeWidth / 2) * topLeftFactor;
    shapeTop += y - (shapeHeight / 2) * topLeftFactor;

    shapeLeft /= canvas.zoomFactor;
    shapeTop /= canvas.zoomFactor;

    shape.setParent(container);
    container.getChildren().insert(shape);
    this.addShape(container, shape, shapeLeft, shapeTop);

    // fix the zIndex of this shape and it's children
    shape.fixZIndex(shape, 1);
    canvas.addToList(shape);
    if (shape.getType() === 'PMEvent' && shape.getEventType() === 'BOUNDARY') {
        container.boundaryArray.insert(shape);
        if (container.boundaryPlaces.isEmpty()) {
            container.makeBoundaryPlaces();
        }
        shape.attachToActivity();
    } else {
        // fix resize minWidth and minHeight and also fix the dimension
        // of this shape (if a child made it grow)
        container.updateDimensions(10);
    }
};
/**
 * @class PMUI.behavior.ContainerDropBehavior
 * Encapsulates the drop behavior of a container
 * @extends PMUI.behavior.DropBehavior
 *
 * @constructor
 * Creates a new instance of the class
 * @param {Array} [selectors=[]] css selectors that this drop behavior will
 * accept
 */
var PMActivityDropBehavior = function (selectors) {
    PMUI.behavior.DropBehavior.call(this, selectors);

};
PMActivityDropBehavior.prototype = new PMUI.behavior.DropBehavior();
/**
 * Type of the instances
 * @property {String}
 */
PMActivityDropBehavior.prototype.type = "PMActivityDropBehavior";
/**
 * Default selectors for this drop behavior
 * @property {String}
 */
PMActivityDropBehavior.prototype.defaultSelector = ".custom_shape";

/**
 * On drop handler for this drop behavior, creates shapes when dropped from the
 * toolbar, or move shapes among containers
 * @param {PMUI.draw.Shape} shape
 * @return {Function}
 */
PMActivityDropBehavior.prototype.onDrop = function (shape) {
    return function (e, ui) {
        var customShape = null,
            canvas = shape.getCanvas(),
            selection,
            sibling,
            i,
            command,
            coordinates,
            id,
            shapesAdded = [],
            containerBehavior = shape.containerBehavior;

        if (canvas.readOnly) {
            return false;
        }
        shape.layers.getFirst().removeCSSClasses(['dropableClass']);
        if (!shape.isValidDropArea) {
            return false;
        }

        shape.entered = false;
        if (ui.helper && ui.helper.attr('id') === "drag-helper") {
            return false;
        }
        id = ui.draggable.attr('id');

        customShape = canvas.shapeFactory(id);
        if (customShape === null) {
            customShape = canvas.customShapes.find('id', id);
            if (!customShape || !shape.dropBehavior.dropHook(shape, e, ui)) {
                return false;
            }

            if (!(customShape.parent &&
                customShape.parent.id === shape.id)) {
                selection = canvas.currentSelection;
                for (i = 0; i < selection.getSize(); i += 1) {
                    sibling = selection.get(i);
                    coordinates = PMUI.getPointRelativeToPage(sibling);
                    coordinates = PMUI.pageCoordinatesToShapeCoordinates(shape, null,
                        coordinates.x, coordinates.y, customShape);
                    shapesAdded.push({
                        shape: sibling,
                        container: shape,
                        x: coordinates.x,
                        y: coordinates.y,
                        topLeft: false
                    });
                }
                command = new PMUI.command.CommandSwitchContainer(shapesAdded);
                command.execute();
                canvas.commandStack.add(command);
                canvas.multipleDrop = true;

            }
            shape.setBoundary(customShape, customShape.numberRelativeToActivity);
            // fix resize minWidth and minHeight and also fix the dimension
            // of this shape (if a child made it grow)
            canvas.hideAllFocusLabels();
            canvas.updatedElement = null;

        } else {
            coordinates = PMUI.pageCoordinatesToShapeCoordinates(shape, e, null, null, customShape);
            if (PMUI.validCoordinatedToCreate(shape, e, customShape)) {
                shape.addElement(customShape, coordinates.x, coordinates.y,
                    customShape.topLeftOnCreation);
                //since it is a new element in the designer, we triggered the
                //custom on create element event
                canvas.updatedElement = customShape;
                if (customShape.getType() === 'PMLane') {
                    command = new PMCommandCreateLane(customShape);
                    canvas.commandStack.add(command);
                    command.execute();
                } else {
                    // create the command for this new shape
                    command = new PMUI.command.CommandCreate(customShape);
                    canvas.commandStack.add(command);
                    command.execute();
                }
                canvas.hideAllFocusLabels();
            }
        }
    };
};

PMActivityDropBehavior.prototype.setSelectors = function (selectors, overwrite) {
    PMUI.behavior.DropBehavior.prototype
        .setSelectors.call(this, selectors, overwrite);
    this.selectors.push(".port");
    return this;
};

PMActivityDropBehavior.prototype.onDragEnter = function (customShape) {
    return function (e, ui) {
        var shapeRelative, i;
        if (customShape.extendedType !== "PARTICIPANT") {
            if (ui.helper && ui.helper.hasClass("dragConnectHandler")) {
                if (customShape.extendedType !== "TEXT_ANNOTATION") {
                    shapeRelative = customShape.canvas.dragConnectHandlers.get(0).relativeShape;
                    if (shapeRelative.id !== customShape.id) {
                        for (i = 0; i < 4; i += 1) {
                            customShape.showConnectDropHelper(i, customShape);
                        }
                    }
                } else {
                    if (customShape.extendedType !== "H_LABEL" && customShape.extendedType !== "V_LABEL") {
                        shapeRelative = customShape.canvas.dragConnectHandlers.get(3).relativeShape;
                        if (shapeRelative.id !== customShape.id) {
                            customShape.canvas.hideDropConnectHandlers();
                            customShape.showConnectDropHelper(3, customShape);
                        }
                    }
                }
            } else {
                customShape.layers.getFirst().addCSSClasses(['dropableClass']);
                return false;
            }
        } else {
            shapeRelative = customShape.canvas.dragConnectHandlers.get(0).relativeShape;
            if (shapeRelative.id !== customShape.id) {
                if (ui.helper && ui.helper.hasClass("dragConnectHandler")) {
                    for (i = 0; i < 10; i += 1) {
                        connectHandler = customShape.canvas.dropConnectHandlers.get(i);
                        connectHandler.setDimension(18 * customShape.canvas.getZoomFactor(), 18 * customShape.canvas.getZoomFactor());
                        connectHandler.setPosition(customShape.getZoomX() + i * customShape.getZoomWidth() / 10, customShape.getZoomY() - connectHandler.height / 2 - 1);
                        connectHandler.relativeShape = customShape;
                        connectHandler.attachDrop();
                        connectHandler.setVisible(true);
                    }

                    for (i = 0; i < 10; i += 1) {
                        connectHandler = customShape.canvas.dropConnectHandlers.get(i + 10);
                        connectHandler.setDimension(18 * customShape.canvas.getZoomFactor(), 18 * customShape.canvas.getZoomFactor());
                        connectHandler.setPosition(customShape.getZoomX() + i * customShape.getZoomWidth() / 10, customShape.getZoomY() + customShape.getZoomHeight() - connectHandler.height / 2 - 1);
                        connectHandler.relativeShape = customShape;
                        connectHandler.attachDrop();
                        connectHandler.setVisible(true);
                    }
                }
            }
        }
    }
};
PMActivityDropBehavior.prototype.onDragLeave = function (shape) {
    return function (e, ui) {
        shape.layers.getFirst().removeCSSClasses(['dropableClass']);
    };
};
'use strict';

/**
 * An empty collection stub. Use {@link RefsCollection.extend} to extend a
 * collection with ref semantics.
 *
 * @classdesc A change and inverse-reference aware collection with set semantics.
 *
 * @class RefsCollection
 */
function RefsCollection() { }

/**
 * Extends a collection with {@link Refs} aware methods
 *
 * @memberof RefsCollection
 * @static
 *
 * @param  {Array<Object>} collection
 * @param  {Refs} refs instance
 * @param  {Object} property represented by the collection
 * @param  {Object} target object the collection is attached to
 *
 * @return {RefsCollection<Object>} the extended array
 */
function extend(collection, refs, property, target) {

    var inverseProperty = property.inverse;

    /**
     * Removes the given element from the array and returns it.
     *
     * @method RefsCollection#remove
     *
     * @param {Object} element the element to remove
     */
    collection.remove = function(element) {
        var idx = this.indexOf(element);
        if (idx !== -1) {
            this.splice(idx, 1);

            // unset inverse
            refs.unset(element, inverseProperty, target);
        }

        return element;
    };

    /**
     * Returns true if the collection contains the given element
     *
     * @method RefsCollection#contains
     *
     * @param {Object} element the element to check for
     */
    collection.contains = function(element) {
        return this.indexOf(element) !== -1;
    };

    /**
     * Adds an element to the array, unless it exists already (set semantics).
     *
     * @method RefsCollection#add
     *
     * @param {Object} element the element to add
     */
    collection.add = function(element) {

        if (!this.contains(element)) {
            this.push(element);

            // set inverse
            refs.set(element, inverseProperty, target);
        }
    };

    return collection;
}


//module.exports.extend = extend;
'use strict';

//var Collection = require('./collection');

function hasOwnProperty(e, property) {
    return Object.prototype.hasOwnProperty.call(e, property.name || property);
}


function defineCollectionProperty(ref, property, target) {
    Object.defineProperty(target, property.name, {
        enumerable: property.enumerable,
        value: Collection.extend(target[property.name] || [], ref, property, target)
    });
}


function defineProperty(ref, property, target) {

    var inverseProperty = property.inverse;

    var _value = target[property.name];
    if(!target[property.name]){
       Object.defineProperty(target, property.name, {
            enumerable: property.enumerable,

            get: function() {
                return _value;
            },

            set: function(value) {

                // return if we already performed all changes
                if (value === _value) {
                    return;
                }

                var old = _value;

                // temporary set null
                _value = null;

                if (old) {
                    ref.unset(old, inverseProperty, target);
                }

                // set new value
                _value = value;

                // set inverse value
                ref.set(_value, inverseProperty, target);
            }
        });
    };
}

/**
 * Creates a new references object defining two inversly related
 * attribute descriptors a and b.
 *
 * <p>
 *   When bound to an object using {@link Refs#bind} the references
 *   get activated and ensure that add and remove operations are applied
 *   reversely, too.
 * </p>
 *
 * <p>
 *   For attributes represented as collections {@link Refs} provides the
 *   {@link RefsCollection#add}, {@link RefsCollection#remove} and {@link RefsCollection#contains} extensions
 *   that must be used to properly hook into the inverse change mechanism.
 * </p>
 *
 * @class Refs
 *
 * @classdesc A bi-directional reference between two attributes.
 *
 * @param {Refs.AttributeDescriptor} a property descriptor
 * @param {Refs.AttributeDescriptor} b property descriptor
 *
 * @example
 *
 * var refs = Refs({ name: 'wheels', collection: true, enumerable: true }, { name: 'car' });
 *
 * var car = { name: 'toyota' };
 * var wheels = [{ pos: 'front-left' }, { pos: 'front-right' }];
 *
 * refs.bind(car, 'wheels');
 *
 * car.wheels // []
 * car.wheels.add(wheels[0]);
 * car.wheels.add(wheels[1]);
 *
 * car.wheels // [{ pos: 'front-left' }, { pos: 'front-right' }]
 *
 * wheels[0].car // { name: 'toyota' };
 * car.wheels.remove(wheels[0]);
 *
 * wheels[0].car // undefined
 */
function Refs(a, b) {

    if (!(this instanceof Refs)) {
        return new Refs(a, b);
    }

    // link
    a.inverse = b;
    b.inverse = a;

    this.props = {};
    this.props[a.name] = a;
    this.props[b.name] = b;
}

/**
 * Binds one side of a bi-directional reference to a
 * target object.
 *
 * @memberOf Refs
 *
 * @param  {Object} target
 * @param  {String} property
 */
Refs.prototype.bind = function(target, property) {
    if (typeof property === 'string') {
        if (!this.props[property]) {
            throw new Error('no property <' + property + '> in ref');
        }
        property = this.props[property];
    }

    if (property.collection) {
        defineCollectionProperty(this, property, target);
    } else {
        defineProperty(this, property, target);
    }
};

Refs.prototype.ensureBound = function(target, property) {
    if (!hasOwnProperty(target, property)) {
        this.bind(target, property);
    }
};

Refs.prototype.unset = function(target, property, value) {

    if (target) {
        this.ensureBound(target, property);

        if (property.collection) {
            target[property.name].remove(value);
        } else {
            target[property.name] = undefined;
        }
    }
};

Refs.prototype.set = function(target, property, value) {

    if (target) {
        this.ensureBound(target, property);

        if (property.collection) {
            target[property.name].add(value);
        } else {
            target[property.name] = value;
        }
    }
};
//
//module.exports = Refs;


/**
 * An attribute descriptor to be used specify an attribute in a {@link Refs} instance
 *
 * @typedef {Object} Refs.AttributeDescriptor
 * @property {String} name
 * @property {boolean} [collection=false]
 * @property {boolean} [enumerable=false]
 */
'use strict';

//
var diRefs = new Refs({ name: 'bpmnElement', enumerable: true }, { name: 'di' });


function BpmnTreeWalker(handler) {

    // list of containers already walked
    var handledProcesses = [];

    // list of elements to handle deferred to ensure
    // prerequisites are drawn
    var deferred = [];

    ///// Helpers /////////////////////////////////

    function contextual(fn, ctx) {
        return function(e) {
            fn(e, ctx);
        };
    }

    function is(element, type) {
        return element.$instanceOf(type);
    }

    function visit(element, ctx) {

        var gfx = element.gfx;

        // avoid multiple rendering of elements
        if (gfx) {
            throw new Error('already rendered <' + element.id + '>');
        }

        // call handler
        return handler.element(element, ctx);
    }

    function visitRoot(element, diagram) {
        return handler.root(element, diagram);
    }

    function visitIfDi(element, ctx) {
        try {
            if (element.di) {
                return visit(element, ctx);
            }
        } catch (e) {
            logError(e.message, { element: element, error: e });
        }
    }

    function logError(message, context) {
        handler.error(message, context);
    }

    ////// DI handling ////////////////////////////

    function registerDi(di) {
        var bpmnElement = di.bpmnElement;

        if (bpmnElement) {
            diRefs.bind(bpmnElement, 'di');
            bpmnElement.di = di;
        } else {
            logError('no bpmnElement for <' + di.$type + '#' + di.id + '>', { element: di });
        }
    }

    function handleDiagram(diagram) {
        handlePlane(diagram.plane);
    }

    function handlePlane(plane) {
        registerDi(plane);

        _.forEach(plane.planeElement, handlePlaneElement);
    }

    function handlePlaneElement(planeElement) {
        registerDi(planeElement);
    }


    ////// Semantic handling //////////////////////

    function handleDefinitions(definitions, diagram) {
        // make sure we walk the correct bpmnElement

        var diagrams = definitions.diagrams, i, rootElement, participant;

        if (diagram && diagrams.indexOf(diagram) === -1) {
            throw new Error('diagram not part of bpmn:Definitions');
        }

        if (!diagram && diagrams && diagrams.length) {
            diagram = diagrams[0];
        }

        // no diagram -> nothing to import
        if (!diagram) {
            return;
        }

        // load DI from selected diagram only
        handleDiagram(diagram);

        var plane = diagram.plane,
            rootElement = plane.bpmnElement;

        if (!rootElement) {
            throw new Error('no rootElement referenced in BPMNPlane <' + diagram.plane.id + '>');
        }


        var ctx = visitRoot(rootElement, plane);

        if (is(rootElement, 'bpmn:Process')) {
            handleProcess(rootElement, ctx);
            rootElement.visited = true;
        } else if (is(rootElement, 'bpmn:Collaboration')) {
            handleCollaboration(rootElement, ctx);

            // force drawing of everything not yet drawn that is part of the target DI
            handleUnhandledProcesses(definitions.rootElements, ctx);

            for (i = 0; i < rootElement.participants.length; i += 1) {
                participant = rootElement.participants[i];
                if(participant.processRef) {
                    participant.processRef.visited = true;
                }
            }


        } else {
            throw new Error('unsupported root element for bpmndi:Diagram <' + rootElement.$type + '>');
        }

        // handle all deferred elements
        handleDeferred(deferred);
        for (i = 0; i < definitions.rootElements.length; i += 1) {
            rootElement = definitions.rootElements[i];
            if (rootElement.$type !== 'bpmn:Collaboration' && !rootElement.visited) {
                //plane
                //var ctx = visitRoot(rootElement, plane);
                if (is(rootElement, 'bpmn:Process')) {
                    handleProcess(rootElement, ctx);
                } else if(rootElement.$type !== 'bpmn:DataStore'){
                    throw new Error('unsupported root element for bpmndi:Diagram <' + rootElement.$type + '>');
                }
                handleDeferred(deferred);
            }
        }

    }

    function handleDeferred(deferred) {
        _.forEach(deferred, function(d) { d(); });
    }

    function handleProcess(process, context) {
        handleFlowElementsContainer(process, context);
        handleIoSpecification(process.ioSpecification, context);

        handleArtifacts(process.artifacts, context);

        // log process handled
        handledProcesses.push(process);
    }

    function handleUnhandledProcesses(rootElements) {

        // walk through all processes that have not yet been drawn and draw them
        // if they contain lanes with DI information.
        // we do this to pass the free-floating lane test cases in the MIWG test suite
        var processes = _.filter(rootElements, function(e) {
            return is(e, 'bpmn:Process') && e.laneSets && handledProcesses.indexOf(e) === -1;
        });

        processes.forEach(contextual(handleProcess));
    }

    function handleMessageFlow(messageFlow, context) {
        visitIfDi(messageFlow, context);
    }

    function handleMessageFlows(messageFlows, context) {
        if (messageFlows) {
            _.forEach(messageFlows, contextual(handleMessageFlow, context));
        }
    }

    function handleDataAssociation(association, context) {
        visitIfDi(association, context);
    }

    function handleDataInput(dataInput, context) {
        visitIfDi(dataInput, context);
    }

    function handleDataOutput(dataOutput, context) {
        visitIfDi(dataOutput, context);
    }

    function handleArtifact(artifact, context) {

        // bpmn:TextAnnotation
        // bpmn:Group
        // bpmn:Association

        visitIfDi(artifact, context);
    }

    function handleArtifacts(artifacts, context) {
        _.forEach(artifacts, contextual(handleArtifact, context));
    }

    function handleIoSpecification(ioSpecification, context) {

        if (!ioSpecification) {
            return;
        }

        _.forEach(ioSpecification.dataInputs, contextual(handleDataInput, context));
        _.forEach(ioSpecification.dataOutputs, contextual(handleDataOutput, context));
    }

    function handleSubProcess(subProcess, context) {
        handleFlowElementsContainer(subProcess, context);
        handleArtifacts(subProcess.artifacts, context);
    }

    function handleFlowNode(flowNode, context) {
        var childCtx = visitIfDi(flowNode, context);

        if (is(flowNode, 'bpmn:SubProcess')) {
            handleSubProcess(flowNode, childCtx || context);
        }
    }

    function handleSequenceFlow(sequenceFlow, context) {
        visitIfDi(sequenceFlow, context);
    }

    function handleDataElement(dataObject, context) {
        visitIfDi(dataObject, context);
    }

    function handleBoundaryEvent(dataObject, context) {
        visitIfDi(dataObject, context);
    }

    function handleLane(lane, context) {
        var newContext = visitIfDi(lane, context);

        if (lane.childLaneSet) {
            handleLaneSet(lane.childLaneSet, newContext || context);
        } else {
            var filterList = _.filter(lane.flowNodeRef, function(e) {
                return e.$type !== 'bpmn:BoundaryEvent';
            });
            handleFlowElements(filterList, newContext || context);
        }
    }

    function handleLaneSet(laneSet, context) {
        _.forEach(laneSet.lanes, contextual(handleLane, context));
    }

    function handleLaneSets(laneSets, context) {
        _.forEach(laneSets, contextual(handleLaneSet, context));
    }

    function handleFlowElementsContainer(container, context) {

        if (container.laneSets) {
            handleLaneSets(container.laneSets, context);
            handleNonFlowNodes(container.flowElements);
        } else {
            handleFlowElements(container.flowElements, context);
        }
    }

    function handleNonFlowNodes(flowElements, context) {
        _.forEach(flowElements, function(e) {
            if (is(e, 'bpmn:SequenceFlow')) {
                deferred.push(function() {
                    handleSequenceFlow(e, context);
                });
            } else if (is(e, 'bpmn:BoundaryEvent')) {
                deferred.unshift(function() {
                    handleBoundaryEvent(e, context);
                });
            } else if (is(e, 'bpmn:DataObject')) {
                // SKIP (assume correct referencing via DataObjectReference)
            } else if (is(e, 'bpmn:DataStoreReference')) {
                handleDataElement(e, context);
            } else if (is(e, 'bpmn:DataObjectReference')) {
                handleDataElement(e, context);
            }
        });
    }

    function handleFlowElements(flowElements, context) {
        _.forEach(flowElements, function(e) {
            if (is(e, 'bpmn:SequenceFlow')) {
                deferred.push(function() {
                    handleSequenceFlow(e, context);
                });
            } else if (is(e, 'bpmn:BoundaryEvent')) {
                deferred.unshift(function() {
                    handleBoundaryEvent(e, context);
                });
            } else if (is(e, 'bpmn:FlowNode')) {
                handleFlowNode(e, context);

                if (is(e, 'bpmn:Activity')) {

                    handleIoSpecification(e.ioSpecification, context);

                    // defer handling of associations
                    deferred.push(function() {
                        _.forEach(e.dataInputAssociations, contextual(handleDataAssociation, context));
                        _.forEach(e.dataOutputAssociations, contextual(handleDataAssociation, context));
                    });
                }
            } else if (is(e, 'bpmn:DataObject')) {
                // SKIP (assume correct referencing via DataObjectReference)
            } else if (is(e, 'bpmn:DataStoreReference')) {
                handleDataElement(e, context);
            } else if (is(e, 'bpmn:DataObjectReference')) {
                handleDataElement(e, context);
            } else {
                logError(
                    'unrecognized flowElement <' + e.$type + '> in context ' + (context ? context.id : null),
                    { element: e, context: context });
            }
        });
    }

    function handleParticipant(participant, context) {
        var newCtx = visitIfDi(participant, context);

        var process = participant.processRef;
        if (process) {
            handleProcess(process, newCtx || context);
        }
    }

    function handleCollaboration(collaboration) {

        _.forEach(collaboration.participants, contextual(handleParticipant));

        handleArtifacts(collaboration.artifacts);

        handleMessageFlows(collaboration.messageFlows);
    }


    ///// API ////////////////////////////////

    return {
        handleDefinitions: handleDefinitions
    };
}

//module.exports = BpmnTreeWalker;
//var BpmnTreeWalker = require('./BpmnTreeWalker');


/**
 * Import the definitions into a diagram.
 *
 * Errors and warnings are reported through the specified callback.
 *
 * @param  {Diagram} diagram
 * @param  {ModdleElement} definitions
 * @param  {Function} done the callback, invoked with (err, [ warning ]) once the import is done
 */
var importBpmnDiagram = function (definitions) {
    var error;
    this.participants = [];
    this.lanes = [];
    this.laneRelPosition = 0;
    this.flowsArray = new PMUI.util.ArrayList();
    this.headerHeight = 0;
    this.eventMarkerMap = {
        'CATCH': {
            'bpmn:MessageEventDefinition': 'MESSAGECATCH',
            'bpmn:TimerEventDefinition': 'TIMER',
            'bpmn:ConditionalEventDefinition': 'CONDITIONAL',
            'bpmn:LinkEventDefinition': 'LINKCATCH',
            'bpmn:SignalEventDefinition': 'SIGNALCATCH',
            'bpmn:ErrorEventDefinition': 'ERRORCATCH',            
            'bpmn:CompensateEventDefinition': 'COMPENSATIONTHROW'
        },
        'THROW': {
            'bpmn:MessageEventDefinition': 'MESSAGETHROW',
            'bpmn:TimerEventDefinition': 'TIMER',
            'bpmn:ConditionalEventDefinition': 'CONDITIONAL',
            'bpmn:CompensateEventDefinition': 'COMPENSATIONTHROW',
            'bpmn:SignalEventDefinition': 'SIGNALTHROW',
            'bpmn:ErrorEventDefinition': 'ERRORTHROW',
            'bpmn:TerminateEventDefinition':'TERMINATETHROW'
        }

    };
    this.taskType = {
        'bpmn:Task': 'EMPTY',
        'bpmn:SendTask': 'SENDTASK',
        'bpmn:ReceiveTask': 'RECEIVETASK',
        'bpmn:UserTask': 'USERTASK',
        'bpmn:ServiceTask': 'SERVICETASK',
        'bpmn:ScriptTask': 'SCRIPTTASK',
        'bpmn:ManualTask': 'MANUALTASK',
        'bpmn:BusinessRuleTask': 'BUSINESSRULE'
    };

    this.loopType = {
        'bpmn:StandardLoopCharacteristics': 'LOOP',
        'bpmn:MultiInstanceLoopCharacteristics':'PARALLEL'        
    };

    this.dataObjectType = {
        'bpmn:DataInput': 'datainput',
        'bpmn:DataOutput':'dataoutput',
        'bpmn:DataObject':'dataobject'                  
    };

    //eventBus.fire('import.start');
    try {
        PMDesigner.businessObject = definitions;
        this.checkXML(definitions);
        if(PMDesigner.project.XMLSupported){
            diRefs = null;
            diRefs = new Refs({ name: 'bpmnElement', enumerable: true }, { name: 'di' });
            this.parse(definitions);    
        }
    } catch (e) {
        error = e;
        PMDesigner.project.setXMLSupported(false);
    }
};
importBpmnDiagram.prototype.parse = function (definitions) {
    var self =  this, sidebarCanvas = [], i, visitor;

    visitor = {

        root: function(element) {
            var businessObject = {},
                canvas,
                project;

            if (element.$type === 'bpmn:Collaboration') {
                // TODO IF THERE IS COLLABORATIONS
                return self.addParticipant(element);
            } else {
                businessObject.elem = element;
                businessObject.di = element.di;
                for (i = 0; i < PMDesigner.sidebar.length; i += 1) {
                    sidebarCanvas = sidebarCanvas.concat(PMDesigner.sidebar[i].getSelectors());
                    jQuery(".bpmn_shapes").append(PMDesigner.sidebar[i].getHTML());
                }
                sidebarCanvas.splice(15, 1);  //to remove lane selector
                //Remove Lasso and Validator
                sidebarCanvas.splice(17, 2);
                // sidebarCanvas = sidebarCanvas + ',.mafe-event-start';
                sidebarCanvas = sidebarCanvas.concat('.pmui-pmevent');
                sidebarCanvas = sidebarCanvas.concat('.pmui-pmactivity');
                sidebarCanvas = sidebarCanvas.concat('.pmui-pmgateway');
                sidebarCanvas = sidebarCanvas.concat('.pmui-pmdata');
                sidebarCanvas = sidebarCanvas.concat('.mafe-artifact-annotation');


                canvas =  PMDesigner.project.buildCanvas(sidebarCanvas, {name: element.$parent.name});
                PMUI.setActiveCanvas(canvas);
                project = PMUI.getActiveCanvas().project;
                project && project.userSettings && project.userSettings.enabled_grid
                    ? canvas.enableGridLine() : canvas.disableGridLine();

                jQuery("#p-center-layout").scroll(canvas.onScroll(canvas, jQuery("#p-center-layout")));
                PMDesigner.canvasList.setValue(canvas.getID());
                element.id = canvas.getID();

                canvas.businessObject = businessObject;

            }
        },

        element: function(element, parentShape) {

            return self.addElement(element, parentShape);

        },
        error: function(message, context) {            
            //warnings.push({ message: message, context: context });
        }
    };

    var walker = new BpmnTreeWalker(visitor);

    // import
    walker.handleDefinitions(definitions);
};

importBpmnDiagram.prototype.checkXML = function (definitions) {
    var self =  this, 
        visitor,
        walker;

    visitor = {
        root: function(element) {
           
        },
        element: function(element, parentShape) {
            return (self.checkElement(element, parentShape));            
        },
        error: function(message, context) {
            console.log('add error');            
        }
    };

    var walker = new BpmnTreeWalker(visitor);

    // import
    walker.handleDefinitions(definitions);
};

/**
 *  Adds bpmn participants, that arrives from a .bpmn file as a collaboration node
 * @param element
 * @returns {importBpmnDiagram}
 */
importBpmnDiagram.prototype.addParticipant = function (collaboration) {
    var i,
        j,
        participants,
        participant,
        sidebarCanvas = [],
        canvas,
        rootElements,
        element,
        tempIndex = -1,
        isParticipant,
        project;

    participants = collaboration.participants;
    for (j = 0; j < PMDesigner.sidebar.length; j += 1) {
        sidebarCanvas = sidebarCanvas.concat(PMDesigner.sidebar[j].getSelectors());
        jQuery(".bpmn_shapes").append(PMDesigner.sidebar[j].getHTML());
    }

    sidebarCanvas.splice(15, 1);  //to remove lane selector
    //Remove Lasso and Validator
    sidebarCanvas.splice(17, 2);
    sidebarCanvas = sidebarCanvas.concat('.pmui-pmevent');
    sidebarCanvas = sidebarCanvas.concat('.pmui-pmactivity');
    sidebarCanvas = sidebarCanvas.concat('.pmui-pmgateway');
    sidebarCanvas = sidebarCanvas.concat('.pmui-pmdata');
    sidebarCanvas = sidebarCanvas.concat('.mafe-artifact-annotation');
    canvas =  PMDesigner.project.buildCanvas(sidebarCanvas, {name:  collaboration.$parent.name});
    PMUI.setActiveCanvas(canvas);
    project = PMUI.getActiveCanvas().project;
    project && project.userSettings && project.userSettings.enabled_grid
        ? canvas.enableGridLine() : canvas.disableGridLine();
    PMDesigner.canvasList.setValue(canvas.getID());

    jQuery("#p-center-layout").scroll(canvas.onScroll(canvas, jQuery("#p-center-layout")));

    rootElements = PMDesigner.definitions.rootElements;
    for (i = 0; i < participants.length; i += 1) {
        participant = participants[i];
        isParticipant = true;
        for (j = 0; j < rootElements.length; j += 1) {
            element = rootElements[j];

            if (element.$type === 'bpmn:Process' && participant.processRef && participant.processRef.id !== element.id) {
                tempIndex = j;
                isParticipant = false;
                break;
            }
        }
        if( typeof participant.processRef !== 'undefined') {
            this.participants.push(participant);
            canvas.businessObject.elem = participant.processRef;
        }
        if (canvas.businessObject && participant.$parent.di) {
            canvas.businessObject.di = participant.$parent.di;
        }
        canvas.buildingDiagram = true;
    }
    if (tempIndex > 0 ) {
        canvas.businessObject.elem = rootElements[tempIndex];
    }
    return this;
};

importBpmnDiagram.prototype.getLoopCharacteristics = function (element) {    
    var loopType;
    if(element.loopCharacteristics){
        loopType = this.loopType[element.loopCharacteristics.$type] === undefined ? "EMPTY": this.loopType[element.loopCharacteristics.$type];
        if(element.loopCharacteristics.isSequential){
            loopType = "SEQUENTIAL";
        }
    }
    return loopType;    
};

importBpmnDiagram.prototype.getOwnProperties = function (element) {
    var ownProp = false,
        parent,
        taskType,
        loopType,
        marker,
        behavior,
        type,
        typeMap = {
            'bpmn:Lane': 'LANE',
            'bpmn:Participant': 'POOL',
            'bpmn:StartEvent': 'START',
            'bpmn:EndEvent': 'END',
            'bpmn:BoundaryEvent': 'BOUNDARY',
            'bpmn:Task': 'TASK',
            'bpmn:SendTask': 'TASK',
            'bpmn:ReceiveTask': 'TASK',
            'bpmn:UserTask': 'TASK',
            'bpmn:ServiceTask': 'TASK',
            'bpmn:ScriptTask': 'TASK',
            'bpmn:ManualTask': 'TASK',
            'bpmn:CallActivity': 'TASK',
            'bpmn:BusinessRuleTask': 'TASK',
            'bpmn:SubProcess': 'SUB_PROCESS',
            'bpmn:ExclusiveGateway': 'EXCLUSIVE',
            'bpmn:ParallelGateway': 'PARALLEL',
            'bpmn:InclusiveGateway': 'INCLUSIVE',
            //'bpmn:EventBasedGateway': 'EVENTBASED',
            'bpmn:IntermediateCatchEvent': 'INTERMEDIATE',
            'bpmn:IntermediateThrowEvent': 'INTERMEDIATE',
            'bpmn:TextAnnotation': 'TEXT_ANNOTATION',
            'bpmn:DataStoreReference': 'DATASTORE',
            'bpmn:DataObjectReference': 'DATAOBJECT',
            'bpmn:Group': 'GROUP',
            'bpmn:DataInput': 'DATAINPUT',
            'bpmn:DataOutput': 'DATAOUTPUT'
            
        };
    type = typeMap[element.$type];
    switch (type) {
    case 'START':
        if (element.eventDefinitions && element.eventDefinitions[0]) {
            marker = this.eventMarkerMap['CATCH'][element.eventDefinitions[0].$type];
        }
        ownProp = {
            'evn_name': element.name,
            'evn_marker': marker ? marker : 'EMPTY'
        };
        break;
    case 'END':
        if (element.eventDefinitions && element.eventDefinitions[0]) {
            marker = this.eventMarkerMap['THROW'][element.eventDefinitions[0].$type];
        }

        ownProp = {
            'evn_name': element.name,
            'evn_marker': marker ? marker : 'EMPTY'
        };
        
        break;
    case 'INTERMEDIATE':
        behavior = (element.$type === 'bpmn:IntermediateCatchEvent') ? 'CATCH' : 'THROW';        
        if (element.eventDefinitions && element.eventDefinitions[0]) {
            marker = this.eventMarkerMap[behavior][element.eventDefinitions[0].$type];
        }        
        //marker = this.eventMarkerMap[behavior][element.eventDefinitions[0].$type];
        ownProp = {
            'evn_behavior': behavior,
            'evn_marker': marker ? marker : 'MESSAGECATCH',
            'evn_name': element.name
        };
        
        break;
    case 'BOUNDARY':
        if (element.eventDefinitions && element.eventDefinitions[0]) {
            marker = this.eventMarkerMap['CATCH'][element.eventDefinitions[0].$type];
        }
        if(typeof marker !== "undefined" && this.validateElementsForDesigner(element.$type,marker)){
            ownProp = {
                'evn_behavior': behavior,
                'evn_marker': marker ? marker : 'EMPTY',
                'evn_name': element.name,
                'bou_container': 'bpmnActivity',
                'bou_element': element.attachedToRef.id
            };
        }else{
             type = undefined;
        }
        break;
    case 'SUB_PROCESS':
        ownProp = {
            'act_name': element.name,
            'act_type': 'SUB_PROCESS'
        };
        break;
    case 'TASK':
        taskType = this.taskType[element.$type];
        loopType = this.getLoopCharacteristics(element);          
        ownProp = {
            'act_name': element.name,
            'act_task_type': taskType,
            'act_loop_type' : loopType
        };
        break;
    case 'EXCLUSIVE':
        ownProp = {
            'gat_uid': 'pmui-' + PMUI.generateUniqueId(),
            'gat_name': element.name,
            'gat_type': 'EXCLUSIVE'
        };
        break;
    case 'PARALLEL':
        ownProp = {
            'gat_uid': 'pmui-' + PMUI.generateUniqueId(),
            'gat_name': element.name,
            'gat_type': 'PARALLEL'
        };
        break;
    case 'INCLUSIVE':
        ownProp = {
            'gat_uid': 'pmui-' + PMUI.generateUniqueId(),
            'gat_name': element.name,
            'gat_type': 'INCLUSIVE'
        };
        break;
    case 'EVENTBASED':
        ownProp = {
            'gat_uid': 'pmui-' + PMUI.generateUniqueId(),
            'gat_name': element.name,
            'gat_type': 'EVENTBASED'
        };
        break;
    case 'POOL':
        ownProp = {
            'par_uid': 'pmui-' + PMUI.generateUniqueId(),
            'par_name': element.name
        };
        if (typeof element.processRef === 'undefined') {
            type = 'PARTICIPANT';
        }

        break;
    case 'LANE':
        this.lanes.push(element);
        parent = this.getParentData(element);
        this.laneRelPosition += 1;
        ownProp = {
            'lan_uid': 'pmui-' + PMUI.generateUniqueId(),
            'lan_name': element.name ? element.name : ' ',
            'bou_container': 'bpmnPool',
            'bou_element': parent.parentUid,
            'bou_rel_position': this.laneRelPosition,
            'bou_x': 40,
            'bou_y': element.di.bounds.y - parent.y
        };
        break;
    case 'GROUP':
    case 'TEXT_ANNOTATION':
        ownProp = {
            'art_uid': 'pmui-' + PMUI.generateUniqueId(),
            'art_name': element.text
        };
        break;
    case 'DATASTORE':
    case 'DATAOBJECT':
        ownProp = {
            'dat_uid': 'pmui-' + PMUI.generateUniqueId(),
            'dat_name': element.name,
            'dat_object_type':this.dataObjectType[element.$type]?this.dataObjectType[element.$type]:'dataobject'
        };
        break;
    case 'DATAINPUT':
        ownProp = {
            'dat_uid': 'pmui-' + PMUI.generateUniqueId(),
            'dat_name': element.name,
            'dat_object_type':this.dataObjectType[element.$type]?this.dataObjectType[element.$type]:'dataobject'
        };
        break;
    case 'DATAOUTPUT':
        ownProp = {
            'dat_uid': 'pmui-' + PMUI.generateUniqueId(),
            'dat_name': element.name,
            'dat_object_type':this.dataObjectType[element.$type]?this.dataObjectType[element.$type]:'dataobject'
        };
        break;            
    }
    return {'properties': ownProp, 'type': type};
};

importBpmnDiagram.prototype.checkElement = function (element) {
    var validation,
        type,
        marker,
        loop,
        parameters;

    parameters = {
        'bpmn:StartEvent':{
            "parallelMultiple":false
        }
    };

    loopTypes= {
        'bpmn:StandardLoopCharacteristics':true,
        'bpmn:MultiInstanceLoopCharacteristics':true,
        'empty':true
    };     

    validation ={
        'bpmn:Lane': true,
        'bpmn:ChoreographyTask':true,
        'bpmn:Participant': true,
        'bpmn:Task': true,
        'bpmn:SendTask': true,
        'bpmn:ReceiveTask': true,
        'bpmn:UserTask': true,
        'bpmn:ServiceTask': true,
        'bpmn:ScriptTask': true,
        'bpmn:ManualTask': true,
        'bpmn:CallActivity': false,
        'bpmn:BusinessRuleTask': true,          
                
        'bpmn:SubProcess': true,
        'bpmn:AdHocSubProcess':false,
        'bpmn:Transaction':false,        
        'bpmn:ExclusiveGateway': true,
        'bpmn:ParallelGateway': true,
        'bpmn:InclusiveGateway': true,
        'bpmn:EventBasedGateway': false,
            //'bpmn:IntermediateCatchEvent': 'INTERMEDIATE',
            //'bpmn:IntermediateThrowEvent': 'INTERMEDIATE',
        'bpmn:TextAnnotation': true,
        'bpmn:DataStoreReference': true,
        'bpmn:DataObjectReference': true,
        'bpmn:DataInput': true,
        'bpmn:DataOutput': true,        
        'bpmn:Group': true,

        'bpmn:SequenceFlow': true,
        'bpmn:Association': true,
        'bpmn:DataOutputAssociation': true,
        'bpmn:DataInputAssociation': true,
        'bpmn:MessageFlow': true,

        'bpmn:StartEvent':{            
            'bpmn:MessageEventDefinition': true,
            'bpmn:TimerEventDefinition': true,
            'bpmn:ConditionalEventDefinition': true,
            'bpmn:CompensateEventDefinition': false,
            'bpmn:SignalEventDefinition': true,
            'bpmn:ErrorEventDefinition': false,
            'bpmn:TerminateEventDefinition':false,
            'bpmn:MultipleEventDefinition':false,
            'bpmn:EscalationEventDefinition':false,
            'empty':true        
        },

        'bpmn:EndEvent':{            
            'bpmn:MessageEventDefinition': true,
            'bpmn:TimerEventDefinition': false,
            'bpmn:ConditionalEventDefinition': false,
            'bpmn:CompensateEventDefinition': false,
            'bpmn:SignalEventDefinition': true,
            'bpmn:ErrorEventDefinition': true,
            'bpmn:TerminateEventDefinition':true,
            'bpmn:MultipleEventDefinition':false,
            'bpmn:EscalationEventDefinition':false,
            'bpmn:CancelEventDefinition':false,            
            'empty':true 
        },

        'bpmn:IntermediateCatchEvent':{
            'bpmn:MessageEventDefinition': true,
            'bpmn:TimerEventDefinition': true,
            'bpmn:ConditionalEventDefinition': true,
            'bpmn:CompensateEventDefinition': false,
            'bpmn:SignalEventDefinition': true,
            'bpmn:ErrorEventDefinition': false,
            'bpmn:TerminateEventDefinition':false,
            'bpmn:MultipleEventDefinition':false,
            'bpmn:EscalationEventDefinition':false,        
            'bpmn:LinkEventDefinition':false,
            'empty':false 
        },
        
        'bpmn:IntermediateThrowEvent':{            
            'bpmn:MessageEventDefinition': true,
            'bpmn:TimerEventDefinition': false,
            'bpmn:ConditionalEventDefinition': false,
            'bpmn:CompensateEventDefinition': false,
            'bpmn:SignalEventDefinition': true,
            'bpmn:ErrorEventDefinition': false,
            'bpmn:TerminateEventDefinition':false,
            'bpmn:MultipleEventDefinition':false,
            'bpmn:EscalationEventDefinition':false,        
            'bpmn:LinkEventDefinition':false,
            'empty':false 
        },
        
        'bpmn:BoundaryEvent':{
            'bpmn:MessageEventDefinition': false,     //true
            'bpmn:TimerEventDefinition': false,       //true
            'bpmn:ConditionalEventDefinition': false, //true
            'bpmn:CompensateEventDefinition': false,
            'bpmn:SignalEventDefinition': false,      //true
            'bpmn:ErrorEventDefinition': false,       //true
            'bpmn:TerminateEventDefinition':false,
            'bpmn:MultipleEventDefinition':false,
            'bpmn:EscalationEventDefinition':false,        
            'bpmn:LinkEventDefinition':false,
            'empty':false
            },        
        };

    type = element.$type;
    //for events
    if (element.eventDefinitions && element.eventDefinitions[0]) {
        marker = element.eventDefinitions[0].$type;
    }else{
        if(element.$type == 'bpmn:StartEvent' ||  element.$type == 'bpmn:IntermediateThrowEvent' || element.$type == 'bpmn:IntermediateCatchEvent' || element.$type == 'bpmn:BoundaryEvent')
            marker = "empty";        
    } 

    //for subprocess
    if (element.loopCharacteristics) {
        if(!loopTypes[element.loopCharacteristics.$type]){
            marker = "unresolved";
        }
    }

    if(marker !== undefined){
        if(this.checkParametersOfElement(element)){
            if(!validation[type][marker]){
                PMDesigner.project.setXMLSupported(false);
            }
        }else{
            PMDesigner.project.setXMLSupported(false);
        }        
    }else{
        if(this.checkParametersOfElement(element)){
            if(!validation[type]){
                PMDesigner.project.setXMLSupported(false);
            }    
        }else{
            PMDesigner.project.setXMLSupported(false);
        }
                   
    }
};

importBpmnDiagram.prototype.checkParametersOfElement= function (element) {
    if(element.$type == 'bpmn:StartEvent'){
        if(element.isInterrupting == false){
            return false;   
        }
        if(element.parallelMultiple == true){
            return false;   
        }
    }
    if(element.$type == 'bpmn:IntermediateThrowEvent' || element.$type == 'bpmn:IntermediateCatchEvent'){
        if(element.isInterrupting == false){
            return false;   
        }
    }
    if(element.$type == 'bpmn:IntermediateThrowEvent' || element.$type == 'bpmn:IntermediateCatchEvent'){
        if(element.isInterrupting == false){
            return false;   
        }
    }
    if(element.$type == 'bpmn:DataObjectReference'){
        if(element.dataObjectRef.isCollection == true){
            return false;   
        }
    }
    if(element.$type == 'bpmn:DataInput'){
        if(element.isCollection == true){
            return false;   
        }
    }
    if(element.$type == 'bpmn:DataOutput'){
        if(element.isCollection == true){
            return false;   
        }
    }               
    
    return true;
};

/**
 * gets parent data
 * @param element
 * @param parentShape
 */
importBpmnDiagram.prototype.getParentData = function (element) {
    var i,
        participant,
        uid,
        canvas = PMUI.getActiveCanvas(),
        pool,
        currentProcess =  element.$parent.$parent;
    for (i = 0; i < this.participants.length; i += 1) {
        participant = this.participants[i];
        if (participant.processRef.id === currentProcess.id) {
            uid = participant.id;
            break;
        }
    }
    pool = canvas.customShapes.find('id', uid);
    return {parentUid: uid, container: pool.extendedType, y: pool.y};
};
importBpmnDiagram.prototype.getContainerShape = function (element) {
    var i,
        j,
        participant,
        uid = null,
        canvas = PMUI.getActiveCanvas(),
        container = null,
        refs,
        currentProcess =  element.$parent;
    if (element.$type !== 'bpmn:TextAnnotation'
        && element.$type !== 'bpmn:DataStoreReference') {
        for (i = 0; i < this.lanes.length; i += 1) {
            refs = this.lanes[i].get('flowNodeRef');
            for (j = 0; j < refs.length; j += 1) {
                if (refs[j].id === element.id) {
                    uid = this.lanes[i].id;
                    break;
                }
            }
        }
        if (!uid) {
            while (currentProcess.$parent.$parent) {
                currentProcess = currentProcess.$parent;
            }
            for (i = 0; i < this.participants.length; i += 1) {
                participant = this.participants[i];
                if (typeof participant.processRef !== 'undefined'
                    && participant.processRef.id === currentProcess.id) {
                    uid = participant.id;
                    break;
                }
            }
        }
        container = canvas.customShapes.find('id', uid);
    } else {
        container = this.getAuxContainer(element);
    }

    return container;
};
importBpmnDiagram.prototype.getAuxContainer = function (element) {
    var  container = null,
        uid = null,
        i,
        x,
        y,
        x1,
        y1,
        x2,
        y2,
        participant,
        canvas = PMUI.getActiveCanvas();
    for (i = 0; i < this.participants.length; i += 1) {
        participant = this.participants[i];
        //console.log(participant);
        x = element.di.bounds.x;

        y = element.di.bounds.y;

        x1 = participant.di.bounds.x;
        x2 = participant.di.bounds.x + participant.di.bounds.width;

        y1 = participant.di.bounds.y;
        y2 = participant.di.bounds.y + participant.di.bounds.height;
        if(x < x2 && x > x1 &&  y < y2 && y > y1) {
            uid = participant.id;
            break;
        }
    }
    if (uid) {
        container = canvas.customShapes.find('id', uid);
    }

    return container;

};
/**
 * import bpmn elements
 * @param element
 * @param parentShape
 */
importBpmnDiagram.prototype.addElement = function (element) {
    var canvas = PMUI.getActiveCanvas(),
        shape,

        x,
        y,
        businessObject = {
            elem: element,
            di: element.di
        },
        bounds = element.di.bounds,
        container,
        bouElement = null,
        bouType = "bpmnDiagram",
        conectionMap = {
            'bpmn:SequenceFlow': 'SEQUENCE',
            'bpmn:Association': 'ASSOCIATION',
            'bpmn:DataOutputAssociation': 'DATAASSOCIATION',
            'bpmn:DataInputAssociation': 'DATAASSOCIATION',
            'bpmn:MessageFlow': 'MESSAGE'
        },
        ownProp;
    //if (PMDesigner.businessObject.id
    //    && PMDesigner.businessObject.id !== 'BPMNProcessmaker'
    //    && bounds) {
    //    //bounds.y += this.headerHeight; // to consider header
    //}
    ownProp = this.getOwnProperties(element);
    if (ownProp.properties) {

        container = this.getContainerShape(element);

        x = bounds.x;
        y = bounds.y;
        // validate for camunda modeler datastore
        if (container) {
            bouElement = container.id;
            bouType = container.type;
            while (container.getType() !== 'PMCanvas') {
                x = x - container.getX();
                y = y - container.getY();
                container = container.parent;
            }
        }
        shape = {
            'bou_container': bouType,
            'bou_element': bouElement,
            'bou_height': bounds.height,
            'bou_width': bounds.width,
            'bou_x': x,
            'bou_y': y

        };
        $.extend(true, shape, ownProp.properties);

        if (element.$type === 'bpmn:Participant') {
            if (typeof element.processRef !== 'undefined') {
                this.laneRelPosition = 0;
                element.processRef.id = 'pmui-' + PMUI.generateUniqueId();
                businessObject.elem = element.processRef;
            }
        }

        canvas.loadShape(ownProp.type, shape, false, businessObject);

        if (element.$type === 'bpmn:Participant') {
            canvas.updatedElement.participantObject = element;
        }
        element.id = canvas.updatedElement.id;

    }
    if (conectionMap[element.$type] &&  !this.flowsArray.find('id', element.id)) {
        //here save a connection element because some elements not has ascending order
        this.flowsArray.insert(element);
    }
};

/**
 * That method must complete all shape connections
 */
importBpmnDiagram.prototype.completeImportFlows = function () {
    var i,
        conn,
        state,
        dest,
        origin,
        canvas = PMUI.getActiveCanvas(),
        conectionMap = {
            'bpmn:SequenceFlow': 'SEQUENCE',
            'bpmn:Association': 'ASSOCIATION',
            'bpmn:DataOutputAssociation': 'DATAASSOCIATION',
            'bpmn:DataInputAssociation': 'DATAASSOCIATION',
            'bpmn:MessageFlow': 'MESSAGE'
        },
        element,
       flowArraySize = this.flowsArray.getSize();

    for (i = 0; i < flowArraySize; i += 1) {
        element = this.flowsArray.get(i);
        if (element.$type === 'bpmn:DataInputAssociation') {
            //dest = element.targetRef ? element.targetRef.id : element.$parent.id;
            dest = element.$parent.id;
            origin = element.sourceRef ? element.sourceRef[0].id : element.$parent.id;
        } else if (element.$type === 'bpmn:DataOutputAssociation') {
            dest = element.targetRef ? element.targetRef.id : element.$parent.id;
            origin = element.$parent.id;
        }else {
            dest = element.targetRef ? element.targetRef.id : element.$parent.id;
            origin = element.sourceRef ? element.sourceRef.id : element.$parent.id;
        }
        state = this.getState(element);
        conn = {
            flo_uid: 'pmui-' + PMUI.generateUniqueId(),
            flo_condition: null,
            flo_element_dest: dest,
            flo_element_origin: origin,
            flo_is_inmediate: "1",
            flo_name: element.name || null,
            flo_state: state,
            flo_type: conectionMap[element.$type],
            flo_x1: state[0].x,
            flo_y1: state[0].y,
            flo_x2: state[state.length - 1].x,
            flo_y2: state[state.length - 1].y
        };

        canvas.loadFlow(conn, false);
        canvas.updatedElement = {
            id : (canvas.updatedElement && canvas.updatedElement.id) || null,
            type : (canvas.updatedElement && canvas.updatedElement.type) || null,
            relatedObject : canvas.updatedElement,
            relatedElements: []
        };
        canvas.items.insert(canvas.updatedElement);
        element.id = canvas.updatedElement.id;
        canvas.updatedElement.relatedObject.businessObject = element;
    }
};
/**
 * geting all states in xml file
 * @param element
 */
importBpmnDiagram.prototype.getState = function (element) {
    var state = [],
        waypoint = element.di.waypoint,
        i;
    for (i = 0; i < waypoint.length; i += 1) {
        //if (PMDesigner.businessObject.id && PMDesigner.businessObject.id !== 'BPMNProcessmaker') {
        //    //waypoint[i].y += 80;   //to consider header
        //}
        state.push({x: waypoint[i].x - 1, y: waypoint[i].y});
    }
    return state;
};

/**
 * geting all states in xml file
 * @param element
 */
importBpmnDiagram.prototype.validateElementsForDesigner = function (mapType, marker) {
    var resp = true, 
        validation;
    validation ={           
        'bpmn:EndEvent':{
            'MESSAGETHROW':true,
            'TIMER':false,
            'SCALATIONTHROW':false,
            'COMPENSATIONTHROW':false,
            'SIGNALTHROW':true,
            'ERRORTHROW':true,
            'TERMINATETHROW':true,
            'MULTIPLETHROW':false,
        },
        'bpmn:IntermediateCatchEvent':{
            'MESSAGECATCH': true,
            'TIMER': true,
            'CONDITIONAL':true,
            'LINKCATCH':false,
            'SIGNALCATCH':true,
            'COMPENSATIONTHROW':false,
            'MULTIPLECATCH':false,
            'PARALLELCATCH':false                
        },
        'bpmn:IntermediateThrowEvent':{
            'MESSAGETHROW':true,
            'SIGNALTHROW':true,            
            'SCALATIONTHROW':false,            
            'COMPENSATIONTHROW':false,
            'LINKTHROW':false
        },
        'bpmn:BoundaryEvent':{
            'MESSAGECATCH': true,
            'TIMER': true,
            'CONDITIONAL':true,
            'LINKCATCH':false,
            'SIGNALCATCH':true,
            'COMPENSATIONTHROW':false,
            'MULTIPLECATCH':false,
            'PARALLELCATCH':false,
            'ERRORTHROW':true  
        }
    };

    if(validation[mapType]){
        if(typeof validation[mapType][marker] !== "undefined"){
            return validation[mapType][marker];
        }      
    }
    return resp;
};

//'use strict';
//
//var _ = require('lodash');

function BpmnFactory(moddle) {
  this._model = moddle;
}

BpmnFactory.$inject = [ 'moddle' ];


BpmnFactory.prototype._needsId = function(element) {
  return element.$instanceOf('bpmn:RootElement') ||
         element.$instanceOf('bpmn:FlowElement') ||
         element.$instanceOf('bpmn:Artifact') ||
         element.$instanceOf('bpmndi:BPMNShape') ||
         element.$instanceOf('bpmndi:BPMNEdge') ||
         element.$instanceOf('bpmndi:BPMNDiagram') ||
         element.$instanceOf('bpmndi:BPMNPlane');
};

//BpmnFactory.prototype._ensureId = function(element) {
//  if (!element.id && this._needsId(element)) {
//    //element.id = this._model.ids.next(element);
//    element.id = this._model.ids.next(element);
//  }
//};


BpmnFactory.prototype.create = function(type, attrs) {
  var element = this._model.create(type, attrs || {});
  element.id = attrs.id;
  //this._ensureId(element);

  return element;
};


BpmnFactory.prototype.createDiLabel = function() {
  return this.create('bpmndi:BPMNLabel', {
    bounds: this.createDiBounds()
  });
};


BpmnFactory.prototype.createDiShape = function(semantic, bounds, attrs) {

  return this.create('bpmndi:BPMNShape', _.extend({
    bpmnElement: semantic,
    bounds: this.createDiBounds(bounds)
  }, attrs));
};


BpmnFactory.prototype.createDiBounds = function(bounds) {
  return this.create('dc:Bounds', bounds);
};


BpmnFactory.prototype.createDiWaypoints = function(waypoints) {
  return _.map(waypoints, function(pos) {
    return this.createDiWaypoint(pos);
  }, this);
};

BpmnFactory.prototype.createDiWaypoint = function(point) {
  return this.create('dc:Point', _.pick(point, [ 'x', 'y' ]));
};


BpmnFactory.prototype.createDiEdge = function(semantic, waypoints, attrs) {
  return this.create('bpmndi:BPMNEdge', _.extend({
    bpmnElement: semantic
  }, attrs));
};


//module.exports = BpmnFactory;
var getData2PMUI = function (form) {
    if (form) {
        var input, field, data2 = {};
        for (var i = 0; i < $(form).find(".pmui-field").length; i += 1) {
            input = null;
            field = null;
            field = PMUI.getPMUIObject($(form).find(".pmui-field")[i]);
            var value = null;
            switch (field.type) {
                case "TextField":
                    value = field.controls[0].html.value;
                    break;
                case "TextAreaField":
                    value = field.controls[0].html.value;
                    break;
                case "DropDownListField":
                    value = field.controls[0].html.value;
                    break;
                case "radio":
                    var dataChekbox = [];
                    for (var j = 0; j < $(field.html).find("input").length; j += 1) {
                        if ($(field.html).find("input")[j].selected) {
                            value = $(field.html).find("input")[j].value;
                        }
                    }
                    break;
                case "CheckBoxGroupField":
                    var dataChekbox = [];
                    for (var j = 0; j < $(field.html).find("input").length; j += 1) {
                        if ($(field.html).find("input")[j].checked) {
                            dataChekbox.push($(field.html).find("input")[j].value);
                        }
                    }
                    value = JSON.stringify(dataChekbox);
                    break;
                case "datetime":
                    value = field.controls[0].html.value;
                    break;
                case "PasswordField":
                    value = field.controls[0].html.value;
                    break;
                case "PMCodeMirrorField":
                    value = field.controls[0].value;
                    break;
            }
            data2[field.getName()] = value;
        }
    }
    return data2;
};
var SuggestField = function (settings) {
    this.id = settings["id"] || "";
    this.label = settings["label"] || null;
    this.placeholder = settings["placeholder"] || null;
    this.type = "suggest";
    this.value = settings["value"] || "";
    this.form = settings["form"] || null;
    this.required = settings["required"] || null;
    this.disabled = settings["disabled"] || false;
    this.maxLength = settings["maxLength"] || null;
    this.mode = settings["mode"] || "edit";
    this.options = settings["options"] || [];
    this.containerList = null;
    this.width = settings.width || "auto";
    this.messageRequired = null;
    this.messageRequiredExtended = null;
    this.searchLoad = null;
    this.helper = settings.helper || null;
    this.html = null;
    this.responseAjax;
    this.data = null;
    this.separatingText = settings.separatingText || null;
    this.setDynamicLoad(settings.dynamicLoad);
    this.dom = {};
    this.dom.fieldRequired = null

};
/**
 * Disables the field. Notice that when a field is disabled it is not validated and it is not returned when its
 * form's getData() method is invoked.
 * @chainable
 */
SuggestField.prototype.disable = function () {

    this.disabled = true;
    this.inputField.prop('disabled', true);

    return this;
};
/**
 * Enables the field. Notice that when a field is disabled it is not validated and it is not returned when its
 * form's getData() method is invoked.
 * @chainable
 */
SuggestField.prototype.enable = function () {

    this.disabled = false;
    this.inputField[0].disabled = false;

    return this;
};

SuggestField.prototype.hideRequired = function (){
    this.dom.fieldRequired.style.display = 'none';
    return this;
};
/**
 * Disables the field. Notice that when a field is disabled it is not validated and it is not returned when its
 * form's getData() method is invoked.
 * @chainable
 */
SuggestField.prototype.disable = function () {
    this.disabled = true;
    this.inputField[0].disabled = true;

    return this;
};

SuggestField.prototype.showRequired = function (){
    this.dom.fieldRequired.style.display = 'inline-block';
    return this;
};
/**
 * Sets if the fields is required or not.
 * @param {Boolean} required
 * @chainable
 */
SuggestField.prototype.setRequired = function (required) {
    this.required = !!required;
    if (this.dom.fieldRequired) {
        if (this.required) {
            this.showRequired();
        } else {
            this.hideRequired();
        }
    }
    return this;
};


SuggestField.prototype.setDynamicLoad = function (dynamicLoad) {
    this.dynamicLoad = !!dynamicLoad ? dynamicLoad : false;
    if (this.dynamicLoad.hasOwnProperty("keys")) {
        this.setKeys(this.dynamicLoad["keys"]);
    }
    return this;
};

SuggestField.prototype.setKeys = function (keys) {
    var endPoints = [];
    this.keys = keys || {};
    return this;
};

SuggestField.prototype.createHTML = function () {
    this.containerLabel;
    this.tooltip;
    this.containerLabel = $(document.createElement("div"));
    this.containerLabel.css({
        width: "23.5%",
        display: "inline-block",
        float: "left",
        "text-align": "left",
        "margin-right": "5px",
        padding: "6px 30px 6px 10px",
        "box-sizing": "border-box",
        "color": "rgb(45, 62, 80)",
        "font-size": "14px"
    });

    this.tooltip = $(document.createElement("span"));
    this.tooltip[0].className = "pmui-icon pmui-icon-help";
    this.tooltip.css({
        "vertical-align": "middle",
        "right": "52px",
        top: "0px",
        width: "18px",
        height: "18px",
        position: "relative",
        "z-index": "auto",
        "float": "right"
    });

    this.requiredTag = $(document.createElement("span"));

    this.colonTag = $(document.createElement("span"));

    this.inputField = $(document.createElement("input"));
    if (this.disabled) {
        this.disable();
    }
    this.inputLabel = $(document.createElement("label"));

    this.inputLabel[0].textContent = this.label || "unlabel :";
    this.requiredTag[0].textContent = "*";
    this.requiredTag.css({
        color: "red"
    });
    this.colonTag[0].textContent = ":";
    this.containerLabel[0].appendChild(this.inputLabel[0]);
    if (this.required) {
        this.containerLabel[0].appendChild(this.requiredTag[0]);
        this.dom.fieldRequired = this.requiredTag[0];
    }
    this.containerLabel[0].appendChild(this.colonTag[0]);
    this.html = $(document.createElement("div"));
    this.html.attr("id", this.id);
    this.html.addClass("suggest-field");
    this.html.addClass("suggest-field pmui-field");
    this.html.append(this.containerLabel[0]);
    this.html.append(this.inputField);
    if (this.helper) {
        this.tooltip[0].setAttribute("title", this.helper);
        this.tooltip.tooltip();
        this.html.append(this.tooltip);
    }
    this.inputField[0].placeholder = this.placeholder.translate();
    $(this.inputLabel).css({
        display: "inline-block",
        float: "left"
    });
    this.containerList = $(document.createElement("ul"));

    this.setStyle();
    this.atachListener();
    this.html.find("input").after(this.containerList);

    if (this.dynamicLoad) {
        this.searchLoad = $(PMUI.createHTMLElement("span"));
        this.searchLoad.addClass("pmui-gridpanel-searchload");
        this.searchLoad.css({
            "margin-left": "-23px",
            "margin-top": "5px"
        });
        this.html.append(this.searchLoad);
    }
    return this.html[0];
};

SuggestField.prototype.atachListener = function () {
    var value = "", that = this, itemLabel;
    this.html.find("input").keyup(function (e) {
        value = this.value;
        if (value) {
            that.hideMessageRequired();
            that.hideMessageRequiredExtended();
            that.containerList.css({
                top: $(e.target).position().top + 18,
                "margin-left": $(e.target).position().left - 10
            });
            that.containerList.show();
            if (!that.dynamicLoad) {
                that.makeOptions(this.value, 10);
            } else {
                that.containerList.empty();
                that.executeAjax(this.value);
            }
        } else {
            that.containerList.hide();
        }
    });
    $("body").click(function (e) {
        that.containerList.hide();
    });
    return this;
};

SuggestField.prototype.makeOptions = function (val, maxItems) {
    var elements = this.options, i, count = 0, that = this, items = [];
    this.containerList.empty().css({
        "max-width": this.html.find("input").outerWidth()
    });
    if (!this.dynamicLoad) {
        $.grep(elements, function (data, index) {
            itemLabel = data.label;
            if ((itemLabel.toLowerCase().indexOf(val.toLowerCase()) !== -1) && count < maxItems) {
                items.push(data);
            }
        });
    }
    this.createItems(items);
    return this;
};
SuggestField.prototype.executeAjax = function (criteria) {
    var responseAjax, that = this, options = [], endpoints = [], restClient;
    this.searchLoad.addClass("load");
    for (var i = 0; i < this.keys.endpoints.length; i += 1) {
        url = this.keys.endpoints[i]["url"] + "?filter=" + (criteria ? criteria : "") + "&start=0&limit=10";
        method = this.keys.endpoints[i]["method"];
        endpoints.push({
            url: url,
            method: method
        });
    }
    restClient = new PMRestClient({
        typeRequest: 'post',
        multipart: true,
        data: {
            calls: endpoints
        },
        functionSuccess: function (xhr, response) {
            var responseAjax = [], dataItem, i;
            for (i = 0; i < response.length; i += 1) {
                if (that.separatingText) {
                    responseAjax.push(that.separatingText[i]);
                }
                responseAjax = responseAjax.concat(response[i].response);
            }
            data = responseAjax;
            for (i = 0; i < data.length; i += 1) {
                if (typeof data[i] === "object") {
                    for (var j = 0; j < that.dynamicLoad["data"].length; j += 1) {
                        if (data[i][that.dynamicLoad["data"][j]["key"]]) {
                            var showLabel = " ";
                            for (var k = 0; k < that.dynamicLoad["data"][j]["label"].length; k += 1) {
                                if (data[i].hasOwnProperty(that.dynamicLoad["data"][j]["label"][k])) {
                                    showLabel = showLabel + data[i][that.dynamicLoad["data"][j]["label"][k]] + " ";
                                } else {
                                    showLabel = showLabel + that.dynamicLoad["data"][j]["label"][k] + " ";
                                }
                            }
                            dataItem = {
                                label: showLabel,
                                value: data[i][that.dynamicLoad["data"][j]["key"]],
                                data: data[i]
                            }
                            options.push(dataItem);
                        }
                    }
                } else {
                    dataItem = {
                        label: data[i],
                        value: null,
                        data: null
                    }
                    options.push(dataItem);
                }
            }
            that.options = options;
            that.createItems(that.options, that.options.length);
            that.searchLoad.removeClass("load");
            return responseAjax;
        }
    });
    restClient.setBaseEndPoint('');
    restClient.executeRestClient();
    return this;
};
SuggestField.prototype.createItems = function (items) {
    var that = this, i, li, span;
    this.containerList.empty();
    for (i = 0; i < items.length; i += 1) {
        li = document.createElement("li");
        li.className = "list-suggest-item";
        span = document.createElement("span");
        $(span).css({
            width: "auto",
            display: "block",
            paddingLeft: "10px",
            paddingTop: "2px",
            paddingBottom: "2px",
            paddingRight: "2px",
            height: "100%"
        });
        span.innerHTML = items[i].label;
        span.setAttribute("data-value", items[i].value);
        $(span).data({data: items[i]["data"]});
        li.appendChild(span);
        if (items[i].value) {
            $(li).css({
                position: "relative",
                display: "block",
                "background-color": "#fff",
                border: "1px solid #ddd",
                "box-sizing": "border-box",
                "height": "auto",
                "line-height": "20px",
                "width": this.inputField.outerWidth()
            });
            li.onclick = this.onclickItem();
        } else {
            $(li).css({
                position: "relative",
                display: "block",
                "background-color": "#3397e1",
                "color": "white",
                "border": "1px solid #ddd",
                "box-sizing": "border-box",
                "height": "auto",
                "line-height": "20px",
                "width": this.inputField.outerWidth(),
                "text-align": "center",
                "cursor": "default",
                "text-transform": "uppercase",
                "font-weight": "bold"
            }).addClass("single-label");
        }
        this.containerList.append(li);
        if (this.containerList.children.length > 4) {
            this.containerList.css("height", "200px");
        } else {
            this.containerList.css("height", "auto");
        }
    }
    this.containerList.show();
    return this;
};

SuggestField.prototype.onclickItem = function () {
    var that = this;
    return function (e) {
        var item, text;
        item = e.currentTarget;
        text = item.textContent;
        that.html.find("input").val(text);
        that.value = $(item).find("span").data().value;
        that.data = $(item).find("span").data().data;
        that.containerList.hide();
    }
};

SuggestField.prototype.setStyle = function () {
    this.html.find("input").css({
        left: "0px",
        top: "0px",
        width: this.width,
        height: "30px",
        position: "relative",
        "z-index": "auto",
        "box-sizing": "border-box",
        "padding": "2px 10px 2px 10px"
    });
    this.containerList.css({
        position: "absolute",
        "z-index": "3",
        "border-radius": "5px",
        "height": "auto",
        "box-shadow": "2px 10px 29px #818181",
        cursor: "pointer",
        overflow: "scroll",
        "padding-right": "0",
        "margin-bottom": "20px",
        "padding-left": "0",
        "max-height": "200px",
        "display": "none"
    });
    return this;
};

SuggestField.prototype.get = function (prototype) {
    if (this[prototype]) {
        return this[prototype];
    } else {
        return null;
    }
};

SuggestField.prototype.set = function (prototype, value) {
    this[prototype] = value;
    return this;
};

SuggestField.prototype.showMessageRequired = function () {
    if (this.messageRequired == null) {
        var messageRequired = $('<span class="pmui-field-message" style="display: block; margin-left: 24%;"><span  class="pmui pmui-textlabel" style="left: 0px; top: 0px; width:auto; height: auto; position: relative; z-index: auto;">' + "This field is required.".translate() + '</span></span>');
        this.messageRequired = messageRequired;
        this.html.find('ul').after(messageRequired);
    }
    else {
        this.messageRequired.show();
    }
};

/**
  * Prepare the message for the required suggested field
 */
SuggestField.prototype.showMessageRequiredExtended = function () {
    if (this.messageRequiredExtended === null) {
        var messageRequiredExtended = $('<span class="pmui-field-message" style="display: block; margin-left: 35.2%;"><span class="pmui pmui-textlabel" style="left: 0px; top: 0px; width:auto; height: auto; position: relative; z-index: auto;">' + "This field is required.".translate() + '</span></span>');
        this.messageRequiredExtended = messageRequiredExtended;
        this.html.find('ul').after(messageRequiredExtended);
    } else {
        this.messageRequiredExtended.show();
    }
    this.html.find("input").css({ border: "1px solid white", "border-radius": "2px", outline: "1px solid #ecc3c2" });
};

SuggestField.prototype.hideMessageRequired = function () {
    if (this.messageRequired != null) {
        this.messageRequired.hide();
    }
};

/**
 * Hide the message required extended when it is no longer needed
 */
SuggestField.prototype.hideMessageRequiredExtended = function () {
    if (this.messageRequiredExtended != null) {
        this.messageRequiredExtended.hide();
    }
};

SuggestField.prototype.isValid = function () {
    if (this.required && this.html.find("input").val() === "") {
        return false
    }
    return true;
};

/**
 * Set the style for the input field
 * 
 * @param {string} border 
 * @param {string} borderRadius 
 * @param {string} color 
 * @param {string} outline 
 */
SuggestField.prototype.repaint = function (border, borderRadius, color, outline) {
    var that = this;
    that.html.find("input").css({
        border: border,
        "border-radius": borderRadius,
        color: color,
        outline: outline
    });
};
var CreateShapeHelper = function (options) {
    this.html = null;
    CreateShapeHelper.prototype.initObject.call(this, options);
};

CreateShapeHelper.prototype.initObject = function (options) {
    var defaults = {
        parent: null,
        x: 0,
        y: 0,
        zOrder: 7,
        className: ''
    };

    $.extend(true, defaults, options);
    this.setParent(defaults.parent);
    this.setPosition(defaults.x, defaults.y);
    this.setZOrder(defaults.zOrder);
    this.setClasName(defaults.className);
};
CreateShapeHelper.prototype.setClasName = function (name) {
    this.className = name;
    return this;
};
CreateShapeHelper.prototype.setZOrder = function (zOrder) {
    this.zOrder = zOrder;
    return this;
};
CreateShapeHelper.prototype.setParent = function (parent) {
    this.parent = parent;
    return this;
};
CreateShapeHelper.prototype.setPosition = function (x, y) {
    this.x = x + 2;
    this.y = y + 2;
    return this;
};
CreateShapeHelper.prototype.createHTML = function () {
    this.html = document.createElement('div');
    this.html.id = 'CreateShapeHelper';
    this.html.className = this.className;
    this.html.style.position = "absolute";
    this.html.style.left = this.x + "px";
    this.html.style.top = this.y + "px";
    this.html.style.height = "30px";
    this.html.style.width = "30px";
    this.html.style.zIndex = this.zOrder;
    return this.html;
};

CreateShapeHelper.prototype.paint = function () {
    if (this.html === null) {
        this.parent.html.appendChild(this.createHTML());
    }

    return this;
};

/**
 * @class AdamMarker
 * Handle Activity Markers
 *
 * @constructor
 * Creates a new instance of the class
 * @param {Object} options
 */
var PMVAlidatorMarker = function (options) {
    PMMarker.call(this, options);
    PMVAlidatorMarker.prototype.initObject.call(this, options);
};
PMVAlidatorMarker.prototype = new PMMarker();
/**
 * Defines the object type
 * @type {String}
 */
PMVAlidatorMarker.prototype.type = 'PMVAlidatorMarker';

/**
 * Initialize the object with the default values
 * @param {Object} options
 */
PMVAlidatorMarker.prototype.initObject = function (options) {
    var defaults = {
        errors: {}
    };
    $.extend(true, defaults, options);
    this.setErrors(defaults.errors);
};

PMVAlidatorMarker.prototype.createHTML = function () {
    PMMarker.prototype.createHTML.call(this);
    this.html.className = 'PMVAlidatorMarker';
    return this.html;
};

PMVAlidatorMarker.prototype.paint = function () {
    PMMarker.prototype.paint.call(this);
    if (this.styleMarker) {
        this.styleMarker.paint();
    }
};
PMVAlidatorMarker.prototype.applyZoom = function () {
    this.setProperties();
    if (this.styleMarker) {
        this.styleMarker.applyZoom();
    }
    return this;
};

PMVAlidatorMarker.prototype.setErrors = function (errors) {
    this.errors = errors;
    this.errors.style.parent = this;
    this.styleMarker = new MarkerItem(this.errors.style);
    return this;
};

PMVAlidatorMarker.prototype.show = function () {
    if (this.html) {
        this.html.style.visibility = 'visible';
    }
    return this;
};

PMVAlidatorMarker.prototype.hide = function () {
    if (this.html) {
        this.html.style.visibility = 'hidden';
    }
    return this;
};

PMVAlidatorMarker.prototype.removeBoxMarker = function () {
    if (this.styleMarker) {
        this.styleMarker.removeErrorBox();
    }
    return this;
};

var MarkerItem = function (options) {
    PMUI.draw.Shape.call(this, options);
    this.markerZoomClassesError = [];
    this.markerZoomClassesWarning = [];
    this.infoDiv = null;
    this.typeMarker = "error";
    MarkerItem.prototype.initObject.call(this, options);
};
MarkerItem.prototype = new PMUI.draw.Shape();
MarkerItem.prototype.type = 'MarkerItem';
MarkerItem.prototype.initObject = function (options) {
    var defaults = {
        width: 14,
        height: 14
    };
    $.extend(true, defaults, options);
    this.setParent(defaults.parent)
        .setHeight(defaults.height)
        .setWidth(defaults.width)
        .setMarkerZoomClassesError(defaults.markerZoomClassesError)
        .setMarkerZoomClassesWarning(defaults.markerZoomClassesWarning);
};

MarkerItem.prototype.setParent = function (newParent) {
    this.parent = newParent;
    return this;
};
/**
 * Set style icons type Error
 * @param classes
 * @returns {MarkerItem}
 */
MarkerItem.prototype.setMarkerZoomClassesError = function (classes) {
    this.markerZoomClassesError = classes;
    return this;
};
/**
 * Set style icons type Warning
 * @param classes
 * @returns {MarkerItem}
 */
MarkerItem.prototype.setMarkerZoomClassesWarning = function (classes) {
    this.markerZoomClassesWarning = classes;
    return this;
};
/**
 * Set the type of Error (error, warning)
 * @param type
 * @returns {MarkerItem}
 */
MarkerItem.prototype.setTypeMarker = function (type) {
    this.typeMarker = type;
    if (this.html) {
        switch (this.getTypeMarker()) {
            case 'warning':
                this.html.className = this.markerZoomClassesWarning[this.parent.canvas.getZoomPropertiesIndex()];
                break;
            default:
                this.html.className = this.markerZoomClassesError[this.parent.canvas.getZoomPropertiesIndex()];
        }
    }
    return this;
};
/**
 * Get the type error marker.
 * @returns {string|*}
 */
MarkerItem.prototype.getTypeMarker = function () {
    return this.typeMarker;
};
/**
 * Apply zoom styles.
 * @returns {MarkerItem}
 */
MarkerItem.prototype.applyZoom = function () {
    var newSprite;
    this.removeAllClasses();
    this.setProperties();
    switch (this.getTypeMarker()) {
        case 'warning':
            newSprite = this.markerZoomClassesWarning[this.parent.canvas.getZoomPropertiesIndex()];
            break;
        default:
            newSprite = this.markerZoomClassesError[this.parent.canvas.getZoomPropertiesIndex()];
    }
    if (this.html) {
        this.html.className = newSprite;
    }
    this.currentZoomClass = newSprite;
    return this;
};
/**
 * Create DIV message of errors and events(show and hide).
 * @returns {*}
 */
MarkerItem.prototype.createHTML = function () {
    var that = this;
    PMUI.draw.Shape.prototype.createHTML.call(this);
    this.html.id = this.id;
    this.setProperties();
    this.currentZoomClass = this.html.className;
    this.parent.html.appendChild(this.html);
    $(this.html).mouseenter(function () {
        return that.showErrorBox();
    });
    $(this.html).mouseout(function () {
        return that.removeErrorBox();
    });
    return this.html;
};
/**
 * Create HTML.
 */
MarkerItem.prototype.paint = function () {
    if (this.getHTML() === null) {
        this.createHTML();
    }
};
/**
 * Show event to make hover.
 * @param item
 * @returns {Function}
 */
MarkerItem.prototype.showErrorBox = function () {
    var self = this,
        i,
        h,
        error,
        shape,
        shapeValidator,
        leftPixels = 7,
        left,
        right,
        widthContainer,
        widthWindow,
        freeSpace,
        missingSpace,
        boxErrors,
        numErrorsShape = 0;
    // Get parent Item Marker
    if (self.getParent()) {
        shapeValidator = self.getParent();
        if (shapeValidator.getParent()) {
            shape = shapeValidator.getParent();
        }
    }
    // Show List Errors
    if (shape) {
        if ($('.arrow_box')) {
            $('.arrow_box').remove();
        }
        numErrorsShape = shape.getNumErrors();
        if (numErrorsShape > 0) {
            // Crete Box Container
            self.infoDiv = document.createElement("div");
            h = document.createElement("ul");
            // Add Errors list
            for (i = 0; i < numErrorsShape; i += 1) {
                error = shape.errors.get(i);
                $(h).append('<li>' + error.description + '</li>');
            }
            self.infoDiv.appendChild(h);
            // Add class style arrow_box
            self.infoDiv.className = 'arrow_box';
            // Set Position of the container of errors
            self.infoDiv.style.left = shape.getAbsoluteX() + shape.getZoomWidth() + leftPixels + "px";
            self.infoDiv.style.top = shape.getAbsoluteY() + shape.canvas.getY() + "px";
            // Add the Container Errors to Body
            document.body.appendChild(self.infoDiv);
            // Reposition Container of Errors
            boxErrors = $('.arrow_box')[0];
            if (boxErrors) {
                right = boxErrors.getBoundingClientRect().right;
                left = boxErrors.getBoundingClientRect().left;
                widthContainer = right - left;
                widthWindow = $(window).width();
                freeSpace = widthWindow - left;

                if (freeSpace > 0 && widthContainer > 0) {
                    missingSpace = freeSpace - widthContainer;
                    if (missingSpace < 0) {
                        boxErrors.style.minWidth = freeSpace + "px";
                    }
                }
            }
        }
    }
};
/**
 * Remove Error Box
 */
MarkerItem.prototype.removeErrorBox = function () {
    var self = this;
    if (self.infoDiv) {
        $(self.infoDiv).remove();
    }
};
/**
 * Set Properties MarkerItem
 * @returns {MarkerItem}
 */
MarkerItem.prototype.setProperties = function () {
    if (this.html) {
        this.html.style.position = 'relative';
        this.html.style.width = this.width * PMUI.getActiveCanvas().getZoomFactor() + 'px';
        this.html.style.height = this.height * PMUI.getActiveCanvas().getZoomFactor() + 'px';
    }
    return this;
};
/**
 * Remove All Clasess MarkerItem
 * @returns {MarkerItem}
 */
MarkerItem.prototype.removeAllClasses = function () {
    if (this.html) {
        this.html.className = '';
    }
    return this;
};

/**
 * RestApi cliento to consume designer end points
 * V 0.0.1
 * @type {{}|RestApi}
 */
//create the RestApi variable if one does not exist already

var RestApi = RestApi || {};

RestApi = (function () {
    var _api;
    var _methodsMap = {
        "read": "GET",
        "update": "PUT",
        "create": "POST",
        "delete": "DELETE"
    };
    var _methods = ["read", "update", "create", "delete"];

    function RestClient(params) {
        var keys = params.keys,
            defaults,
            serverUrl = (params && params.serverUrl) || "/rest/v10";
        //console.log('init RestClient instance');
        //TODO  restClient
        return {
            execute: function (params) {
                defaults = {
                    url: HTTP_SERVER_HOSTNAME + "/rest/v10",
                    method: "GET",
                    contentType: "application/json",
                    data: '',
                    beforeSend: function (xhr) {
                        xhr.setRequestHeader("Authorization", "Bearer " + keys.access_token);
                    },
                    success: function () {
                    },
                    error: function () {
                    }
                };
                _.extend(defaults, params);
                defaults.url = defaults.url;
                defaults.method = _methodsMap[defaults.method];
                defaults.data = defaults.data;
                defaults.success = defaults.success;
                defaults.error = defaults.error;

                $.ajax(defaults);
            }
        };
    };
    return {
        /**
         * Gets an instance of rest api
         * @param params
         * @returns {PMDesigner.RestApi}
         */
        getRestApi: function (params) {
            return _api || this.createRestApi(params);
        },
        /**
         * creates a rest api instance
         * @param params
         * @returns {*}
         */
        createRestApi: function (params) {
            _api = new RestClient(params);
            return _api;
        }
    };
})();


var FormDesigner = {};
FormDesigner.version = '0.1';
FormDesigner.DEPRECATION_LINK = 'http://wiki.processmaker.com/Deprecated_Features';
FormDesigner.DEPRECATION_TEXT = 'Control Deprecated. Refer to '.translate() + '<a href="'
    + FormDesigner.DEPRECATION_LINK + '" target="_blank">' + FormDesigner.DEPRECATION_LINK + '</a>.';
FormDesigner.extendNamespace = function (path, newClass) {
    var current,
        pathArray,
        extension,
        i;

    if (arguments.length !== 2) {
        throw new Error("FormDesigner.extendNamespace(): method needs 2 arguments");
    }

    pathArray = path.split('.');
    if (pathArray[0] === 'FormDesigner') {
        pathArray = pathArray.slice(1);
    }
    current = FormDesigner;

    // create the 'path' namespace
    for (i = 0; i < pathArray.length - 1; i += 1) {
        extension = pathArray[i];
        if (typeof current[extension] === 'undefined') {
            current[extension] = {};
        }
        current = current[extension];
    }

    extension = pathArray[pathArray.length - 1];
    if (current[extension]) {
    }
    current[extension] = newClass;
    return newClass;
};
FormDesigner.generateUniqueId = function () {
    var rand = function (min, max) {
        // Returns a random number
        //
        // version: 1109.2015
        // discuss at: http://phpjs.org/functions/rand
        // +   original by: Leslie Hoare
        // +   bugfixed by: Onno Marsman
        // %          note 1: See the commented out code below for a
        // version which will work with our experimental
        // (though probably unnecessary) srand() function)
        // *     example 1: rand(1, 1);
        // *     returns 1: 1

        // fix for jsLint
        // from: var argc = arguments.length;
        if (typeof min === "undefined") {
            min = 0;
        }
        if (typeof max === "undefined") {
            max = 999999999;
        }
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }, uniqid = function (prefix, more_entropy) {
        // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
        // +    revised by: Kankrelune (http://www.webfaktory.info/)
        // %        note 1: Uses an internal counter (in php_js global) to avoid collision
        // *     example 1: uniqid();
        // *     returns 1: 'a30285b160c14'
        // *     example 2: uniqid('foo');
        // *     returns 2: 'fooa30285b1cd361'
        // *     example 3: uniqid('bar', true);
        // *     returns 3: 'bara20285b23dfd1.31879087'
        if (typeof prefix === 'undefined') {
            prefix = "";
        }

        var retId,
            formatSeed = function (seed, reqWidth) {
                var tempString = "",
                    i;
                seed = parseInt(seed, 10).toString(16); // to hex str
                if (reqWidth < seed.length) { // so long we split
                    return seed.slice(seed.length - reqWidth);
                }
                if (reqWidth > seed.length) { // so short we pad
                    // jsLint fix
                    tempString = "";
                    for (i = 0; i < 1 + (reqWidth - seed.length); i += 1) {
                        tempString += "0";
                    }
                    return tempString + seed;
                }
                return seed;
            };

        // BEGIN REDUNDANT
        if (!this.php_js) {
            this.php_js = {};
        }
        // END REDUNDANT
        if (!this.php_js.uniqidSeed) { // init seed with big random int
            this.php_js.uniqidSeed = Math.floor(Math.random() * 0x75bcd15);
        }
        this.php_js.uniqidSeed += 1;

        retId = prefix; // start with prefix, add current milliseconds hex string
        retId += formatSeed(parseInt(new Date().getTime() / 1000, 10), 8);
        retId += formatSeed(this.php_js.uniqidSeed, 5); // add seed hex string
        if (more_entropy) {
            // for more entropy we add a float lower to 10
            retId += (Math.random() * 10).toFixed(8).toString();
        }

        return retId;
    }, sUID;
    do {
        sUID = uniqid(rand(0, 999999999), true);
        sUID = sUID.replace('.', '0');
    } while (sUID.length !== 32);
    return sUID;
};
FormDesigner.getNextNumber = function (string, type, field, max) {
    var a,
        i;
    if (max === undefined) {
        max = 0;
    }
    for (i in string) {
        if (typeof string[i] === "object")
            max = FormDesigner.getNextNumber(string[i], type, field, max);
        if (typeof string[i] === "string" && string[i] === type && string[field]) {
            a = parseInt(string[field].replace(/^\D+/g, ''));
            if (a >= max)
                max = a;
        }
    }
    return max;
};
FormDesigner.getNextNumberVar = function (jsonData, properties, callback) {
    PMDesigner.restApi.execute({
        method: "read",
        url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id + "/process-variables",
        success: function (data, textStatus, xhr) {
            var i,
                num,
                max,
                nextVar = FormDesigner.getNextNumber(jsonData, properties.type.value, "variable");
            max = nextVar;
            for (i = 0; i < data.length; i += 1) {
                if (data[i].var_name.indexOf(properties.type.value) >= 0 && data[i].var_name) {
                    num = parseInt(data[i].var_name.replace(/^\D+/g, ''));
                    if (num >= max) {
                        max = num;
                    }
                }
            }
            nextVar = max + 1;
            nextVar = nextVar.toString();
            nextVar = FormDesigner.leftPad(nextVar, 3, "0");
            nextVar = properties.type.value + "Var" + nextVar;
            callback(nextVar);
        },
        error: function (xhr, textStatus, errorThrown) {
        }
    });
};
FormDesigner.leftPad = function (string, length, fill) {
    if (string && typeof string === "string") {
        while (string.length < length) {
            string = fill + string;
        }
    }
    return string;
};

(function () {
    var FormItem = function (options) {
        this.render = options.type;
        this.variable = options.variable;
        this.parent = options.parentObject;
        this.onSelect = new Function();
        this.onSetProperty = new Function();
        if (options.onSelect) {
            this.onSelect = options.onSelect;
        }
        this.onRemove = new Function();
        this.disabled = false;
        FormItem.prototype.init.call(this);
    };
    FormItem.prototype.init = function () {
        var that = this,
            html,
            label = "";
        switch (this.render) {
            case FormDesigner.main.TypesControl.text:
                label = "<span class='fd-gridForm-field-label'></span>";
                html = "<div class='fd-gridForm-grid-text'><input type='text' style='width:100%;box-sizing:border-box;'/><div>";
                break;
            case FormDesigner.main.TypesControl.textarea:
                label = "<span class='fd-gridForm-field-label'></span>";
                html = "<div class='fd-gridForm-grid-textarea'><textarea style='width:100%;box-sizing:border-box;height:18px;resize:vertical;padding:0px;'></textarea></div>";
                break;
            case FormDesigner.main.TypesControl.dropdown:
                label = "<span class='fd-gridForm-field-label'></span>";
                html = "<div class='fd-gridForm-grid-dropdown'><select style='width:100%;box-sizing:border-box;'></select></div>";
                break;
            case FormDesigner.main.TypesControl.checkbox:
                label = "<span class='fd-gridForm-field-label'></span>";
                html = "<div class='fd-gridForm-grid-checkbox'><input type='checkbox' value=''></div>";
                break;
            case FormDesigner.main.TypesControl.checkgroup:
                label = "<span class='fd-gridForm-field-label'></span>";
                html = "<div class='fd-gridForm-grid-checkgroup'></div>";
                break;
            case FormDesigner.main.TypesControl.radio:
                label = "<span class='fd-gridForm-field-label'></span>";
                html = "<div class='fd-gridForm-grid-radio'></div>";
                break;
            case FormDesigner.main.TypesControl.datetime:
                label = "<span class='fd-gridForm-field-label'></span>";
                html = "<div class='fd-gridForm-grid-datetime'><span class='fd-gridForm-grid-suggest-placeholder'></span><img src='" + $.imgUrl + "fd-calendar.png' style='float:right;'></img></div>";
                break;
            case FormDesigner.main.TypesControl.suggest:
                label = "<span class='fd-gridForm-field-label'></span>";
                html = "<div class='fd-gridForm-grid-suggest'><span class='fd-gridForm-grid-suggest-placeholder'></span><img src='" + $.imgUrl + "fd-ui-list-box.png' style='float:right;'></img></div>";
                break;
            case FormDesigner.main.TypesControl.hidden:
                label = "<span class='fd-gridForm-field-label'></span>";
                html = "<div class='fd-gridForm-grid-hidden'><img src='" + $.imgUrl + "fd-ui-text-field-hidden.png' style='float:right;'></img></div>";
                break;
            case FormDesigner.main.TypesControl.title:
                html = "<div class='fd-gridForm-grid-title'><span style='white-space:nowrap;'></span></div>";
                break;
            case FormDesigner.main.TypesControl.subtitle:
                html = "<div class='fd-gridForm-grid-subtitle'><span style='white-space:nowrap;'></span></div>";
                break;
            case FormDesigner.main.TypesControl.annotation:
                html = "<div class='fd-gridForm-grid-annotation'><span style='white-space:nowrap;'></span></div>";
                break;
            case FormDesigner.main.TypesControl.link:
                label = "<span class='fd-gridForm-field-label'></span>";
                html = "<div class='fd-gridForm-grid-link'><span style='color:blue;text-decoration:underline;'></span></div>";
                break;
            case FormDesigner.main.TypesControl.image:
                label = "<span class='fd-gridForm-field-label'></span>";
                html = "<div class='fd-gridForm-grid-image'><img src='" + $.imgUrl + "fd-image.png'></img></div>";
                break;
            case FormDesigner.main.TypesControl.file:
                label = "<span class='fd-gridForm-field-label'></span>";
                html = "<div class='fd-gridForm-grid-file'><button>Select file</button><span>No file was selected</span></div>";
                break;
            case FormDesigner.main.TypesControl.multipleFile:
                label = "<span class='fd-gridForm-field-label'></span>";
                html = "<div class='fd-gridForm-grid-file'><button>Select file</button><span>No file was selected</span></div>";
                break;
            case FormDesigner.main.TypesControl.submit:
                html = "<button class='fd-gridForm-grid-submit'></button>";
                break;
            case FormDesigner.main.TypesControl.button:
                html = "<button class='fd-gridForm-grid-button'></button>";
                break;
            case FormDesigner.main.TypesControl.panel:
                html = "<div class='fd-gridForm-grid-panel'>" + "Panel: ".translate() + "<span class='fd-gridForm-grid-panel-placeholder'></span></div>";
                break;
            case FormDesigner.main.TypesControl.msgPanel:
                html = "<div class='fd-gridForm-grid-msgpanel'>" + "The specified subform could not be found in the process.".translate() + "<span class='fd-gridForm-grid-panel-placeholder'></span></div>";
                break;
            case FormDesigner.main.TypesControl.geomap:
                label = "<span class='fd-gridForm-field-label'></span>";
                html = "<div class='fd-gridForm-grid-geomap'><img src='" + $.imgUrl + "fd-map.png'></img></div>";
                break;
            case FormDesigner.main.TypesControl.qrcode:
                label = "<span class='fd-gridForm-field-label'></span>";
                html = "<div class='fd-gridForm-grid-qrcode'><img src='" + $.imgUrl + "fd-qr-code.png'></img></div>";
                break;
            case FormDesigner.main.TypesControl.signature:
                label = "<span class='fd-gridForm-field-label'></span>";
                html = "<div class='fd-gridForm-grid-signature'><img src='" + $.imgUrl + "fd-text_signature.png'></img></div>";
                break;
            case FormDesigner.main.TypesControl.imagem:
                label = "<span class='fd-gridForm-field-label'></span>";
                html = "<div class='fd-gridForm-grid-imagem'><img src='" + $.imgUrl + "fd-image-instagram.png'></img></div>";
                break;
            case FormDesigner.main.TypesControl.audiom:
                label = "<span class='fd-gridForm-field-label'></span>";
                html = "<div class='fd-gridForm-grid-audiom'><img src='" + $.imgUrl + "fd-audio-card.png'></img></div>";
                break;
            case FormDesigner.main.TypesControl.videom:
                label = "<span class='fd-gridForm-field-label'></span>";
                html = "<div class='fd-gridForm-grid-videom'><img src='" + $.imgUrl + "fd-video.png'></img></div>";
                break;
        }
        if (label !== "")
            label = label + "<span class='fd-gridForm-grid-contentRequired'>*</span>";
        this.html = $("<div class='grid-item-field' tabindex='-1'>" + label + html + "</div>");
        this.html.data("objectInstance", this);
        this.html.on("click", function (e) {
            this.focus();
            e.stopPropagation();
            $.designerSelectElement(this, function () {
                if (that.disabled === true) {
                    return false;
                }
                that.onRemove();
            });
            that.onSelect(that.properties, that);
        });
        this.properties = new FormDesigner.main.Properties(this.render, this.html, that);
        this.properties.onClick = function (property) {
            var a,
                b,
                fields,
                dialogCreateVariable,
                dialog;
            if (property === "formula") {
                fields = that.parent.getFieldObjects([
                    FormDesigner.main.TypesControl.text
                ]);
                a = new FormDesigner.main.DialogFormula(null, fields);
                a.onSave = function () {
                    that.properties.set(property, a.editor.getValue());
                };
                a.editor.setValue(that.properties.get()[property].value);
            }
            if (property === "variable") {
                dialogCreateVariable = new FormDesigner.main.DialogCreateVariable(null, that.render, [], that.properties.get()[property].value);
                dialogCreateVariable.onSave = function (variable) {
                    that.setVariable(variable);
                };
                dialogCreateVariable.onSelect = function (variable) {
                    dialogCreateVariable.dialog.dialog("close");
                    that.setVariable(variable);
                };
                FormDesigner.getNextNumberVar(that.getData(), that.properties, function (nextVar) {
                    dialogCreateVariable.setVarName(nextVar);
                });
            }
            if (property === "inputDocument") {
                dialog = new FormDesigner.main.DialogInputDocument(null);
                dialog.onClick = function (option) {
                    dialog.dialog.dialog("close").remove();
                    that.setInputDocument({
                        size: {
                            value: option.inp_doc_max_filesize,
                            disabled: true
                        },
                        sizeUnity: {
                            value: option.inp_doc_max_filesize_unit,
                            disabled: true
                        },
                        extensions: {
                            value: option.inp_doc_type_file,
                            disabled: true
                        },
                        enableVersioning: {
                            value: (option.inp_doc_versioning) ? true : false,
                            disabled: true
                        },
                        inp_doc_uid: {
                            value: option.inp_doc_uid,
                            disabled: true
                        },
                        inputDocument: {
                            value: option.inp_doc_title,
                            disabled: false
                        }
                    });
                };
                return false;
            }
            if (property === "dbConnectionLabel") {
                a = new FormDesigner.main.DialogDBConnection(null);
                a.onClick = function (option) {
                    a.dialog.dialog("close").remove();
                    b = that.properties.set(property, option.label);
                    b.node.textContent = option.label;

                    that.properties.set("dbConnection", option.value);
                };
            }
            if (property === "sql") {
                a = new FormDesigner.main.DialogSql(null);
                a.onSave = function () {
                    b = that.properties.set(property, a.editor.getValue());
                    b.node.textContent = a.editor.getValue();
                };
                a.editor.setValue(that.properties.get()[property].value);
            }
            if (property === "options") {
                a = new FormDesigner.main.DialogOptions(null, that.properties.get()[property].value);
                a.onApply = function () {
                    b = that.properties.set(property, a.getOptions());
                    b.node.textContent = JSON.stringify(a.getOptions());
                };
                if (that.properties.dataType.value === "boolean") {
                    a.setOptionsBoolean();
                }
            }
            if (property === "validate") {
                a = new FormDesigner.main.DialogRegularExpression(null, that.properties["dataType"].value);
                a.onClick = function (regex) {
                    a.dialog.dialog("close").remove();
                    b = that.properties.set("validate", regex.value);
                    if (b.node)
                        b.node.value = regex.value;
                };
            }
            if (property === "content") {
                var a = new FormDesigner.main.DialogContent();
                a.onSave = function () {
                    that.properties.set(property, a.editor.getValue());
                };
                a.editor.setValue(that.properties.get()[property].value);
            }
            if (property === "dataVariable") {
                a = new FormDesigner.main.DialogVariable(null, FormDesigner.main.TypesControl.checkgroup);
                a.onClick = function (variable) {
                    a.dialog.dialog("close").remove();
                    b = that.properties.set("dataVariable", "@@" + variable.var_name);
                    if (b.node)
                        b.node.value = variable.var_name;
                };
                a.load();
            }
        };
        this.properties.onSet = function (prop, value) {
            var oValue, oLabel;
            that.onSetProperty(prop, value, that);
            if (prop === "id") {
                that.html.find(".fd-gridForm-grid-panel-placeholder").text(value);
            }
            if (prop === "label") {
                that.html.find(".fd-gridForm-field-label").text(value);
                that.html.find(".fd-gridForm-grid-title").find("span").text(value);
                that.html.find(".fd-gridForm-grid-subtitle").find("span").text(value);
                that.html.find(".fd-gridForm-grid-annotation").find("span").text(value);
                that.html.find(".fd-gridForm-grid-submit").text(value);
                that.html.find(".fd-gridForm-grid-button").text(value);
            }
            if (prop === "value") {
                that.html.find(".fd-gridForm-grid-link").find("span").text(value);
            }
            if (prop === "required") {
                if (value)
                    that.html.find(".fd-gridForm-grid-contentRequired").show();
                else
                    that.html.find(".fd-gridForm-grid-contentRequired").hide();
            }
            if (prop === "options") {
                that.html.find(".fd-gridForm-grid-dropdown").find("select").empty();
                that.html.find(".fd-gridForm-grid-checkgroup").empty();
                that.html.find(".fd-gridForm-grid-radio").empty();
                for (var i = 0; i < value.length; i++) {
                    oValue = $('<div />').text(value[i].value).html();
                    oLabel = $('<div />').text(value[i].label).html();
                    that.html.find(".fd-gridForm-grid-dropdown").find("select").append("<option value='" + oValue + "'>" + oLabel + "</option>");
                    that.html.find(".fd-gridForm-grid-checkgroup").append("<label><input type='checkbox' value='" + oValue + "'><span>" + oLabel + "</span></label>");
                    that.html.find(".fd-gridForm-grid-radio").append("<label><input type='radio' value='" + oValue + "'><span>" + oLabel + "</span></label>");
                }
            }
            if (prop === "placeholder") {
                that.html.find(".fd-gridForm-grid-text").find("input").attr("placeholder", value);
                that.html.find(".fd-gridForm-grid-textarea").find("textarea").attr("placeholder", value);
                that.html.find(".fd-gridForm-grid-suggest-placeholder").text(value);
            }
        };
        this.properties.onClickClearButton = function (property) {
            var b, prop;
            if (property === "variable" && that.properties[property].value !== "") {
                var a = new FormDesigner.main.DialogConfirmClearVariable();
                a.onAccept = function () {
                    var label = that.properties.label.value;
                    that.parent.setNextLabel(that.properties);

                    that.properties.id.node.value = that.properties.id.value;
                    b = that.properties.set("variable", "");
                    b.node.textContent = "...";
                    b = that.properties.set("dataType", "");
                    b.node.textContent = "";
                    b = that.properties.set("label", label);
                    if (b.node)
                        b.node.value = label;

                    b = that.properties.set("dbConnectionLabel", "PM Database");
                    if (b.node)
                        b.node.textContent = "PM Database";
                    b = that.properties.set("dbConnection", "workflow");
                    b = that.properties.set("sql", "");
                    if (b.node)
                        b.node.textContent = "...";
                    b = that.properties.set("options", JSON.parse("[]"));
                    if (b.node)
                        b.node.textContent = "[]";

                    that.properties.size.disabled = false;
                    b = that.properties.set("size", "1024");
                    if (b.node) {
                        b.node.value = "1024";
                    }

                    that.properties.sizeUnity.disabled = false;
                    b = that.properties.set("sizeUnity", "KB");
                    if (b.node) {
                        b.node.value = "KB";
                    }

                    that.properties.extensions.disabled = false;
                    b = that.properties.set("extensions", "*");
                    if (b.node) {
                        b.node.value = "*";
                    }
                    that.properties.set("inp_doc_uid", "");
                };
            }
            if (property === "minDate" || property === "maxDate" || property === "defaultDate") {
                b = that.properties.set(property, "");
                if (b.node)
                    b.node.value = "";
            }
            if (property === "inputDocument") {
                that.setInputDocument({
                    size: {
                        value: 0,
                        disabled: false
                    },
                    sizeUnity: {
                        value: 'MB',
                        disabled: false
                    },
                    extensions: {
                        value: '*',
                        disabled: false
                    },
                    enableVersioning: {
                        value: false,
                        disabled: true
                    },
                    inp_doc_uid: {
                        value: '',
                        disabled: true
                    },
                    inputDocument: {
                        value: '...',
                        disabled: false
                    }
                });
            }
        };
        this.createDeprecatedIcon();
    };
    FormItem.prototype.getData = function () {
        var data = {}, property, prop = this.properties.get();
        for (property in prop) {
            data[property] = prop[property].value;
        }
        if (this.variable) {
            data["var_name"] = this.variable["var_name"];
        }
        return data;
    };
    FormItem.prototype.setDisabled = function (disabled) {
        this.disabled = disabled;
        this.properties.setDisabled(disabled);
    };
    FormItem.prototype.setVariable = function (variable) {
        var that = this,
            b,
            label;
        that.variable = variable;
        label = this.defaultLabel(variable);
        that.properties.set("label", label);
        that.properties.set("var_uid", variable.var_uid);
        b = that.properties.set("variable", variable.var_name);
        if (b.node) {
            that.properties.label.node.value = label;
        }
        if (b.node)
            b.node.textContent = variable.var_name;
        b = that.properties.set("dataType", variable.var_field_type);
        if (b.node)
            b.node.textContent = variable.var_field_type;
        b = that.properties.set("id", variable.var_name);
        if (b.node)
            b.node.value = variable.var_name;
        b = that.properties.set("name", variable.var_name);
        if (b.node)
            b.node.value = variable.var_name;

        b = that.properties.set("dbConnectionLabel", variable.var_dbconnection_label);
        if (b.node)
            b.node.textContent = variable.var_dbconnection_label;
        that.properties.set("dbConnection", variable.var_dbconnection);

        b = that.properties.set("sql", variable.var_sql);
        if (b.node)
            b.node.textContent = variable.var_sql === "" ? "..." : variable.var_sql;
        b = that.properties.set("options", JSON.parse(variable.var_accepted_values));
        if (b.node)
            b.node.textContent = variable.var_accepted_values;

        if (typeof variable.inp_doc_uid === "string" && variable.inp_doc_uid.length > 0) {
            $.ajax({
                async: false,
                url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id + "/input-document/" + variable.inp_doc_uid,
                method: "GET",
                contentType: "application/json",
                beforeSend: function (xhr) {
                    xhr.setRequestHeader("Authorization", "Bearer " + PMDesigner.project.keys.access_token);
                },
                success: function (data) {
                    that.properties.size.disabled = true;
                    b = that.properties.set("size", data.inp_doc_max_filesize);
                    if (b.node) {
                        b.node.value = data.inp_doc_max_filesize;
                    }

                    that.properties.sizeUnity.disabled = true;
                    b = that.properties.set("sizeUnity", data.inp_doc_max_filesize_unit);
                    if (b.node) {
                        b.node.value = data.inp_doc_max_filesize_unit;
                    }
                    b = that.properties.set("enableVersioning", data.inp_doc_versioning === 1);
                    if (b.node) {
                        b.node.enableVersioning = data.inp_doc_versioning === 1;
                    }
                    that.properties.extensions.disabled = true;
                    b = that.properties.set("extensions", data.inp_doc_type_file);
                    if (b.node) {
                        b.node.value = data.inp_doc_type_file;
                    }

                    that.properties.set("inp_doc_uid", data.inp_doc_uid);
                }
            });
        }
    };
    /**
     * Set inputDocument properties to a field
     * @param params
     */
    FormItem.prototype.setInputDocument = function (params) {
        var property,
            key;
        for (key in params) {
            property = this.properties.set(key, params[key].value);
            switch (key) {
                case 'inp_doc_uid':
                    break;
                case 'inputDocument':
                    if (property.node) {
                        property.node.textContent = params[key].value;
                        property.value = (params[key].value === '...') ? '' : params[key].value;
                    }
                    break;
                case 'enableVersioning':
                    if (property.node) {
                        property.node.textContent = (params[key].value) ? 'Yes' : 'No';
                    }
                default:
                    if (property.node) {
                        property.node.value = params[key].value;
                        property.node.disabled = params[key].disabled;
                    }
                    break;
            }
        }
    };
    /**
     * Create deprecation icon.
     */
    FormItem.prototype.createDeprecatedIcon = function () {
        this.deprecatedIcon = $("<div class='mafe-deprecated-control'></div>");
        this.deprecatedIcon.attr('title', "");
        this.deprecatedIcon.hide();
        this.deprecatedIcon.tooltip({
            content: FormDesigner.DEPRECATION_TEXT,
            close: function (event, ui) {
                ui.tooltip.hover(function () {
                    $(this).stop(true).fadeTo(400, 1);
                }, function () {
                    $(this).fadeOut("400", function () {
                        $(this).remove();
                    });
                });
            }
        });
        this.html.prepend(this.deprecatedIcon);
    };
    /**
     * Enable or disable deprecated icon.
     * @param {boolean} status
     */
    FormItem.prototype.deprecated = function (status) {
        if (status === true) {
            this.deprecatedIcon.show();
        } else {
            this.deprecatedIcon.hide();
        }
    };

    /**
     * Get a default label from a variable
     * @param {object} variable
     * @returns {string}
     */
    FormItem.prototype.defaultLabel = function (variable) {
        var name = variable.var_name,
            stringAux,
            flagUpper = true,
            i = 1;
        name = name.replace(/_/g, " ");
        if (name === name.toUpperCase()) {
            name = name.toLowerCase();
        }
        stringAux = name.charAt(0).toUpperCase();
        while (i < name.length) {
            if (name.charAt(i) !== " ") {
                if (name.charAt(i) !== name.charAt(i).toUpperCase()) {
                    stringAux += name.charAt(i);
                    flagUpper = true;
                } else {
                    if (flagUpper) {
                        stringAux += " " + name.charAt(i);
                    } else {
                        stringAux +=  name.charAt(i).toLowerCase();
                    }
                    flagUpper = false;
                }
            } else {
                i += 1;    
                stringAux += " " + name.charAt(i).toUpperCase();
            }
            i += 1;
        }
        return stringAux;
    };
    FormDesigner.extendNamespace('FormDesigner.main.FormItem', FormItem);
}());

(function () {
    var AreaButtons = function () {
        AreaButtons.prototype.init.call(this);
    };
    AreaButtons.prototype.init = function () {
        this.save = $("<div class='fd-designer-button' title='" + "Save".translate() + "'><img src='" + $.imgUrl + "fd-save.png' style='width:24px;height:24px;'/></div>");
        $.hoverToolbarButtons(this.save, "fd-save.png", "fd-savew.png");
        this.export_ = $("<div class='fd-designer-button' title='" + "Export".translate() + "'><img src='" + $.imgUrl + "fd-export.png' style='width:24px;height:24px;'/></div>");
        $.hoverToolbarButtons(this.export_, "fd-export.png", "fd-exportw.png");
        this.import_ = $("<div class='fd-designer-button' title='" + "Import".translate() + "'><img src='" + $.imgUrl + "fd-import.png' style='width:24px;height:24px;'/></div>");
        $.hoverToolbarButtons(this.import_, "fd-import.png", "fd-importw.png");
        this.preview = $("<div class='fd-designer-button' title='" + "Preview".translate() + "'><img src='" + $.imgUrl + "fd-preview.png' style='width:24px;height:24px;'/></div>");
        $.hoverToolbarButtons(this.preview, "fd-preview.png", "fd-previeww.png");
        this.clear = $("<div class='fd-designer-button' title='" + "Clear".translate() + "'><img src='" + $.imgUrl + "fd-clear.png' style='width:24px;height:24px;'/></div>");
        $.hoverToolbarButtons(this.clear, "fd-clear.png", "fd-clearw.png");
        this.language = $("<div class='fd-designer-button' title='" + "Language".translate() + "'><img src='" + $.imgUrl + "fd-language.png' style='width:24px;height:24px;'/></div>");
        $.hoverToolbarButtons(this.language, "fd-language.png", "fd-languagew.png");
        this.close = $("<div class='fd-designer-button' title='" + "Close".translate() + "'><img src='" + $.imgUrl + "fd-close.png' style='width:24px;height:24px;'/></div>");
        $.hoverToolbarButtons(this.close, "fd-close.png", "fd-closew.png");
        this.body = $("<div style='position:absolute;right:0px;background-color:#3397e1;'></div>");
        this.body.append(this.save);
        this.body.append(this.export_);
        this.body.append(this.import_);
        this.body.append(this.preview);
        this.body.append(this.clear);
        this.body.append(this.language);
        this.body.append(this.close);
    };
    FormDesigner.extendNamespace('FormDesigner.main.AreaButtons', AreaButtons);
}());
(function () {
    var AreaToolBox = function () {
        AreaToolBox.prototype.init.call(this);
    };
    AreaToolBox.prototype.init = function () {
        var that = this;
        this.accordion = $("<div></div>");
        this.accordion.accordion({
            heightStyle: "content",
            collapsible: true,
            icons: false,
            event: "mousedown"
        });
        this.body = this.accordion;
    };
    AreaToolBox.prototype.addItem = function (title, item) {
        var name = $("<h3 class='fd-accordion-title' style='margin:0px;'>" + title + "</h3>");
        var body = $("<div style='padding:0px;border:none;border-radius:0px;'></div>");
        body.append(item.body);
        this.accordion.append(name);
        this.accordion.append(body);
        this.accordion.accordion("refresh");
    };
    FormDesigner.extendNamespace('FormDesigner.main.AreaToolBox', AreaToolBox);
}());
(function () {
    var Properties = function (type, ele, owner) {
        this.onGet = new Function();
        this.onSet = new Function();
        this.onClick = new Function();
        this.onClickClearButton = new Function();
        this.ele = ele;
        this.owner = owner;
        this.pf = [];
        Properties.prototype.init.call(this, type);
    };
    Properties.prototype.init = function (type) {
        this.type = {label: "type".translate(), value: type, type: "label"};
        this.id = {
            label: "id".translate(),
            value: "",
            type: "text",
            on: "change",
            regExp: new RegExp(__env.pmVariable.regEx.substr(1, __env.pmVariable.regEx.length - 2)),
            regExpInv: new RegExp(__env.pmVariable.regEx.substr(1, __env.pmVariable.regEx.length - 2), "gi"),
            required: true
        };
        this.name = {label: "name".translate(), value: "", type: "hidden", labelButton: "...", required: true};
        this.description = {label: "description".translate(), value: "", type: "textarea"};
        this.placeholder = {label: "placeholder".translate(), value: "", type: "textarea"};
        this.colSpan = {
            label: "col-span".translate(),
            value: "12",
            type: "text",
            on: "change",
            helpButton: "Colspan is used to define the size and number of columns of a selected row. Twelve columns can be defined as maximum. ".translate() + "The column sizes are defined with integer numbers separated by spaces. Any combination of column sizes can be defined but all the columns sizes should add 12. <br>".translate() + "ex:<br>two columns of different sizes: 8 4<br>two columns of the same size: 6 6<br>Three columns of the same size: 4 4 4<br>Three columns of different sizes: 2 8 2<br><br>" + "For a better design we recommend using values above 3.<br>If you need more info please visit <a target='_blank' href='http://getbootstrap.com/css/'>Bootstrap grid system</a>.".translate()
        };
        this.label = {label: "label".translate(), value: "", type: "text"};
        this.tabIndex = {
            label: "tab index".translate(),
            value: "",
            type: "text"
        };
        this.href = {label: "href".translate(), value: "http://www.processmaker.com/", type: "textarea"};
        this.hint = {label: "hint".translate(), value: "", type: "textarea"};
        this.ariaLabel = {label: "aria label".translate(), value: "", type: "textarea"};
        this.ariaLabelVisible = {label: "aria label visible".translate(), value: true, type: "checkbox"};
        this.src = {label: "src".translate(), value: "", type: "text"};
        this.shape = {
            label: "shape".translate(), value: "", type: "select", items: [
                {value: "thumbnail", label: "thumbnail".translate()},
                {value: "rounded", label: "rounded".translate()},
                {value: "circle", label: "circle".translate()}
            ]
        };
        this.alternateText = {label: "alternate text".translate(), value: "", type: "textarea"};
        this.comment = {label: "comment".translate(), value: "", type: "text"};
        this.required = {label: "required".translate(), value: false, type: "checkbox"};
        this.dnd = {label: "drag & drop".translate(), value: false, type: "hidden"};
        this.extensions = {label: "file extensions".translate(), value: "*", type: "text"};
        this.galleryEnabled = {label: "Enable gallery".translate(), value: true, type: "checkbox"};
        this.availableOffline = {label: "Available offline".translate(), value: false, type: "checkbox"};
        this.cameraEnabled = {label: "Enable camera".translate(), value: true, type: "checkbox"};
        this.size = {label: "max file size".translate(), value: 1024, type: "text"};
        this.sizeUnity = {
            label: "size unit".translate(), value: "KB", type: "select", items: [
                {value: "KB", label: "KB".translate()},
                {value: "MB", label: "MB".translate()}
            ]
        };
        this.maxFileNumber = {
            label: "Max file number".translate(),
            value: "0",
            type: "text",
            regExpNumber: /^\d*$/,
            regExpString: /^[@][@%=]+[a-zA-Z\_]{1}\w+$/
        };
        this.enableVersioning = {
            label: "versioning".translate(),
            value: false,
            type: "label"
        };
        this.columns = {label: "columns".translate(), value: [], type: "hidden"};
        this.data = {label: "data".translate(), value: [], type: "hidden"};
        this.dataType = {label: "variable data type".translate(), value: "", type: "label"};
        this.value = {label: "value".translate(), value: "", type: "text"};
        this.defaultValue = {
            label: "default value".translate(),
            value: "",
            type: "text",
            helpButton: "Allows setting a default value manually<br>or variables permitted using prefixes @@, @=, @#".translate()
        };
        this.textTransform = {
            label: "text transform to".translate(), value: "none", type: "select", items: [
                {value: "none", label: "none".translate()},
                {value: "lower", label: "lowercase".translate()},
                {value: "upper", label: "UPPERCASE".translate()},
                {value: "capitalizePhrase", label: "Capitalize phrase".translate()},
                {value: "titleCase", label: "Title Case".translate()}
            ]
        };
        this.validate = {
            label: "validate".translate(),
            value: "",
            type: "textareabutton",
            labelButton: "help".translate(),
            placeholder: "Use a pattern (to be used in a search).".translate()
        };
        this.validateMessage = {
            label: "validation error message".translate(),
            value: "",
            type: "textarea",
            placeholder: "Error message".translate()
        };
        this.requiredFieldErrorMessage = {
            label: "required field error message".translate(),
            value: "",
            type: "textarea",
            placeholder: "Required field error message".translate()
        };
        this.maxLength = {label: "max length".translate(), value: 1000, type: "text", regExp: /^[0-9]+$/};
        this.formula = {label: "formula".translate(), value: "", type: "button", labelButton: "edit...".translate()};
        this.mode = {
            label: "display mode".translate(), value: "parent", type: "select", items: [
                {value: "parent", label: "parent".translate()},
                {value: "edit", label: "edit".translate()},
                {value: "view", label: "view".translate()},
                {value: "disabled", label: "disabled".translate()}
            ], helpButton: "" +
            "Select the mode of the control:<br>".translate() +
            "<ul style='margin:2px 2px 2px -25px;'>".translate() +
            "<li>Parent: Inherit the mode from parent.</li>".translate() +
            "<li>Edit: Allow user to change the control's value.</li>".translate() +
            "<li>View: Allow user to only view the control's value.</li>".translate() +
            "<li>Disabled: Control is greyed out, but still displays its value.</li>".translate() +
            "</ul>"
        };
        this.variable = {
            label: "variable".translate(),
            value: "",
            type: "labelbutton",
            labelButton: "...",
            clearButton: "clear".translate()
        };
        this.inputDocument = {
            label: "Input Document".translate(),
            value: "",
            type: "labelbutton",
            labelButton: "...",
            clearButton: "clear".translate()
        };
        this.var_uid = {label: "var_uid".translate(), value: "", type: "hidden"};
        this.protectedValue = {label: "protected value".translate(), value: false, type: "checkbox"};
        this.delay = {label: "Delay".translate(), value: 0, type: "text", regExp: /^[0-9]+$/};
        this.resultsLimit = {
            label: "Results Limit".translate(),
            value: 10,
            type: "text",
            regExpNumber: /^\d*[0-9]\d*$/,
            regExpString: /^[@][@%=]+[a-zA-Z\_]{1}\w+$/
        };
        this.forceSelection = {label: "force selection".translate(), value: false, type: "checkbox"};
        this.alt = {label: "title (mouseover)".translate(), value: "", type: "text"};
        this.multiple = {label: "multiple".translate(), value: false, type: "hidden"};
        this.script = {label: "javascript".translate(), value: "", type: "button", labelButton: "edit...".translate()};
        this.layout = {
            label: "layout".translate(), value: "responsive", type: "select", items: [
                {value: "responsive", label: "responsive".translate()},
                {value: "static", label: "static".translate()}
            ]
        };
        this.pageSize = {
            label: "page size".translate(), value: "0", type: "select", items: [
                {value: "0", label: "none".translate()},
                {value: "5", label: "5"},
                {value: "10", label: "10"},
                {value: "20", label: "20"},
                {value: "50", label: "50"},
                {value: "100", label: "100"},
                {value: "200", label: "200"}
            ]
        };
        this.addRow = {label: "add row".translate(), value: true, type: "checkbox"};
        this.deleteRow = {label: "delete row".translate(), value: true, type: "checkbox"};
        this.columnWidth = {label: "column width".translate(), value: "10", type: "text", helpButton: ""};
        this.operation = {
            label: "function".translate(), value: "", type: "select",
            items: [
                {value: "", label: "none".translate()},
                {value: "sum", label: "sum".translate()},
                {value: "avg", label: "average".translate()}
            ]
        };
        this.datasource = {
            label: "datasource".translate(), value: "database", type: "select",
            items: [
                {value: "database", label: "database".translate()},
                {value: "dataVariable", label: "array variable".translate()}
            ]
        };
        this.dbConnectionLabel = {
            label: "DB Connection".translate(),
            value: "PM Database",
            type: "labelbutton",
            labelButton: "..."
        };
        this.dbConnection = {label: "", value: "workflow", type: "hidden"};
        this.sql = {label: "sql".translate(), value: "", type: "labelbutton", labelButton: "..."};
        this.memoryCache = {label: "Memory cache".translate(), value: false, type: "checkbox"};
        this.dataVariable = {
            label: "data variable".translate(),
            value: "",
            type: "hidden",
            labelButton: "@@",
            placeholder: "@@myData".translate()
        };
        this.options = {label: "options".translate(), value: [], type: "labelbutton", labelButton: "..."};
        this.language = {
            label: "language".translate(), value: "en", type: "select", items: [
                {value: "en", label: "English".translate()}
            ]
        };
        this.content = {label: "content".translate(), value: "", type: "button", labelButton: "edit...".translate()};
        this.border = {label: "border".translate(), value: "1px", type: "text"};
        this.externalLibs = {label: "external libs".translate(), value: "", type: "textarea"};
        this.gridStore = {label: "grid store".translate(), value: false, type: "checkbox"};
        this.rows = {label: "rows".translate(), value: "5", type: "text", regExp: /^[0-9]+$/};
        this.inp_doc_uid = {label: "", value: "", type: "hidden"};
        this.printable = {label: "print dynaform".translate(), value: false, type: "hidden"};

        this.format = {
            label: "format".translate(),
            value: "YYYY-MM-DD",
            type: "text",
            helpButton: "Default: false".translate() +
            "<p>See <a href='http://momentjs.com/docs/#/displaying/format/' target='_blank'>http://momentjs.com/docs/#/displaying/format/</a> ".translate() + "for valid datetime formats. If only the date is included in the format then the time picker will not be displayed.</p>".translate() +
            "Examples:".translate() +
            "<ul style='margin:2px 2px 2px -25px;'>" +
            "<li>dddd, MMMM Do YYYY, h:mm:ss a >> \"Sunday, February 14th 2010, 3:25:50 pm\"</li>".translate() +
            "<li>ddd, hA >> \"Sun, 3PM\"</li>".translate() +
            "<li>YYYY MM DD >> \"Valid date\"</li>".translate() +
            "</ul>" +
            "<table border='1' style='border-collapse:collapse;'><tbody>" + "<tr><th></th><th>Token</th><th>Output</th></tr><tr><td><b>Month</b></td><td>M</td><td>1 2 ... 11 12</td></tr>".translate() + "<tr><td></td><td>Mo</td><td>1st 2nd ... 11th 12th</td></tr><tr><td></td><td>MM</td><td>01 02 ... 11 12</td></tr>".translate() + "<tr><td></td><td>MMM</td><td>Jan Feb ... Nov Dec</td></tr><tr><td></td><td>MMMM</td><td>January February ... November December</td></tr>".translate() + "<tr><td><b>Quarter</b></td><td>Q</td><td>1 2 3 4</td></tr><tr><td><b>Day of Month</b></td><td>D</td><td>1 2 ... 30 31</td></tr>".translate() + "<tr><td></td><td>Do</td><td>1st 2nd ... 30th 31st</td></tr><tr><td></td><td>DD</td><td>01 02 ... 30 31</td></tr>".translate() + "<tr><td><b>Day of Year</b></td><td>DDD</td><td>1 2 ... 364 365</td></tr><tr><td></td><td>DDDo</td><td>1st 2nd ... 364th 365th</td></tr>".translate() + "<tr><td></td><td>DDDD</td><td>001 002 ... 364 365</td></tr><tr><td><b>Day of Week</b></td><td>d</td><td>0 1 ... 5 6</td></tr>".translate() + "<tr><td></td><td>do</td><td>0th 1st ... 5th 6th</td></tr><tr><td></td><td>dd</td><td>Su Mo ... Fr Sa</td></tr>".translate() + "<tr><td></td><td>ddd</td><td>Sun Mon ... Fri Sat</td></tr><tr><td></td><td>dddd</td><td>Sunday Monday ... Friday Saturday</td></tr>".translate() + "<tr><td><b>Day of Week (Locale)</b></td><td>e</td><td>0 1 ... 5 6</td></tr><tr><td><b>Day of Week (ISO)</b></td><td>E</td><td>1 2 ... 6 7</td></tr>".translate() + "<tr><td><b>Week of Year</b></td><td>w</td><td>1 2 ... 52 53</td></tr><tr><td></td><td>wo</td><td>1st 2nd ... 52nd 53rd</td></tr>".translate() + "<tr><td></td><td>ww</td><td>01 02 ... 52 53</td></tr><tr><td><b>Week of Year (ISO)</b></td><td>W</td><td>1 2 ... 52 53</td></tr>".translate() + "<tr><td></td><td>Wo</td><td>1st 2nd ... 52nd 53rd</td></tr><tr><td></td><td>WW</td><td>01 02 ... 52 53</td></tr>".translate() + "<tr><td><b>Year</b></td><td>YY</td><td>70 71 ... 29 30</td></tr><tr><td></td><td>YYYY</td><td>1970 1971 ... 2029 2030</td></tr>".translate() + "<tr><td><b>Week Year</b></td><td>gg</td><td>70 71 ... 29 30</td></tr><tr><td></td><td>gggg</td><td>1970 1971 ... 2029 2030</td></tr>".translate() + "<tr><td><b>Week Year (ISO)</b></td><td>GG</td><td>70 71 ... 29 30</td></tr><tr><td></td><td>GGGG</td><td>1970 1971 ... 2029 2030</td></tr>".translate() + "<tr><td><b>AM/PM</b></td><td>A</td><td>AM PM</td></tr><tr><td></td><td>a</td><td>am pm</td></tr>".translate() + "<tr><td><b>Hour</b></td><td>H</td><td>0 1 ... 22 23</td></tr><tr><td></td><td>HH</td><td>00 01 ... 22 23</td></tr>".translate() + "<tr><td></td><td>h</td><td>1 2 ... 11 12</td></tr><tr><td></td><td>hh</td><td>01 02 ... 11 12</td></tr>".translate() + "<tr><td><b>Minute</b></td><td>m</td><td>0 1 ... 58 59</td></tr><tr><td></td><td>mm</td><td>00 01 ... 58 59</td></tr>".translate() + "<tr><td><b>Second</b></td><td>s</td><td>0 1 ... 58 59</td></tr><tr><td></td><td>ss</td><td>00 01 ... 58 59</td></tr>".translate() + "<tr><td><b>Fractional Second</b></td><td>S</td><td>0 1 ... 8 9</td></tr><tr><td></td><td>SS</td><td>0 1 ... 98 99</td></tr>".translate() + "<tr><td></td><td>SSS</td><td>0 1 ... 998 999</td></tr><tr><td><b>Timezone</b></td><td>z or zz</td><td>EST CST ... MST PST<br><b>Note:</b> as of <b>1.6.0</b>, the z/zz format tokens have been deprecated. ".translate() + "<a href=\"https://github.com/moment/moment/issues/162\">Read more about it here.</a></td></tr>".translate() + "<tr><td></td><td>Z</td><td>-07:00 -06:00 ... +06:00 +07:00</td></tr><tr><td></td><td>ZZ</td><td>-0700 -0600 ... +0600 +0700</td></tr>".translate() + "<tr><td><b>Unix Timestamp</b></td><td>X</td><td>1360013296</td></tr><tr><td><b>Unix Millisecond Timestamp</b></td><td>x</td><td>1360013296123</td></tr></tbody></table>".translate(),
            helpButtonCss: "fd-tooltip-date-format"
        };
        this.dayViewHeaderFormat = {label: "day view header format".translate(), value: "MMMM YYYY", type: "hidden"};
        this.extraFormats = {label: "extra formats".translate(), value: false, type: "hidden"};
        this.stepping = {label: "stepping".translate(), value: 1, type: "hidden"};
        this.minDate = {
            label: "min date".translate(),
            value: "",
            type: "datepicker",
            helpButton: "Allows date selection after this date<br>(in YYYY-MM-DD HH:MM:SS format)<br>Variables permitted @@, @=<br>(must use the same date format for dependencies)".translate(),
            clearButton: "clear".translate(),
            regExp: /^[1-9][0-9][0-9][0-9]-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1])$|^(@@|@#|@%|@=|@\?|@\$)[a-zA-Z]+[0-9a-zA-Z_]*$|^$|^[1-9][0-9][0-9][0-9]-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1])\s(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]):(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])$|^[1-9][0-9][0-9][0-9]-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1])\s(0[0-9]|1[0-9]|2[0-3])$|^[1-9][0-9][0-9][0-9]-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1])\s(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])$/
        };
        this.maxDate = {
            label: "max date".translate(),
            value: "",
            type: "datepicker",
            helpButton: "Allows date selection before this date<br>(in YYYY-MM-DD HH:MM:SS format)<br>Variables permitted @@, @=<br>(must use the same date format for dependencies)".translate(),
            clearButton: "clear".translate(),
            regExp: /^[1-9][0-9][0-9][0-9]-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1])$|^(@@|@#|@%|@=|@\?|@\$)[a-zA-Z]+[0-9a-zA-Z_]*$|^$|^[1-9][0-9][0-9][0-9]-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1])\s(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]):(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])$|^[1-9][0-9][0-9][0-9]-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1])\s(0[0-9]|1[0-9]|2[0-3])$|^[1-9][0-9][0-9][0-9]-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1])\s(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])$/
        };
        this.useCurrent = {
            label: "initial selection date".translate(),
            value: "false",
            type: "select",
            items: [
                {
                    value: "false",
                    label: "false".translate()
                },
                {
                    value: "true",
                    label: "true".translate()
                },
                {
                    value: "year",
                    label: "year".translate()
                },
                {
                    value: "month",
                    label: "month".translate()
                },
                {
                    value: "day",
                    label: "day".translate()
                },
                {
                    value: "hour",
                    label: "hour".translate()
                },
                {
                    value: "minute",
                    label: "minute".translate()
                }
            ],
            helpButton: "Default: false<br>On show, will set the picker to:<br>".translate() + "false: No default selection <br>true: current date<br>year: the first day of the current year<br>month: the first day of the month<br>day: the current day<br>hour: the current hour without minutes<br>minute: the current minute".translate()
        };
        this.collapse = {label: "collapse".translate(), value: true, type: "hidden"};
        this.locale = {
            label: "locale".translate(),
            value: "",
            type: "hidden",
            accepts: "string, moment.local('locale')".translate()
        };
        this.defaultDate = {
            label: "default date".translate(),
            value: "",
            type: "datepicker",
            helpButton: "Set the date picker to this date by default<br>(in YYYY-MM-DD HH:MM:SS format)<br>Variables permitted @@, @=<br>(must use the same date format for dependencies)".translate(),
            clearButton: "clear".translate(),
            regExp: /^[1-9][0-9][0-9][0-9]-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1])$|^(@@|@#|@%|@=|@\?|@\$)[a-zA-Z]+[0-9a-zA-Z_]*$|^$|^[1-9][0-9][0-9][0-9]-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1])\s(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]):(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])$|^[1-9][0-9][0-9][0-9]-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1])\s(0[0-9]|1[0-9]|2[0-3])$|^[1-9][0-9][0-9][0-9]-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1])\s(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])$/
        };
        this.disabledDates = {
            label: "disabled dates".translate(),
            value: false,
            type: "hidden",
            accepts: "array of [date, moment, string]".translate()
        };
        this.enabledDates = {
            label: "enabled dates".translate(),
            value: false,
            type: "hidden",
            accepts: "array of [date, moment, string]".translate()
        };
        this.icons = {
            label: "icons".translate(), value: {
                time: 'glyphicon glyphicon-time',
                date: 'glyphicon glyphicon-calendar',
                up: 'glyphicon glyphicon-chevron-up',
                down: 'glyphicon glyphicon-chevron-down',
                previous: 'glyphicon glyphicon-chevron-left',
                next: 'glyphicon glyphicon-chevron-right',
                today: 'glyphicon glyphicon-screenshot',
                clear: 'glyphicon glyphicon-trash'
            }, type: "hidden", accepts: "object with all or some of the parameters above".translate()
        };
        this.useStrict = {label: "useStrict".translate(), value: false, type: "hidden"};
        this.sideBySide = {label: "side by side".translate(), value: false, type: "hidden"};
        this.daysOfWeekDisabled = {
            label: "days of week disabled".translate(),
            value: false,
            type: "hidden",
            accepts: "array of numbers from 0-6".translate()
        };
        this.calendarWeeks = {label: "calendar weeks".translate(), value: false, type: "hidden"};
        this.viewMode = {
            label: "datepicker view mode".translate(),
            value: "days",
            type: "select",
            items: [
                {value: "days", label: "days".translate()},
                {value: "months", label: "months".translate()},
                {value: "years", label: "years".translate()}
            ],
            helpButton: "Select dates by days, months or years by default when the date picker is initially shown.<br>".translate() + "Note: To remove days, months or years from the date picker, use a format which does not have those elements. For example a format of \"MM/YYY\" will not allow the user to select days.".translate()
        };
        this.toolbarPlacement = {
            label: "toolbar placement".translate(),
            value: "default",
            type: "hidden",
            accepts: "'default', 'top', 'bottom'".translate()
        };
        this.showTodayButton = {label: "show today button".translate(), value: false, type: "hidden"};
        this.showClear = {
            label: "show clear button".translate(),
            value: "false",
            type: "select",
            items: [
                {value: "false", label: "hide".translate()},
                {value: "true", label: "show".translate()}
            ],
            helpButton: "Default: false<br>Show the \"Clear\" button in the icon toolbar.<br>".translate() + "Clicking the \"Clear\" button will set the calendar to null.".translate()
        };
        this.widgetPositioning = {
            label: "widget positioning".translate(),
            value: {
                horizontal: 'auto',
                vertical: 'auto'
            },
            type: "hidden",
            accepts: "object with the all or one of the parameters above; horizontal: 'auto', 'left', 'right' or vertical: 'auto', 'top', 'bottom'".translate()
        };
        this.widgetParent = {
            label: "widget parent".translate(),
            value: null,
            type: "hidden",
            accepts: "string or jQuery object".translate()
        };
        this.keepOpen = {label: "keep open".translate(), value: false, type: "hidden"};
        this.fixedLocation = {label: "Fixed location".translate(), value: false, type: "checkbox"};

        //custom properties
        if (this.owner instanceof FormDesigner.main.GridItem) {
            this.variable.type = "hidden";
            this.dataType.type = "hidden";
        }
        if (type === FormDesigner.main.TypesControl.form) {
            this.pf = ["type", "variable", "var_uid", "dataType", "id", "name", "description", "mode", "script",
                "language", "externalLibs", "printable"];
            this.id.type = "label";
            this.id.required = false;
            this.name.type = "text";
            this.name.on = "change";
            this.name.regExp = !/^\s+|\s+$/g;
            this.language.type = "hidden";
            this.variable.type = "hidden";
            this.dataType.type = "hidden";
        }
        if (type === FormDesigner.main.TypesControl.title) {
            this.pf = ["type", "id", "label", "ariaLabel"];
            this.name.type = "text";
            this.label.type = "textarea";
        }
        if (type === FormDesigner.main.TypesControl.subtitle) {
            this.pf = ["type", "id", "label", "ariaLabel"];
            this.name.type = "text";
            this.label.type = "textarea";
        }
        if (type === FormDesigner.main.TypesControl.link) {
            this.pf = ["type", "id", "name", "label", "tabIndex", "value", "href", "hint", "ariaLabel"];
            this.name.type = "text";
            this.label.type = "text";
            this.value.label = "display text".translate();
            if (this.owner instanceof FormDesigner.main.GridItem) {
                this.pf.push("columnWidth");
                this.tabIndex.type = "hidden";
                this.ariaLabel.type = "hidden";
            }
        }
        if (type === FormDesigner.main.TypesControl.image) {
            this.pf = ["type", "id", "name", "label", "tabIndex", "hint", "ariaLabel", "src", "shape", "alternateText", "comment", "alt"];
            this.name.type = "text";
            this.label.type = "text";
        }
        if (type === FormDesigner.main.TypesControl.file) {
            this.pf = ["type", "variable", "var_uid", "dataType", "protectedValue", "id", "name", "label", "tabIndex", "hint", "ariaLabel", "ariaLabel",
                "required", "requiredFieldErrorMessage", "dnd", "extensions", "size", "sizeUnity", "mode", "multiple",
                "inp_doc_uid"];
            this.name.type = "text";
            this.label.type = "text";
            if (this.owner instanceof FormDesigner.main.GridItem) {
                this.pf.push("columnWidth");
                this.tabIndex.type = "hidden";
                this.ariaLabel.type = "hidden";
            }
        }
        if (type === FormDesigner.main.TypesControl.multipleFile) {
            this.pf = ["type", "variable", "var_uid", "dataType", "protectedValue", "id", "name", "label", "tabIndex", "ariaLabel",
                "inputDocument", "required", "requiredFieldErrorMessage", "dnd", "extensions", "size", "sizeUnity",
                "maxFileNumber", "enableVersioning", "mode", "multiple", "inp_doc_uid"];
            this.name.type = "hidden";
            this.label.type = "text";
            if (this.owner instanceof FormDesigner.main.GridItem) {
                this.pf.push("columnWidth");
                this.tabIndex.type = "hidden";
                this.ariaLabel.type = "hidden";
            }
        }
        if (type === FormDesigner.main.TypesControl.submit) {
            this.pf = ["type", "id", "name", "label", "tabIndex", "ariaLabel"];
            this.name.type = "text";
            this.label.type = "text";
        }
        if (type === FormDesigner.main.TypesControl.button) {
            this.pf = ["type", "id", "name", "label", "tabIndex", "ariaLabel"];
            this.name.type = "text";
            this.label.type = "text";
        }
        if (type === FormDesigner.main.TypesControl.grid) {
            this.pf = ["type", "variable", "var_uid", "dataType", "protectedValue", "id", "name", "label", "hint",
                "required", "requiredFieldErrorMessage", "columns", "data", "mode", "layout", "pageSize", "addRow",
                "deleteRow"];
            this.label.label = "title".translate();
            this.label.type = "text";
        }
        if (type === FormDesigner.main.TypesControl.subform) {
            this.pf = ["type", "id", "name", "description", "mode"];
        }
        if (type === FormDesigner.main.TypesControl.text) {
            this.pf = ["type", "variable", "var_uid", "dataType", "protectedValue", "id", "name", "label", "tabIndex",
                "defaultValue", "placeholder", "hint", "ariaLabel", "required", "requiredFieldErrorMessage", "textTransform",
                "validate", "validateMessage", "maxLength", "formula", "mode", "operation", "dbConnection",
                "dbConnectionLabel", "sql", "memoryCache"];
            if (this.owner instanceof FormDesigner.main.FormItem) {
                this.operation.type = "hidden";
            }
            if (this.owner instanceof FormDesigner.main.GridItem) {
                this.pf.push("columnWidth");
                this.tabIndex.type = "hidden";
                this.ariaLabel.type = "hidden";
            }
        }
        if (type === FormDesigner.main.TypesControl.textarea) {
            this.pf = ["type", "variable", "var_uid", "dataType", "protectedValue", "id", "name", "label", "tabIndex",
                "defaultValue", "placeholder", "hint", "ariaLabel", "required", "requiredFieldErrorMessage", "validate",
                "validateMessage", "mode", "dbConnection", "dbConnectionLabel", "sql", "memoryCache", "rows"];
            if (this.owner instanceof FormDesigner.main.GridItem) {
                this.pf.push("columnWidth");
                this.tabIndex.type = "hidden";
                this.ariaLabel.type = "hidden";
            }
        }
        if (type === FormDesigner.main.TypesControl.dropdown) {
            this.pf = ["type", "variable", "var_uid", "dataType", "protectedValue", "id", "name", "label", "tabIndex",
                "defaultValue", "placeholder", "hint", "ariaLabel", "required", "requiredFieldErrorMessage", "mode", "datasource",
                "dbConnection", "dbConnectionLabel", "sql", "memoryCache", "dataVariable", "options"];
            if (this.owner instanceof FormDesigner.main.GridItem) {
                this.pf.push("columnWidth");
                this.tabIndex.type = "hidden";
                this.ariaLabel.type = "hidden";
            }
        }
        if (type === FormDesigner.main.TypesControl.checkbox) {
            this.pf = ["type", "variable", "var_uid", "dataType", "protectedValue", "id", "name", "label", "tabIndex",
                "defaultValue", "hint", "ariaLabel", "ariaLabelVisible", "required", "requiredFieldErrorMessage", "mode", "options"];
            this.defaultValue.type = "checkbox";
            this.defaultValue.helpButton = "";
            if (this.owner instanceof FormDesigner.main.FormItem) {
                this.options.type = "hidden";
            }
            if (this.owner instanceof FormDesigner.main.GridItem) {
                this.pf.push("columnWidth");
                this.options.type = "hidden";
                this.dbConnectionLabel.type = "hidden";
                this.dbConnection.type = "hidden";
                this.sql.type = "hidden";
                this.tabIndex.type = "hidden";
                this.ariaLabel.type = "hidden";
                this.ariaLabelVisible.type = "hidden";
            }
        }
        if (type === FormDesigner.main.TypesControl.checkgroup) {
            this.pf = ["type", "variable", "var_uid", "dataType", "protectedValue", "id", "name", "label", "tabIndex",
                "defaultValue", "hint", "ariaLabel", "ariaLabelVisible", "required", "requiredFieldErrorMessage", "mode", "datasource", "dbConnection",
                "dbConnectionLabel", "sql", "memoryCache", "dataVariable", "options"];
        }
        if (type === FormDesigner.main.TypesControl.radio) {
            this.pf = ["type", "variable", "var_uid", "dataType", "protectedValue", "id", "name", "label", "tabIndex",
                "defaultValue", "hint", "ariaLabel", "ariaLabel", "ariaLabelVisible", "required", "requiredFieldErrorMessage", "mode", "datasource", "dbConnection",
                "dbConnectionLabel", "sql", "memoryCache", "dataVariable", "options"];
        }
        if (type === FormDesigner.main.TypesControl.datetime) {
            this.pf = ["type", "variable", "var_uid", "dataType", "protectedValue", "id", "name", "label", "tabIndex",
                "placeholder", "hint", "ariaLabel", "required", "requiredFieldErrorMessage", "mode", "format", "dayViewHeaderFormat",
                "extraFormats", "stepping", "minDate", "maxDate", "useCurrent", "collapse", "locale", "defaultDate",
                "disabledDates", "enabledDates", "icons", "useStrict", "sideBySide", "daysOfWeekDisabled",
                "calendarWeeks", "viewMode", "toolbarPlacement", "showTodayButton", "showClear", "widgetPositioning",
                "widgetParent", "keepOpen"];
            this.type.helpButton = "Date/time picker widget based on twitter bootstrap <br>" +
                "<a href='http://eonasdan.github.io/bootstrap-datetimepicker/' target='_blank'>" +
                "http://eonasdan.github.io/bootstrap-datetimepicker/</a>".translate();
            if (this.owner instanceof FormDesigner.main.GridItem) {
                this.pf.push("columnWidth");
                this.tabIndex.type = "hidden";
                this.ariaLabel.type = "hidden";
            }
        }
        if (type === FormDesigner.main.TypesControl.suggest) {
            this.pf = ["type", "variable", "var_uid", "dataType", "protectedValue", "id", "name", "label", "tabIndex",
                "defaultValue", "placeholder", "hint", "ariaLabel", "required", "requiredFieldErrorMessage", "mode", "datasource",
                "dbConnection", "dbConnectionLabel", "sql", "memoryCache", "dataVariable", "options", "delay", "resultsLimit",
                "forceSelection"];
            if (this.owner instanceof FormDesigner.main.GridItem) {
                this.pf.push("columnWidth");
                this.tabIndex.type = "hidden";
                this.ariaLabel.type = "hidden";
            }
        }
        if (type === FormDesigner.main.TypesControl.annotation) {
            this.pf = ["type", "id", "label", "ariaLabel"];
            this.name.type = "text";
            this.label.label = "text".translate();
            this.label.type = "textarea";
        }
        if (type === FormDesigner.main.TypesControl.hidden) {
            this.pf = ["type", "variable", "var_uid", "dataType", "protectedValue", "id", "name", "defaultValue",
                "dbConnection", "dbConnectionLabel", "sql"];
        }
        if (type === FormDesigner.main.TypesControl.panel) {
            this.pf = ["type", "id", "content", "border"];
        }
        if (type === FormDesigner.main.TypesControl.msgPanel) {
            this.pf = ["type"];
        }
        if (type === FormDesigner.main.TypesControl.geomap) {
            this.pf = ["type", "variable", "var_uid", "protectedValue", "id", "name", "label", "hint", "fixedLocation"];
            this.label.type = "text";
        }
        if (type === FormDesigner.main.TypesControl.qrcode) {
            this.pf = ["type", "variable", "var_uid", "protectedValue", "id", "name", "label", "hint"];
            this.label.type = "text";
        }
        if (type === FormDesigner.main.TypesControl.signature) {
            this.pf = ["type", "id", "name", "label", "hint"];
            this.label.type = "text";
        }
        if (type === FormDesigner.main.TypesControl.imagem) {
            this.pf = ["type", "id", "name", "label", "inputDocument", "hint", "required", "requiredFieldErrorMessage",
                "cameraEnabled", "galleryEnabled", "availableOffline", "inp_doc_uid", "mode"];
            this.label.type = "text";
            if (this.owner instanceof FormDesigner.main.GridItem) {
                this.pf.push("columnWidth");
            }
        }
        if (type === FormDesigner.main.TypesControl.audiom) {
            this.pf = ["type", "id", "name", "label", "inputDocument", "hint", "required", "requiredFieldErrorMessage",
                "inp_doc_uid", "mode"];
            this.label.type = "text";
        }
        if (type === FormDesigner.main.TypesControl.videom) {
            this.pf = ["type", "id", "name", "label", "inputDocument", "hint", "required", "requiredFieldErrorMessage",
                "inp_doc_uid", "mode"];
            this.label.type = "text";
        }
        if (type === FormDesigner.main.TypesControl.cell) {
            this.pf = ["type", "colSpan"];
        }
    };
    Properties.prototype.set = function (prop, value) {
        var that = this;
        if (this[prop] === undefined)
            return;
        this[prop].oldestValue = this[prop].oldValue;
        this[prop].oldValue = this[prop].value;
        this[prop].value = value;
        that.onSet(prop, value);
        return this[prop];
    };
    Properties.prototype.get = function () {
        var a = {}, s;
        for (var i = 0; i < this.pf.length; i++) {
            a[this.pf[i]] = this[this.pf[i]];
        }
        //stack invalid properties
        for (var i in a) {
            s = a[i].value;
            if (a[i].disabled === false && a[i].regExp && a[i].regExp.test(s) === false) {
                $.globalInvalidProperties.push("- Property '" + i + "' in the '" + a.type.value + "' type is invalid.");
                break;
            }
        }
        //end stack invalid properties
        this.onGet(a);
        return a;
    };
    Properties.prototype.setNode = function (prop, node) {
        this[prop].node = node;
    };
    Properties.prototype.setDisabled = function (disabled) {
        var dt = this.get();
        for (var i in dt) {
            dt[i].disabled = disabled;
        }
    };
    FormDesigner.extendNamespace('FormDesigner.main.Properties', Properties);
}());

(function () {
    $.imgUrl = "/lib/img/";
    $.icons = $("<div style='position:absolute;margin:0px;top:1px;right:1px;background:white;'>" +
        "<img src='" + $.imgUrl + "fd-move2.png' style='width:16px;height:16px;margin-right:2px;cursor:move;' class='formdesigner-move-row'>" +
        "<img src='" + $.imgUrl + "fd-close.png' style='width:16px;height:16px;margin-right:0px;cursor:pointer;' class='formdesigner-close-row'>" +
        "</div>");
    $.elicons = $("<div style='position:absolute;top:1px;margin:0px;right:1px;background:white;width:16px;height:16px;'>" +
        "<img src='" + $.imgUrl + "fd-close.png' style='width:16px;height:16px;margin-right:0px;cursor:pointer;' class='formdesigner-close-row'>" +
        "</div>");
    $.isOpenValidator = false;
    $.fmold = null;
    $.fmoldparent = null;
    $.fmoldborder = null;
    $.fmborder = "1px dashed black";
    $.designerSelectElement = function (el, fn, fn2) {
        $.icons.hide();
        $.elicons.hide();
        if ($.fmold !== null) {
            if ($.fmold.tagName === "TD") {
                var tr = $.fmoldparent;
                for (var i = 0; i < tr.childNodes.length; i++) {
                    td = tr.childNodes[i];
                    td.style.border = $.fmoldborder;
                }
            } else {
                $.fmold.style.border = $.fmoldborder;
            }
        }
        if (el.tagName === "TD") {
            $.fmoldborder = el.style.border;
            var tr = el.parentNode, td;
            for (var i = 0; i < tr.childNodes.length; i++) {
                td = tr.childNodes[i];
                if (i === 0) {
                    td.style.borderLeft = $.fmborder;
                }
                td.style.borderTop = $.fmborder;
                td.style.borderBottom = $.fmborder;
            }
            td = tr.childNodes[i - 1];
            td.style.borderRight = $.fmborder;
            $.fmold = el;
            $.fmoldparent = el.parentNode;
            //icons
            $(tr.childNodes[tr.childNodes.length - 1].childNodes[0]).append($.icons);
            $.icons.show();
            $.icons.find(".formdesigner-close-row")[0].onclick = function (e) {
                e.stopPropagation();
                if (fn2 && fn2() === false) {
                    return;
                }
                $(tr).remove();
                if (fn) {
                    fn(tr);
                }
            };
        } else {
            $.fmoldborder = el.style.border;
            el.style.border = $.fmborder;
            $.fmold = el;
            //icons
            $(el).append($.elicons);
            $.elicons.show();
            $.elicons.find(".formdesigner-close-row")[0].onclick = function (e) {
                if ($.elicons.parent().parent()[0] && $.elicons.parent().parent()[0].tagName === "TD") {
                    $.elicons.parent().parent().addClass("itemVariables");
                    $.elicons.parent().parent().addClass("itemControls");
                    $.elicons.parent().parent().addClass("cellDragDrop");
                }
                e.stopPropagation();
                if (fn && fn() === false) {
                    return;
                }
                $(el).remove();
            };
        }
    };
    $.globalInvalidProperties = [];
    $.changeAllValue = function (string, prop, oldValue, newValue) {
        for (var i in string) {
            if (typeof string[i] === "object") {
                string[i] = $.changeAllValue(string[i], prop, oldValue, newValue);
            }
            if (typeof string[i] === "string" && i === prop && string[i] === oldValue) {
                string[i] = newValue;
            }
        }
        return string;
    };
    $.countValue = function (string, prop, value, withTrim) {
        var n = 0, a, b;
        for (var i in string) {
            if (typeof string[i] === "object")
                n = n + $.countValue(string[i], prop, value, withTrim);
            if (typeof string[i] === "string" && i === prop) {
                a = string[i];
                b = value;
                if (withTrim === true) {
                    a = a.trim();
                    b = b.trim();
                }
                if (a === b) {
                    n = n + 1;
                }
            }
        }
        return n;
    };
    $.getProp = function (string, prop) {
        var a = [], b;
        for (var i in string) {
            if (typeof string[i] === "object") {
                b = $.getProp(string[i], prop);
                for (var j = 0; j < b.length; j++) {
                    a.push(b[j]);
                }
            }
            if (typeof string[i] === "string" && i === prop) {
                a.push(string[i]);
            }
        }
        return a;
    };
    $.setAllPropForType = function (string, prop, value, types) {
        for (var i in string) {
            if (typeof string[i] === "object") {
                string[i] = $.setAllPropForType(string[i], prop, value, types);
            }
            if (typeof string[i] === "string" && i === prop && string["type"] && types.indexOf(string["type"]) >= 0) {
                string[i] = value;
            }
        }
        return string;
    };
    $.hoverToolbarButtons = function (a, img1, img2) {
        a.on("mouseout", function () {
            a.find("img")[0].src = "" + $.imgUrl + img1;
        });
        a.on("mouseover", function () {
            a.find("img")[0].src = "" + $.imgUrl + img2;
        });
    };
    $.rPadDT = function (str) {
        str = str.toString();
        return str.length < 2 ? $.rPadDT("0" + str) : str;
    };
    /**
     * Function: validkeys
     * Note: valid characteres for file name in http://support.microsoft.com/kb/177506/es
     *
     * (A-z)letter
     * (0-9)number
     *  ^   Accent circumflex (caret)
     *  &   Ampersand
     *  '   Apostrophe (single quotation mark)
     *  @   At sign
     *  {   Brace left
     *  }   Brace right
     *  [   Bracket opening
     *  ]   Bracket closing
     *  ,   Comma
     *  $   Dollar sign
     *  =   Equal sign
     *  !   Exclamation point
     *  -   Hyphen
     *  #   Number sign
     *  (   Parenthesis opening
     *  )   Parenthesis closing
     *  %   Percent
     *  .   Period
     *  +   Plus
     *  ~   Tilde
     *  _   Underscore
     *
     *  Example: only backspace, number and letter.
     *  $.validkeys(objectHtmlInput, ['isbackspace', 'isnumber', 'isletter']);
     *
     *  Aditional support:
     *  :   Colon
     *
     * @param {type} object
     * @param {type} validates
     * @returns {undefined}
     */
    $.validkeys = function (object, validates) {
        object.onkeypress = function (e) {
            var key = document.all ? e.keyCode : e.which;
            if (key === 0)
                return true;
            var isbackspace = key === 8;
            var isnumber = key > 47 && key < 58;
            var isletter = (key > 96 && key < 123) || (key > 64 && key < 91);
            var isaccentcircumflex = key === 94;
            var isampersand = key === 41;
            var isapostrophe = key === 145;
            var isatsign = key === 64;
            var isbraceleft = key === 123;
            var isbraceright = key === 125;
            var isbracketopening = key === 91;
            var isbracketclosing = key === 93;
            var iscomma = key === 130;
            var isdollarsign = key === 36;
            var isequalsign = key === 61;
            var isexclamationpoint = key === 33;
            var ishyphen = key === 45;
            var isnumbersign = key === 35;
            var isparenthesisopening = key === 40;
            var isparenthesisclosing = key === 41;
            var ispercent = key === 37;
            var isperiod = key === 46;
            var isplus = key === 43;
            var istilde = key === 126;
            var isunderscore = key === 95;
            var iscolon = key === 58;

            var sw = eval(validates[0]);
            for (var i = 1; i < validates.length; i++) {
                sw = sw || eval(validates[i]);
            }
            return sw;
        };
    };
    $.validDataTypeAndControlType = function (dataType, controlType, fn) {
        if (controlType === FormDesigner.main.TypesControl.text && (dataType === "string" || dataType === "integer" || dataType === "float")) {
            fn();
        }
        if (controlType === FormDesigner.main.TypesControl.textarea && (dataType === "string" || dataType === "integer" || dataType === "float")) {
            fn();
        }
        if (controlType === FormDesigner.main.TypesControl.dropdown && (dataType === "string" || dataType === "integer" || dataType === "float")) {
            fn();
        }
        if (controlType === FormDesigner.main.TypesControl.checkbox && (dataType === "boolean")) {
            fn();
        }
        if (controlType === FormDesigner.main.TypesControl.checkgroup && (dataType === "array")) {
            fn();
        }
        if (controlType === FormDesigner.main.TypesControl.radio && (dataType === "string" || dataType === "integer" || dataType === "float" || dataType === "boolean")) {
            fn();
        }
        if (controlType === FormDesigner.main.TypesControl.datetime && (dataType === "datetime")) {
            fn();
        }
        if (controlType === FormDesigner.main.TypesControl.suggest && (dataType === "string" || dataType === "integer" || dataType === "float")) {
            fn();
        }
        if (controlType === FormDesigner.main.TypesControl.hidden && (dataType === "string" || dataType === "integer" || dataType === "float" || dataType === "boolean" || dataType === "datetime")) {
            fn();
        }
        if (controlType === FormDesigner.main.TypesControl.grid && (dataType === "grid")) {
            fn();
        }
        if (controlType === FormDesigner.main.TypesControl.file && (dataType === "file")) {
            fn();
        }
        if (controlType === FormDesigner.main.TypesControl.multipleFile && (dataType === "multiplefile")) {
            fn();
        }
        if (controlType === FormDesigner.main.TypesControl.geomap && (dataType === "string")) {
            fn();
        }
        if (controlType === FormDesigner.main.TypesControl.qrcode && (dataType === "string")) {
            fn();
        }
        if (controlType === FormDesigner.main.TypesControl.form && (dataType === "grid")) {
            fn();
        }
    };
    $.recovered = {data: [], date: ""};
    var Designer = function (dynaform) {
        this.onHide = new Function();
        this.onSave = new Function();
        this.dynaform = dynaform;
        this.form1 = null;
        this._auxForm = null;
        //this.form2 = null;
        Designer.prototype.init.call(this);
    };
    Designer.prototype.init = function () {
        var that = this,
            listControls,
            listMobileControls,
            listProperties;
        this.loadDynaforms();
        this.container = $("<div style='position:absolute;top:0;right:0;bottom:0;left:0;z-index:100;'></div>");
        this.center = $("<div class='ui-layout-center'></div>");
        this.north = $("<div class='ui-layout-north fd-toolbar-designer' style='overflow:hidden;background-color:#3397e1;padding:0px;'></div>");
        this.west = $("<div class='ui-layout-west' style='padding:0px;overflow-y:scroll;'></div>");
        this.west.on("scroll", function () {
            $(document).trigger("pm.fd.scroll");
        });
        this.container.append(this.center);
        this.container.append(this.north);
        this.container.append(this.west);
        $("body").append(this.container);
        var layout = this.container.layout({
            north: {
                enableCursorHotkey: false,
                resizable: false,
                spacing_open: 0,
                closable: false,
                size: 35
            },
            west: {
                enableCursorHotkey: false,
                resizable: true,
                spacing_open: 1,
                spacing_closed: 1,
                size: 233
            }
        });
        $(".ui-layout-toggler-west-open").css({"overflow": "visible"}).parent().css({"overflow": "visible"});
        $(".ui-layout-toggler-west-open").append("<img id='toggleWest' src='" + $.imgUrl + "fd-toggle.png' style='position:absolute;left:-6px;'/>");
        this.center[0].style.width = "";//todo

        //new objects
        listControls = new FormDesigner.main.ListControls();
        listMobileControls = new FormDesigner.main.ListMobileControls();
        listProperties = new FormDesigner.main.ListProperties();
        this.form1 = new FormDesigner.main.Form();
        this.title = $("<div style='float:left;font-family:Montserrat,sans-serif;font-size:20px;color:white;margin:5px;white-space:nowrap;'>Titulo</div>");
        this.areaButtons = new FormDesigner.main.AreaButtons();
        this.areaToolBox = new FormDesigner.main.AreaToolBox();
        this.areaToolBox.addItem("Web controls".translate(), listControls);
        if (window.distribution === "1")
            this.areaToolBox.addItem("Mobile controls".translate(), listMobileControls);
        this.areaToolBox.addItem("Properties".translate(), listProperties);
        this.areaTabs = new FormDesigner.main.TabsForm();
        this.areaTabs.addItem("Master", this.form1);
        this.north.append(this.title);
        this.north.append(this.areaButtons.body);
        this.west.append(this.areaToolBox.body);
        this.center.append(this.areaTabs.body);
        //events
        var confirmRecovery = false;
        this.areaButtons.save[0].onclick = function () {
            //save lost dynaforms
            if ($.recovered.data.length > 0) {
                if (confirmRecovery === false) {
                    var a = new FormDesigner.main.DialogConfirm(null, "warning", "The form was recovered from a previous version {0}. Possible missing controls are going to be recreated.".translate([$.recovered.date]));
                    a.onAccept = function () {
                        confirmRecovery = true;
                        that.areaButtons.save[0].onclick();
                    };
                    a.onCancel = function () {
                    };
                    return false;
                }
                confirmRecovery = false;
                var calls = [];
                for (var i = 0; i < $.recovered.data.length; i++) {
                    calls.push({url: "dynaform", method: "POST", data: $.recovered.data[i]});
                }
                calls.push({url: "dynaforms", method: "GET"});
                $.ajax({
                    async: false,
                    url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id + "/",
                    data: JSON.stringify({calls: calls}),
                    method: "POST",
                    contentType: "application/json",
                    beforeSend: function (xhr) {
                        xhr.setRequestHeader('X-Requested-With', 'MULTIPART');
                        xhr.setRequestHeader("Authorization", "Bearer " + PMDesigner.project.keys.access_token);
                    },
                    success: function (responses) {
                        //synchronize id
                        var json = that.getData();
                        for (var i = 0; i < responses.length - 1; i++) {
                            if (responses[i].status === 201) {
                                json = $.changeAllValue(json, "id", $.recovered.data[i].dyn_uid_old, responses[i].response.dyn_uid)
                            }
                        }
                        $.recovered.data = [];
                        //update list dynaforms
                        if (responses[responses.length - 1].status === 200) {
                            $.remoteDynaforms = responses[responses.length - 1].response;
                        }
                        //update form
                        var form = that.form1.getData();
                        var dynaform = {
                            dyn_content: JSON.stringify(json),
                            dyn_description: form.description,
                            dyn_title: form.name,
                            dyn_type: "xmlform",
                            dyn_uid: form.id,
                            dyn_version: 2
                        };
                        that.form1.setData(dynaform);
                    },
                    error: function (responses) {
                    }
                });
            }
            //validations
            var data = that.getData();
            var a = $.getProp(data, "variable");
            for (var i = 0; i < a.length; i++) {
                if (a[i] !== "" && $.countValue(data, "variable", a[i], true) > 1) {
                    var b = new FormDesigner.main.DialogInvalid(null, a[i], "duplicated");
                    b.onAccept = function () {
                        b.dialog.dialog("close");
                    };
                    return false;
                }
            }
            a = $.getProp(data, "id");
            for (var i = 0; i < a.length; i++) {
                if (a[i] !== "" && $.countValue(data, "id", a[i], true) > 1) {
                    var b = new FormDesigner.main.DialogInvalid(null, a[i], "duplicated");
                    b.onAccept = function () {
                        b.dialog.dialog("close");
                    };
                    return false;
                }
            }
            if ($.globalInvalidProperties.length > 0) {
                new FormDesigner.main.DialogInvalidProperties();
                return false;
            }
            //save
            that.dynaform.dyn_title = data.name;
            that.dynaform.dyn_description = data.description;
            that.dynaform.dyn_content = JSON.stringify(data);
            var restClient = new PMRestClient({
                endpoint: 'dynaform/' + that.dynaform.dyn_uid,
                typeRequest: 'update',
                data: that.dynaform,
                functionSuccess: function (xhr, response) {
                    that.form1.setDirty();
                    that.onSave();
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                },
                messageError: 'There are problems creating the DynaForm, please try again.'.translate(),
                messageSuccess: 'DynaForm saved successfully'.translate(),
                flashContainer: ''
            });
            restClient.executeRestClient();
            return false;
        };
        this.areaButtons.export_[0].onclick = function () {
            var jsondata = that.getData(),
                i,
                variables,
                size= (typeof jsondata['items'] !== 'undefined') ? jsondata['items'].length : 0; // validation for undefined issues
            for (i = 0; i < size; i += 1) {
                variables = jsondata['items'][i]['variables'];
                variables.forEach(function(v){ delete v.create });
            };
            var name = (that.form1.properties.name.value ? that.form1.properties.name.value : 'untitle') + '.json';
            if (window.navigator.msSaveBlob) {
                window.navigator.msSaveBlob(new Blob([JSON.stringify(jsondata)], {'type': 'application/octet-stream'}), name);
                return false;
            }
            var a = document.createElement('a');
            document.body.appendChild(a);
            a.href = window.URL.createObjectURL(new Blob([JSON.stringify(jsondata)], {'type': 'application/octet-stream'}));
            a.download = name;
            a.click();
            document.body.removeChild(a);
            delete a;
            return false;
        };
        this.areaButtons.import_[0].onclick = function () {
            var actionImport = function () {
                var form = that._getAuxForm();
                var input = document.createElement("input");
                input.type = "file";
                form.appendChild(input);
                form.style.display = "none";
                document.body.appendChild(form);
                input.onchange = function () {
                    var file = input.files[0];
                    if (file.name.indexOf(".json") < 0) {
                        new FormDesigner.main.DialogInvalidFile(null, file.name);
                        return;
                    }
                    var reader = new FileReader();
                    reader.readAsText(file, "UTF-8");
                    reader.onload = function (e) {
                        listProperties.clear();
                        var dynaform = that.form1.getData();
                        that.form1.setData({
                            dyn_content: e.target.result,
                            dyn_description: dynaform.description,
                            dyn_title: dynaform.name,
                            dyn_type: "xmlform",
                            dyn_uid: dynaform.id,
                            dyn_version: 2
                        });
                        //patch to clear _items because is duplicating the items
                        that.form1._items.clear();
                        that.form1.synchronizeVariables();
                    };
                    reader.onerror = function (evt) {
                    };
                    //IE8 implementation
                    //var filePath = f:\oo.txt;
                    //var fso = new ActiveXObject("Scripting.FileSystemObject");
                    //var textStream = fso.OpenTextFile(filePath);
                    //var fileData = file.ReadAll();
                };
                input.click();
            };
            if (that.form1.getData().items.length > 0) {
                var a = new FormDesigner.main.DialogConfirmImport();
                a.onAccept = actionImport;
            } else {
                actionImport();
            }
            return false;
        };
        this.areaButtons.preview[0].onclick = function () {
            that.onSave = function () {
                $(that.container).hide();
                var preview = new FormDesigner.main.Preview(this.dynaform.dyn_uid, this.dynaform.dyn_title, PMDesigner.project.id);
                preview.show();
                preview.setData();
                preview.onClose = function () {
                    that.show();
                };
                that.onSave = new Function();
            };
            that.areaButtons.save[0].onclick();
            return false;
        };
        this.areaButtons.clear[0].onclick = function () {
            if (that.form1.getData().items.length > 0) {
                var a = new FormDesigner.main.DialogConfirmClear();
                a.onAccept = function () {
                    that.form1.clear();
                    that.form1.clearItemsDeprecated();
                    if (!that.form1.checkForDeprecatedControls()) {
                        that.form1.hideDeprecationMessage();
                    }
                    listProperties.clear();
                };
            }
            return false;
        };
        this.areaButtons.language[0].onclick = function () {
            that.onSave = function () {
                var a = new FormDesigner.main.DialogLanguage(null, that.dynaform.dyn_uid);
                a.onLoad = function (response) {
                    that.form1.setLanguages();
                };
                that.onSave = new Function();
            };
            that.areaButtons.save[0].onclick();
            return false;
        };
        this.areaButtons.close[0].onclick = function () {
            if (that.form1.isDirty()) {
                var a = new FormDesigner.main.DialogConfirmClose();
                a.onAccept = function () {
                    that.hide();
                };
            } else {
                that.hide();
            }
            return false;
        };
        this.form1.onSelect = function (properties) {
            listProperties.body.find('input').focus();
            listProperties.clear();
            listProperties.load(properties);
            that.areaToolBox.accordion.accordion("option", "active", (window.distribution === "1") ? 2 : 1);
            //initial form only
            if (that.form1 === properties.owner && properties.type.value === FormDesigner.main.TypesControl.form) {
                $.elicons.hide();
            }
        };
        this.form1.onRemove = function () {
            return false;
        };
        this.form1.onRemoveItem = function (formItem) {
            if (formItem) {
                if (arguments.length > 1) {
                    formItem.parent._items.remove(formItem);
                }
                that.form1._items.remove(formItem);
            }
            if (!that.form1.checkForDeprecatedControls()) {
                that.form1.hideDeprecationMessage();
            }
            listProperties.clear();
        };
        this.form1.onRemoveCell = function () {
            listProperties.clear();
        };
        this.form1.onDrawControl = function (properties) {
            if (properties) {
                that.form1.setNextLabel(properties);
            }
            if (properties && properties.get().variable && properties.get().variable.type === "labelbutton") {
                that.form1.setNextVar(properties);
            }
        };
        this.form1.onDrawDroppedItem = function (render, target) {
            var i,
                controls = listControls.controls.concat(listMobileControls.controls);
            if (Array.isArray(controls)) {
                for (i = 0; i < controls.length; i++) {
                    if (controls[i].render === render &&
                        controls[i].deprecated === true) {
                        if (typeof target.deprecated === "function") {
                            target.deprecated(true);
                            if (arguments.length > 2) {
                                target.parent._items.insert(target);
                                // TODO forced way if a form has three levels, Will not work with fourth levels
                                if (target.parent.parent.parent) {
                                    target.parent.parent._items.insert(target);
                                 }
                            }
                            that.form1._items.insert(target);
                        }
                        that.form1.showDeprecationMessage();
                    }
                }
            }
        };
        this.form1.onSetProperty = function (prop, value, target) {
            var dialogMessage,
                object,
                oldValue,
                required,
                duplicated,
                regExp,
                type,
                existRegExp,
                messageDialog,
                messageMaxFile = 'Invalid Configuration: the "Max File number" value should be integer.'.translate(),
                showMessage = false,
                dateLimit,
                regExp,
                validateValue;
            switch (prop) {
                case "name":
                    if (target.properties[prop].node && target instanceof FormDesigner.main.Form) {
                        if (target === that.form1) {
                            that.title.text(value).attr("title", value);
                        }
                        if (value === "" || !value.replace(/\s/g, '').length) {
                            dialogMessage = new FormDesigner.main.DialogInvalid(null, prop, "required");
                            dialogMessage.onClose = function () {
                                oldValue = target.properties[prop].oldValue;
                                object = target.properties.set(prop, oldValue);
                                if (object.node) {
                                    object.node.value = oldValue;
                                }
                            };
                            dialogMessage.onAccept = function () {
                                dialogMessage.dialog.dialog("close");
                            };
                        }
                    }
                    break;
                case "id":
                    if (target.properties[prop].node) {
                        if ($.isOpenValidator === false) {
                            required = value === "";
                            duplicated = $.countValue(that.getData(), prop, value, true) > 1;
                            regExp = target.properties[prop].regExp && target.properties[prop].regExp.test(target.properties[prop].value) === false;
                            type = required ? "required" : duplicated ? "duplicated" : regExp ? "invalid" : "";
                            if (type !== "") {
                                dialogMessage = new FormDesigner.main.DialogInvalid(null, prop, type);
                                dialogMessage.onClose = function () {
                                    existRegExp = target.properties[prop].regExp && target.properties[prop].regExpInv;
                                    oldValue = target.properties[prop].oldValue;
                                    if (existRegExp && target.properties[prop].regExp.test(oldValue) === false) {
                                        oldValue = oldValue.replace(target.properties[prop].regExpInv, "");
                                    }
                                    object = target.properties.set(prop, oldValue);
                                    if (object.node) {
                                        object.node.value = oldValue;
                                    }
                                };
                                dialogMessage.onAccept = function () {
                                    dialogMessage.dialog.dialog("close");
                                };
                            }
                        }
                        if (target instanceof FormDesigner.main.GridItem) {
                            target.properties["name"].value = value;
                        }
                    }
                    break;
                case "maxLength":
                    if (target.properties[prop].node) {
                        if (value === "") {
                            dialogMessage = new FormDesigner.main.DialogInvalid(null, prop, "required");
                            dialogMessage.onClose = function () {
                                oldValue = target.properties[prop].oldValue;
                                object = target.properties.set(prop, oldValue);
                                if (object.node) {
                                    object.node.value = oldValue;
                                }
                            };
                            dialogMessage.onAccept = function () {
                                dialogMessage.dialog.dialog("close");
                            };
                        }
                    }
                    break;
                case "variable":
                    if (target.properties[prop].node && value !== "" && $.countValue(that.getData(), prop, value) > 1) {
                        $.isOpenValidator = true;
                        dialogMessage = new FormDesigner.main.DialogInvalid(null, prop, "duplicated");
                        dialogMessage.onClose = function () {
                            $.isOpenValidator = false;

                            oldValue = target.properties[prop].oldValue;
                            object = target.properties.set(prop, oldValue);
                            if (object.node) {
                                object.node.textContent = oldValue === "" ? "..." : oldValue;
                            }

                            oldValue = target.properties["dataType"].oldValue;
                            object = target.properties.set("dataType", oldValue);
                            if (object.node) {
                                object.node.textContent = oldValue;
                            }

                            oldValue = target.properties["name"].oldValue;
                            target.properties.set("name", oldValue);

                            oldValue = target.properties["dbConnectionLabel"].oldValue;
                            object = target.properties.set("dbConnectionLabel", oldValue);
                            if (object.node) {
                                object.node.textContent = oldValue;
                            }

                            oldValue = target.properties["dbConnection"].oldValue;
                            target.properties.set("dbConnection", oldValue);

                            oldValue = target.properties["sql"].oldValue;
                            object = target.properties.set("sql", oldValue);
                            if (object.node) {
                                object.node.textContent = oldValue === "" ? "..." : oldValue;
                            }

                            oldValue = target.properties["options"].oldValue;
                            object = target.properties.set("options", oldValue);
                            if (object.node) {
                                object.node.textContent = JSON.stringify(oldValue);
                            }

                            oldValue = target.properties["id"].oldValue;
                            object = target.properties.set("id", oldValue);
                            if (object.node) {
                                object.node.value = oldValue;
                            }
                        };
                        dialogMessage.onAccept = function () {
                            dialogMessage.dialog.dialog("close");
                        };
                    }
                    if (target.properties.type.value === FormDesigner.main.TypesControl.checkbox) {
                        target.properties.options.type = value === "" ? "hidden" : "labelbutton";
                        if (target.properties[prop].node) {
                            listProperties.clear();
                            listProperties.load(target.properties);
                        }
                    }
                    break;
                case "gridStore":
                    if (target.properties.type.value === FormDesigner.main.TypesControl.form) {
                        if (target.properties.gridStore.value === false) {
                            target.properties.variable.value = "";
                            target.properties.dataType.value = "";
                            target.properties.protectedValue.value = false;
                        }
                        target.properties.variable.type = target.properties.gridStore.value ? "labelbutton" : "hidden";
                        target.properties.dataType.type = target.properties.gridStore.value ? "label" : "hidden";
                        target.properties.protectedValue.type = target.properties.gridStore.value ? "checkbox" : "hidden";
                        if (target.properties[prop].node) {
                            listProperties.clear();
                            listProperties.load(target.properties);
                        }
                    }
                    break;
                case "datasource":
                    if (value === "database") {
                        target.properties.dbConnectionLabel.type = "labelbutton";
                        target.properties.sql.type = "labelbutton";
                        target.properties.dataVariable.type = "hidden";
                    }
                    if (value === "dataVariable") {
                        target.properties.dbConnectionLabel.type = "hidden";
                        target.properties.sql.type = "hidden";
                        target.properties.dataVariable.type = "textbutton";
                    }
                    if (target.properties[prop].node) {
                        target.properties.dbConnectionLabel.value = "PM Database";
                        target.properties.dbConnection.value = "workflow";
                        target.properties.sql.value = "";
                        target.properties.dataVariable.value = "";
                        listProperties.clear();
                        listProperties.load(target.properties);
                    }
                    break;
                case "inp_doc_uid":
                    if (target.properties.type.value === FormDesigner.main.TypesControl.file) {
                        if (target.properties.inp_doc_uid.value !== "") {
                            target.properties.size.disabled = true;
                            target.properties.sizeUnity.disabled = true;
                            target.properties.extensions.disabled = true;
                        }
                        if (target.properties.variable.node) {
                            listProperties.clear();
                            listProperties.load(target.properties);
                        }
                    }
                    break;
                case "useRelative":
                    if (target.properties.type.value === FormDesigner.main.TypesControl.datetime) {
                        target.properties.minDate.disabled = value;
                        target.properties.maxDate.disabled = value;
                        target.properties.relativeMinDate.disabled = !value;
                        target.properties.relativeMaxDate.disabled = !value;
                        if (target.properties[prop].node) {
                            listProperties.clear();
                            listProperties.load(target.properties);
                        }
                    }
                    break;
                case "defaultDate":
                    if (target.properties.type.value === FormDesigner.main.TypesControl.datetime) {
                        if (target.properties[prop].node) {
                            if (target.properties.type.value === FormDesigner.main.TypesControl.datetime) {
                                if (value === "today") {
                                    target.properties.defaultDate.disabledTodayOption = true;
                                    listProperties.clear();
                                    listProperties.load(target.properties);
                                } else if (value === "") {
                                    target.properties.defaultDate.disabledTodayOption = false;
                                    listProperties.clear();
                                    listProperties.load(target.properties);
                                } else {
                                    dateLimit = that.thereIsDefaultDateLimit(
                                        target.properties.minDate.value,
                                        target.properties.maxDate.value,
                                        target.properties.defaultDate.value
                                    );
                                    if (dateLimit) {
                                        dialogMessage = new FormDesigner.main.DialogMessage(null, "success", "Default date is out of range.".translate());
                                        dialogMessage.onClose = function () {
                                            target.properties.set("defaultDate", dateLimit);
                                            target.properties.defaultDate.node.value = dateLimit;
                                        };
                                    }
                                }
                            }
                        }

                    }
                    break;
                case "maxDate":
                    if (target.properties[prop].node) {
                        if (target.properties.type.value === FormDesigner.main.TypesControl.datetime) {
                            dateLimit = that.thereIsMaxDateLimit(
                                target.properties.minDate.value,
                                target.properties.maxDate.value,
                                target.properties.defaultDate.value
                            );
                            if (dateLimit) {
                                dialogMessage = new FormDesigner.main.DialogMessage(null, "success", "Max date must be greater than the min and default date".translate());
                                dialogMessage.onClose = function () {
                                    target.properties.set("maxDate", dateLimit);
                                    target.properties.maxDate.node.value = dateLimit;
                                };
                            }
                        }
                    }
                    break;
                case "minDate":
                    if (target.properties[prop].node) {
                        if (target.properties.type.value === FormDesigner.main.TypesControl.datetime) {
                            dateLimit = that.thereIsMinDateLimit(
                                target.properties.minDate.value,
                                target.properties.maxDate.value,
                                target.properties.defaultDate.value
                            );
                            if (dateLimit) {
                                dialogMessage = new FormDesigner.main.DialogMessage(null, "success", "Min date must be lesser than the max and default date.".translate());
                                dialogMessage.onClose = function () {
                                    target.properties.set("minDate", dateLimit);
                                    target.properties.minDate.node.value = dateLimit;
                                };
                            }
                        }
                    }
                    break;
                case "required":
                    if (target.properties["requiredFieldErrorMessage"].node) {
                        target.properties.requiredFieldErrorMessage.node.disabled = !value;
                    }
                    break;
                case "resultsLimit":
                    validateValue = !isNaN(parseInt(value)) ? target.properties[prop].regExpNumber.test(value) :
                        target.properties[prop].regExpString.test(value);
                    if (!validateValue) {
                        messageDialog = 'The value provided for the Results limit property of the field "'.translate() + target.properties.id.value + '" is invalid'.translate();
                        dialogMessage = new FormDesigner.main.DialogMessage(null, 'success', messageDialog);
                        dialogMessage.onClose = function () {
                            oldValue = target.properties[prop].oldValue;
                            object = target.properties.set(prop, oldValue);
                            if (object.node) {
                                object.node.value = oldValue;
                            }
                        };
                        dialogMessage.onAccept = function () {
                            dialogMessage.dialog.dialog("close");
                        };
                    } else {
                        target.properties[prop].value = value;
                    }
                    break;
                case "size":
                case "sizeUnity":
                    //the "maxFileSizeInformation" variable, comes from backend.
                    that.validateMaxFileSize(
                        target,
                        maxFileSizeInformation,
                        function (target, message) {
                            var dialogMessage,
                                value,
                                oldValue;
                            value = target.properties[prop].value;
                            oldValue = target.properties[prop].oldValue;
                            if (target.properties[prop].node && value !== oldValue) {
                                dialogMessage = new FormDesigner.main.DialogMessage(null, "alert", message);
                                dialogMessage.onClose = function () {
                                    that.setMinimalFileSize(target, maxFileSizeInformation);
                                };
                            }
                        }
                    );
                    break;
                case "tabIndex":
                    regExp = /^-{0,1}\d+$/;
                    validateValue = (!isNaN(parseInt(value)) && Number.isInteger(parseInt(value)) && regExp.test(value)) || value === "";
                    if (this.dirty === null && !validateValue) { // First Time set tab index
                        validateValue = true;
                        value = "";
                    }    
                    if (!validateValue) {
                        messageDialog = 'The value provided for the tab index property of the field "{0}" is invalid'.translate([target.properties.id.value]);
                        dialogMessage = new FormDesigner.main.DialogInvalid(null, prop, "invalid");
                        dialogMessage.onClose = function () {
                            oldValue = target.properties[prop].oldValue;
                            if(!(!isNaN(parseInt(oldValue)) && Number.isInteger(parseInt(oldValue)) && regExp.test(oldValue)) || oldValue === ""){
                                oldValue = "";
                            }
                            
                            object = target.properties.set(prop, oldValue);
                            if (object.node) {
                                object.node.value = oldValue;
                            }
                        };
                        dialogMessage.onAccept = function () {
                            dialogMessage.dialog.dialog("close");
                        };
                    } else {
                        target.properties[prop].value = value;
                    }
                    break;
                case "maxFileNumber":
                    if (!target.properties[prop].regExpNumber.test(value)) {
                        showMessage = (target.variable && target.variable.var_field_type === "grid") || target instanceof FormDesigner.main.GridItem? true : !target.properties[prop].regExpString.test(value);
                    }
                    if (showMessage || value === '') {
                        dialogMessage = new FormDesigner.main.DialogMessage(null, "warning", messageMaxFile);
                        dialogMessage.onClose = function () {
                            oldValue = target.properties[prop].oldValue;
                            object = target.properties.set(prop, oldValue);
                            if (object.node) {
                                object.node.value = oldValue;
                            }
                        };
                    }
                    break;
            }
        };
        this.form1.onSynchronizeVariables = function (variables) {
            var msg = "";
            var json = that.getData();
            for (var i = 0; i < variables.length; i++) {
                if (variables[i].create === undefined || variables[i].var_name !== variables[i].var_name_old) {
                    msg = msg + "+ '" + variables[i].var_name + "'<br>";
                }
                json = $.changeAllValue(json, "variable", variables[i].var_name_old, variables[i].var_name);
                json = $.changeAllValue(json, "id", variables[i].var_name_old, variables[i].var_name);
                json = $.changeAllValue(json, "name", variables[i].var_name_old, variables[i].var_name);
                json = $.changeAllValue(json, "var_name", variables[i].var_name_old, variables[i].var_name);
                json = $.changeAllValue(json, "var_uid", variables[i].var_uid_old, variables[i].var_uid);
                json = $.changeAllValue(json, "prj_uid", variables[i].prj_uid_old, variables[i].prj_uid);
            }
            listProperties.clear();
            var dynaform = that.form1.getData();
            that.form1.setData({
                dyn_content: JSON.stringify(json),
                dyn_description: dynaform.description,
                dyn_title: dynaform.name,
                dyn_type: "xmlform",
                dyn_uid: dynaform.id,
                dyn_version: 2
            });
            if (msg !== "") {
                new FormDesigner.main.DialogChangedVariables(null, msg);
            }
        };
        this.form1.properties.language.type = "select";
        this.form1.properties.mode.value = "edit";
        this.form1.properties.mode.items = [
            {value: "edit", label: "edit".translate()},
            {value: "view", label: "view".translate()},
            {value: "disabled", label: "disabled".translate()}
        ];
        this.form1.properties.mode.helpButton = "";
        this.form1.properties.gridStore.type = "hidden";
        this.form1.properties.printable.type = "checkbox";
        this.form1.properties.protectedValue.type = "hidden";
        this.form1.setData(this.dynaform);
        this.form1.setDirty();
        that.title.text(this.dynaform.dyn_title).attr("title", this.dynaform.dyn_title).tooltip({
            tooltipClass: "fd-tooltip",
            position: {my: "left top+1"}
        });

        $(document).on("pm.fd.show", that.onShowHandler.bind(this, listControls, listMobileControls));
    };
    Designer.prototype._getAuxForm = function () {
        var form;
        if (!this._auxForm) {
            form = document.createElement('form');
            this._auxForm = form;
        }
        return this._auxForm;
    };
    Designer.prototype._disposeAuxForm = function () {
        var form = this._auxForm;
        if (form) {
            form.parentNode.removeChild(form);
        }
        this._auxForm = null;
    };
    Designer.prototype.show = function () {
        var a = document.body.childNodes;
        for (var i = 0; i < a.length; i++)
            $(a[i]).hide();
        $(this.container).show();
        $(document).trigger("pm.fd.show");
    };
    Designer.prototype.hide = function () {
        var a = document.body.childNodes;
        for (var i = 0; i < a.length; i++)
            $(a[i]).show();
        $(this.container).remove();
        this._disposeAuxForm();
        $(".loader").hide();
        this.onHide();
    };
    Designer.prototype.getForms = function () {
        return this.form1;
    };
    Designer.prototype.getData = function () {
        $.globalInvalidProperties = [];
        var data = {};
        data["name"] = this.form1.properties.name.value;
        data["description"] = this.form1.properties.description.value;
        data["items"] = [];
        data["items"].push(this.form1.getData());
        return data;
    };
    Designer.prototype.loadDynaforms = function () {
        var isSeen = 1;
        $.ajax({
            async: false,
            url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id
                + "/dynaforms?seen=" + isSeen,
            method: "GET",
            contentType: "application/json",
            beforeSend: function (xhr) {
                xhr.setRequestHeader("Authorization", "Bearer " + PMDesigner.project.keys.access_token);
            },
            success: function (data) {
                $.remoteDynaforms = data;
            }
        });
    };
    /**
     * Validate the date
     * @param date
     *
     */
    Designer.prototype.parseDate = function (stringDate) {
        var parts;
        if (typeof stringDate !== "undefined" && stringDate !== null) {
            parts = stringDate.split("-");
            // new Date(year, month [, day [, hours[, minutes[, seconds[, ms]]]]])
            return new Date(parts[0], parts[1] - 1, parts[2]); // Note: months are 0-based
        }
        return null;
    };
    /**
     * Verifies if a date is valid
     * @param date
     * @returns {boolean}
     */
    Designer.prototype.isValidDate = function (date) {
        return date instanceof Date && !isNaN(date);
    };
    /**
     * Search if there is a limit lower than minDate property.
     * @param minDateString
     * @param maxDateString
     * @param defaultDateString
     * @returns {Object}
     */
    Designer.prototype.thereIsMinDateLimit = function (minDateString, maxDateString, defaultDateString) {
        var minDate = this.parseDate(minDateString),
            maxDate = this.parseDate(maxDateString),
            defaultDate = this.parseDate(defaultDateString),
            result = null;
        if (this.isValidDate(minDate)) {
            if (this.isValidDate(defaultDate)) {
                if (minDate > defaultDate) {
                    result = defaultDateString;
                }
            }
            if (!result && this.isValidDate(maxDate)) {
                if (minDate > maxDate) {
                    result = maxDateString;
                }
            }
        }
        return result;
    };
    /**
     * Search if there is a limit highest than maxDate property.
     * @param minDateString
     * @param maxDateString
     * @param defaultDateString
     * @returns {Object}
     */
    Designer.prototype.thereIsMaxDateLimit = function (minDateString, maxDateString, defaultDateString) {
        var minDate = this.parseDate(minDateString),
            maxDate = this.parseDate(maxDateString),
            defaultDate = this.parseDate(defaultDateString),
            result = null;
        if (this.isValidDate(maxDate)) {
            if (this.isValidDate(defaultDate)) {
                if (maxDate < defaultDate) {
                    result = defaultDateString;
                }
            }
            if (!result && this.isValidDate(minDate)) {
                if (maxDate < minDate) {
                    result = minDateString;
                }
            }
        }
        return result;
    };
    /**
     * Search if defaultDate property is in range of min date and max date.
     * @param minDateString
     * @param maxDateString
     * @param defaultDateString
     * @returns {Object}
     */
    Designer.prototype.thereIsDefaultDateLimit = function (minDateString, maxDateString, defaultDateString) {
        var minDate = this.parseDate(minDateString),
            maxDate = this.parseDate(maxDateString),
            defaultDate = this.parseDate(defaultDateString),
            result = null;
        if (this.isValidDate(defaultDate)) {
            if (this.isValidDate(minDate)) {
                if (defaultDate < minDate) {
                    result = minDateString;
                }
            }
            if (!result && this.isValidDate(maxDate)) {
                if (defaultDate > maxDate) {
                    result = maxDateString;
                }
            }
        }
        return result;
    };
    /**
     * Handler for on show trigger.
     * @param {array} listControls
     * @param {array} listMobileControls
     */
    Designer.prototype.onShowHandler = function (listControls, listMobileControls) {
        var i,
            popOver,
            controls = listControls.controls.concat(listMobileControls.controls);
        if (Array.isArray(controls)) {
            for (i = 0; i < controls.length; i += 1) {
                if (controls[i].deprecated === true) {
                    popOver = controls[i].target.getPopOver();
                    $(document).on('pm.fd.scroll', popOver.hide.bind(popOver));
                    if (!PMDYNAFORM_FIRST_TIME) {
                        popOver.show();
                    }
                }
            }
        }
        PMDYNAFORM_FIRST_TIME = true;
    };
    /**
     * Validate Max File Size, if the value exceeds the allowed limit, an alert message
     * is displayed.
     *
     * @param object target
     * @param object maxFileSizeInformation
     * @param function callback
     */
    Designer.prototype.validateMaxFileSize = function (target, maxFileSizeInformation, callback) {
        var isTypeValid,
            message;

        if (target.properties === null) {
            return;
        }

        isTypeValid = target.properties.type.value === FormDesigner.main.TypesControl.file ||
                target.properties.type.value === FormDesigner.main.TypesControl.multipleFile;
        if (!isTypeValid) {
            return;
        }
        if (this.isValidMaxFileSizeFromProperties(maxFileSizeInformation, target.properties) === false) {
            message = "The maximum value of this field is ".translate() + maxFileSizeInformation.uploadMaxFileSize;
            callback(target, message);
        }
    };
    /**
     * Returns true if the value of Max files Size, satisfies the configuration of
     * the php ini directives, false otherwise.
     *
     * @param {object} maxFileSizeInformation
     * @param {object} properties
     * @returns {Boolean}
     */
    Designer.prototype.isValidMaxFileSizeFromProperties = function (maxFileSizeInformation, properties) {
        var value,
            unit,
            items,
            i;
        value = parseInt(properties.size.value, 10);
        unit = properties.sizeUnity.value;
        items = properties.sizeUnity.items;

        if (Array.isArray(items)) {
            for (i = 0; i < items.length; i += 1) {
                if (unit === items[i].value) {
                    value = value * Math.pow(1024, i + 1);
                }
            }
        }

        if (maxFileSizeInformation.uploadMaxFileSizeBytes < value) {
            return false;
        }
        return true;
    };
    /**
     * Set Minimal File Size.
     *
     * @param object target
     * @param object maxFileSizeInformation
     */
    Designer.prototype.setMinimalFileSize = function (target, maxFileSizeInformation) {
        var size,
            sizeValue,
            sizeUnity,
            sizeUnityValue;

        sizeValue = maxFileSizeInformation.uploadMaxFileSizeMBytes;
        size = target.properties.set("size", sizeValue);
        if (size && size.node) {
            size.node.value = sizeValue;
        }

        sizeUnityValue = maxFileSizeInformation.uploadMaxFileSizeUnit;
        sizeUnity = target.properties.set("sizeUnity", sizeUnityValue);
        if (sizeUnity.node) {
            sizeUnity.node.value = sizeUnityValue;
        }
    };
    FormDesigner.extendNamespace('FormDesigner.main.Designer', Designer);
}());

(function () {
    var DialogDynaforms = function (appendTo, uidParentDynaform) {
        this.onClose = new Function();
        this.onSelectItem = new Function();
        this.uidParentDynaform = uidParentDynaform;
        DialogDynaforms.prototype.init.call(this, appendTo);
    };
    DialogDynaforms.prototype.init = function (appendTo) {
        var that = this;
        this.dialog = $("<div title='" + "Select a Control".translate() + "' style='padding:10px;'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 500,
            height: 400,
            resizable: false,
            close: function (event, ui) {
                that.onClose(event, ui);
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog);

        this.body = $("<div style='border:1px solid #bbb;'></div>");
        this.dialog.append("<p>Please select the control you want to use with your variable.</p>".translate());
        this.dialog.append(this.body);
        this.load();
    };
    DialogDynaforms.prototype.load = function () {
        var that = this;
        this.body.find(">div").remove();
        (new PMRestClient({
            endpoint: 'dynaforms',
            typeRequest: 'get',
            functionSuccess: function (xhr, response) {
                for (var i = 0; i < response.length; i++) {
                    if (response[i].dyn_uid !== that.uidParentDynaform) {//todo
                        that.addItem(response[i]);
                    }
                }
            },
            messageError: 'There are problems getting the list of DynaForms, please try again.'.translate()
        })).executeRestClient();
    };
    DialogDynaforms.prototype.addItem = function (dynaform) {
        var that = this;
        var item = $(
            "<div class='fd-list' style='width:auto;cursor:pointer;'>" +
            "<div style='display:inline-block;background-size:contain;background-image:url(" + $.imgUrl + "fd-application-form.png);width:16px;height:16px;vertical-align:middle;'></div>" +
            "<div style='display:inline-block;margin-left:10px;'>" + dynaform.dyn_title + "</div>" +
            "</div>");
        this.body.append(item);
        item.attr("render", "subform");
        item.attr("dynaform", JSON.stringify(dynaform));
        item.on("click", function (event) {
            that.onSelectItem(event, item);
            that.dialog.dialog("close").remove();
        });
        return item;
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogDynaforms', DialogDynaforms);
}());
(function () {
    var DialogTypeControl = function (appendTo) {
        this.onClose = new Function();
        this.onSelectItem = new Function();
        DialogTypeControl.prototype.init.call(this, appendTo);
    };
    DialogTypeControl.prototype.init = function (appendTo) {
        var that = this;
        this.dialog = $("<div title='" + "Select a Control".translate() + "' style='padding:10px;'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 500,
            height: 400,
            resizable: false,
            close: function (event, ui) {
                that.onClose(event, ui);
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog);

        this.body = $("<div style='border:1px solid #bbb;'></div>");
        this.dialog.append("<p>" + "Please select the control you want to use with your variable.".translate() + "<p>");
        this.dialog.append(this.body);
    };
    DialogTypeControl.prototype.load = function (variable) {
        var controls = [{
            url: "" + $.imgUrl + "fd-ui-text-field.png",
            label: "textfield".translate(),
            render: FormDesigner.main.TypesControl.text
        }, {
            url: "" + $.imgUrl + "fd-ui-text-area.png",
            label: "textarea".translate(),
            render: FormDesigner.main.TypesControl.textarea
        }, {
            url: "" + $.imgUrl + "fd-ui-combo-box.png",
            label: "dropdown".translate(),
            render: FormDesigner.main.TypesControl.dropdown
        }, {
            url: "" + $.imgUrl + "fd-ui-check-boxes-list.png",
            label: "checkbox".translate(),
            render: FormDesigner.main.TypesControl.checkbox
        }, {
            url: "" + $.imgUrl + "fd-ui-radio-buttons-list.png",
            label: "radio".translate(),
            render: FormDesigner.main.TypesControl.radio
        }, {
            url: "" + $.imgUrl + "fd-calendar.png",
            label: 'datetime',
            render: FormDesigner.main.TypesControl.datetime
        }, {
            url: "" + $.imgUrl + "fd-ui-list-box.png",
            label: "suggest".translate(),
            render: FormDesigner.main.TypesControl.suggest
        }, {
            url: "" + $.imgUrl + "fd-ui-text-field-hidden.png",
            label: "hidden".translate(),
            render: FormDesigner.main.TypesControl.hidden
        }
        ];
        this.body.find(">div").remove();
        for (var i = 0; i < controls.length; i++) {
            if (variable.var_field_type === "string" && (controls[i].label === "textfield" || controls[i].label === "textarea" || controls[i].label === "dropdown" || controls[i].label === "checkbox" || controls[i].label === "radio" || controls[i].label === "suggest" || controls[i].label === "hidden")) {
                this.addItem(controls[i]);
            }
            if (variable.var_field_type === "integer" && (controls[i].label === "textfield" || controls[i].label === "dropdown" || controls[i].label === "checkbox" || controls[i].label === "radio" || controls[i].label === "suggest" || controls[i].label === "hidden")) {
                this.addItem(controls[i]);
            }
            if (variable.var_field_type === "float" && (controls[i].label === "textfield" || controls[i].label === "dropdown" || controls[i].label === "checkbox" || controls[i].label === "radio" || controls[i].label === "suggest" || controls[i].label === "hidden")) {
                this.addItem(controls[i]);
            }
            if (variable.var_field_type === "boolean" && (controls[i].label === "dropdown" || controls[i].label === "radio" || controls[i].label === "hidden" || controls[i].label === "checkbox")) {
                this.addItem(controls[i]);
            }
            if (variable.var_field_type === "datetime" && (controls[i].label === "datetime" || controls[i].label === "datetime" || controls[i].label === "suggest" || controls[i].label === "hidden")) {
                this.addItem(controls[i]);
            }
        }
    };
    DialogTypeControl.prototype.addItem = function (control) {
        var that = this;
        var item = $(
            "<div class='fd-list' style='width:auto;height:16px;position:relative;cursor:pointer;'>" +
            "<div style='display:inline-block;vertical-align:middle;'><img src=" + control.url + "></img></div>" +
            "<div style='display:inline-block;margin-left:10px;'>" + control.label + "</div>" +
            "</div>");
        this.body.append(item);
        item.attr("render", control.render);
        item.on("click", function (event) {
            that.onSelectItem(event, item);
            that.dialog.dialog("close").remove();
        });
        return item;
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogTypeControl', DialogTypeControl);
}());

(function () {
    /**
     * Represents an item in the list of controls.
     *
     * @param {object} control
     * @returns {NoticeL#1.Notice}
     * @constructor
     */
    var Notice = function () {
        Notice.prototype.init.call(this);
    };
    /**
     * Initialize the class.
     */
    Notice.prototype.init = function () {
        var element;
        this.message = $("<div></div>");
        element = $("<div class='mafe-alert'></div>");
        element.append('<button class="button-close"><span class="fa fa-times"></span></button>')
            .append(this.message)
            .on('click', '.button-close', function () {
                element.fadeOut();
            });
        element.hide();
        this.body = element;
    };
    /**
     * Show message.
     * @param {type} message
     */
    Notice.prototype.show = function (message) {
        this.message.empty();
        this.message.append(message);
        this.body.show();
    };
    /**
     * Close message.
     */
    Notice.prototype.close = function () {
        this.body.fadeOut();
    };
    FormDesigner.extendNamespace('FormDesigner.main.Notice', Notice);
}());

(function () {
    var Form = function (parent) {
        this.id = PMUI.generateUniqueId();
        this.onRemove = new Function();
        this.onRemoveItem = new Function();
        this.onRemoveCell = new Function();
        this.onSelect = new Function();
        this.onDrawControl = new Function();
        this.onSetProperty = new Function();
        this.onSynchronizeVariables = new Function();
        this.onDrawDroppedItem = new Function();
        this.sourceNode = null;
        this.targetNode = null;
        this.parent = parent;
        this.stopValidateRows = false;
        this.variable = null;
        this.dirty = null;
        this.subformSupport = true;
        this.disabled = false;
        this.recovery = false;
        this.checkColspan = true;
        this.typesControlSupported = [
            FormDesigner.main.TypesControl.title,
            FormDesigner.main.TypesControl.subtitle,
            FormDesigner.main.TypesControl.label,
            FormDesigner.main.TypesControl.link,
            FormDesigner.main.TypesControl.image,
            FormDesigner.main.TypesControl.file,
            FormDesigner.main.TypesControl.multipleFile,
            FormDesigner.main.TypesControl.submit,
            FormDesigner.main.TypesControl.button,
            FormDesigner.main.TypesControl.text,
            FormDesigner.main.TypesControl.textarea,
            FormDesigner.main.TypesControl.dropdown,
            FormDesigner.main.TypesControl.checkbox,
            FormDesigner.main.TypesControl.checkgroup,
            FormDesigner.main.TypesControl.radio,
            FormDesigner.main.TypesControl.datetime,
            FormDesigner.main.TypesControl.suggest,
            FormDesigner.main.TypesControl.hidden,
            FormDesigner.main.TypesControl.annotation,
            FormDesigner.main.TypesControl.geomap,
            FormDesigner.main.TypesControl.qrcode,
            FormDesigner.main.TypesControl.signature,
            FormDesigner.main.TypesControl.imagem,
            FormDesigner.main.TypesControl.audiom,
            FormDesigner.main.TypesControl.videom,
            FormDesigner.main.TypesControl.panel,
            FormDesigner.main.TypesControl.msgPanel
        ];
        this._items = new PMUI.util.ArrayList();
        Form.prototype.init.call(this);
    };
    Form.prototype.init = function () {
        var that = this;
        this.thead = $("<thead><tr><th></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th></tr></thead>");
        this.tbody = $("<tbody class='itemsVariablesControls'></tbody>");
        this.tbody.sortable({
            placeholder: "fd-gridForm-placeholder",
            handle: ".formdesigner-move-row",
            stop: function (event, ui) {
                if (ui.item.attr("variable"))
                    that.variable = JSON.parse(ui.item.attr("variable"));
                if (ui.item.attr("render")) {
                    var c = that.addCell();
                    that.targetNode = c;
                    c.data("createdRow", true);
                    ui.item.replaceWith(that.addRow().append(c));
                    var properties = that.drawDroppedItem(ui.item.attr("render"));
                    that.onDrawControl(properties);
                }
                that.validateDragDrop();
                that.validateRows();
            }
        }).droppable({
            drop: function (event, ui) {
            }
        });
        this.table = $("<table style='width:100%;'></table>");
        this.table.append(this.thead);
        this.table.append(this.tbody);
        this.body = $("<div id='" + this.id + "' class=\"pm-mafe-form\" style='background:#F6F5F3;margin:6px;padding:10px;border-radius:5px;border:1px solid #DADADA;position:relative;'></div>");
        this.body.append(this.table);
        this.body.on("click", function (e) {
            e.stopPropagation();
            $.designerSelectElement(this, that.onRemove);
            that.onSelect(that.properties);
        });
        this.body.data("objectInstance", this);
        this.properties = new FormDesigner.main.Properties(FormDesigner.main.TypesControl.form, this.body, that);
        this.properties.onClick = function (property) {
            var a, b;
            if (property === "script") {
                a = new FormDesigner.main.DialogScript();
                a.onSave = function () {
                    that.properties.set(property, {
                        type: "js",
                        code: a.editor.getValue()
                    });
                };
                if (typeof that.properties.get()[property].value === "object") {
                    a.editor.setValue(that.properties.get()[property].value.code);
                }
            }
            if (property === "variable") {
                a = new FormDesigner.main.DialogCreateVariable(null, FormDesigner.main.TypesControl.form, [], that.properties.get()[property].value);
                a.onSave = function (variable) {
                    that.setVariable(variable);
                };
                a.onSelect = function (variable) {
                    a.dialog.dialog("close");
                    that.setVariable(variable);
                };
            }
        };
        this.properties.onClickClearButton = function (property) {
            var a, b;
            if (property === "variable" && that.properties[property].value !== "") {
                a = new FormDesigner.main.DialogConfirmClearVariable();
                a.onAccept = function () {
                    b = that.properties.set("variable", "");
                    b.node.textContent = "...";
                    b = that.properties.set("dataType", "");
                    b.node.textContent = "";
                    b = that.properties.set("protectedValue", false);
                    b.node.checked = false;
                };
            }
        };
        this.properties.onSet = function (prop, value) {
            that.onSetProperty(prop, value, that);
        };
        this.clear();
        this.notice = new FormDesigner.main.Notice();
        this.notice.body.insertBefore(this.table);
    };
    Form.prototype.addRow = function () {
        var row = $("<tr style='padding:5px;'></tr>");
        this.tbody.append(row);
        return row;
    };
    Form.prototype.addCell = function () {
        var that = this,
            cell,
            properties;
        cell = $("<td style='height:56px;background:white;position:relative;vertical-align:top;border:1px dotted gray;' class='itemVariables itemControls cellDragDrop colspan-12' colspan='12'></td>");
        cell[0].disabled = false;
        cell[0].setDisabled = function (disabled) {
            cell[0].disabled = disabled;
            cell.data("properties").setDisabled(disabled);
        };
        cell.sortable({
            placeholder: "fd-gridForm-placeholder",
            connectWith: ".cellDragDrop",
            items: '>*:not(.containerCellDragDrop)',
            receive: function (event, ui) {
                that.sourceNode = ui.sender;
                that.targetNode = $(this);
            },
            stop: function (event, ui) {
                if (ui.item.attr("variable"))
                    that.variable = JSON.parse(ui.item.attr("variable"));
                that.targetNode = that.targetNode ? $(ui.item[0].parentNode) : that.targetNode;
                if (ui.item.attr("render")) {
                    $(ui.item).remove();
                    var properties = that.drawDroppedItem(ui.item.attr("render"));
                    that.onDrawControl(properties);
                }
                that.validateDragDrop();
                that.validateRows();
            }
        }).droppable({
            drop: function (event, ui) {
            }
        });
        properties = new FormDesigner.main.Properties(FormDesigner.main.TypesControl.cell, cell, cell[0]);
        properties.onSet = function (prop, value) {
            if (prop === "colSpan" && properties[prop].node) {
                //calculate colspan
                value = $.trim(value);
                var dt = value.split(" "), s = 0, ss = "", i, r, col;
                for (i = 0; i < dt.length; i++) {
                    col = parseInt(dt[i], 10);
                    if (s + col <= 12 && col !== 0 && col > 0) {
                        s = s + col;
                        ss = ss + col + " ";
                    }
                }
                if (s < 12) {
                    r = 12 - s;
                    s = s + r;
                    ss = ss + r;
                }
                ss = $.trim(ss);
                //validation
                if (that.checkColspan === true && cell.parent().children().length > ss.split(" ").length) {
                    var a = new FormDesigner.main.DialogConfirm(null, "warning", "The colspan change is going to remove columns and content fields. Do you want to continue?".translate());
                    a.onAccept = function () {
                        that.checkColspan = false;
                        properties.set(prop, ss);
                        that.checkColspan = true;
                    };
                    a.onClose = function () {
                        that.checkColspan = false;
                        var oldValue = properties[prop].oldValue;
                        properties.set(prop, oldValue);
                        properties[prop].node.value = oldValue;
                        that.checkColspan = true;
                    };
                    return;
                }
                //update value in properties and input
                properties[prop].node.value = ss;
                properties[prop].value = ss;
                //complete cells
                var row = cell.parent();
                dt = ss.split(" ");
                if (row.children().length < dt.length) {
                    for (i = 0; i < dt.length; i++) {
                        if (i < row.children().length) {

                        } else {
                            row.append(that.addCell());
                        }
                        row.children()[i].style.width = (dt[i] * 100 / 12) + "%";//todo colspan
                        row.children()[i].colSpan = dt[i];
                    }
                }
                if (row.children().length >= dt.length) {
                    for (i = 0; i < dt.length; i++) {
                        row.children()[i].style.width = (dt[i] * 100 / 12) + "%";//todo colspan
                        row.children()[i].colSpan = dt[i];
                    }
                    while (row.children().length > i) {
                        $(row.children()[i]).remove();
                    }
                }
                //update icons
                $.designerSelectElement(row.children()[0]);
                //update all cells to new value
                row.children().each(function (i, e) {
                    var a = $(e).data("properties").onSet;
                    $(e).data("properties").onSet = new Function();
                    $(e).data("properties").set(prop, ss);
                    $(e).data("properties").onSet = a;
                });
                //update colspan class for resize media query
                row.children().each(function (i, e) {
                    for (var i = 1; i <= 12; i++) {
                        $(e).removeClass("colspan-" + i);
                    }
                    $(e).addClass("colspan-" + e.colSpan);
                });
            }
        };
        cell.data("properties", properties);
        cell.on("click", function (e) {
            e.stopPropagation();
            $.designerSelectElement(this, function (row) {
                var itemsToRemove;
                that.validateRows();
                that.validateDragDrop();
                that.onRemoveCell();
                itemsToRemove = that._items.asArray().filter(function (i) {
                    return row.innerHTML.indexOf(i.html.parent().parent().get(0).innerHTML) !== -1;
                });
                itemsToRemove.forEach(function (item) {
                    that._items.remove(item);
                });
                if (!that.checkForDeprecatedControls()) {
                    that.hideDeprecationMessage();
                }
            }, function () {
                if (that.disabled === true) {
                    return false;
                }
            });
            that.onSelect(properties);
        });
        cell.append("<div class='containerCellDragDrop' style='float:right;'></div>");
        return cell;
    };
    Form.prototype.drawDroppedItem = function (render, data) {
        var that = this,
            properties = null,
            target = null;
        switch (render) {
            case that.inTypesControl(render):
                var formItem = new FormDesigner.main.FormItem({
                    parentObject: that,
                    type: render,
                    variable: that.variable,
                    onSelect: function (properties) {
                        that.onSelect(properties);
                    }
                });
                formItem.onRemove = function () {
                    that.onRemoveItem(this);
                };
                formItem.onSetProperty = function (prop, value, target) {
                    that.onSetProperty(prop, value, target);
                };
                that.targetNode.append(formItem.html);
                properties = formItem.properties;
                target = formItem;
                break;
            case FormDesigner.main.TypesControl.grid:
                var grid = new FormDesigner.main.Grid(that);
                grid.onRemove = function () {
                    this.clearItemsDeprecated();
                    that.onRemoveItem();
                };
                grid.onRemoveItem = function (gridItem) {
                    that.onRemoveItem(gridItem, "grid");
                };
                grid.onSelect = function (properties) {
                    that.onSelect(properties);
                };
                grid.onVariableDrawDroppedItem = function (variable) {
                    if (that.isVariableUsed(variable.var_uid)) {
                        new FormDesigner.main.DialogInformation();
                        return false;
                    }
                };
                grid.onDrawControl = function (properties) {
                    that.onDrawControl(properties);
                };
                grid.onDrawDroppedItem = function (render, target) {
                    that.onDrawDroppedItem(render, target, "grid");
                };
                grid.onSetProperty = function (prop, value, target) {
                    that.onSetProperty(prop, value, target);
                };
                that.targetNode.append(grid.body);
                properties = grid.properties;
                target = grid;
                break;
            case FormDesigner.main.TypesControl.form:
                var form = new FormDesigner.main.Form(that);
                form.onRemove = function () {
                    form.clearItemsDeprecated();
                    that.onRemoveItem();
                };
                form.onRemoveItem = function () {
                    that.onRemoveItem();
                };
                form.onRemoveCell = function () {
                    that.onRemoveItem();
                };
                form.onSelect = function (properties) {
                    that.onSelect(properties);
                };
                form.onDrawControl = function (properties) {
                    that.onDrawControl(properties);
                };
                form.onDrawDroppedItem = function (render, target) {
                    that.onDrawDroppedItem(render, target, "form");
                };
                form.onSetProperty = function (prop, value, target) {
                    that.onSetProperty(prop, value, target);
                };
                form.setData(data);
                form.setDisabled(true);
                form.properties.gridStore.disabled = false;
                form.properties.variable.disabled = false;
                form.properties.mode.disabled = false;
                form.properties.protectedValue.disabled = false;
                form.body[0].style.margin = "20px 6px 6px 6px";
                that.targetNode.append(form.body);
                that.validateDragDrop();
                that.validateRows();
                that.variable = null;
                properties = form.properties;
                target = form;
                break;
            case FormDesigner.main.TypesControl.variable:
                if (that.isVariableUsed(that.variable.var_uid)) {
                    var dialogInformation = new FormDesigner.main.DialogInformation();
                    dialogInformation.onAccept = function () {
                        if (that.targetNode.data("createdRow") === true) {
                            that.targetNode.parent().remove();
                        }
                    };
                    return;
                }
                that.stopValidateRows = true;
                var dialogTypeControl = new FormDesigner.main.DialogTypeControl();
                dialogTypeControl.load(that.variable);
                dialogTypeControl.onSelectItem = function (event, item) {
                    that.drawDroppedItem(item.attr("render"));
                    that.validateDragDrop();
                    that.validateRows();
                    that.variable = null;
                };
                dialogTypeControl.onClose = function () {
                    that.validateDragDrop();
                    that.validateRows();
                    that.variable = null;
                };
                target = dialogTypeControl;
                break;
            case FormDesigner.main.TypesControl.subform:
                if (that.subformSupport === false) {
                    new FormDesigner.main.DialogUnsupported();
                    return;
                }
                that.stopValidateRows = true;
                var dialogDynaforms = new FormDesigner.main.DialogDynaforms(null, that.properties.id.value);
                dialogDynaforms.onSelectItem = function (event, item) {
                    var prop,
                        subDynaform;
                    subDynaform = JSON.parse(item.attr("dynaform"));
                    //todo validation form with subform
                    var sf, sfi, sfj, jsp;
                    jsp = JSON.parse(subDynaform.dyn_content);
                    if (jsp.items.length > 0) {
                        sf = jsp.items[0];
                        for (sfi = 0; sfi < sf.items.length; sfi++) {
                            for (sfj = 0; sfj < sf.items[sfi].length; sfj++) {
                                if (sf.items[sfi][sfj].type === FormDesigner.main.TypesControl.form) {
                                    new FormDesigner.main.DialogUnsupported();
                                    return;
                                }
                            }
                        }
                    }
                    prop = that.drawDroppedItem(FormDesigner.main.TypesControl.form, subDynaform);
                    prop.owner.subformSupport = false;
                };
                dialogDynaforms.onClose = function () {
                    that.validateDragDrop();
                    that.validateRows();
                    that.variable = null;
                };
                target = dialogDynaforms;
                break;
        }
        that.onDrawDroppedItem(render, target);
        return properties;
    };
    Form.prototype.validateDragDrop = function () {
        this.tbody.find("td").each(function (i, ele) {
            if (ele.childNodes.length >= 2) {
                $(ele).removeClass("itemVariables");
                $(ele).removeClass("itemControls");
                $(ele).removeClass("cellDragDrop");
            } else {
                $(ele).addClass("itemVariables");
                $(ele).addClass("itemControls");
                $(ele).addClass("cellDragDrop");
            }
        });
    };
    Form.prototype.validateRows = function () {
        var that = this, row, sw, cell, length;
        if (that.stopValidateRows === true) {
            that.stopValidateRows = false;
            return;
        }
        length = this.tbody.find(">tr").length;
        for (var i = 1; i < length; i++) {
            sw = true;
            row = this.tbody.find(">tr").last();
            cell = row.find(">td");
            if (cell.length === 1) {
                cell.each(function (i, ele) {
                    if (ele.childNodes.length >= 2) {
                        sw = false;
                    }
                });
                if (sw) {
                    row.remove();
                }
            }
        }
        this.tbody.find(">tr").last().find(">td").each(function (i, ele) {
            if (ele.childNodes.length >= 2) {
                that.addRow().append(that.addCell());
                return false;
            }
        });
        if (this.tbody.find(">tr").length === 0) {
            that.addRow().append(that.addCell());
        }
    };
    /**
     * Init controls supported
     * @param val
     * @return {*}
     */
    Form.prototype.inTypesControl = function (val) {
        if ($.inArray(val, this.typesControlSupported) > -1) {
            return val;
        }
        return null;
    };
    Form.prototype.getData = function () {
        var data, fieldObject, rows, i, j, k, itemsrow, itemsTable, dataCell, flag, propertiesForm, property, cells,
            variables;
        data = {};
        itemsTable = [];
        variables = [];
        propertiesForm = this.properties.get();
        for (property in propertiesForm) {
            data[property] = propertiesForm[property].value;
        }
        rows = this.tbody[0].childNodes;
        for (i = 0; i < rows.length; i++) {
            itemsrow = [];
            flag = false;
            cells = rows[i].childNodes;
            for (j = 0; j < cells.length; j++) {
                dataCell = {};
                if (cells[j].children.length > 1) {
                    fieldObject = $(cells[j].children[1]).data("objectInstance");
                    if (fieldObject) {
                        dataCell = fieldObject.getData();
                        //get variable
                        if (fieldObject.variable) {
                            for (k = 0; k < variables.length; k++) {
                                if (variables[k] && variables[k].var_uid === fieldObject.variable.var_uid) {
                                    break;
                                }
                            }
                            if (k === variables.length && fieldObject.variable !== null) {
                                variables.push(fieldObject.variable);
                            }
                        }
                        //get variable in columns
                        if (dataCell.columns) {
                            var sb = fieldObject.getVariables();
                            for (var sbi = 0; sbi < sb.length; sbi++) {
                                for (k = 0; k < variables.length; k++) {
                                    if (sb[sbi] && variables[k] && variables[k].var_uid === sb[sbi].var_uid) {
                                        break;
                                    }
                                }
                                if (k === variables.length && sb[sbi] !== null) {
                                    variables.push(sb[sbi]);
                                }
                            }
                        }
                    }
                    flag = true;
                }
                dataCell["colSpan"] = cells[j].colSpan;
                itemsrow.push(dataCell);
            }
            if (flag) {
                itemsTable.push(itemsrow);
            }
        }
        data["items"] = itemsTable;
        data["variables"] = variables;
        return data;
    };
    Form.prototype.setData = function (dynaform) {
        if (dynaform === undefined) {
            return;
        }
        if (dynaform.dyn_content === undefined || dynaform.dyn_content === "" || dynaform.dyn_content === "{}") {
            dynaform.dyn_content = JSON.stringify({
                "name": dynaform.dyn_title,
                "description": dynaform.dyn_description,
                "items": []
            });
        }
        this.tbody.find(">tr").remove();
        var that = this, i, j, k, l, content, forms, form, rows, cells, row, properties, variables, propertiesColumns;

        content = JSON.parse(dynaform.dyn_content);
        that.properties.set("id", dynaform.dyn_uid);
        that.properties.set("name", dynaform.dyn_title);
        that.properties.set("description", dynaform.dyn_description);
        that.properties.set("mode", "edit");
        that.properties.set("script", "");
        that.properties.set("language", "en");
        that.properties.set("externalLibs", "");
        that.properties.set("gridStore", false);
        that.properties.set("variable", "");
        that.properties.set("dataType", "");
        that.properties.set("printable", false);
        that.properties.set("protectedValue", false);
        forms = content.items;
        for (i = 0; i < forms.length; i++) {
            form = forms[i];
            form = that.compatibilityType(form);
            that.properties.set("id", dynaform.dyn_uid);
            that.properties.set("name", dynaform.dyn_title);
            that.properties.set("description", dynaform.dyn_description);
            that.properties.set("mode", form.mode);
            that.properties.set("script", form.script);
            that.properties.set("language", form.language);
            that.properties.set("externalLibs", form.externalLibs);
            that.properties.set("gridStore", form.gridStore ? form.gridStore : false);//compatibility with older forms
            that.properties.set("variable", form.variable ? form.variable : "");//compatibility with older forms
            that.properties.set("dataType", form.dataType ? form.dataType : "");//compatibility with older forms
            that.properties.set("printable", form.printable ? form.printable : false);//compatibility with older forms
            that.properties.set("protectedValue", form.protectedValue ? form.protectedValue : false);//compatibility with older forms
            variables = form.variables;
            rows = form.items;
            for (j = 0; j < rows.length; j++) {
                cells = rows[j];
                if (cells.length > 0)
                    row = that.addRow();
                var cs = "";
                for (k = 0; k < cells.length; k++) {
                    cs = cs + cells[k].colSpan + " ";
                }
                cs = cs.trim();
                for (k = 0; k < cells.length; k++) {
                    //get variable
                    if (cells[k].var_uid) {
                        for (l = 0; l < variables.length; l++) {
                            if (variables[l] && cells[k].var_uid === variables[l].var_uid) {
                                that.variable = variables[l];
                                break;
                            }
                        }
                    }
                    that.targetNode = that.addCell();
                    that.targetNode[0].colSpan = cells[k].colSpan;
                    that.targetNode[0].style.width = (cells[k].colSpan * 100 / 12) + "%";//todo colspan
                    that.targetNode.removeClass("colspan-12");
                    that.targetNode.addClass("colspan-" + cells[k].colSpan);
                    that.targetNode.data("properties").set("colSpan", cs);
                    row.append(that.targetNode);
                    //load elements & properties
                    if (cells[k].type !== FormDesigner.main.TypesControl.form) {
                        properties = that.drawDroppedItem(cells[k].type);
                        if (properties) {
                            for (var pro in cells[k]) {
                                properties.set(pro, cells[k][pro]);
                                //load columns if element is grid
                                if (pro === "columns") {
                                    var grid = properties.ele.data("objectInstance");
                                    if (grid) {
                                        grid.targetNode = grid.body;
                                        var columns = properties[pro].value;
                                        for (var ic = 0; ic < columns.length; ic++) {
                                            //get variable for column
                                            if (columns[ic].var_uid) {
                                                for (l = 0; l < variables.length; l++) {
                                                    if (variables[l] && columns[ic].var_uid === variables[l].var_uid) {
                                                        grid.variable = variables[l];
                                                        break;
                                                    }
                                                }
                                            }
                                            propertiesColumns = grid.drawDroppedItem(columns[ic].type);
                                            if (propertiesColumns) {
                                                for (var proc in columns[ic]) {
                                                    propertiesColumns.set(proc, columns[ic][proc]);
                                                }
                                            }
                                            grid.variable = null;
                                        }
                                    }
                                }
                            }
                        }
                        that.variable = null;
                    }
                    //load form element
                    if (cells[k].type === FormDesigner.main.TypesControl.form) {
                        //get local dynaform
                        var subFormType = cells[k].type;
                        var subDynaform = {
                            dyn_content: JSON.stringify({
                                name: cells[k].name,
                                description: cells[k].description,
                                items: [
                                    cells[k]
                                ]
                            }),
                            dyn_description: cells[k].description,
                            dyn_title: cells[k].name,
                            dyn_type: "xmlform",
                            dyn_uid: cells[k].id,
                            dyn_version: 2
                        };
                        //get remote dynaform
                        var sw = false;
                        for (var i = 0; i < $.remoteDynaforms.length; i++) {
                            if ($.remoteDynaforms[i].dyn_uid === subDynaform.dyn_uid) {
                                subDynaform = $.remoteDynaforms[i];
                                sw = true;
                                //set subform data from owner form
                                var sd = JSON.parse(subDynaform.dyn_content);
                                sd.items[0].gridStore = cells[k].gridStore;
                                sd.items[0].variable = cells[k].variable;
                                sd.items[0].dataType = cells[k].dataType;
                                sd.items[0].mode = cells[k].mode;
                                sd.items[0].protectedValue = cells[k].protectedValue;
                                subDynaform.dyn_content = JSON.stringify(sd);
                                break;
                            }
                        }
                        if (sw === false && that.recovery === true) {
                            subDynaform.dyn_uid_old = subDynaform.dyn_uid;
                            var newName = subDynaform.dyn_title + " - " + dynaform.history_date;
                            for (var i = 0; i < $.remoteDynaforms.length; i++) {
                                if (newName === $.remoteDynaforms[i].dyn_title) {
                                    newName = newName + "-copy";
                                    i - 1;
                                }
                            }
                            subDynaform.dyn_title = newName;
                            $.recovered.date = dynaform.history_date;
                            $.recovered.data.push(subDynaform);
                        }
                        if (sw === false && that.recovery === false) {
                            subFormType = FormDesigner.main.TypesControl.msgPanel;
                            subDynaform = null;
                        }
                        that.stopValidateRows = true;
                        that.drawDroppedItem(subFormType, subDynaform);
                        that.variable = null;
                    }
                }
            }
        }
        that.validateDragDrop();
        that.validateRows();
        that.setLanguages();
    };
    Form.prototype.clear = function () {
        this.tbody.find(">tr").remove();
        this.addRow().append(this.addCell());
    };
    Form.prototype.getFieldObjects = function (filter) {
        var i, j, a, rows, cells, fieldObject;
        a = [];
        rows = this.tbody[0].childNodes;
        for (i = 0; i < rows.length; i++) {
            cells = rows[i].childNodes;
            for (j = 0; j < cells.length; j++) {
                if (cells[j].children.length > 1) {
                    fieldObject = $(cells[j].children[1]).data("objectInstance");
                    if (fieldObject && filter.indexOf(fieldObject.properties.type.value) > -1)
                        a.push(fieldObject);
                }
            }
        }
        return a;
    };
    Form.prototype.isDirty = function () {
        return this.dirty !== JSON.stringify(this.getData());
    };
    Form.prototype.setDirty = function () {
        this.dirty = JSON.stringify(this.getData());
    };
    Form.prototype.isVariableUsed = function (var_uid) {
        var that = this;
        var variable = that.getData();
        for (var i = 0; i < variable.variables.length; i++) {
            if (variable.variables[i] && variable.variables[i].var_uid === var_uid) {
                return true;
            }
        }
        return false;
    };
    Form.prototype.setLanguages = function () {
        var that = this;
        (new PMRestClient({
            endpoint: "dynaform/" + that.properties.id.value + "/list-language",
            typeRequest: "get",
            functionSuccess: function (xhr, response) {
                var i, a = that.properties.language.items[0];
                that.properties.language.items = [];
                that.properties.language.items.push(a)
                for (i = 0; i < response.length; i++) {
                    that.properties.language.items.push({
                        value: response[i]["Lang"],
                        label: response[i]["X-Poedit-Language"] + " (" + response[i]["Lang"] + ")"
                    });
                }
                if (that.properties.language.node !== undefined) {
                    $(that.properties.language.node).empty();
                    for (i = 0; i < that.properties.language.items.length; i++) {
                        $(that.properties.language.node).append("<option value='" + that.properties.language.items[i].value + "'>" + that.properties.language.items[i].label + "</option>");
                    }
                    $(that.properties.language.node).val("en");
                    $(that.properties.language.node).each(function (i, e) {
                        if (that.properties.language.value === e.value) {
                            $(that.properties.language.node).val(e.value);
                        }
                    });
                }
            }
        })).executeRestClient();
    };
    Form.prototype.compatibilityType = function (form) {
        var j, k;
        for (j = 0; j < form.items.length; j++) {
            for (k = 0; k < form.items[j].length; k++) {
                if (form.items[j][k].type === "form") {
                    form.items[j][k] = this.compatibilityType(form.items[j][k]);
                }
                if (form.items[j][k].type === "annotation") {
                    form.items[j][k].type = "label";
                }
            }
        }
        return form;
    };
    Form.prototype.setDisabled = function (disabled) {
        this.disabled = disabled;
        var obj;
        if (disabled) {
            this.tbody.sortable("disable");
            this.tbody.find("td").sortable("disable");
        } else {
            this.tbody.sortable("enable");
            this.tbody.find("td").sortable("enable");
        }
        this.tbody.find("td").each(function (i, cell) {
            cell.setDisabled(disabled);
            obj = $(cell.children[1]).data("objectInstance");
            if (obj && obj.setDisabled) {
                obj.setDisabled(disabled);
            }
        });
        this.tbody.find("td").css({
            "background-color": disabled ? "#F6F5F3" : "white"
        });
        this.tbody.find(".grid-item-field").css({
            "background-color": disabled ? "#F6F5F3" : "white"
        });
        this.properties.setDisabled(disabled);
    };
    Form.prototype.synchronizeVariables = function () {
        var that = this, calls = [];
        $.ajax({
            url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id + "/process-variables",
            method: "GET",
            contentType: "application/json",
            beforeSend: function (xhr) {
                xhr.setRequestHeader("Authorization", "Bearer " + PMDesigner.project.keys.access_token);
            },
            success: function (data) {
                var variables = that.getData().variables,
                    i,
                    j,
                    changeName;
                for (i = 0; i < variables.length; i++) {
                    variables[i].var_accepted_values = that._getVarAcceptedValues(variables[i]);
                    variables[i].var_uid_old = variables[i].var_uid;
                    variables[i].var_name_old = variables[i].var_name;
                    variables[i].prj_uid_old = variables[i].prj_uid;
                    changeName = true;
                    for (j = 0; j < data.length; j++) {
                        data[j].var_accepted_values = that._getVarAcceptedValues(data[j]);
                        if (changeName && that._compareVariable(variables[i], data[j])) {
                            variables[i].var_uid = data[j].var_uid;
                            variables[i].prj_uid = data[j].prj_uid;
                            variables[i].create = false;
                        } else {
                            if (variables[i].var_name === data[j].var_name) {
                                variables[i].var_name = variables[i].var_name + "_1";
                                j = -1;
                                changeName = false;
                            }
                        }
                    }
                }
                for (i = 0; i < variables.length; i++) {
                    if (variables[i].create === undefined) {
                        calls.push({
                            url: "process-variable", method: "POST", data: variables[i]
                        });
                    }
                }
                if (calls.length > 0) {
                    $.ajax({
                        url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id + "/",
                        data: JSON.stringify({calls: calls}),
                        method: "POST",
                        contentType: "application/json",
                        beforeSend: function (xhr) {
                            xhr.setRequestHeader('X-Requested-With', 'MULTIPART');
                            xhr.setRequestHeader("Authorization", "Bearer " + PMDesigner.project.keys.access_token);
                        },
                        success: function (responses) {
                            for (var i = 0; i < responses.length; i++) {
                                if (responses[i].status === 201) {
                                    for (var j = 0; j < variables.length; j++) {
                                        if (responses[i].response.var_name === variables[j].var_name) {
                                            variables[j].var_uid = responses[i].response.var_uid;
                                            variables[j].prj_uid = responses[i].response.prj_uid;
                                        }
                                    }
                                }
                            }
                            that.onSynchronizeVariables(variables);
                        },
                        error: function (responses) {
                            for (var i = 0; i < responses.length; i++) {
                                if (responses[i].status === 201) {
                                    for (var j = 0; j < variables.length; j++) {
                                        if (responses[i].response.var_name === variables[j].var_name) {
                                            variables[j].var_uid = responses[i].response.var_uid;
                                            variables[j].prj_uid = responses[i].response.prj_uid;
                                        }
                                    }
                                }
                            }
                            that.onSynchronizeVariables(variables);
                        }
                    });
                } else {
                    that.onSynchronizeVariables(variables);
                }
            }
        });
    };
    /**
     * Gets the AcceptedValues values of a variable
     * @param variable
     * @returns {*|Array}
     * @private
     */
    Form.prototype._getVarAcceptedValues = function (variable) {
        var acceptedValues = variable.var_accepted_values;

        try {
            //In previous versions it was a string (acceptedValues)
            if (typeof acceptedValues === 'string') {
                acceptedValues = JSON.parse(acceptedValues);
            }
        } catch (e) {
            throw new Error('Accepted Values is an empty string '.translate() + e.message);
        }
        return acceptedValues;
    };
    /**
     * Compare the variable if it exists
     * @param importedVariable
     * @param currentVariable
     * @returns {boolean}
     * @private
     */
    Form.prototype._compareVariable = function (importedVariable, currentVariable) {
        //Four properties of the variable are compared (name, fieldType, sql and acceptedValues)
        return (
            importedVariable.var_name === currentVariable.var_name &&
            importedVariable.var_field_type === currentVariable.var_field_type &&
            importedVariable.var_sql === currentVariable.var_sql &&
            JSON.stringify(importedVariable.var_accepted_values) === JSON.stringify(currentVariable.var_accepted_values)
        );
    };
    Form.prototype.setVariable = function (variable) {
        var that = this, b;
        that.properties.set("var_uid", variable.var_uid);
        b = that.properties.set("variable", variable.var_name);
        if (b.node)
            b.node.textContent = variable.var_name;
        b = that.properties.set("dataType", variable.var_field_type);
        if (b.node)
            b.node.textContent = variable.var_field_type;
    };
    Form.prototype.setNextLabel = function (properties) {
        var nextLabel,
            that = this;
        nextLabel = FormDesigner.getNextNumber(that.getData(), properties.type.value, "id") + 1;
        nextLabel = nextLabel.toString();
        while (nextLabel.length < 10) {
            nextLabel = "0" + nextLabel;
        }
        nextLabel = properties.type.value + nextLabel;
        properties.set("id", nextLabel.replace(/\s/g, ""));
        properties.set("name", nextLabel.replace(/\s/g, ""));

        nextLabel = FormDesigner.getNextNumber(that.getData(), properties.type.value, "label") + 1;
        nextLabel = properties.type.value + '_' + nextLabel;
        properties.set("label", nextLabel);
    };
    Form.prototype.setNextVar = function (properties) {
        var that = this,
            nextVar = "",
            dialogCreateVariable;
        nextVar = properties.type.value + "Var" + nextVar;
        dialogCreateVariable = new FormDesigner.main.DialogCreateVariable(null, properties.type.value, [], null);
        dialogCreateVariable.setVarName(nextVar);
        dialogCreateVariable.onSave = function (variable) {
            if (properties.owner.setVariable) {
                properties.owner.setVariable(variable);
            }
        };
        dialogCreateVariable.onSelect = function (variable) {
            if ($.countValue(that.getData(), "variable", variable.var_name) > 0) {
                $.isOpenValidator = true;
                var di = new FormDesigner.main.DialogInvalid(null, "variable", "duplicated");
                di.onClose = function () {
                    $.isOpenValidator = false;
                };
                di.onAccept = function () {
                    di.dialog.dialog("close");
                };
                return;
            }
            if (properties.owner.setVariable) {
                properties.owner.setVariable(variable);
            }
            dialogCreateVariable.dialog.dialog("close");
        };
        FormDesigner.getNextNumberVar(that.getData(), properties, function (nextVar) {
            dialogCreateVariable.setVarName(nextVar);
        });
    };
    /**
     * Creates the alert message to notify about deprecated controls being used in current form.
     * @private
     */
    Form.prototype.showDeprecationMessage = function () {
        var message = '<strong>' + 'Warning!'.translate() + '</strong>'
            + ('<span class="sr-only">Error:</span>' + ' '
            + 'This form contains deprecated controls marked with the'.translate() + ' '
            + '<span class="fa fa-exclamation-circle" style="color:red;" aria-hidden="true"></span> ' + 'icon.'.translate() + ' '
            + 'Those controls will no longer be supported and probably will not be available in future versions.'.translate() + ' '
            + 'Please refer to the following link to get more information:').translate()
            + '<br/><a href="' + FormDesigner.DEPRECATION_LINK + '" target="_blank">' + FormDesigner.DEPRECATION_LINK + '</a>';
        this.notice.show(message);
    };
    /**
     * Hide the alert message about deprecated controls.
     */
    Form.prototype.hideDeprecationMessage = function () {
        this.notice.close();
    };
    /**
     * Verify if we have deprecated controls in the form.
     * @return {boolean}
     */
    Form.prototype.checkForDeprecatedControls = function () {
        return this._items.asArray().length > 0;
    };
    /**
     * Clear list of deprecated control in the Form.
     */
    Form.prototype.clearItemsDeprecated = function () {
        var itemsForm = this._items.asArray(),
            i;
        for (i = 0; i < itemsForm.length; i+= 1) {
            if (this.parent) {
                this.parent._items.remove(itemsForm[i])
            }
        }
        this._items.clear();
    };
    FormDesigner.extendNamespace('FormDesigner.main.Form', Form);
}());

(function () {
    /**
     * Represents an item in the list of controls.
     *
     * @param {object} control
     * @returns {ListItemL#1.ListItem}
     * @constructor
     */
    var ListItem = function (control) {
        this.control = control;
        ListItem.prototype.init.call(this);
    };
    /**
     * Initialize the class.
     */
    ListItem.prototype.init = function () {
        this.deprecatedControlClassName = 'mafe-deprecated-control';
        this.body = $(
            "<div class='fd-list-responsive'>" +
            "<div style=''><img src='" + this.control.url + "'></img></div>" +
            "<div style=''>" + this.control.label + "</div>" +
            "<div class='" + this.deprecatedControlClassName + "'>" +
            "</div>");
        this.body.attr("render", this.control.render);
        this.body.draggable({
            appendTo: document.body,
            revert: "invalid",
            helper: "clone",
            cursor: "move",
            zIndex: 1000,
            connectToSortable: ".itemControls,.itemsVariablesControls"
        });
        this.deprecated(this.control.deprecated);
        this.createPopOver();
    };
    /**
     * Enable or disable deprecated icon.
     * @param {boolean} status
     */
    ListItem.prototype.deprecated = function (status) {
        var element = this.body.find("." + this.deprecatedControlClassName);
        if (status === true) {
            element.show();
        } else {
            element.hide();
        }
    };
    /**
     * Create FormDesigner.main.PMPopOver element.
     */
    ListItem.prototype.createPopOver = function () {
        var content = $('<div><div class="mafe-deprecated-title">' + 'Warning!'.translate() + '</div><p>'
            + this.control.deprecationMessage + ' For additional information:'.translate() + '</p><p><a href="'
            + FormDesigner.DEPRECATION_LINK + '" target="_blank">' + FormDesigner.DEPRECATION_LINK
            + '</a></p><button type="button" class="deprecated-ok-btn">' + 'Got it'.translate() + '</button></div>');
        this.pmPopOver = new FormDesigner.main.PMPopOver({
            body: content,
            targetElement: this.body.find("." + this.deprecatedControlClassName),
            placement: "right",
            class: "deprecated"
        });
        content.find('button').data('parentBody', this.pmPopOver);
        content.on('click', 'button', function () {
            $(this).data('parentBody').hide();
        });
    };
    /**
     * Get FormDesigner.main.PMPopOver instance.
     * @returns {FormDesigner.main.PMPopOver}
     */
    ListItem.prototype.getPopOver = function () {
        return this.pmPopOver;
    };
    FormDesigner.extendNamespace('FormDesigner.main.ListItem', ListItem);
}());

(function () {
    var ListControls = function () {
        ListControls.prototype.init.call(this);
    };
    /**
     * Initialize controls.
     */
    ListControls.prototype.init = function () {
        this.body = $("<div style='background:#262932;overflow:hidden;padding:4px;'></div>");
        this.controls = [
            {
                url: "" + $.imgUrl + "fd-text.png",
                label: "textbox".translate(),
                render: FormDesigner.main.TypesControl.text
            }, {
                url: "" + $.imgUrl + "fd-textarea.png",
                label: "textarea".translate(),
                render: FormDesigner.main.TypesControl.textarea
            }, {
                url: "" + $.imgUrl + "fd-dropdown.png",
                label: "dropdown".translate(),
                render: FormDesigner.main.TypesControl.dropdown
            }, {
                url: "" + $.imgUrl + "fd-checkbox.png",
                label: "checkbox".translate(),
                render: FormDesigner.main.TypesControl.checkbox
            }, {
                url: "" + $.imgUrl + "fd-checkgroup.png",
                label: "checkgroup".translate(),
                render: FormDesigner.main.TypesControl.checkgroup
            }, {
                url: "" + $.imgUrl + "fd-radio.png",
                label: "radio".translate(),
                render: FormDesigner.main.TypesControl.radio
            }, {
                url: "" + $.imgUrl + "fd-datetime.png",
                label: "datetime".translate(),
                render: FormDesigner.main.TypesControl.datetime
            }, {
                url: "" + $.imgUrl + "fd-suggest.png",
                label: "suggest".translate(),
                render: FormDesigner.main.TypesControl.suggest
            }, {
                url: "" + $.imgUrl + "fd-hidden.png",
                label: "hidden".translate(),
                render: FormDesigner.main.TypesControl.hidden
            }, {
                url: "" + $.imgUrl + "fd-h1.png",
                label: "title".translate(),
                render: FormDesigner.main.TypesControl.title
            }, {
                url: "" + $.imgUrl + "fd-h2.png",
                label: "subtitle".translate(),
                render: FormDesigner.main.TypesControl.subtitle
            }, {
                url: "" + $.imgUrl + "fd-label.png",
                label: "label".translate(),
                render: FormDesigner.main.TypesControl.annotation
            }, {
                url: "" + $.imgUrl + "fd-link.png",
                label: "link".translate(),
                render: FormDesigner.main.TypesControl.link
            }, {
                url: "" + $.imgUrl + "fd-image2.png",
                label: "image".translate(),
                render: FormDesigner.main.TypesControl.image
            }, {
                url: "" + $.imgUrl + "fd-file.png",
                label: "file".translate(),
                render: FormDesigner.main.TypesControl.file,
                deprecated: true,
                deprecationMessage: "This control is being deprecated. Please use FileUpload instead."
            }, {
                url: "" + $.imgUrl + "fd-file-upload.png",
                label: "fileupload".translate(),
                render: FormDesigner.main.TypesControl.multipleFile
            }, {
                url: "" + $.imgUrl + "fd-submit.png",
                label: "submit".translate(),
                render: FormDesigner.main.TypesControl.submit
            }, {
                url: "" + $.imgUrl + "fd-button.png",
                label: "button".translate(),
                render: FormDesigner.main.TypesControl.button
            }, {
                url: "" + $.imgUrl + "fd-grid.png",
                label: "grid".translate(),
                render: FormDesigner.main.TypesControl.grid
            }, {
                url: "" + $.imgUrl + "fd-panel32.png",
                label: "panel".translate(),
                render: FormDesigner.main.TypesControl.panel
            }, {
                url: "" + $.imgUrl + "fd-subform.png",
                label: "subform".translate(),
                render: FormDesigner.main.TypesControl.subform
            }
        ];
        this.load();
    };
    ListControls.prototype.load = function () {
        var i;
        for (i = 0; i < this.controls.length; i += 1) {
            this.controls[i].target = this.addItem(this.controls[i]);
        }
    };
    ListControls.prototype.addItem = function (control) {
        var item;
        item = new FormDesigner.main.ListItem(control);
        this.body.append(item.body);
        return item;
    };
    FormDesigner.extendNamespace('FormDesigner.main.ListControls', ListControls);
}());

(function () {
    var ListMobileControls = function () {
        ListMobileControls.prototype.init.call(this);
    };
    /**
     * Initialize mobile controls.
     */
    ListMobileControls.prototype.init = function () {
        this.body = $("<div style='background:#262932;overflow:hidden;padding:4px;'></div>");
        this.controls = [
            {
                url: "" + $.imgUrl + "fd-geomap-mobile.png",
                label: "geomap".translate(),
                render: FormDesigner.main.TypesControl.geomap
            }, {
                url: "" + $.imgUrl + "fd-qrcode-mobile.png",
                label: "qr code".translate(),
                render: FormDesigner.main.TypesControl.qrcode
            }, {
                url: "" + $.imgUrl + "fd-signature-mobile.png",
                label: "signature".translate(),
                render: FormDesigner.main.TypesControl.signature
            }, {
                url: "" + $.imgUrl + "fd-image2.png",
                label: "image".translate(),
                render: FormDesigner.main.TypesControl.imagem
            }, {
                url: "" + $.imgUrl + "fd-audio-mobile.png",
                label: "audio".translate(),
                render: FormDesigner.main.TypesControl.audiom
            }, {
                url: "" + $.imgUrl + "fd-video-mobile.png",
                label: "video".translate(),
                render: FormDesigner.main.TypesControl.videom
            }
        ];
        this.load();
    };
    /**
     * Load mobile controls.
     */
    ListMobileControls.prototype.load = function () {
        var i;
        for (i = 0; i < this.controls.length; i += 1) {
            this.controls[i].target = this.addItem(this.controls[i]);
        }
    };
    /**
     * Add mobile control.
     * @param control
     * @return {PMUI.item.ListItem|ListItemL#1.ListItem}
     */
    ListMobileControls.prototype.addItem = function (control) {
        var item;
        item = new FormDesigner.main.ListItem(control);
        this.body.append(item.body);
        return item;
    };
    FormDesigner.extendNamespace('FormDesigner.main.ListMobileControls', ListMobileControls);
}());

(function () {
    var ListProperties = function () {
        ListProperties.prototype.init.call(this);
    };
    ListProperties.prototype.init = function () {
        this.tbody = $("<tbody class='fd-tbody'></tbody>");
        this.table = $(
            "<table class='fd-table' style='width:100%;'>" +
            "<thead>" +
            "<th class='fd-table-th' style='min-width:30px;width:50px;max-width:100px;'><span>" + "Property".translate() + "</span></th>" +
            "<th class='fd-table-th' style='width:auto;'><span>" + "Value".translate() + "</span>" +
            "</th>" +
            "</thead>" +
            "</table>");
        this.table.append(this.tbody);
        this.body = $("<div></div>");
        this.body.append(this.table);
        this.cache = {};
    };
    ListProperties.prototype.addItem = function (propertiesGot, property, properties) {
        var input,
            i,
            minDate = '',
            maxDate = '',
            defaultDate = '',
            dialogMessage = '',
            width = "width:100%;border:1px solid gray;box-sizing:border-box;",
            id = FormDesigner.generateUniqueId(),
            that = this;
        switch (propertiesGot[property].type) {
            case "label":
                //improvement changes in value
                var a = propertiesGot[property].value;
                if (property === "type" && a === "cell") {
                    a = "row";
                }
                input = $("<div style='position:relative;width:100%;overflow:hidden;height:16px;'><span style='position:absolute;-webkit-user-select:text;-moz-user-select:text;user-select:text;'>" + a + "</span></div>");
                properties.setNode(property, input[0]);
                break;
            case "text":
                input = $("<input type='text' style='" + width + "' id='" + id + "'>").val(propertiesGot[property].value);
                input.on(propertiesGot[property].on ? propertiesGot[property].on : "change", function () {
                    properties.set(property, this.value);
                });
                input.attr("placeholder", propertiesGot[property].placeholder ? propertiesGot[property].placeholder : "");
                properties.setNode(property, input[0]);
                break;
            case "textarea":
                input = $("<textarea style='" + width + "resize: vertical;' id='" + id + "'></textarea>").val(propertiesGot[property].value);
                input.on("keyup", function () {
                    properties.set(property, this.value);
                });
                input.attr("placeholder", propertiesGot[property].placeholder ? propertiesGot[property].placeholder : "");
                properties.setNode(property, input[0]);
                break;
            case "select":
                input = $("<select style='" + width + "' id='" + id + "'></select>");
                for (i = 0; i < propertiesGot[property].items.length; i++)
                    input.append("<option value='" + propertiesGot[property].items[i].value + "'>" + propertiesGot[property].items[i].label + "</option>");
                input.val(propertiesGot[property].value);
                input.on("change", function () {
                    properties.set(property, this.value);
                });
                properties.setNode(property, input[0]);
                break;
            case "checkbox":
                input = $("<input type='checkbox' id='" + id + "'>");
                input.prop("checked", propertiesGot[property].value);
                input.on("change", function () {
                    properties.set(property, this.checked);
                });
                properties.setNode(property, input[0]);
                break;
            case "button":
                input = $("<input type='button' value='" + propertiesGot[property].labelButton + "' id='" + id + "'>");
                input.on("click", function () {
                    properties.onClick(property, this);
                });
                properties.setNode(property, input[0]);
                break;
            case "labelbutton":
                var a = propertiesGot[property].value;
                if (typeof a === "object")
                    a = JSON.stringify(a);
                if (a === "")
                    a = propertiesGot[property].labelButton;
                input = $("<div style='position:relative;width:100%;overflow:hidden;height:16px;'><span style='position:absolute;-webkit-user-select:text;-moz-user-select:text;user-select:text;text-decoration:underline;cursor:pointer;white-space:nowrap;'></span></div>");
                input.find("span").text(a).on("click", function (e) {
                    e.stopPropagation();
                    if ($(e.target).data("disabled") === true)
                        return;
                    properties.onClick(property, input.find("span")[0]);
                });
                properties.setNode(property, input.find("span")[0]);
                break;
            case "textbutton":
                input = $("<input type='text' style='" + width + "' id='" + id + "'><input type='button' value='" + propertiesGot[property].labelButton + "'>");
                $(input[0]).val(propertiesGot[property].value);
                $(input[0]).on("keyup", function () {
                    properties.set(property, this.value);
                });
                $(input[0]).attr("placeholder", propertiesGot[property].placeholder ? propertiesGot[property].placeholder : "");
                $(input[1]).on("click", function () {
                    properties.onClick(property, this);
                });
                properties.setNode(property, input[0]);
                break;
            case "textareabutton":
                input = $("<textarea style='" + width + "resize:vertical;height:50px;' id='" + id + "'></textarea><input type='button' value='" + propertiesGot[property].labelButton + "'>");
                $(input[0]).val(propertiesGot[property].value);
                $(input[0]).on("keyup", function () {
                    properties.set(property, this.value);
                });
                $(input[0]).attr("placeholder", propertiesGot[property].placeholder ? propertiesGot[property].placeholder : "");
                $(input[1]).on("click", function () {
                    properties.onClick(property, this);
                });
                properties.setNode(property, input[0]);
                break;
            case "datepicker":
                input = $("<input type='text' style='" + width + "' id='" + id + "'/>").val(propertiesGot[property].value);
                input.on(propertiesGot[property].on ? propertiesGot[property].on : "change", function () {
                    properties.set(property, this.value);
                });
                input.attr("placeholder", propertiesGot[property].placeholder ? propertiesGot[property].placeholder : "");
                properties.setNode(property, input[0]);
                break;
            case "textselect":
                input = $("<input type='text' style='" + width.replace("100%", "50%") + "' id='" + id + "'/><select style='" + width.replace("100%", "50%") + "'></select>");
                $(input[0]).val(propertiesGot[property].value.text);
                $(input[0]).on("keyup", function () {
                    properties.set(property, { text: this.value, select: $(input[1]).val() });
                });
                $(input[0]).attr("placeholder", propertiesGot[property].placeholder ? propertiesGot[property].placeholder : "");
                for (i = 0; i < propertiesGot[property].items.length; i++)
                    $(input[1]).append("<option value='" + propertiesGot[property].items[i].value + "'>" + propertiesGot[property].items[i].label + "</option>");
                $(input[1]).val(propertiesGot[property].value.select);
                $(input[1]).on("change", function () {
                    properties.set(property, { text: $(input[0]).val(), select: this.value });
                });
                properties.setNode(property, { text: $(input[0]).val(), select: $(input[1]).val() });
                break;
        }
        if (propertiesGot[property].type !== "hidden") {
            var cellName = $("<td class='fd-table-td'><label for=" + id + ">" + propertiesGot[property].label + "</label></td>");
            var cellValue = $("<td class='fd-table-td' style='position:relative;'></td>").append(input);
            var row = $("<tr id='" + property + "'></tr>");
            row.append(cellName);
            row.append(cellValue);
            this.tbody.append(row);

            if (propertiesGot[property].required === true) {
                cellName.append("<span style='color:red;float:right;'>*</span>");
            }
            var n = 0;
            if (propertiesGot[property].helpButton) {
                var button = $("<img src='" + $.imgUrl + "fd-help.png' style='cursor:pointer;position:absolute;top:0;right:" + n + ";'>");
                button[0].title = propertiesGot[property].helpButton.translate();
                button.tooltip({
                    tooltipClass: propertiesGot[property].helpButtonCss ? propertiesGot[property].helpButtonCss : null,
                    position: { my: "left+15 center", at: "right center" },
                    content: function () {
                        return $(this).prop('title');
                    },
                    close: function (event, ui) {
                        ui.tooltip.hover(function () {
                            $(this).stop(true).fadeTo(400, 1);
                        }, function () {
                            $(this).fadeOut("400", function () {
                                $(this).remove();
                            });
                        });
                    }
                });
                cellValue.append(button);
                n = n + 16;
                cellValue[0].style.paddingRight = n + "px";
            }
            if (propertiesGot[property].clearButton) {
                var button = $("<img src='" + $.imgUrl + "fd-remove.png' style='cursor:pointer;position:absolute;top:0;right:" + n + "px;' title='" + propertiesGot[property].clearButton + "'>");
                button.on("click", function (e) {
                    e.stopPropagation();
                    if ($(e.target).data("disabled") === true)
                        return;
                    properties.onClickClearButton(property);
                });
                cellValue.append(button);
                n = n + 16;
                cellValue[0].style.paddingRight = n + "px";
            }
            if (propertiesGot[property].type === "datepicker") {
                button = $("<img src='" + $.imgUrl + "fd-calendar.png' style='cursor:pointer;position:absolute;top:0;right:" + n + "px;' title='" + "datepicker".translate() + "'>");
                button.on("click", function (e) {
                    e.stopPropagation();
                    if ($(e.target).data("disabled") === true) {
                        return;
                    }
                    if ($(e.target).data("disabledTodayOption") === true) {
                        return;
                    }
                    // init jQyery datepicker
                    that.datepicker = that.dateComponentFactory(cellValue, {
                        minDate: minDate,
                        maxDate: maxDate,
                        onSelect: function (dateText, inst) {
                            properties.set(property, dateText, cellValue.find("input[type='text']")[0]);
                        },
                        onClose: function (dateText, inst) {
                            that.datepicker.datepicker("destroy");
                            $("#ui-datepicker-div").remove();
                        },
                        beforeShow: function () {
                            var params = null;
                            switch (property) {
                                case "maxDate":
                                    params = {
                                        minDate: that.getDateByParam(cellValue, "minDate")
                                    }
                                    break;
                                case "minDate":
                                    params = {
                                        maxDate: that.getDateByParam(cellValue, "maxDate")
                                    }
                                    break;
                                case "defaultDate":
                                    params = {
                                        maxDate: that.getDateByParam(cellValue, "maxDate"),
                                        minDate: that.getDateByParam(cellValue, "minDate")
                                    }
                                    break;
                            }
                            return params;
                        }
                    });
                    cellValue.find(".ui-datepicker-trigger").hide();
                });
                cellValue.append(button);
                n = n + 16;
                cellValue[0].style.paddingRight = n + "px";
                // init jQuery autocomplete
                this.autoCopleteFactory(cellValue, "datetime");
            }
            if (propertiesGot[property].type === "datepicker" && propertiesGot[property].todayOption) {
                cellValue.append("<span style='text-decoration:underline;'><input type='checkbox'/>" + propertiesGot[property].todayOption + "</span>");
                var sw = propertiesGot[property].value === "today";
                var checkboxToday = cellValue.find("input[type='checkbox']");
                checkboxToday.prop("checked", sw);
                checkboxToday.on("change", function () {
                    properties.set(property, this.checked ? "today" : "", cellValue.find("input[type='text']")[0]);
                });
            }
        }
        var disabled = propertiesGot[property].disabled === true;
        var scope = this.tbody.find("#" + property);
        scope.find("input").prop("disabled", disabled);
        scope.find("textarea").prop("disabled", disabled);
        scope.find("select").prop("disabled", disabled);
        scope.find("button").prop("disabled", disabled);
        scope.find("span,img").each(function (i, e) {
            $(e).data("disabled", disabled);
        });
        if (!propertiesGot[property].disabled && property === "requiredFieldErrorMessage") {
            scope.find("textarea").prop("disabled", !propertiesGot["required"].value);
        }
        if (!propertiesGot[property].disabled && propertiesGot.type.value === 'multipleFile' &&
            (property === 'extensions' || property === 'size' || property === 'sizeUnity')) {
            propertiesGot[property].node.disabled = propertiesGot['inputDocument'].value;
        }
        if (property === 'enableVersioning') {
            propertiesGot[property].node.textContent = (propertiesGot[property].value) ? 'Yes' : 'No';
        }
        if (cellValue && propertiesGot[property].disabledTodayOption !== undefined) {
            cellValue.find("input[type='text']").prop("disabled", propertiesGot[property].disabledTodayOption);
            scope.find("span,img").each(function (i, e) {
                $(e).data("disabledTodayOption", propertiesGot[property].disabledTodayOption);
            });
        }
        if (property === "defaultValue") {
            // init jQuery autocomplete
            var dataTypeMap = {
                text: "string",
                textarea: "string",
                dropdown: "string",
                checkbox: "boolean",
                checkgroup: "array",
                radio: "string",
                suggest: "string",
                hidden: "string"
            };
            this.autoCopleteFactory(cellValue, dataTypeMap[propertiesGot["type"].value] || "string");
        }
    };
    ListProperties.prototype.autoCopleteFactory = function (cellValue, type) {
        var that = this;
        // implement autocomple widget
        cellValue.find("input[type='text']").autocomplete({
            minLength: 2,
            source: function (request, response) {
                var term = request.term,
                    cachekey = type + "-" + request.term,
                    regExg = /[@][@#=]+[a-zA-Z]?/;

                // To validate prefix @@ @# @=
                if (!regExg.test(term)) {
                    return;
                }
                cellValue.find("input[type='text']").datepicker('hide');
                $("#ui-datepicker-div").remove();
                if (cachekey in that.cache) {
                    response(that.cache[cachekey]);
                    return;
                }
                // add a new rule to validate
                term = term.substring(1) !== "@" ? term[0] + encodeURIComponent(term.substring(1)) : term;
                $.ajax({
                    url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id + "/process-variables/" + type + "/paged/?search=" + term,
                    processData: false,
                    success: function (json) {
                        that.cache[cachekey] = json;
                        response(json);
                    },
                    error: function (XMLHttpRequest, textStatus, errorThrown) {
                        console.error(errorThrown);
                    },
                    beforeSend: function (xhr) {
                        xhr.setRequestHeader("Authorization", "Bearer " + PMDesigner.project.keys.access_token);
                    }
                });
            }
        });
    };
    ListProperties.prototype.load = function (properties) {
        var that = this, property;
        var propertiesGot = properties.get();
        for (property in propertiesGot) {
            that.addItem(propertiesGot, property, properties);
        }
    };
    ListProperties.prototype.clear = function () {
        this.tbody.find("tr").remove();
    };
    /**
     * Creates the date picker component.
     * @param control the form control to set de datepicker.
     * @param options dynamic params to set the properties.
     * @returns {*}
     */
    ListProperties.prototype.dateComponentFactory = function (control, options) {
        var defaults = {
            showOtherMonths: true,
            selectOtherMonths: true,
            dateFormat: "yy-mm-dd",
            changeMonth: true,
            changeYear: true,
            yearRange: "-100:+100",
            minDate: '',
            maxDate: '',
            onSelect: function () { },
            onClose: function () { },
            beforeShow: function () { }
        },
            datePicker;
        $.extend(true, defaults, options);
        // append the date picker to the control component
        datePicker = control
            .find("input[type='text']")
            .datepicker({
                buttonImage: $.imgUrl + "fd-calendar.png",
                buttonImageOnly: true,
                constrainInput: false,
                buttonText: "Select date",
                showOn: "button",
                showOtherMonths: defaults.showOtherMonths,
                selectOtherMonths: defaults.selectOtherMonths,
                dateFormat: defaults.dateFormat,
                changeMonth: defaults.changeMonth,
                changeYear: defaults.changeYear,
                yearRange: defaults.yearRange,
                minDate: defaults.minDate,
                maxDate: defaults.maxDate,
                onSelect: defaults.onSelect,
                onClose: defaults.onClose,
                beforeShow: defaults.beforeShow
            }).next(".ui-datepicker-trigger").addClass("datetime-gadget-class");
        datePicker.datepicker("show");
        return datePicker;
    };
    /**
     * Gets the date according to the passed param
     */
    ListProperties.prototype.getDateByParam = function (control, param) {
        var varRegex = /\@(?:([\@\%\#\?\$\=\&Qq\!])([a-zA-Z\_]\w*)|([a-zA-Z\_][\w\-\>\:]*)\(((?:[^\\\\\)]*(?:[\\\\][\w\W])?)*)\))((?:\s*\[['"]?\w+['"]?\])+|\-\>([a-zA-Z\_]\w*))?/,
            value = '';
        if (control && param) {
            value = $(control.parent().parent()[0].rows[param])
                .find("input")
                .val();
            if (!varRegex.test(value)) {
                return value;
            } else {
                return '';
            }
        } else {
            return '';
        }
    };
    FormDesigner.extendNamespace('FormDesigner.main.ListProperties', ListProperties);
}());

(function () {
    var TabsForm = function () {
        TabsForm.prototype.init.call(this);
    };
    TabsForm.prototype.init = function () {
        this.count = -1;
        this.body = $("<div style='border:none;padding:0px;'></div>");
        this.head = $("<ul style='border:none;background:white;padding:0px;border-bottom: 1px solid #DADADA;border-radius:0px;'></ul>");
        this.body.append(this.head);
        this.body.tabs({
            heightStyle: "content",
            active: 0
        });
    };
    TabsForm.prototype.addItem = function (name, gridForm) {
        this.count = this.count + 1;
        var head = $("<li style='background:white;border-left:1px solid #DADADA;border-top:1px solid #DADADA;border-right:1px solid #DADADA;'><a href='#tabs-" + this.count + "'>" + name + "</a></li>");
        var body = $("<div id='tabs-" + this.count + "' style='padding:0px;'></div>");
        body.append(gridForm.body);
        this.head.append(head);
        this.body.append(body);
        this.body.tabs("destroy");
        this.body.tabs({
            heightStyle: "content",
            active: this.count
        });

        if (this.body.find(".ui-tabs-panel").length === 1)
            this.head.hide();
        else
            this.head.show();

        return {head: head, body: body};
    };
    FormDesigner.extendNamespace('FormDesigner.main.TabsForm', TabsForm);
}());
(function () {
    var TypesControl = {
        title: "title",
        subtitle: "subtitle",
        label: "label", //deprecated
        link: "link",
        image: "image",
        file: "file",
        multipleFile: "multipleFile",
        submit: "submit",
        button: "button",
        grid: "grid",
        subform: "subform",
        variable: "variable", //only variable
        text: "text",
        textarea: "textarea",
        dropdown: "dropdown",
        checkbox: "checkbox",
        checkgroup: "checkgroup",
        radio: "radio",
        datetime: "datetime",
        suggest: "suggest",
        hidden: "hidden",
        form: "form",
        cell: "cell",
        annotation: "label", //todo
        geomap: "location",
        qrcode: "scannerCode",
        signature: "signature",
        imagem: "imageMobile",
        audiom: "audioMobile",
        videom: "videoMobile",
        panel: "panel",
        msgPanel: "msgPanel"
    };
    FormDesigner.extendNamespace('FormDesigner.main.TypesControl', TypesControl);
}());

(function () {
    var DialogStyle = function (dialog, type) {
        var bo = dialog[0].parentNode;
        bo.style.border = '1px solid rgba(0, 0, 0, 0.4)';
        bo.style.borderRadius = '0px';
        bo.style.padding = '0px';

        var b = bo.previousSibling.style;
        b.background = 'transparent';
        b.backgroundColor = 'rgba(0, 0, 0, 0.4)';
        b.opacity = '1';

        var titleBar = bo.firstChild;
        titleBar.style.border = 'none';
        titleBar.style.borderRadius = '0px';
        titleBar.style.background = '#3397e1';
        titleBar.style.color = 'white';
        titleBar.style.height = '25px';
        titleBar.style.fontSize = '16px';
        titleBar.style.fontFamily = 'SourceSansPro';
        titleBar.style.fontWeight = 'normal';

        titleBar.firstChild.style.marginTop = '5px';
        titleBar.firstChild.style.textAlign = 'center';

        var button = titleBar.firstChild.nextSibling;
        button.style.background = '#3397e1';
        button.style.border = 'none';
        button.style.marginRight = '4px';
        button.style.width = "18px";
        button.style.height = "18px";
        button.title = "close".translate();

        var icon = button.firstChild;
        icon.style.backgroundImage = 'url(' + $.imgUrl + 'fd-close.png)';
        icon.style.margin = "0px";
        icon.style.backgroundPosition = "0px 0px";
        icon.style.left = "-3px";
        icon.style.top = "-3px";
        icon.style.width = "24px";
        icon.style.height = "24px";
        icon.parentNode.onmouseover = function () {
            icon.style.backgroundImage = 'url(' + $.imgUrl + 'fd-closew.png)';
        };
        icon.parentNode.onmouseout = function () {
            icon.style.backgroundImage = 'url(' + $.imgUrl + 'fd-close.png)';
        };
        icon.nextSibling.style.padding = "0px";

        dialog.removeClass("ui-state-focus");
        dialog.removeClass("ui-state-hover");

        if (type === "alert") {
            //red
            titleBar.style.background = "#e84c3d";
            button.style.background = "#e84c3d";
        }
        if (type === "warning") {
            //yellow
            titleBar.style.background = "#edb60b";
            button.style.background = "#edb60b";
        }
        if (type === "success") {
            //green  
        }
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogStyle', DialogStyle);
}());
(function () {
    var Grid = function (parent) {
        this.onSelect = new Function();
        this.onRemove = new Function();
        this.onRemoveItem = new Function();
        this.onSetProperty = new Function();
        this.onVariableDrawDroppedItem = new Function();
        this.onDrawControl = new Function();
        this.onDrawDroppedItem = new Function();
        this.parent = parent;
        this.properties = null;
        this.variable = parent.variable;
        this.disabled = false;
        this.typesControlSupported = [
            FormDesigner.main.TypesControl.link,
            FormDesigner.main.TypesControl.file,
            FormDesigner.main.TypesControl.multipleFile,
            FormDesigner.main.TypesControl.text,
            FormDesigner.main.TypesControl.textarea,
            FormDesigner.main.TypesControl.dropdown,
            FormDesigner.main.TypesControl.checkbox,
            FormDesigner.main.TypesControl.datetime,
            FormDesigner.main.TypesControl.suggest,
            FormDesigner.main.TypesControl.hidden
        ];
        Grid.prototype.init.call(this);
        this._items = new PMUI.util.ArrayList();
    };
    Grid.prototype.init = function () {
        var that = this;
        this.body = $("" +
            "<div class='itemVariables itemControls gridCellDragDrop fd-gridForm-grid' style='position:relative;font-size:11px;'>" +
            "<div class='fd-gridForm-grid-griditem-gridtitle'></div>" +
            "<div class='fd-gridForm-grid-griditem-gridplaceholder'>" + "Grid: drag & drop controls.<br>Supports: textbox, textarea, dropdown, checkbox, datetime, suggest, hidden, link, multiplefile.".translate() + "</div>" +
            "</div>");
        this.body.sortable({
            placeholder: "fd-gridForm-grid-placeholder",
            connectWith: ".gridCellDragDrop",
            items: ">*:not(.fd-gridForm-grid-griditem-gridtitle):not(.fd-gridForm-grid-griditem-gridplaceholder)",
            receive: function (event, ui) {
                that.sourceNode = ui.sender;
                that.targetNode = $(this);
            },
            stop: function (event, ui) {
                if (ui.item.attr("variable"))
                    that.variable = JSON.parse(ui.item.attr("variable"));
                that.targetNode = that.targetNode ? $(ui.item[0].parentNode) : that.targetNode;
                if (ui.item.attr("render")) {
                    $(ui.item).remove();
                    var properties = that.drawDroppedItem(ui.item.attr("render"));
                    that.onDrawControl(properties);
                }
            }
        });
        this.body.on("click", function (e) {
            e.stopPropagation();
            $.designerSelectElement(this, function () {
                if (that.disabled === true) {
                    return false;
                }
                that.onRemove();
            });
            that.onSelect(that.properties);
        });
        this.body.data("objectInstance", this);
        this.properties = new FormDesigner.main.Properties(FormDesigner.main.TypesControl.grid, this.body, that);
        this.properties.onSet = function (prop, value) {
            that.onSetProperty(prop, value, that);
            if (prop === "label") {
                that.body.find(".fd-gridForm-grid-griditem-gridtitle").text(value);
            }
        };
        this.properties.onClick = function (property) {
            var dialogCreateVariable;
            if (property === "variable") {
                dialogCreateVariable = new FormDesigner.main.DialogCreateVariable(null, FormDesigner.main.TypesControl.grid, [], that.properties.get()[property].value);
                dialogCreateVariable.onSave = function (variable) {
                    that.setVariable(variable);
                };
                dialogCreateVariable.onSelect = function (variable) {
                    dialogCreateVariable.dialog.dialog("close");
                    that.setVariable(variable);
                };
                FormDesigner.getNextNumberVar(that.getData(), that.properties, function (nextVar) {
                    dialogCreateVariable.setVarName(nextVar);
                });
            }
        };
        this.properties.onClickClearButton = function (property) {
            var b;
            if (property === "variable" && that.properties[property].value !== "") {
                var a = new FormDesigner.main.DialogConfirmClearVariable();
                a.onAccept = function () {
                    var label = that.properties.label.value;
                    that.parent.setNextLabel(that.properties);

                    that.properties.id.node.value = that.properties.id.value;
                    b = that.properties.set("variable", "");
                    b.node.textContent = "...";
                    b = that.properties.set("dataType", "");
                    b.node.textContent = "";
                    b = that.properties.set("label", label);
                    b.node.value = label;
                    b = that.properties.set("protectedValue", false);
                    b.node.checked = false;
                };
            }
        };
    };
    Grid.prototype.drawDroppedItem = function (render) {
        var that = this,
            properties = null,
            target = null;
        switch (render) {
            case FormDesigner.main.TypesControl.variable:
                if (that.onVariableDrawDroppedItem(that.variable) === false)
                    return;
                if (that.isVariableUsed(that.variable.var_uid)) {
                    var dialogInformation = new FormDesigner.main.DialogInformation();
                    dialogInformation.onAccept = function () {
                    };
                    return;
                }
                var dialogTypeControl = new FormDesigner.main.DialogTypeControl();
                dialogTypeControl.load(that.variable);
                dialogTypeControl.onSelectItem = function (event, item) {
                    that.drawDroppedItem(item.attr("render"));
                    that.variable = null;
                };
                dialogTypeControl.onClose = function () {
                    that.variable = null;
                };
                target = dialogTypeControl;
                break;
            case that.inTypesControl(render):
                var gridItem = new FormDesigner.main.GridItem(render, that.variable, that);
                gridItem.onSelect = function (properties) {
                    that.onSelect(properties);
                };
                gridItem.onRemove = function () {
                    that.onRemoveItem(this);
                };
                gridItem.onSetProperty = function (prop, value, target) {
                    that.onSetProperty(prop, value, target);
                };
                that.targetNode.append(gridItem.html);
                properties = gridItem.properties;
                target = gridItem;
                break;
        }
        that.onDrawDroppedItem(render, target);
        return properties;
    };
    Grid.prototype.inTypesControl = function (val) {
        if ($.inArray(val, this.typesControlSupported) > -1) {
            return val;
        }
        new FormDesigner.main.DialogUnsupported();//todo
        return null;
    };
    Grid.prototype.getData = function () {
        var columns = [], obj;
        this.body.find(">div").each(function (i, ele) {
            obj = $(ele).data("objectInstance");
            if (obj) {
                columns.push(obj.getData());
            }
        });
        var prop = {};
        var a = this.properties.get();
        for (var b in a) {
            prop[b] = a[b].value;
        }
        prop["columns"] = columns;
        prop["title"] = prop.label;
        return prop;
    };
    Grid.prototype.getVariables = function () {
        var obj, variables = [], variable, i;
        this.body.find(">div").each(function (ie, ele) {
            obj = $(ele).data("objectInstance");
            if (obj) {
                variable = $(ele).data("objectInstance").variable;
                for (i = 0; i < variables.length; i++) {
                    if (variable && variables[i] && variables[i].var_uid === variable.var_uid) {
                        break;
                    }
                }
                if (i === variables.length) {
                    variables.push(variable);
                }
            }
        });
        return variables;
    };
    Grid.prototype.isVariableUsed = function (var_uid) {
        var that = this;
        var variables = that.getVariables();
        for (var i = 0; i < variables.length; i++) {
            if (variables[i] && variables[i].var_uid === var_uid) {
                return true;
            }
        }
        return false;
    };
    Grid.prototype.getFieldObjects = function (filter) {
        var a = [], obj;
        this.body.find(">div").each(function (i, ele) {
            obj = $(ele).data("objectInstance");
            if (obj && filter.indexOf(obj.properties.type.value) > -1)
                a.push(obj);
        });
        return a;
    };
    Grid.prototype.setDisabled = function (disabled) {
        this.disabled = disabled;
        var obj;
        if (disabled) {
            this.body.sortable("disable");
        } else {
            this.body.sortable("enable");
        }
        this.body.find(">div").each(function (i, ele) {
            obj = $(ele).data("objectInstance");
            if (obj && obj.setDisabled) {
                obj.setDisabled(disabled);
            }
        });
        this.properties.setDisabled(disabled);
    };
    Grid.prototype.setVariable = function (variable) {
        var that = this, b;
        that.variable = variable;
        that.properties.set("var_uid", variable.var_uid);
        b = that.properties.set("variable", variable.var_name);
        if (b.node)
            b.node.textContent = variable.var_name;
        b = that.properties.set("dataType", variable.var_field_type);
        if (b.node)
            b.node.textContent = variable.var_field_type;
        b = that.properties.set("id", variable.var_name);
        if (b.node)
            b.node.value = variable.var_name;
        that.properties.set("name", variable.var_name);
    };
    /**
     * Verify if in the grid exist a deprecated control.
     * @return {number}
     */
    Grid.prototype.checkForDeprecatedControls = function () {
        return this._items.asArray().length;
    };
    /**
     * Clear list of deprecated control in the grid.
     */
    Grid.prototype.clearItemsDeprecated = function () {
        var itemsGrid = this._items.asArray(),
            i;
        for (i = 0; i < itemsGrid.length; i+= 1) {
            this.parent._items.remove(itemsGrid[i])
        }
        this._items.clear();
    };
    FormDesigner.extendNamespace('FormDesigner.main.Grid', Grid);
}());

(function () {
    var GridItem = function (render, variable, parent) {
        this.onSelect = new Function();
        this.onRemove = new Function();
        this.onSetProperty = new Function();
        this.render = render;
        this.variable = variable;
        this.parent = parent;
        this.disabled = false;
        GridItem.prototype.init.call(this);
    };
    GridItem.prototype.init = function () {
        var that = this,
            html;

        switch (this.render) {
            case FormDesigner.main.TypesControl.text:
                html = "<input type='text' value='' class='fd-gridForm-grid-text-column'>";
                break;
            case FormDesigner.main.TypesControl.textarea:
                html = "<textarea class='fd-gridForm-grid-textarea-column' style='resize:none;'></textarea>";
                break;
            case FormDesigner.main.TypesControl.dropdown:
                html = "<select class='fd-gridForm-grid-text-column' style='box-sizing:border-box;width:100%;'></select>";
                break;
            case FormDesigner.main.TypesControl.checkbox:
                html = "<input type='checkbox'>";
                break;
            case FormDesigner.main.TypesControl.checkgroup:
                html = "<input type='checkbox'>";
                break;
            case FormDesigner.main.TypesControl.radio:
                html = "<input type='radio'>";
                break;
            case FormDesigner.main.TypesControl.datetime:
                html = "<div style='height:18px;border:1px solid #c0c0c0;box-sizing:border-box;width:100%;background-color:white;'><span class='fd-gridForm-grid-suggest-placeholder'></span><img src='" + $.imgUrl + "fd-calendar.png' style='float:right;'/></div>";
                break;
            case FormDesigner.main.TypesControl.suggest:
                html = "<div style='height:18px;border:1px solid #c0c0c0;box-sizing:border-box;width:100%;background-color:white;'><span class='fd-gridForm-grid-suggest-placeholder'></span><img src='" + $.imgUrl + "fd-ui-list-box.png' style='float:right;'/></div>";
                break;
            case FormDesigner.main.TypesControl.hidden:
                html = "<div style='height:18px;border:1px dashed #c0c0c0;box-sizing:border-box;width:100%;background-color:white;'><img src='" + $.imgUrl + "fd-ui-text-field-hidden.png' style='float:right;'/></div>";
                break;
            case FormDesigner.main.TypesControl.title:
                html = "<span>...</span>";
                break;
            case FormDesigner.main.TypesControl.subtitle:
                html = "<span>...</span>";
                break;
            case FormDesigner.main.TypesControl.annotation:
                html = "<span>...</span>";
                break;
            case FormDesigner.main.TypesControl.link:
                html = "<span class='fd-gridForm-grid-link-column'></span>";
                break;
            case FormDesigner.main.TypesControl.image:
                html = "<img src='" + $.imgUrl + "fd-image.png'/>";
                break;
            case FormDesigner.main.TypesControl.file:
                html = "<button>Select file</button><span>No file was selected</span>";
            case FormDesigner.main.TypesControl.multipleFile:
                html = "<button>Select file</button><span>No file was selected</span>";
                break;
            case FormDesigner.main.TypesControl.submit:
                html = "<input type='button' value='button'>";
                break;
            case FormDesigner.main.TypesControl.button:
                html = "<input type='button' value='button'>";
                break;
        }
        this.html = $(
            "<div class=\"pm-mafe-grid-item\" pm-mafe-grid-item-type=\"" + this.render + "\" style='position:relative;background:white;'>" +
            "<div style='font-weight:bold;margin-bottom:2px;text-align:center;background:#2d3e50;color:white;padding:5px;height:12px;'>" +
            "   <span class='fd-gridForm-grid-griditem-columnLabel'></span><span class='fd-gridForm-grid-griditem-columnRequired'>*</span>" +
            "</div>" +
            "<div class='fd-gridForm-grid-griditem-columnType' style='padding:2px;background:#eaebed;height:31px;'></div>" +
            "</div>");
        this.html.find(".fd-gridForm-grid-griditem-columnType").append(html);
        this.html.data("objectInstance", this);
        this.html.on("click", function (e) {
            e.stopPropagation();
            $.designerSelectElement(this, function () {
                if (that.disabled === true) {
                    return false;
                }
                that.onRemove();
            });
            that.onSelect(that.properties, that);
        });

        this.properties = new FormDesigner.main.Properties(this.render, this.html, that);
        this.properties.onClick = function (property) {
            var a, b, fields, dialog;
            if (property === "formula") {
                fields = that.parent.getFieldObjects([
                    FormDesigner.main.TypesControl.text
                ]);
                a = new FormDesigner.main.DialogFormula(null, fields);
                a.onSave = function () {
                    that.properties.set(property, a.editor.getValue());
                };
                a.editor.setValue(that.properties.get()[property].value);
            }
            if (property === "dbConnectionLabel") {
                a = new FormDesigner.main.DialogDBConnection(null);
                a.onClick = function (option) {
                    a.dialog.dialog("close").remove();
                    b = that.properties.set(property, option.label);
                    b.node.textContent = option.label;

                    that.properties.set("dbConnection", option.value);
                };
            }
            if (property === "inputDocument") {
                dialog = new FormDesigner.main.DialogInputDocument(null);
                dialog.onClick = function (option) {
                    dialog.dialog.dialog("close").remove();
                    that.setInputDocument({
                        size: {
                            value: option.inp_doc_max_filesize,
                            disabled: true
                        },
                        sizeUnity: {
                            value: option.inp_doc_max_filesize_unit,
                            disabled: true
                        },
                        extensions: {
                            value: option.inp_doc_type_file,
                            disabled: true
                        },
                        enableVersioning: {
                            value: (option.inp_doc_versioning) ? true : false,
                            disabled: true
                        },
                        inp_doc_uid: {
                            value: option.inp_doc_uid,
                            disabled: true
                        },
                        inputDocument: {
                            value: option.inp_doc_title,
                            disabled: false
                        }
                    });
                };
                return false;
            }
            if (property === "sql") {
                a = new FormDesigner.main.DialogSql(null);
                a.onSave = function () {
                    b = that.properties.set(property, a.editor.getValue());
                    b.node.textContent = a.editor.getValue();
                };
                a.editor.setValue(that.properties.get()[property].value);
            }
            if (property === "options") {
                a = new FormDesigner.main.DialogOptions(null, that.properties.get()[property].value);
                a.onApply = function () {
                    b = that.properties.set(property, a.getOptions());
                    b.node.textContent = JSON.stringify(a.getOptions());
                };
                if (that.properties.dataType.value === "boolean") {
                    a.setOptionsBoolean();
                }
            }
            if (property === "validate") {
                a = new FormDesigner.main.DialogRegularExpression(null, that.properties["dataType"].value);
                a.onClick = function (regex) {
                    a.dialog.dialog("close").remove();
                    b = that.properties.set("validate", regex.value);
                    b.node.value = regex.value;
                };
            }
            if (property === "dataVariable") {
                a = new FormDesigner.main.DialogVariable(null, FormDesigner.main.TypesControl.checkgroup);
                a.onClick = function (variable) {
                    a.dialog.dialog("close").remove();
                    b = that.properties.set("dataVariable", "@@" + variable.var_name);
                    if (b.node)
                        b.node.value = variable.var_name;
                };
                a.load();
            }
        };
        this.properties.onSet = function (prop, value) {
            that.onSetProperty(prop, value, that);
            if (prop === "label") {
                that.html.find(".fd-gridForm-grid-griditem-columnLabel").text(value);
            }
            if (prop === "value") {
                that.html.find(".fd-gridForm-grid-link-column").text(value);
            }
            if (prop === "required") {
                if (value)
                    that.html.find(".fd-gridForm-grid-griditem-columnRequired").show();
                else
                    that.html.find(".fd-gridForm-grid-griditem-columnRequired").hide();
            }
            if (prop === "options") {
                that.html.find(".fd-gridForm-grid-text-column").empty();
                for (var i = 0; i < value.length; i++) {
                    that.html.find(".fd-gridForm-grid-text-column").append("<option value='" + value[i].value + "'>" + value[i].label + "</option>");
                }
            }
            if (prop === "placeholder") {
                that.html.find(".fd-gridForm-grid-text-column").attr("placeholder", value);
                that.html.find(".fd-gridForm-grid-textarea-column").attr("placeholder", value);
                that.html.find(".fd-gridForm-grid-suggest-placeholder").text(value);
            }
        };
        this.properties.onGet = function (properties) {
            if (that.parent.properties.get().layout.value === "form") {
                that.properties.columnWidth.type = "hidden";
                that.properties.columnWidth.helpButton = "";
            }
            if (that.parent.properties.get().layout.value === "responsive") {
                that.properties.columnWidth.type = "text";
                that.properties.columnWidth.helpButton = "Percentage value.";
            }
            if (that.parent.properties.get().layout.value === "static") {
                that.properties.columnWidth.type = "text";
                that.properties.columnWidth.helpButton = "Pixel value.";
            }
        };
        // This section reset properties in clearbutton in grids
        this.properties.onClickClearButton = function (property) {
            if (property === "inputDocument") {
                that.setInputDocument({
                    size: {
                        value: 0,
                        disabled: false
                    },
                    sizeUnity: {
                        value: 'MB',
                        disabled: false
                    },
                    extensions: {
                        value: '*',
                        disabled: false
                    },
                    enableVersioning: {
                        value: false,
                        disabled: true
                    },
                    inp_doc_uid: {
                        value: '',
                        disabled: true
                    },
                    inputDocument: {
                        value: '...',
                        disabled: false
                    }
                });
            }
        };
        this.createDeprecatedIcon();
    };
    GridItem.prototype.getData = function () {
        var prop = {};
        var a = this.properties.get();
        for (var b in a) {
            prop[b] = a[b].value;
        }
        prop["width"] = 100;//todo
        prop["title"] = prop.label;
        if (this.variable) {
            prop["var_name"] = this.variable.var_name;
        }
        return prop;
    };
    GridItem.prototype.setDisabled = function (disabled) {
        this.disabled = disabled;
        this.properties.setDisabled(disabled);
    };
    /**
     * Set properties for input document
     * @param params
     */
    GridItem.prototype.setInputDocument = function (params) {
        var property,
            key;
        for (key in params) {
            property = this.properties.set(key, params[key].value);
            switch (key) {
                case 'inp_doc_uid':
                    break;
                case 'inputDocument':
                    if (property.node) {
                        property.node.textContent = params[key].value;
                        property.value = (params[key].value === '...') ? '' : params[key].value;
                    }
                    break;
                case 'enableVersioning':
                    if (property.node) {
                        property.node.textContent = (params[key].value) ? 'Yes' : 'No';
                    }
                default:
                    if (property.node) {
                        property.node.value = params[key].value;
                        property.node.disabled = params[key].disabled;
                    }
                    break;
            }
        }
    };
    /**
     * Create deprecation icon.
     */
    GridItem.prototype.createDeprecatedIcon = function () {
        this.deprecatedIcon = $("<div class='mafe-deprecated-control'></div>");
        this.deprecatedIcon.attr('title', "");
        this.deprecatedIcon.hide();
        this.deprecatedIcon.tooltip({
            content: FormDesigner.DEPRECATION_TEXT,
            close: function (event, ui) {
                ui.tooltip.hover(function () {
                    $(this).stop(true).fadeTo(400, 1);
                }, function () {
                    $(this).fadeOut("400", function () {
                        $(this).remove();
                    });
                });
            }
        });
        this.html.find('.fd-gridForm-grid-griditem-columnLabel').parent().prepend(this.deprecatedIcon);
    };
    /**
     * Enable or disable deprecated icon.
     * @param {boolean} status
     */
    GridItem.prototype.deprecated = function (status) {
        if (status === true) {
            this.deprecatedIcon.show();
        } else {
            this.deprecatedIcon.hide();
        }
    };
    FormDesigner.extendNamespace('FormDesigner.main.GridItem', GridItem);
}());

(function () {
    var Preview = function (dyn_uid, dyn_title, prj_uid, iframeOptions) {
        this.dyn_uid = dyn_uid;
        this.dyn_title = dyn_title;
        this.prj_uid = prj_uid;
        this.onClose = new Function();
        Preview.prototype.init.call(this, iframeOptions);
    };
    Preview.prototype.init = function (iframeOptions) {
        var that = this,
            defaultIframeOptions = {
                id: 'pm-dynaform-preview-iframe',
                name: 'pm-dynaform-preview-iframe'
            };

        jQuery.extend(true, defaultIframeOptions, iframeOptions);

        this.srcPreview = 'cases/pmDynaform?dyn_uid=' + this.dyn_uid + "&prj_uid=" + this.prj_uid;
        this.desktop = $("<div class='fd-designer-button' title='" + "Desktop".translate() + "'><img src='" + $.imgUrl + "fd-desktop.png' style='width:24px;height:24px;'/></div>");
        this.desktop.on("click", function () {
            that.iframe.css('width', '100%');
            return false;
        });
        $.hoverToolbarButtons(this.desktop, "fd-desktop.png", "fd-desktopw.png");
        this.tablet = $("<div class='fd-designer-button' title='" + "Tablet".translate() + "'><img src='" + $.imgUrl + "fd-tablet.png' style='width:24px;height:24px;'/></div>");
        this.tablet.on("click", function () {
            that.iframe.css('width', '800px');
            return false;
        });
        $.hoverToolbarButtons(this.tablet, "fd-tablet.png", "fd-tabletw.png");
        this.smartphone = $("<div class='fd-designer-button' title='" + "Smartphone".translate() + "'><img src='" + $.imgUrl + "fd-mobile_phone.png' style='width:24px;height:24px;'/></div>");
        this.smartphone.on("click", function () {
            that.iframe.css('width', '400px');
            return false;
        });
        $.hoverToolbarButtons(this.smartphone, "fd-mobile_phone.png", "fd-mobile_phonew.png");
        this.back = $("<div class='fd-designer-button' title='" + "Close".translate() + "'><img src='" + $.imgUrl + "fd-close.png' style='width:24px;height:24px;'/></div>");
        this.back.on("click", function () {
            that.close();
            return false;
        });
        $.hoverToolbarButtons(this.back, "fd-close.png", "fd-closew.png");

        this.title = $("<div style='float:left;font-family:Montserrat,sans-serif;font-size:20px;color:white;margin:5px;white-space:nowrap;'></div>");
        this.title.text(this.dyn_title).attr("title", this.dyn_title).tooltip({
            tooltipClass: "fd-tooltip",
            position: {my: "left top+1"}
        });

        this.buttons = $("<div style='position:absolute;right:0px;background-color:#3397e1;'>");
        this.buttons.append(this.desktop);
        this.buttons.append(this.tablet);
        this.buttons.append(this.smartphone);
        this.buttons.append(this.back);

        this.toolbar = $("<div class='fd-toolbar-preview' style='overflow:hidden;width:100%;height:33px;background-color:#3397e1;margin-bottom:15px;border:1px solid #2979b8;'></div>");
        this.toolbar.append(this.title);
        this.toolbar.append(this.buttons);

        this.iframe = $("<iframe></iframe>");
        this.iframe.attr("id", defaultIframeOptions.id);
        this.iframe.attr("name", defaultIframeOptions.name);
        this.iframe.css("border", "none");
        this.iframe.css("width", "100%");
        this.iframe.css("height", "100%");

        this.body = $("<div style='z-index:100;position:absolute;top:0;right:0;bottom:35px;left:0;'></div>");
        this.body.append(this.toolbar);
        this.body.append(this.iframe);
    };
    Preview.prototype.show = function () {
        jQuery('body').append(this.body);
        this.body.show();
    };
    Preview.prototype.close = function () {
        this.onClose();
        this.body.remove();
    };
    Preview.prototype.setData = function () {
        var that = this;
        that.iframe[0].src = this.srcPreview;
        that.iframe[0].onload = function () {
            var pm = this.contentWindow.window;
            if (pm && pm.PMDynaform) {
                pm.dynaform = new pm.PMDynaform.core.Project({
                    data: this.contentWindow.window.jsonData,
                    keys: {
                        server: HTTP_SERVER_HOSTNAME,
                        projectId: PMDesigner.project.id,
                        workspace: WORKSPACE
                    },
                    token: {
                        accessToken: PMDesigner.project.keys.access_token
                    },
                    isPreview: true,
                    isRTL: this.contentWindow ? this.contentWindow.window.isRTL : false
                });
                $(this.contentWindow.document).find('form').submit(function (e) {
                    e.preventDefault();
                    return false;
                });
                if (pm.PMDynaform.view && pm.PMDynaform.view.ModalProgressBar) {
                    pm.PMDynaform.view.ModalProgressBar.prototype.render = function () {
                        return this;
                    };
                }
            }
        };
    };
    FormDesigner.extendNamespace('FormDesigner.main.Preview', Preview);
}());


(function () {
    var DialogConfirmClose = function (appendTo) {
        this.onAccept = new Function();
        DialogConfirmClose.prototype.init.call(this, appendTo);
    };
    DialogConfirmClose.prototype.init = function (appendTo) {
        var that = this;
        this.accept = $("<a href='#' class='fd-button fd-button-yes'>" + "Yes".translate() + "</a>");
        this.accept.on("click", function () {
            that.onAccept();
            that.dialog.dialog("close");
            return false;
        });
        this.cancel = $("<a href='#' class='fd-button fd-button-no'>" + "No".translate() + "</a>");
        this.cancel.on("click", function () {
            that.dialog.dialog("close");
            return false;
        });
        this.buttons = $("<div class='fd-button-panel'><div></div></div>");
        this.buttons.find("div:nth-child(1)").append(this.cancel);
        this.buttons.find("div:nth-child(1)").append(this.accept);

        this.dialog = $("<div title='" + "Confirm".translate() + "'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 470,
            height: 170,
            resizable: false,
            close: function (event, ui) {
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog, "warning");

        this.dialog.append("<div style='font-size:14px;margin:20px;'>" +
            "Do you want to close? All your changes will be lost if you close it.".translate() +
            "</div>");
        this.dialog.append(this.buttons);
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogConfirmClose', DialogConfirmClose);
}());
(function () {
    var DialogInvalidProperties = function (appendTo) {
        this.onAccept = new Function();
        DialogInvalidProperties.prototype.init.call(this, appendTo);
    };
    DialogInvalidProperties.prototype.init = function (appendTo) {
        var that = this;
        this.accept = $("<a href='#' class='fd-button fd-button-yes'>" + "Ok".translate() + "</a>");
        this.accept.on("click", function () {
            that.onAccept();
            that.dialog.dialog("close");
            return false;
        });
        this.buttons = $("<div class='fd-button-panel'><div></div></div>");
        this.buttons.find("div:nth-child(1)").append(this.accept);

        this.dialog = $("<div title='" + "Errors".translate() + "'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 470,
            height: 170,
            resizable: true,
            close: function (event, ui) {
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog, "alert");

        var msg = "";
        var dt = $.globalInvalidProperties;
        for (var i = 0; i < dt.length; i++) {
            msg = msg + dt[i] + "<br> ";
        }
        msg = msg.replace(/[,\s]+$/, "");
        this.dialog.append("<div style='font-size:14px;margin:20px;height:60px;overflow-y:auto;'>" +
            "Properties invalid elements:<br><br>".translate() + msg +
            "</div>");
        this.dialog.append(this.buttons);
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogInvalidProperties', DialogInvalidProperties);
}());
(function () {
    var DialogConfirmImport = function (appendTo) {
        this.onAccept = new Function();
        DialogConfirmImport.prototype.init.call(this, appendTo);
    };
    DialogConfirmImport.prototype.init = function (appendTo) {
        var that = this;
        this.accept = $("<a href='#' class='fd-button fd-button-yes'>" + "Yes".translate() + "</a>");
        this.accept.on("click", function () {
            that.onAccept();
            that.dialog.dialog("close");
            return false;
        });
        this.cancel = $("<a href='#' class='fd-button fd-button-no'>" + "No".translate() + "</a>");
        this.cancel.on("click", function () {
            that.dialog.dialog("close");
            return false;
        });
        this.buttons = $("<div class='fd-button-panel'><div></div></div>");
        this.buttons.find("div:nth-child(1)").append(this.cancel);
        this.buttons.find("div:nth-child(1)").append(this.accept);

        this.dialog = $("<div title='" + "Confirm".translate() + "'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 470,
            height: 170,
            resizable: false,
            close: function (event, ui) {
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog, "warning");

        this.dialog.append("<div style='font-size:14px;margin:20px;'>" +
            "Do you want to import? All your changes will be lost if you import it.".translate() +
            "</div>");
        this.dialog.append(this.buttons);
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogConfirmImport', DialogConfirmImport);
}());
(function () {
    var DialogConfirmClear = function (appendTo) {
        this.onAccept = new Function();
        DialogConfirmClear.prototype.init.call(this, appendTo);
    };
    DialogConfirmClear.prototype.init = function (appendTo) {
        var that = this;
        this.accept = $("<a href='#' class='fd-button fd-button-yes'>" + "Yes".translate() + "</a>");
        this.accept.on("click", function () {
            that.onAccept();
            that.dialog.dialog("close");
            return false;
        });
        this.cancel = $("<a href='#' class='fd-button fd-button-no'>" + "No".translate() + "</a>");
        this.cancel.on("click", function () {
            that.dialog.dialog("close");
            return false;
        });
        this.buttons = $("<div class='fd-button-panel'><div></div></div>");
        this.buttons.find("div:nth-child(1)").append(this.cancel);
        this.buttons.find("div:nth-child(1)").append(this.accept);

        this.dialog = $("<div title='" + "Confirm".translate() + "'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 470,
            height: 170,
            resizable: false,
            close: function (event, ui) {
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog, "warning");

        this.dialog.append("<div style='font-size:14px;margin:20px;'>" +
            "Do you want to clear? All your changes will be lost if you clear it.".translate() +
            "</div>");
        this.dialog.append(this.buttons);
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogConfirmClear', DialogConfirmClear);
}());
(function () {
    var DialogInformation = function (appendTo) {
        this.onAccept = new Function();
        DialogInformation.prototype.init.call(this, appendTo);
    };
    DialogInformation.prototype.init = function (appendTo) {
        var that = this;
        this.accept = $("<a href='#' class='fd-button fd-button-yes'>" + "Yes".translate() + "</a>");
        this.accept.on("click", function () {
            that.onAccept();
            that.dialog.dialog("close");
            return false;
        });
        this.buttons = $("<div class='fd-button-panel'><div></div></div>");
        this.buttons.find("div:nth-child(1)").append(this.accept);

        this.dialog = $("<div title='" + "Information".translate() + "'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 470,
            height: 170,
            resizable: false,
            close: function (event, ui) {
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog, "warning");

        this.dialog.append("<div style='font-size:14px;margin:20px;'>" +
            "The variable is being used.".translate() +
            "</div>");
        this.dialog.append(this.buttons);
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogInformation', DialogInformation);
}());
(function () {
    var DialogScript = function (appendTo) {
        this.onSave = new Function();
        this.onClose = new Function();
        DialogScript.prototype.init.call(this, appendTo);
    };
    DialogScript.prototype.init = function (appendTo) {
        var that = this;
        this.save = $("<a href='#' class='fd-button fd-button-success'>" + "Save".translate() + "</a>");
        this.save.on("click", function () {
            that.onSave();
            that.dialog.dialog("close").remove();
            return false;
        });
        this.cancel = $("<a href='#' class='fd-button fd-button-no'>" + "Cancel".translate() + "</a>");
        this.cancel.on("click", function () {
            that.dialog.dialog("close").remove();
            return false;
        });
        this.buttons = $("<div class='fd-button-panel'><div style='float:right'></div></div>");
        this.buttons.find("div:nth-child(1)").append(this.cancel);
        this.buttons.find("div:nth-child(1)").append(this.save);

        $.ui.keyCode.TAB = null;//focus button prevents dialog with CodeMirror tab
        this.dialog = $("<div title='" + "Javascript".translate() + "' style='padding:10px;'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 940,
            height: 500,
            resizable: false,
            close: function (event, ui) {
                that.onClose(event, ui);
                that.dialog.remove();
                $.ui.keyCode.TAB = 9;
            }
        });
        FormDesigner.main.DialogStyle(this.dialog);

        this.textarea = $("<textarea></textarea>");
        this.body = $("<div style='border:1px solid #bbb;'></div>");
        this.body.append(this.textarea);
        this.dialog.append(this.body);
        this.dialog.append("<p>" + "Press".translate() + " <strong>ctrl-space</strong> " + "to activate autocompletion".translate() + ".</p>");
        this.dialog.append(this.buttons);

        this.editor = CodeMirror.fromTextArea(this.textarea[0], {
            lineNumbers: true,
            matchBrackets: true,
            autoCloseBrackets: true,
            extraKeys: {"Ctrl-Space": "autocomplete"},
            mode: "application/javascript",
            viewportMargin: Infinity,
            className: "CodeMirror-hints-custom-mafe"
        });
        this.editor.setSize(910, 350);
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogScript', DialogScript);
}());
(function () {
    var DialogFormula = function (appendTo, fields) {
        this.onSave = new Function();
        this.onClose = new Function();
        this.fields = fields;
        DialogFormula.prototype.init.call(this, appendTo);
    };
    DialogFormula.prototype.init = function (appendTo) {
        var that = this;
        this.save = $("<a href='#' class='fd-button fd-button-success'>" + "Save".translate() + "</a>");
        this.save.on("click", function () {
            that.onSave();
            that.dialog.dialog("close").remove();
            return false;
        });
        this.cancel = $("<a href='#' class='fd-button fd-button-no'>" + "Cancel".translate() + "</a>");
        this.cancel.on("click", function () {
            that.dialog.dialog("close").remove();
            return false;
        });
        this.buttons = $("<div class='fd-button-panel'><div style='float:right'></div></div>");
        this.buttons.find("div:nth-child(1)").append(this.cancel);
        this.buttons.find("div:nth-child(1)").append(this.save);

        $.ui.keyCode.TAB = null;//focus button prevents dialog with CodeMirror tab
        this.dialog = $("<div title='" + "Formula".translate() + "' style='padding:10px;'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 940,
            height: 500,
            resizable: false,
            close: function (event, ui) {
                that.onClose(event, ui);
                that.dialog.remove();
                $.ui.keyCode.TAB = 9;
            }
        });
        FormDesigner.main.DialogStyle(this.dialog);

        this.textarea = $("<textarea></textarea>");
        this.body = $("<div style='border:1px solid #bbb;'></div>");
        this.body.append(this.textarea);
        this.dialog.append(this.body);
        this.dialog.append("<p>" + "Press".translate() + " <strong>ctrl-space</strong> " + "to activate autocompletion".translate() + ".</p>");
        this.dialog.append(this.buttons);

        this.editor = CodeMirror.fromTextArea(this.textarea[0], {
            lineNumbers: true,
            matchBrackets: true,
            autoCloseBrackets: true,
            extraKeys: {"Ctrl-Space": "autocomplete"},
            mode: "application/javascript",
            viewportMargin: Infinity,
            className: "CodeMirror-hints-custom-mafe"
        });
        this.editor.setSize(910, 350);
        //add elements for codemirror autocomplete
        CodeMirror.hint.javascript = function (cm) {
            var inner = {from: cm.getCursor(), to: cm.getCursor(), list: []};
            for (var i = 0; i < that.fields.length; i++) {
                if (that.fields[i].var_name) {
                    inner.list.push(that.fields[i].var_name);
                }
            }
            var others = [
                "parseInt()",
                "parseFloat()",
                ".concat()",
                ".toString()",
                ".toUpperCase()",
                ".toLowerCase()",
                ".indexOf()",
                ".substring()",
                ".lastIndexOf()",
                ".split()"
            ];
            for (var i = 0; i < others.length; i++) {
                inner.list.push(others[i]);
            }
            return inner;
        };
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogFormula', DialogFormula);
}());
(function () {
    var DialogInvalidFile = function (appendTo, msg) {
        this.onAccept = new Function();
        this.msg = msg;
        DialogInvalidFile.prototype.init.call(this, appendTo);
    };
    DialogInvalidFile.prototype.init = function (appendTo) {
        var that = this;
        this.accept = $("<a href='#' class='fd-button fd-button-success'>" + "Ok".translate() + "</a>");
        this.accept.on("click", function () {
            that.onAccept();
            that.dialog.dialog("close");
            return false;
        });
        this.buttons = $("<div class='fd-button-panel'><div></div></div>");
        this.buttons.find("div:nth-child(1)").append(this.accept);

        this.dialog = $("<div title='" + "Errors".translate() + "'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 470,
            height: 170,
            resizable: false,
            close: function (event, ui) {
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog, "alert");

        this.dialog.append("<div style='font-size:14px;margin:20px;'>" +
            "Invalid file: ".translate() + that.msg + ". " +
            "Please upload a file with a valid extension (.json)".translate() + "." +
            "</div>");
        this.dialog.append(this.buttons);
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogInvalidFile', DialogInvalidFile);
}());
(function () {
    var DialogVariable = function (appendTo, render) {
        this.render = render;
        this.onClose = new Function();
        this.onLoad = new Function();
        this.onClick = new Function();
        DialogVariable.prototype.init.call(this, appendTo);
    };
    DialogVariable.prototype.init = function (appendTo) {
        var that = this;
        this.dialog = $("<div title='" + "Variable".translate() + "' style='padding:10px;'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 500,
            height: 400,
            resizable: false,
            close: function (event, ui) {
                that.onClose(event, ui);
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog);

        this.search = $("<div style='padding-bottom:10px;'><span style='vertical-align:top;'>" + "Search".translate() + " : </span><input type='text'/><img src='" + $.imgUrl + "fd-refresh.png' style='margin-top:-4px;vertical-align:middle;cursor: pointer;' title='" + "refresh".translate() + "'></div>");
        this.search.find("input").on("keyup", function () {
            var input = this;
            that.body.find(">div").each(function (i, e) {
                var sw = false;
                $(e).find(">div").each(function (i, e) {
                    sw = sw || $(e).text().toLowerCase().indexOf(input.value.toLowerCase()) > -1;
                });
                if (sw)
                    $(e).show();
                else
                    $(e).hide();
            });
        });
        this.search.find("img").on("click", function () {
            that.load();
        });

        this.body = $("<div style='overflow-x:hidden;border:1px solid #bbb;height:305px;'></div>");
        this.dialog.append(this.search);
        this.dialog.append(this.body);
    };
    DialogVariable.prototype.load = function () {
        var that = this;
        this.body.find(">div").remove();
        var restProxy = new PMRestClient({
            endpoint: 'process-variables',
            typeRequest: 'get',
            functionSuccess: function (xhr, response) {
                response.sort(function (a, b) {
                    if (a.var_name < b.var_name)
                        return -1;
                    if (a.var_name > b.var_name)
                        return 1;
                    return 0;
                });
                for (var i = 0; i < response.length; i++) {
                    $.validDataTypeAndControlType(response[i].var_field_type, that.render, function () {
                        that.addItem(response[i]);
                    });
                }
                that.onLoad(response);
            }
        });
        restProxy.executeRestClient();
    };
    DialogVariable.prototype.addItem = function (variable) {
        var that = this;
        var item = $("<div class='fd-list' style='cursor:pointer;'>" +
            "<div style='width:auto;display:inline-block;margin:1px;color:rgb(238, 113, 15);'>[" + variable.var_field_type + "]</div>" +
            "<div style='width:auto;display:inline-block;color:rgb(33, 54, 109);'>" + variable.var_name + "</div>" +
            "</div>");
        item.on("click", function () {
            that.onClick(variable);
        });
        this.body.append(item);
        return item;
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogVariable', DialogVariable);
}());
(function () {
    var DialogDBConnection = function (appendTo) {
        this.onClose = new Function();
        this.onLoad = new Function();
        this.onClick = new Function();
        DialogDBConnection.prototype.init.call(this, appendTo);
    };
    DialogDBConnection.prototype.init = function (appendTo) {
        var that = this;
        this.dialog = $("<div title='" + "Database Connection".translate() + "' style='padding:10px;'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 500,
            height: 400,
            resizable: false,
            close: function (event, ui) {
                that.onClose(event, ui);
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog);

        this.search = $("<p>" + "Search".translate() + " : <input type='text'/><img src='" + $.imgUrl + "fd-refresh.png' style='margin-top:-4px;vertical-align:middle;cursor: pointer;' title='" + "refresh".translate() + "'></p>");
        this.search.find(">input").on("keyup", function () {
            var input = this;
            that.body.find(">div").each(function (i, e) {
                var sw = false;
                $(e).find(">div").each(function (i, e) {
                    sw = sw || $(e).text().toLowerCase().indexOf(input.value.toLowerCase()) > -1;
                });
                if (sw)
                    $(e).show();
                else
                    $(e).hide();
            });
        });
        this.search.find("img").on("click", function () {
            that.load();
        });
        this.body = $("<div style='overflow-x:hidden;border:1px solid #bbb;height:305px;'></div>");
        this.dialog.append(this.search);
        this.dialog.append(this.body);
        this.load();
    };
    DialogDBConnection.prototype.load = function () {
        var that = this;
        this.body.find(">div").remove();
        that.addItem({label: "PM Database", value: "workflow"});
        var restProxy = new PMRestClient({
            endpoint: "database-connections",
            typeRequest: "get",
            functionSuccess: function (xhr, response) {
                for (var i = 0; i < response.length; i++) {
                    if (response[i].dbs_connection_type === "TNS") {
                        that.addItem({
                            label: "[" + response[i].dbs_tns + "] " + response[i].dbs_type + " : " + response[i].dbs_database_description,
                            value: response[i].dbs_uid
                        });
                    } else {
                        that.addItem({
                            label: "[" + response[i].dbs_server + ":" + response[i].dbs_port + "] " + response[i].dbs_type + ": " + response[i].dbs_database_name + response[i].dbs_database_description,
                            value: response[i].dbs_uid
                        });
                    }
                }
                that.onLoad(response);
            }
        });
        restProxy.executeRestClient();
    };
    DialogDBConnection.prototype.addItem = function (variable) {
        var that = this;
        var item = $("<div class='fd-list' style='cursor:pointer;'>" +
            "<div style='width:auto;display:inline-block;color:rgb(33, 54, 109);'>" + variable.label + "</div>" +
            "</div>");
        item.on("click", function () {
            that.onClick(variable);
        });
        this.body.append(item);
        return item;
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogDBConnection', DialogDBConnection);
}());
(function () {
    var DialogSql = function (appendTo) {
        this.onSave = new Function();
        this.onClose = new Function();
        DialogSql.prototype.init.call(this, appendTo);
    };
    DialogSql.prototype.init = function (appendTo) {
        var that = this;
        this.save = $("<a href='#' class='fd-button fd-button-success'>" + "Save".translate() + "</a>");
        this.save.on("click", function () {
            that.onSave();
            that.dialog.dialog("close").remove();
            return false;
        });
        this.cancel = $("<a href='#' class='fd-button fd-button-no'>" + "Cancel".translate() + "</a>");
        this.cancel.on("click", function () {
            that.dialog.dialog("close").remove();
            return false;
        });
        this.buttons = $("<div class='fd-button-panel'><div style='float:right'></div></div>");
        this.buttons.find("div:nth-child(1)").append(this.cancel);
        this.buttons.find("div:nth-child(1)").append(this.save);

        $.ui.keyCode.TAB = null;//focus button prevents dialog with CodeMirror tab
        this.dialog = $("<div title='" + "SQL Editor".translate() + "' style='padding:10px;'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 940,
            height: 500,
            resizable: false,
            close: function (event, ui) {
                that.onClose(event, ui);
                that.dialog.remove();
                $.ui.keyCode.TAB = 9;
            }
        });
        FormDesigner.main.DialogStyle(this.dialog);

        this.textarea = $("<textarea></textarea>");
        this.body = $("<div style='border:1px solid #bbb;'></div>");
        this.body.append(this.textarea);
        this.dialog.append(this.body);
        this.dialog.append("<p>" + "Press".translate() + " <strong>ctrl-space</strong> " + "to activate autocompletion".translate() + ".</p>");
        this.dialog.append(this.buttons);

        this.editor = CodeMirror.fromTextArea(this.textarea[0], {
            lineNumbers: true,
            matchBrackets: true,
            autoCloseBrackets: true,
            extraKeys: {"Ctrl-Space": "autocomplete"},
            mode: "text/x-sql",
            viewportMargin: Infinity,
            className: "CodeMirror-hints-custom-mafe"
        });
        this.editor.setSize(910, 350);
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogSql', DialogSql);
}());
(function () {
    var DialogOptions = function (appendTo, options) {
        this.onApply = new Function();
        this.onClose = new Function();
        this.appendTo = appendTo;
        this.options = options;
        DialogOptions.prototype.init.call(this);
    };
    DialogOptions.prototype.init = function () {
        var that = this;
        this.save = $("<a href='#' class='fd-button fd-button-success'>" + "Apply".translate() + "</a>");
        this.save.on("click", function () {
            if (!that.isValid()) {
                return false;
            }
            that.onApply();
            that.dialog.dialog("close").remove();
            return false;
        });
        this.cancel = $("<a href='#' class='fd-button fd-button-no'>" + "Cancel".translate() + "</a>");
        this.cancel.on("click", function () {
            that.dialog.dialog("close").remove();
            return false;
        });
        this.buttons = $("<div class='fd-button-panel'><div style='float:right'></div></div>");
        this.buttons.find("div:nth-child(1)").append(this.cancel);
        this.buttons.find("div:nth-child(1)").append(this.save);

        this.edit = $("<input type='text' style='width:100%;height:100%;box-sizing:border-box;'/>");
        this.add = $("<a href='#' class='fd-button fd-button-create' style='float:right;padding:7px 10px 7px 35px;margin-bottom:5px;'>" + "Create".translate() + "</a>");
        this.add.on("click", function () {
            var row = that.addItem("", "");
            var cells = row.find(".fd-table-td-edit");
            that.setInputEdit($(cells[0]));
            return false;
        });
        this.add.on("keydown", function (e) {
            var row = that.table.find("tr");
            if (row.length > 0 && e.keyCode === 9 && !e.shiftKey) {
                that.setInputEdit($($(row[1]).find(".fd-table-td-edit")[0]));
                return false;
            }
        });

        this.tbody = $("<tbody class='fd-tbody'></tbody>");
        this.tbody.sortable({
            placeholder: "fd-drag-drop-placeholder",
            handle: ".fd-drag-drop",
            stop: function (event, ui) {

            },
            start: function (event, ui) {
                ui.placeholder.html("<td class='fd-drag-drop-placeholder' colspan='6'></td>")
            }
        }).droppable({
            drop: function (event, ui) {
            }
        });

        this.table = $(
            "<table class='fd-table' style='width:100%;border:1px solid #bbb;'>" +
            "<thead>" +
            "<th class='fd-table-th' style='height:21px;width:16px;font-size:14px;'>" + "Key".translate() + "</th>" +
            "<th class='fd-table-th' style='height:21px;width:auto;font-size:14px;'>" + "Label".translate() + "</th>" +
            "<th class='fd-table-th' style='height:21px;width:40px;font-size:14px;'></th>" +
            "</thead>" +
            "</table>");
        this.table.append(this.tbody);
        this.msgError = $("<div style='color:red;'></div>");

        this.dialog = $("<div title='" + "Options".translate() + "'></div>");
        this.dialog.dialog({
            appendTo: this.appendTo ? this.appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 500,
            height: 400,
            resizable: false,
            close: function (event, ui) {
                that.onClose(event, ui);
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog);

        this.dialog.append("<p>" + "Please add option and click in cell for editing the cell value".translate() + ".</p>");
        this.dialog.append(this.add);
        this.dialog.append($("<div style='overflow-y:auto;height:210px;width:100%;'></div>").append(this.table));
        this.dialog.append(this.msgError);
        this.dialog.append(this.buttons);
        this.load();
    };
    DialogOptions.prototype.load = function () {
        for (var i = 0; i < this.options.length; i++)
            this.addItem(this.options[i].value, this.options[i].label);
    };
    DialogOptions.prototype.addItem = function (value, label) {
        var that = this, cell;
        var del = $("<td class='fd-table-td' style='text-align:center;height:27px;'>" +
            "<a href='#' class='fd-button fd-button-no' style='margin:0px 5px 0px 5px;font-family:SourceSansPro,Arial,Tahoma,Verdana;font-size:14px;padding: 5px 5px;'>" + "Delete".translate() + "</a>" +
            "<img class='fd-drag-drop' src='" + $.imgUrl + "fd-dragdrop.png' title='" + "up & down".translate() + "'/>" +
            "</td>");
        del.find(".fd-button").on("click", function () {
            var a = new FormDesigner.main.DialogConfirmDeleteOption();
            a.onAccept = function () {
                del.parent().remove();
            };
            return false;
        });
        del.find(".fd-button").on("keydown", function (e) {
            var ri = $(this).parent().parent()[0].rowIndex + 1;
            if (e.keyCode === 9 && !e.shiftKey && that.table.find("tr").length > ri) {
                that.setInputEdit($($(that.table.find("tr")[ri]).find(".fd-table-td-edit")[0]));
                return false;
            }
            if (e.keyCode === 9 && e.shiftKey) {
                var row = $(this).parent().parent();
                that.setInputEdit($($(row).find(".fd-table-td-edit")[1]));
                return false;
            }
        });
        var row = $("<tr style='height:20px;'></tr>");
        cell = $("<td class='fd-table-td fd-table-td-edit' style='padding:0px 2px 0px 1px;'></td>").text(value);
        row.append(cell);
        cell = $("<td class='fd-table-td fd-table-td-edit' style='padding:0px 2px 0px 1px;'></td>").text(label);
        row.append(cell);
        row.append(del);
        row.find(".fd-table-td-edit").on("click", function (e) {
            e.stopPropagation();
            that.setInputEdit($(e.target));
            return false;
        });
        this.tbody.append(row);
        return row;
    };
    DialogOptions.prototype.getOptions = function () {
        var a = [];
        if (this.edit.parent()[0] && this.edit.parent()[0].nodeName === "TD") {
            this.edit.parent().text(this.edit.val().trim());
            this.edit.remove();
        }
        var aux = "";
        this.tbody.find(".fd-table-td-edit").each(function (i, e) {
            if (i % 2 === 1) {
                a.push({
                    value: aux,
                    label: $(e).text()
                });
            }
            aux = $(e).text();
        });
        return a;
    };
    DialogOptions.prototype.isValid = function () {
        this.tbody.find(".fd-table-td-edit").removeClass("fd-table-td-error");
        var options = this.getOptions();
        for (var i = 0; i < options.length; i++) {
            for (var j = 0; j < options.length; j++) {
                if (i !== j && options[j].value === options[i].value) {
                    this.msgError.text("Duplicate value for key".translate() + ".");
                    $(this.tbody.find("tr")[j]).find(".fd-table-td-edit").addClass("fd-table-td-error");
                    return false;
                }
                if (options[j].label === "") {
                    this.msgError.text("The label is empty".translate() + ".");
                    $(this.tbody.find("tr")[j]).find(".fd-table-td-edit").addClass("fd-table-td-error");
                    return false;
                }
            }
        }
        this.msgError.text("");
        return true;
    };
    DialogOptions.prototype.setOptionsBoolean = function () {
        this.tbody.find("tr").each(function (i, e) {
            $(e).find(".fd-table-td-edit").each(function (i, cell) {
                if (0 === i)
                    $(cell).off();
            });
            $(e).find(".fd-button").hide();
            $(e).find(".fd-drag-drop").hide();
        });
        this.add.hide();
    };
    DialogOptions.prototype.setInputEdit = function (cell) {
        var that = this;
        if (that.edit.parent()[0] && that.edit.parent()[0].nodeName === "TD") {
            that.edit.parent().text(that.edit.val().trim());
        }
        that.edit.val(cell.text());
        cell.text("");
        cell.append(that.edit);
        setTimeout(function () {
            that.edit.focus();
        }, 50);
        that.edit.on("click", function (e) {
            e.stopPropagation();
            return false;
        });
        that.edit.on("keydown", function (e) {
            var cell = that.edit.parent();
            var ci = cell[0].cellIndex;
            if (e.keyCode === 9 && ci === 0 && !e.shiftKey) {
                that.setInputEdit(cell.next());
                return false;
            }
            if (e.keyCode === 9 && ci === 1 && e.shiftKey) {
                that.setInputEdit(cell.prev());
                return false;
            }
        });
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogOptions', DialogOptions);
}());
(function () {
    var DialogLanguage = function (appendTo, dyn_uid) {
        this.onClose = new Function();
        this.onLoad = new Function();
        this.dyn_uid = dyn_uid;
        DialogLanguage.prototype.init.call(this, appendTo);
    };
    DialogLanguage.prototype.init = function (appendTo) {
        var that = this;
        this.dialog = $("<div title='" + "Languages".translate() + "' style='padding:10px;'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 500,
            height: 400,
            resizable: false,
            close: function (event, ui) {
                that.onClose(event, ui);
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog);

        this.uploadFile = $("<form style='margin-top:11px;float:right;'>" +
            "<input type='file' name='LANGUAGE' class='fd-gridform-language-upload'/>" +
            "</form>");
        this.uploadFile.find(".fd-gridform-language-upload").on("change", function (e) {
            var win = window, fd = new FormData(), xhr, val = 'LANGUAGE';
            fd.append(val, e.target.files[0]);
            if (win.XMLHttpRequest)
                xhr = new XMLHttpRequest();
            else if (win.ActiveXObject)
                xhr = new ActiveXObject('Microsoft.XMLHTTP');
            xhr.open('POST', '/api/1.0/' + WORKSPACE + '/project/' + PMDesigner.project.id + '/dynaform/' + that.dyn_uid + '/upload-language', true);
            xhr.setRequestHeader('Authorization', 'Bearer ' + PMDesigner.project.keys.access_token);
            xhr.onload = function () {
                if (this.status === 200) {
                    that.uploadFile[0].reset();
                    that.load();
                } else {
                    that.uploadFile[0].reset();
                }
            };
            xhr.send(fd);
        });

        this.search = $("<p>" + "Search".translate() + " : <input type='text'/><img src='" + $.imgUrl + "fd-refresh.png' style='margin-top:-4px;vertical-align:middle;cursor: pointer;' title='" + "refresh".translate() + "'></p>");
        this.search.find(">input").on("keyup", function () {
            var input = this;
            that.body.find(">div").each(function (i, e) {
                var sw = false;
                $(e).find(">div").each(function (i, e) {
                    sw = sw || $(e).text().toLowerCase().indexOf(input.value.toLowerCase()) > -1;
                });
                if (sw)
                    $(e).show();
                else
                    $(e).hide();
            });
        });
        this.search.find("img").on("click", function () {
            that.load();
        });
        this.body = $("<div style='overflow-x:hidden;border:1px solid #bbb;height:305px;'></div>");
        this.dialog.append(this.uploadFile);
        this.dialog.append(this.search);
        this.dialog.append(this.body);
        this.load();
    };
    DialogLanguage.prototype.load = function () {
        var that = this;
        this.body.find(">div").remove();
        var restProxy = new PMRestClient({
            endpoint: "dynaform/" + this.dyn_uid + "/list-language",
            typeRequest: "get",
            functionSuccess: function (xhr, response) {
                //default lang
                var item = that.addItem({
                    label: "English [processmaker.en.po]",
                    value: "en"
                });
                item.find(".fd-button").hide();
                //lang
                for (var i = 0; i < response.length; i++) {
                    that.addItem({
                        label: response[i]["X-Poedit-Language"] + " [" + response[i]["File-Name"] + "]",
                        value: response[i].Lang
                    });
                }
                that.onLoad(response);
            }
        });
        restProxy.executeRestClient();
    };
    DialogLanguage.prototype.addItem = function (variable) {
        var that = this;
        var item = $("<div class='fd-list' style='cursor:pointer;height:27px;'>" +
            "<div style='width:410px;display:inline-block;color:rgb(33, 54, 109);'>" + variable.label + "</div>" +
            "<div style='width:auto;display:inline-block;color:rgb(33, 54, 109);'>" +
            "<a href='#' class='fd-button fd-button-no' style='margin:0px 5px 0px 5px;font-family:SourceSansPro,Arial,Tahoma,Verdana;font-size:14px;padding:5px 5px;'>" + "Delete".translate() + "</a>" +
            "</div>" +
            "</div>");
        item.find(".fd-button").on("click", function (e) {
            e.stopPropagation();
            var a = new FormDesigner.main.DialogConfirmDeleteLang();
            a.onAccept = function () {
                var restProxy = new PMRestClient({
                    endpoint: "dynaform/" + that.dyn_uid + "/delete-language/" + variable.value,
                    typeRequest: "post",
                    functionSuccess: function (xhr, response) {
                        that.load();
                    }
                });
                restProxy.executeRestClient();
            }
            return false;
        });
        item.on("click", function (e) {
            e.stopPropagation();
            var restProxy = new PMRestClient({
                endpoint: "dynaform/" + that.dyn_uid + "/download-language/" + variable.value,
                typeRequest: "get",
                functionSuccess: function (xhr, response) {
                    var name = "processmaker." + response.lang + ".po";
                    if (window.navigator.msSaveBlob) {
                        window.navigator.msSaveBlob(new Blob([response.labels], {'type': 'application/octet-stream'}), name);
                        return false;
                    }
                    var a = document.createElement('a');
                    document.body.appendChild(a);
                    a.href = window.URL.createObjectURL(new Blob([response.labels], {'type': 'application/octet-stream'}));
                    a.download = name;
                    a.click();
                    document.body.removeChild(a);
                }
            });
            restProxy.executeRestClient();
            return false;
        });
        this.body.append(item);
        return item;
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogLanguage', DialogLanguage);
}());
(function () {
    var DialogConfirmDeleteLang = function (appendTo) {
        this.onAccept = new Function();
        DialogConfirmDeleteLang.prototype.init.call(this, appendTo);
    };
    DialogConfirmDeleteLang.prototype.init = function (appendTo) {
        var that = this;
        this.accept = $("<a href='#' class='fd-button fd-button-yes'>" + "Yes".translate() + "</a>");
        this.accept.on("click", function () {
            that.onAccept();
            that.dialog.dialog("close");
            return false;
        });
        this.cancel = $("<a href='#' class='fd-button fd-button-no'>" + "No".translate() + "</a>");
        this.cancel.on("click", function () {
            that.dialog.dialog("close");
            return false;
        });
        this.buttons = $("<div class='fd-button-panel'><div></div></div>");
        this.buttons.find("div:nth-child(1)").append(this.cancel);
        this.buttons.find("div:nth-child(1)").append(this.accept);

        this.dialog = $("<div title='" + "Confirm".translate() + "'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 470,
            height: 170,
            resizable: false,
            close: function (event, ui) {
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog, "warning");

        this.dialog.append("<div style='font-size:14px;margin:20px;'>" +
            "Do you want to delete this lang?".translate() + "." +
            "</div>");
        this.dialog.append(this.buttons);
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogConfirmDeleteLang', DialogConfirmDeleteLang);
}());
(function () {
    var DialogConfirmDeleteOption = function (appendTo) {
        this.onAccept = new Function();
        DialogConfirmDeleteOption.prototype.init.call(this, appendTo);
    };
    DialogConfirmDeleteOption.prototype.init = function (appendTo) {
        var that = this;
        this.accept = $("<a href='#' class='fd-button fd-button-yes'>" + "Yes".translate() + "</a>");
        this.accept.on("click", function () {
            that.onAccept();
            that.dialog.dialog("close");
            return false;
        });
        this.cancel = $("<a href='#' class='fd-button fd-button-no'>" + "No".translate() + "</a>");
        this.cancel.on("click", function () {
            that.dialog.dialog("close");
            return false;
        });
        this.buttons = $("<div class='fd-button-panel'><div></div></div>");
        this.buttons.find("div:nth-child(1)").append(this.cancel);
        this.buttons.find("div:nth-child(1)").append(this.accept);

        this.dialog = $("<div title='" + "Confirm".translate() + "'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 470,
            height: 170,
            resizable: false,
            close: function (event, ui) {
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog, "warning");

        this.dialog.append("<div style='font-size:14px;margin:20px;'>" +
            "Do you want to delete this option?".translate() + "." +
            "</div>");
        this.dialog.append(this.buttons);
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogConfirmDeleteOption', DialogConfirmDeleteOption);
}());
(function () {
    var DialogInvalid = function (appendTo, property, type, config) {
        this.property = property;
        this.type = type;
        this.config = config || null;
        this.onAccept = new Function();
        this.onClose = new Function();
        DialogInvalid.prototype.init.call(this, appendTo);
    };
    DialogInvalid.prototype.init = function (appendTo) {
        var that = this,
            configDialog = this.getErrorMessage(this.type, this.property, this.config);
        this.accept = $("<a href='#' class='fd-button fd-button-success'>" + "Ok".translate() + "</a>");
        this.accept.on("click", function () {
            that.onAccept();
            return false;
        });
        this.buttons = $("<div class='fd-button-panel'><div></div></div>");
        this.buttons.find("div:nth-child(1)").append(this.accept);

        this.dialog = $("<div title='" + configDialog.title + "'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 470,
            height: 170,
            resizable: false,
            close: function (event, ui) {
                that.onClose();
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog, "alert");

        this.dialog.append("<div style='font-size:14px;margin:20px;'>" +
            configDialog.message +
            "</div>");
        this.dialog.append(this.buttons);
        this.accept.focus();
    };
    /**
     * Get error message
     * @param type
     * @param property
     * @param config
     * @returns {Object}
     */
    DialogInvalid.prototype.getErrorMessage = function (type, property, config) {
        var conf = {};
        conf.title = (config) ? this.config.title.translate() : 'Errors'.translate();
        switch (type) {
            case 'required':
                conf.message = "The ".translate() + property + " is required.".translate();
                break;
            case 'invalid':
                conf.message = "The ".translate() + property + " is invalid.".translate();
                break;
            case 'duplicated':
                conf.message = "The ".translate() + property + " is duplicated.".translate();
                break;
            case 'custom':
                conf.message = (config) ? config.msg.translate() : '';
                break;
        }
        return conf;
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogInvalid', DialogInvalid);
}());

(function () {
    var DialogRegularExpression = function (appendTo, dataType) {
        this.dataType = dataType;
        this.onClose = new Function();
        this.onClick = new Function();
        DialogRegularExpression.prototype.init.call(this, appendTo);
    };
    DialogRegularExpression.prototype.init = function (appendTo) {
        var that = this;
        this.dialog = $("<div title='" + "Regular Expression".translate() + "' style='padding:10px;'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 500,
            height: 400,
            resizable: false,
            close: function (event, ui) {
                that.onClose(event, ui);
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog);

        this.body = $("<div style='overflow-x:hidden;border:1px solid #bbb;height:265px;'></div>");
        this.dialog.append("<span>" +
            "Enter a regular expression which is a search pattern which matches the text entered in the field. ".translate() +
            "To learn more about regular expressions, see the <a href=\"http://wiki.processmaker.com/3.0/Text_and_Textarea_Controls#Validate\" target=\"_blank\">wiki</a>.<br><br>".translate() +
            "Examples:<br>".translate() +
            "</span>");
        this.dialog.append(this.body);
        this.load();
    };
    DialogRegularExpression.prototype.load = function () {
        var that = this, data;
        data = [{
            dataType: "integer", name: "An integer".translate(), value: "^[-+]?[0-9]+$"
        }, {
            dataType: "float", name: "An integer or decimal number".translate(), value: "[-+]?[0-9]+\\.[0-9]+$"
        }, {
            dataType: "string", name: "An email address".translate(), value: "^\\w+(\\.\\w+)*@(\\w+\\.)+\\w{2,4}$"
        }
        ];
        this.body.find(">div").remove();
        for (var i = 0; i < data.length; i++) {
            if (that.dataType === data[i].dataType || that.dataType === "") {
                that.addItem(data[i]);
            }
        }
    };
    DialogRegularExpression.prototype.addItem = function (regex) {
        var that = this;
        var item = $("<div class='fd-list' style='cursor:pointer;'>" +
            "<div style='width:150px;display:inline-block;margin:1px;color:rgb(238, 113, 15);'>" + regex.name + "</div>" +
            "<div style='width:auto;display:inline-block;color:rgb(33, 54, 109);'>" + regex.value + "</div>" +
            "</div>");
        item.on("click", function () {
            that.onClick(regex);
        });
        this.body.append(item);
        return item;
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogRegularExpression', DialogRegularExpression);
}());
(function () {
    var DialogConfirmClearVariable = function (appendTo) {
        this.onAccept = new Function();
        DialogConfirmClearVariable.prototype.init.call(this, appendTo);
    };
    DialogConfirmClearVariable.prototype.init = function (appendTo) {
        var that = this;
        this.accept = $("<a href='#' class='fd-button fd-button-yes'>" + "Yes".translate() + "</a>");
        this.accept.on("click", function () {
            that.onAccept();
            that.dialog.dialog("close");
            return false;
        });
        this.cancel = $("<a href='#' class='fd-button fd-button-no'>" + "No".translate() + "</a>");
        this.cancel.on("click", function () {
            that.dialog.dialog("close");
            return false;
        });
        this.buttons = $("<div class='fd-button-panel'><div></div></div>");
        this.buttons.find("div:nth-child(1)").append(this.cancel);
        this.buttons.find("div:nth-child(1)").append(this.accept);

        this.dialog = $("<div title='" + "Confirm".translate() + "'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 470,
            height: 170,
            resizable: false,
            close: function (event, ui) {
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog, "warning");

        this.dialog.append("<div style='font-size:14px;margin:20px;'>" +
            "Do you want to clear this variable? The following properties are reset: Variable, Data Type, DB Connection, SQL and Options.".translate() +
            "</div>");
        this.dialog.append(this.buttons);
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogConfirmClearVariable', DialogConfirmClearVariable);
}());
(function () {
    var DialogContent = function (appendTo) {
        this.onSave = new Function();
        this.onClose = new Function();
        DialogContent.prototype.init.call(this, appendTo);
    };
    DialogContent.prototype.init = function (appendTo) {
        var that = this;
        this.save = $("<a href='#' class='fd-button fd-button-success'>" + "Save".translate() + "</a>");
        this.save.on("click", function () {
            that.onSave();
            that.dialog.dialog("close").remove();
            return false;
        });
        this.cancel = $("<a href='#' class='fd-button fd-button-no'>" + "Cancel".translate() + "</a>");
        this.cancel.on("click", function () {
            that.dialog.dialog("close").remove();
            return false;
        });
        this.buttons = $("<div class='fd-button-panel'><div style='float:right'></div></div>");
        this.buttons.find("div:nth-child(1)").append(this.cancel);
        this.buttons.find("div:nth-child(1)").append(this.save);

        $.ui.keyCode.TAB = null;//focus button prevents dialog with CodeMirror tab
        this.dialog = $("<div title='" + "Content".translate() + "' style='padding:10px;'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 940,
            height: 500,
            resizable: false,
            close: function (event, ui) {
                that.onClose(event, ui);
                that.dialog.remove();
                $.ui.keyCode.TAB = 9;
            }
        });
        FormDesigner.main.DialogStyle(this.dialog);

        this.textarea = $("<textarea></textarea>");
        this.body = $("<div style='border:1px solid #bbb;'></div>");
        this.body.append(this.textarea);
        this.dialog.append(this.body);
        this.dialog.append("<p>" + "Press".translate() + " <strong>ctrl-space</strong> " + "to activate autocompletion".translate() + ".</p>");
        this.dialog.append(this.buttons);

        this.editor = CodeMirror.fromTextArea(this.textarea[0], {
            lineNumbers: true,
            matchBrackets: true,
            autoCloseBrackets: true,
            extraKeys: {"Ctrl-Space": "autocomplete"},
            mode: "text/html",
            viewportMargin: Infinity,
            className: "CodeMirror-hints-custom-mafe"
        });
        this.editor.setSize(910, 350);
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogContent', DialogContent);
}());
(function () {
    var DialogUnsupported = function (appendTo) {
        this.onAccept = new Function();
        DialogUnsupported.prototype.init.call(this, appendTo);
    };
    DialogUnsupported.prototype.init = function (appendTo) {
        var that = this;
        this.accept = $("<a href='#' class='fd-button fd-button-yes'>" + "Ok".translate() + "</a>");
        this.accept.on("click", function () {
            that.onAccept();
            that.dialog.dialog("close");
            return false;
        });
        this.buttons = $("<div class='fd-button-panel'><div></div></div>");
        this.buttons.find("div:nth-child(1)").append(this.accept);

        this.dialog = $("<div title='" + "Information".translate() + "'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 470,
            height: 170,
            resizable: true,
            close: function (event, ui) {
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog, "success");

        var msg = "";
        var dt = $.globalInvalidProperties;
        for (var i = 0; i < dt.length; i++) {
            msg = msg + dt[i] + "<br> ";
        }
        msg = msg.replace(/[,\s]+$/, "");
        this.dialog.append("<div style='font-size:14px;margin:20px;'>" +
            "Unsupported element.".translate() +
            "</div>");
        this.dialog.append(this.buttons);
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogUnsupported', DialogUnsupported);
}());
(function () {
    var DialogChangedVariables = function (appendTo, message) {
        this.onAccept = new Function();
        this.message = message;
        DialogChangedVariables.prototype.init.call(this, appendTo);
    };
    DialogChangedVariables.prototype.init = function (appendTo) {
        var that = this;
        this.accept = $("<a href='#' class='fd-button fd-button-yes'>" + "Ok".translate() + "</a>");
        this.accept.on("click", function () {
            that.onAccept();
            that.dialog.dialog("close");
            return false;
        });
        this.buttons = $("<div class='fd-button-panel'><div></div></div>");
        this.buttons.find("div:nth-child(1)").append(this.accept);

        this.dialog = $("<div title='" + "New variables created".translate() + "'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 470,
            height: 200,
            resizable: true,
            close: function (event, ui) {
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog, "warning");

        this.message = this.message.replace(/[,\s]+$/, "");
        this.dialog.append("<div style='font-size:14px;margin:20px;height:85px;overflow-y:auto;'>" +
            "The imported dynaform includes new variables and existing variables that require changes.".translate() + " " +
            "The changed variables have been added with the suffix “_1”.".translate() + " " +
            "Please take note of the changes to update your process logic.".translate() + " " +
            "The following variables have been created:<br>".translate() + this.message +
            "</div>");
        this.dialog.append(this.buttons);
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogChangedVariables', DialogChangedVariables);
}());
(function () {
    var DialogCreateVariable = function (appendTo, type, options, var_name) {
        this.onSave = new Function();
        this.onSelect = new Function();
        this.onClose = new Function();
        this.appendTo = appendTo;
        this.type = type;
        this.options = options;
        this.var_name = var_name;
        DialogCreateVariable.prototype.init.call(this);
    };
    DialogCreateVariable.prototype.init = function () {
        var that = this;
        this.var_dbconnection = "workflow";
        this.var_sql = "";
        this.var_accepted_values = that.options;
        this.inp_doc_uid = "";

        this.add = $("<a href='#' class='fd-button fd-button-create' style='padding:7px 10px 7px 35px;margin-bottom:5px;position:absolute;right:10px;top:10px;'>" + "Variables".translate() + "</a>");
        this.add.on("click", function () {
            that.dialog.dialog("close");
            var pmvariables = new PMVariables();
            pmvariables.onWindowClose = function (variable) {
                if (variable !== null && variable.var_name_old === that.var_name) {
                    that.onSave(variable);
                }
            };
            pmvariables.load();
            return false;
        });

        this.buttonSave = $("<a href='#' class='fd-button fd-button-success'>" + "Save".translate() + "</a>");
        this.buttonSave.on("click", function () {
            that.save();
            return false;
        });
        this.cancel = $("<a href='#' class='fd-button fd-button-no'>" + "Cancel".translate() + "</a>");
        this.cancel.on("click", function () {
            that.dialog.dialog("close").remove();
            return false;
        });
        this.buttons = $("<div class='fd-button-panel'><div style='float:right'></div></div>");
        this.buttons.find("div:nth-child(1)").append(this.cancel);
        this.buttons.find("div:nth-child(1)").append(this.buttonSave);

        this.optionSelect = $(
            "   <div style='padding:10px;'>" +
            "       <label>" +
            "           <input type='radio' id='optionCreate' name='optionCreateSelect' checked/>" +
            "           <span style='width:110px;display:inline-block;vertical-align:super;'>" + "Create variable".translate() + "</span>" +
            "       </label>" +
            "       <label>" +
            "           <input type='radio' id='optionSelect' name='optionCreateSelect'/>" +
            "           <span style='width:110px;display:inline-block;vertical-align:super;'>" + "Select variable".translate() + "</span>" +
            "       </label>" +
            "   </div>");
        this.optionSelect.find("#optionCreate").on("click", function () {
            that.buttonSave.show();
            that.content.show();
            that.selectVariable.dialog.hide();
            that.dialog.css({height: heightCreate});
        });
        this.optionSelect.find("#optionSelect").on("click", function () {
            that.buttonSave.hide();
            that.content.hide();
            that.selectVariable.dialog.show();
            that.selectVariable.load();
            that.dialog.css({height: 452});
        });

        this.selectVariable = new FormDesigner.main.DialogVariable(null, this.type);
        this.selectVariable.onClick = function (variable) {
            that.onSelect(variable);
        };
        this.selectVariable.dialog.dialog("destroy");
        this.selectVariable.dialog.hide();

        this.content = $(
            "<div>" +
            "   <div style='padding:10px;'>" +
            "       <label><span style='width:110px;display:inline-block;vertical-align:top;'>" + "Variable Name:".translate() + "</span>" +
            "           <input type='text' name='var_name' id='var_name' style='width:200px;'/>" +
            "       </label>" +
            "   </div>" +
            "   <div style='padding:0px 10px 10px 10px;'>" +
            "       <a title='" + "Settings".translate() + "' style='cursor:pointer;margin-bottom:5px;text-decoration:underline;' id='buttonSettings'>" + "Settings".translate() + "</a>" +
            "   </div>" +
            "   <div id='settings' style='padding: 0px 10px 10px;'>" +
            "       <label style='display:block;margin-bottom:5px;'>" +
            "           <span style='width:110px;display:inline-block;vertical-align:top;'>" + "Type:".translate() + "</span>" +
            "           <select id='var_field_type' style='width:auto;'></select>" +
            "       </label>" +
            "       <label style='display:block;margin-bottom:5px;' id='var_dbconnection'>" +
            "           <span style='width:110px;display:inline-block;vertical-align:top;'>" + "Database Connection:".translate() + "</span>" +
            "           <div style='overflow:hidden;width:250px;display:inline-block;'><a style='cursor:pointer;white-space:nowrap;text-decoration:underline;' id='buttonDbConnection'>PM Database</a></div>" +
            "       </label>" +
            "       <label style='display:block;margin-bottom:5px;' id='var_sql'>" +
            "           <span style='width:110px;display:inline-block;vertical-align:top;'>" + "Sql:".translate() + "</span>" +
            "           <div style='overflow:hidden;width:250px;display:inline-block;'><a style='cursor:pointer;white-space:nowrap;text-decoration:underline;' id='buttonSql'>...</a></div>" +
            "       </label>" +
            "       <label style='display:block;margin-bottom:5px;' id='var_accepted_values'>" +
            "           <span style='width:110px;display:inline-block;vertical-align:top;'>" + "Options:".translate() + "</span>" +
            "           <div style='overflow:hidden;width:250px;display:inline-block;'><a style='cursor:pointer;white-space:nowrap;text-decoration:underline;' id='buttonOptions'>[]</a></div>" +
            "       </label>" +
            "       <label style='display:block;margin-bottom:5px;' id='inp_doc_uid'>" +
            "           <span style='width:110px;display:inline-block;vertical-align:top;'>" + "Input Document<span style='color:red;'>*</span>:".translate() + "</span>" +
            "           <div style='overflow:hidden;width:250px;display:inline-block;'><a style='cursor:pointer;white-space:nowrap;text-decoration:underline;' id='buttonInputDocument'>...</a></div>" +
            "       </label>" +
            "   </div>" +
            "</div>"
        );

        this.content.find("#settings").css({display: "none"});

        var show = true;
        var heightCreate = 180;
        this.content.find("#buttonSettings").on("click", function () {
            if (show) {
                heightCreate = 260;
                that.dialog.css({height: heightCreate});
                that.content.find("#settings").css({display: "block"});
                show = false;
            } else {
                heightCreate = 165;
                that.dialog.css({height: heightCreate});
                that.content.find("#settings").css({display: "none"});
                show = true;
            }
            return false;
        });

        var a;
        this.content.find("#buttonDbConnection").on("click", function () {
            a = new FormDesigner.main.DialogDBConnection(null);
            a.onClick = function (option) {
                a.dialog.dialog("close").remove();
                that.var_dbconnection = option.value;
                that.content.find("#buttonDbConnection").text(option.label);
            };
            return false;
        });
        this.content.find("#buttonSql").on("click", function () {
            a = new FormDesigner.main.DialogSql(null);
            a.onSave = function () {
                that.var_sql = a.editor.getValue();
                that.content.find("#buttonSql").text(a.editor.getValue());
            };
            a.editor.setValue(that.var_sql);
            return false;
        });
        this.content.find("#buttonOptions").on("click", function () {
            a = new FormDesigner.main.DialogOptions(null, that.var_accepted_values);
            a.onApply = function () {
                that.var_accepted_values = a.getOptions();
                that.content.find("#buttonOptions").text(JSON.stringify(a.getOptions()));
            };
            if (that.content.find("#var_field_type").val() === "boolean") {
                a.setOptionsBoolean();
            }
            return false;
        });
        this.content.find("#buttonInputDocument").on("click", function () {
            a = new FormDesigner.main.DialogInputDocument(null);
            a.onClick = function (option) {
                a.dialog.dialog("close").remove();
                that.inp_doc_uid = option.value;
                that.content.find("#buttonInputDocument").text(option.label);
            };
            return false;
        });

        this.dialog = $("<div title='" + "Create/Select Variable".translate() + "'></div>");
        this.dialog.dialog({
            appendTo: this.appendTo ? this.appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 500,
            height: 195,
            resizable: false,
            position: { my: "center top+50", at: "center top", of: window },
            close: function (event, ui) {
                that.onClose(event, ui);
                that.dialog.remove();
            }
        });
        this.dialog.append(this.optionSelect);
        this.dialog.append(this.content);
        this.dialog.append(this.selectVariable.dialog);
        this.dialog.append(this.buttons);
        this.dialog.append(this.add);
        FormDesigner.main.DialogStyle(this.dialog);

        $.validkeys(that.content.find("#var_name")[0], ['isbackspace', 'isnumber', 'isletter', 'isunderscore']);
        that.content.find("#var_name").focus();

        //data
        that.content.find("#var_field_type").on("change", function () {
            that.setFieldsVisibility(this.value);
        });
        var types = [{
            value: "string", label: "String"
        }, {
            value: "integer", label: "Integer"
        }, {
            value: "float", label: "Float"
        }, {
            value: "boolean", label: "Boolean"
        }, {
            value: "datetime", label: "Datetime"
        }, {
            value: "grid", label: "Grid"
        }, {
            value: "array", label: "Array"
        }, {
            value: "file", label: "File"
        }, {
            value: "multiplefile", label: "Multiple File"
        }
        ];
        that.content.find("#var_field_type").empty();
        for (var i = 0; i < types.length; i++) {
            $.validDataTypeAndControlType(types[i].value, that.type, function () {
                that.addItem(types[i]);
            });
        }
        that.setFieldsVisibility(that.content.find("#var_field_type").val());
    };
    DialogCreateVariable.prototype.save = function () {
        var that = this;
        if (!that.isValid()) {
            var a = new FormDesigner.main.DialogMessage(null, "alert", "A valid variable starts with a letter or underscore, followed by any number of letters, numbers, or underscores.".translate());
            a.onClose = function () {
            };
            a.onAccept = function () {
            };
            return false;
        }
        if (!that.isValidInputDocument()) {
            var a = new FormDesigner.main.DialogMessage(null, "alert", "The input document is required, please select the value.".translate());
            a.onClose = function () {
            };
            a.onAccept = function () {
            };
            return false;
        }
        $.ajax({
            url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id + "/process-variable",
            data: JSON.stringify({
                var_name: that.content.find("#var_name").val(),
                var_field_type: that.content.find("#var_field_type").val(),
                var_dbconnection: that.var_dbconnection,
                var_sql: that.var_sql,
                var_label: that.content.find("#var_field_type").val(),
                var_default: "",
                var_accepted_values: that.var_accepted_values,
                var_field_size: 10,
                inp_doc_uid: that.inp_doc_uid
            }),
            method: "POST",
            contentType: "application/json",
            beforeSend: function (xhr) {
                xhr.setRequestHeader("Authorization", "Bearer " + PMDesigner.project.keys.access_token);
            },
            success: function (variable) {
                that.dialog.dialog("close").remove();
                that.onSave(variable);
            },
            error: function (responses) {
                var msg;
                try {
                    msg = JSON.parse(responses.responseText).error.message;
                } catch (e) {
                    msg = "Error";
                }
                var a = new FormDesigner.main.DialogMessage(null, "alert", msg);
                a.onClose = function () {
                };
                a.onAccept = function () {
                };
            }
        });
    };
    DialogCreateVariable.prototype.isValid = function () {
        return /^[a-zA-Z_\x7f-\xff]+[a-zA-Z0-9_\x7f-\xff]*$/.test(this.content.find("#var_name").val());
    };
    DialogCreateVariable.prototype.isValidInputDocument = function () {
        if (this.content.find("#var_field_type").val() === "file" || this.content.find("#var_field_type").val() === "multipleFile") {
            return this.inp_doc_uid !== "";
        }
        return true;
    };
    DialogCreateVariable.prototype.setVarName = function (var_name) {
        this.content.find("#var_name").val(var_name);
    };
    DialogCreateVariable.prototype.addItem = function (type) {
        var that = this;
        that.content.find("#var_field_type").append("<option value='" + type.value + "'>" + type.label + "</option>");
    };
    DialogCreateVariable.prototype.setFieldsVisibility = function (type) {
        var that = this;
        that.content.find("#var_dbconnection").show();
        that.content.find("#var_sql").show();
        that.content.find("#var_accepted_values").show();
        that.content.find("#inp_doc_uid").hide();
        if (type === "boolean") {
            that.content.find("#var_dbconnection").hide();
            that.content.find("#var_sql").hide();
            that.var_accepted_values = [{value: "1", label: "true"}, {value: "0", label: "false"}];
            that.content.find("#buttonOptions").text(JSON.stringify(that.var_accepted_values));
        }
        if (type === "datetime" || type === "grid" || type === "file" || type === "multiplefile") {
            that.content.find("#var_dbconnection").hide();
            that.content.find("#var_sql").hide();
            that.content.find("#var_accepted_values").hide();
        }
        if (type === "file") {
            that.content.find("#inp_doc_uid").show();
        }
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogCreateVariable', DialogCreateVariable);
}());
(function () {
    var DialogMessage = function (appendTo, type, message) {
        this.type = type;
        this.message = message;
        this.onAccept = new Function();
        this.onClose = new Function();
        DialogMessage.prototype.init.call(this, appendTo);
    };
    DialogMessage.prototype.init = function (appendTo) {
        var that = this,
            title;
        this.accept = $("<a href='#' class='fd-button fd-button-success'>" + "Ok".translate() + "</a>");
        this.accept.on("click", function () {
            that.onAccept();
            that.dialog.dialog("close");
            return false;
        });
        this.buttons = $("<div class='fd-button-panel'><div></div></div>");
        this.buttons.find("div:nth-child(1)").append(this.accept);

        title = that.type === "success"? "Information" : "Errors";
        this.dialog = $("<div title='" + title.translate() + "'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 470,
            height: 170,
            resizable: false,
            close: function (event, ui) {
                that.onClose();
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog, this.type);

        this.dialog.append("<div style='font-size:14px;margin:20px;text-align:center'>" +
            this.message +
            "</div>");
        this.dialog.append(this.buttons);
        this.accept.focus();
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogMessage', DialogMessage);
}());

(function () {
    var DialogConfirm = function (appendTo, type, message) {
        this.type = type;
        this.message = message;
        this.onAccept = new Function();
        this.onCancel = new Function();
        this.onClose = new Function();
        DialogConfirm.prototype.init.call(this, appendTo);
    };
    DialogConfirm.prototype.init = function (appendTo) {
        var that = this;
        this.accept = $("<a href='#' class='fd-button fd-button-yes'>" + "Yes".translate() + "</a>");
        this.accept.on("click", function () {
            that.onAccept();
            that.dialog.dialog("close");
            return false;
        });
        this.cancel = $("<a href='#' class='fd-button fd-button-no'>" + "No".translate() + "</a>");
        this.cancel.on("click", function () {
            that.onCancel();
            that.dialog.dialog("close");
            return false;
        });
        this.buttons = $("<div class='fd-button-panel'><div></div></div>");
        this.buttons.find("div:nth-child(1)").append(this.cancel);
        this.buttons.find("div:nth-child(1)").append(this.accept);

        this.dialog = $("<div title='" + "Confirm".translate() + "'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 470,
            height: 170,
            resizable: false,
            close: function (event, ui) {
                that.dialog.remove();
                that.onClose();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog, this.type);

        this.dialog.append("<div style='font-size:14px;margin:20px;'>" +
            this.message +
            "</div>");
        this.dialog.append(this.buttons);
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogConfirm', DialogConfirm);
}());
(function () {
    var DialogInputDocument = function (appendTo) {
        this.onClose = new Function();
        this.onLoad = new Function();
        this.onClick = new Function();
        DialogInputDocument.prototype.init.call(this, appendTo);
    };
    DialogInputDocument.prototype.init = function (appendTo) {
        var that = this;
        this.dialog = $("<div title='" + "Input Documents".translate() + "' style='padding:10px;'></div>");
        this.dialog.dialog({
            appendTo: appendTo ? appendTo : document.body,
            modal: true,
            autoOpen: true,
            width: 500,
            height: 400,
            resizable: false,
            close: function (event, ui) {
                that.onClose(event, ui);
                that.dialog.remove();
            }
        });
        FormDesigner.main.DialogStyle(this.dialog);

        this.search = $("<p>" + "Search".translate() + " : <input type='text'/><img src='" + $.imgUrl + "fd-refresh.png' style='margin-top:-4px;vertical-align:middle;cursor: pointer;' title='" + "refresh".translate() + "'></p>");
        this.search.find(">input").on("keyup", function () {
            var input = this;
            that.body.find(">div").each(function (i, e) {
                var sw = false;
                $(e).find(">div").each(function (i, e) {
                    sw = sw || $(e).text().toLowerCase().indexOf(input.value.toLowerCase()) > -1;
                });
                if (sw)
                    $(e).show();
                else
                    $(e).hide();
            });
        });
        this.search.find("img").on("click", function () {
            that.load();
        });
        this.body = $("<div style='overflow-x:hidden;border:1px solid #bbb;height:305px;'></div>");
        this.dialog.append(this.search);
        this.dialog.append(this.body);
        this.load();
    };
    DialogInputDocument.prototype.load = function () {
        var that = this;
        this.body.find(">div").remove();
        var restProxy = new PMRestClient({
            endpoint: "input-documents",
            typeRequest: "get",
            functionSuccess: function (xhr, response) {
                var resp;
                for (var i = 0; i < response.length; i++) {
                    resp = response[i];
                    resp["label"] = response[i].inp_doc_title;
                    resp["value"] = response[i].inp_doc_uid;
                    that.addItem(resp);
                }
                that.onLoad(response);
            }
        });
        restProxy.executeRestClient();
    };
    DialogInputDocument.prototype.addItem = function (variable) {
        var that = this;
        var item = $("<div class='fd-list' style='cursor:pointer;'>" +
            "<div style='width:auto;display:inline-block;color:rgb(33, 54, 109);'>" + variable.label + "</div>" +
            "</div>");
        item.on("click", function () {
            that.onClick(variable);
        });
        this.body.append(item);
        return item;
    };
    FormDesigner.extendNamespace('FormDesigner.main.DialogInputDocument', DialogInputDocument);
}());
(function () {
    /**
     * PopOver for PM Dynaform's designer.
     * @param {Object} settings An object containing the config options for the new PopOver.
     * @cfg {String|JQuery Object| Element} body The content for the PopOver, it casn be an HTML string, a JQuery Object
     * or an Element.
     * @cfg {String} [class=""] The class name for the popover, useful for applying custom styles through a css class.
     * @cfg {String} [placement=PMPopOver.PLACEMENT.RIGHT] A string that determines the placement of the popover related
     * to its target element. Please Use one of the keys defined under PMPopOver.PLACEMENT.
     *
     * @constructor
     */
    var PMPopOver = function (settings) {
        this._body = null;
        this._targetElement = null;
        this._dom = {};
        this._class = null;
        this._placement = null;
        this._visible = false;

        PMPopOver.prototype._init.call(this, settings);
    };
    /**
     * All the possible values for placement config option.
     * @type {{TOP: string, RIGHT: string, BOTTOM: string, LEFT: string}}
     */
    PMPopOver.PLACEMENT = {
        TOP: 'top',
        RIGHT: 'right',
        BOTTOM: 'bottom',
        LEFT: 'left'
    };
    /**
     * Initialize the class
     * @param settings
     * @private
     */
    PMPopOver.prototype._init = function (settings) {
        settings = $.extend({
            body: "",
            class: "",
            placement: PMPopOver.PLACEMENT.RIGHT
        }, settings);

        this._placement = settings.placement;
        this._class = settings.class;

        this.setBody(settings.body)
            .setTargetElement(settings.targetElement);
    };
    /**
     * Sets the element the PopOver belongs to.
     * @param targetElement
     * @chainable
     */
    PMPopOver.prototype.setTargetElement = function (targetElement) {
        if (!targetElement) {
            throw new Error('setTargetElement(): Invalid parameter.');
        }

        this._clickHandler = this._clickHandler || this.toggleVisible.bind(this);

        if (this._targetElement) {
            $(this._targetElement).off("click", this._clickHandler);
        }

        this._targetElement = targetElement;

        $(this._targetElement).on("click", this._clickHandler);

        return this;
    };
    /**
     * Sets the PopOver body.
     * @param body
     * @chainable
     */
    PMPopOver.prototype.setBody = function (body) {
        if (!(body instanceof Element || body instanceof jQuery || typeof body === 'string')) {
            throw new Error("setBody(): The parameter must be a DOM Element, jQuery element or a string.");
        }

        this._body = body;

        if (this._dom.content) {
            $(this._dom.content).empty().append(body);
        }

        return this;
    };
    /**
     * Returns the position of the PopOver target element.
     * @returns {Object}
     * @private
     */
    PMPopOver.prototype._getTargetPosition = function () {
        var $element = this._targetElement instanceof jQuery ? this._targetElement : $(this._targetElement),
            element = this._targetElement instanceof jQuery ? this._targetElement.get(0) : this._targetElement;

        return $.extend(element.getBoundingClientRect ? element.getBoundingClientRect() : {
            width: element.offsetWidth,
            height: element.offsetHeight
        }, $element.offset());
    };
    /**
     * Returns the final position for the popover.
     * @returns {{top: *, left: *}}
     * @private
     */
    PMPopOver.prototype._getPosition = function () {
        var targetPosition = this._getTargetPosition(),
            arrowOffset = 10,
            html = this.getHTML(),
            htmlWidth,
            htmlHeight,
            placement,
            top,
            left;

        document.body.appendChild(html);
        html.style.display = 'block';

        htmlWidth = html.offsetWidth;
        htmlHeight = html.offsetHeight;
        placement = this._placement;

        switch (placement) {
            case PMPopOver.PLACEMENT.TOP:
                top = targetPosition.top - htmlHeight - arrowOffset;
                left = targetPosition.left + (targetPosition.width / 2) - (htmlWidth / 2);
                break;
            case PMPopOver.PLACEMENT.RIGHT:
                top = targetPosition.top + (targetPosition.height / 2) - (htmlHeight / 2);
                left = targetPosition.left + targetPosition.width + arrowOffset;
                break;
            case PMPopOver.PLACEMENT.BOTTOM:
                top = targetPosition.top + targetPosition.height + arrowOffset;
                left = targetPosition.left + (targetPosition.width / 2) - (htmlWidth / 2);
                break;
            case PMPopOver.PLACEMENT.LEFT:
                top = targetPosition.top + (targetPosition.height / 2) - (htmlHeight / 2);
                left = targetPosition.left - htmlWidth - arrowOffset;
                break;
            default:
                throw new Error('_getPosition(): Invalid placement parameter.');
        }

        return {
            top: top,
            left: left
        };
    };
    /**
     * Displays the PopOver.
     * @chainable
     * @return {body}
     */
    PMPopOver.prototype.show = function () {
        var position = this._getPosition();

        $(this._html).removeClass("top right bottom left").addClass(this._placement).addClass("in");
        this._html.style.top = position.top + 'px';
        this._html.style.left = position.left + 'px';

        this._visible = true;

        return this;
    };
    /**
     * Hides the PopOver.
     * @chainable
     * @return {body}
     */
    PMPopOver.prototype.hide = function () {
        if (this._html) {
            $(this._html).fadeOut(150, "linear", function () {
                this.style.display = 'none';
                $(this).removeClass('in');
            });
        }

        this._visible = false;

        return this;
    };
    /**
     * Toggles the PopOver visibility.
     * @chainable
     */
    PMPopOver.prototype.toggleVisible = function () {
        return this._visible ? this.hide() : this.show();
    };
    /**
     * Creates the PopOver HTML.
     * @chainable
     * @return {body}
     * @private
     */
    PMPopOver.prototype._createHTML = function () {
        var container,
            arrow,
            content;

        if (this._html) {
            return this;
        }

        container = document.createElement('div');
        arrow = document.createElement('div');
        content = document.createElement('div');

        container.className = "mafe-popover fade " + this._class;
        arrow.className = "arrow";
        content.className = "mafe-popover-content";

        container.appendChild(arrow);
        container.appendChild(content);

        this._dom.container = container;
        this._dom.arrow = arrow;
        this._dom.content = content;

        this._html = container;

        this.setBody(this._body);

        this._html.addEventListener("mousedown", function (e) {
            e.stopPropagation();
        });

        document.addEventListener("mousedown", this.hide.bind(this), false);

        return this;
    };
    /**
     * Returns the PopOver HTML.
     * @returns {Element}
     */
    PMPopOver.prototype.getHTML = function () {
        if (!this._html) {
            this._createHTML();
        }

        return this._html;
    };

    FormDesigner.extendNamespace('FormDesigner.main.PMPopOver', PMPopOver);
})();

/**
 * @class ItemRule
 * @param object
 * @constructor
 */
var ItemRule = function (object) {
    /**
     * Code of the Rule
     * @type {String}
     */
    this.code = null;
    /**
     * Type Shape
     * @type {String}
     */
    this.type = null;
    /**
     * Category of the Rule (BPMN | ENGINE)
     * @type {String}
     */
    this.category = null;
    /**
     * Type of alert (Error | Warning)
     * @type {String}
     */
    this.severity = null;
    /**
     * Description rule
     * @type {String}
     */
    this.description = null;
    /**
     * Criteria of validation
     * @type {Function}
     */
    this.criteria = null;

    this.init(object);
};
/**
 * Inicialize ItemRules.
 * @param object
 */
ItemRule.prototype.init = function (object) {
    if (typeof object === "object") {
        this.setCode(object.code);
        this.setTypeElement(object.type);
        this.setCategory("BPMN");
        this.setSeverity(object.severity);
        this.setDescription(object.description);
        this.setCriteria(object.criteria);
    }
};
/**
 * Get code rule
 * @returns {String}
 */
ItemRule.prototype.getCode = function () {
    return this.code;
};
/**
 * Get type rule
 * @returns {String}
 */
ItemRule.prototype.getTypeElement = function () {
    return this.type;
};
/**
 * Get category
 * @returns {String}
 */
ItemRule.prototype.getCategory = function () {
    return this.category;
};
/**
 * Get severity
 * @returns {String}
 */
ItemRule.prototype.getSeverity = function () {
    return this.severity;
};
/**
 * Get description
 * @returns {String}
 */
ItemRule.prototype.getDescription = function () {
    return this.description;
};
/**
 * Get criteria
 * @returns {function}
 */
ItemRule.prototype.getCriteria = function () {
    return this.criteria;
};
/**
 * Validate code
 * @param codeRule {String}
 * @returns {Boolean}
 */
ItemRule.prototype.isCodeRule = function (codeRule) {
    var isCode = false;
    if (codeRule === this.code) {
        isCode = true;
    }
    return isCode;
};
/**
 * Set code
 * @param code
 * @returns {ItemRule}
 */
ItemRule.prototype.setCode = function (code) {
    if (code && typeof code === "string") {
        this.code = code;
    }
    return this;
};
/**
 * Set type element
 * @param typeElement {String}
 * @returns {ItemRule}
 */
ItemRule.prototype.setTypeElement = function (typeElement) {
    if (typeElement && typeof typeElement === "string") {
        this.type = typeElement;
    }
    return this;
};
/**
 * Set type element
 * @param typeElement {String}
 * @returns {ItemRule}
 */
ItemRule.prototype.setCategory = function (category) {
    if (category && typeof category === "string") {
        this.category = category;
    }
    return this;
};
/**
 * Set severity
 * @param severity {String}
 * @returns {ItemRule}
 */
ItemRule.prototype.setSeverity = function (severity) {
    if (severity && typeof severity === "string") {
        this.severity = severity;
    }
    return this;
};
/**
 * Set description
 * @param description {String}
 * @returns {ItemRule}
 */
ItemRule.prototype.setDescription = function (description) {
    if (description && typeof description === "string") {
        this.description = description;
    }
    return this;
};
/**
 * Set criteria
 * @param criteria {Function}
 * @returns {ItemRule}
 */
ItemRule.prototype.setCriteria = function (criteria) {
    if (criteria && typeof criteria === "function") {
        this.criteria = criteria;
    }
    return this;
};
/**
 * @class CollectionRules
 * @param collectionRules
 * @constructor
 */
var CollectionRules = function (collectionRules) {
    /**
     * Type Shape
     * @type {String}
     */
    this.typeShape = null;
    /**
     * Array of Rules
     * @type {Rules}
     */
    this.rules = new PMUI.util.ArrayList();
    this.init(collectionRules);
};
/**
 * Inicialize CollectionRules
 * @param collectionRules
 */
CollectionRules.prototype.init = function (collectionRules) {
    if (typeof collectionRules === "object") {
        this.setTypeShape(collectionRules.typeShape);
        this.loadRules(collectionRules.rules);
    }
};
/**
 * Get type shape
 * @returns {String}
 */
CollectionRules.prototype.getTypeShape = function () {
    return this.typeShape;
};
/**
 * Validate type shape
 * @param typeShape
 * @returns {Boolean}
 */
CollectionRules.prototype.isTypeShape = function (typeShape) {
    var isType = false;
    if (typeShape === this.getTypeShape()) {
        isType = true;
    }
    return isType;
};
/**
 * Get Array Rules
 * @returns {Array Rules}
 */
CollectionRules.prototype.getRules = function () {
    return this.rules;
};
/**
 * Get Item Rule
 * @returns {ItemRule}
 */
CollectionRules.prototype.getItemRule = function (codeRule) {
    var itemRule,
        itemsRules;
    if (codeRule) {
        itemsRules = this.getRules();
        itemRule = itemsRules.find("code", codeRule);
    }
    return itemRule;
};
/**
 * Set type shape
 * @param typeShape
 * @returns {CollectionRules}
 */
CollectionRules.prototype.setTypeShape = function (typeShape) {
    this.typeShape = (typeShape && typeof typeShape === "string") ? typeShape : this.typeShape;
    return this;
};
/**
 * Set array rules
 * @param arrRules
 * @returns {CollectionRules}
 */
CollectionRules.prototype.setRules = function (arrRules) {
    if (arrRules && Array.isArray(arrRules)) {
        this.rules = arrRules;
    }
    return this;
};
/**
 * Load rules array
 * @param arrRules
 * @returns {CollectionRules}
 */
CollectionRules.prototype.loadRules = function (arrRules) {
    var itemRule,
        i,
        max;
    if (arrRules && Array.isArray(arrRules) && arrRules.length > 0) {
        max = arrRules.length;
        for (i = 0; i < max; i += 1) {
            itemRule = new ItemRule(arrRules[i]);
            this.rules.insert(itemRule);
        }
    }
    return this;
};
/**
 * Add new ItemRule object
 * @param itemRule {Object}
 */
CollectionRules.prototype.addItemRule = function (itemRule) {
    if (itemRule && typeof itemRule === "object") {
        this.rules.insert(itemRule);
    }
    return this;
};
/**
 * Remove ItemRule object
 * @param itemRule
 * @returns {CollectionRules}
 */
CollectionRules.prototype.removeItemRule = function (itemRule) {
    if (itemRule && typeof itemRule === "object") {
        this.rules.remove(itemRule);
    }
    return this;
};
/**
 * @class ModelRules
 * @param object
 * @constructor
 */
var ModelRules = function (object, status) {
    /**
     * Status Rules
     * @type {Boolean}
     */
    this.enable = true;
    /**
     * Items collection
     * @type {PMUI.util.ArrayList}
     */
    this.itemsCollection = new PMUI.util.ArrayList();
    this.init(object, status);
};
/**
 * Inicialize Model Rules
 * @param object
 */
ModelRules.prototype.init = function (object, status) {
    var prop;
    this.setStatus(status);
    if (object && typeof object === "object") {
        for (prop in object) {
            this.enable
            this.loadCollection(prop, object[prop]);
        }
    }
};
/**
 * Load Collection Rules
 * @param type
 * @param arrRules
 * @returns {ModelRules}
 */
ModelRules.prototype.loadCollection = function (type, arrRules) {
    var collectionObject;
    if (type && arrRules) {
        if (typeof type === "string" && Array.isArray(arrRules)) {
            collectionObject = new CollectionRules({
                'typeShape': type,
                'rules': arrRules
            });
            this.itemsCollection.insert(collectionObject);
        }
    }
    return this;
};
/**
 * Get the status of the enable property (true | false)
 * @returns {Boolean}
 */
ModelRules.prototype.getStatus = function () {
    return this.enable;
};
/**
 * Get Items Collection Arralist
 * @returns {PMUI.util.ArrayList}
 */
ModelRules.prototype.getItemsCollection = function () {
    return this.itemsCollection;
};
/**
 * Set the status of the enable property
 * @param status
 * @returns {ModelRules}
 */
ModelRules.prototype.setStatus = function (status) {
    this.enable = (typeof status === "boolean") ? status : this.enable;
    return this;
};

/**
 * Get Collection Rules for type Bpmn Element
 * Example:
 *          PMDesigner.modelRules.getCollectionType('bpmnActivity');
 * @param type {String}
 * @returns CollectionRules {PMUI.util.ArrayList}
 */
ModelRules.prototype.getCollectionType = function (type) {
    var collection,
        i,
        itemCollection,
        max;
    if (type && typeof type === "string") {
        max = this.itemsCollection.getSize();
        for (i = 0; i < max; i += 1) {
            itemCollection = this.itemsCollection.get(i);
            if (itemCollection.isTypeShape(type)) {
                collection = itemCollection.getRules();
                break;
            }
        }
    }
    return collection;
};
/**
 * Add Item Rule
 * Example:
 *          PMDesigner.modelRules.addItemRuleToCollection('bpmnActivity', {
 *                  code: '123456',
 *                  description: 'This is a Message of Error'.translate(),
 *                  type: 'bpmnActivity',
 *                  severity: 'Warning', {'Warning | Error'}
 *                  criteria: function (shape, error) {
 *                       //Validation Rule
*                        shape.addErrorLog(error);
 *                  }
 *          });
 * @param type {String}
 * @param item {Object}
 * @returns {ModelRules}
 */
ModelRules.prototype.addItemRuleToCollection = function (type, item) {
    var itemCollection,
        itemRule,
        max,
        index = 0;
    if (type && item) {
        max = this.itemsCollection.getSize();
        while (index < max) {
            itemCollection = this.itemsCollection.get(index);
            if (itemCollection.isTypeShape(type)) {
                itemRule = new ItemRule(item);
                itemCollection.addItemRule(itemRule);
                index = max;
            }
            index += 1;
        }
    }
    return this;
};
/**
 * Remove Item Rule
 * Example:
 *          PMDesigner.modelRules.removeItemRuleFromCollection('123465');
 * @param code {String}
 * @returns {ModelRules}
 */
ModelRules.prototype.removeItemRuleFromCollection = function (code) {
    var itemCollection,
        itemRule,
        max,
        index = 0;
    if (code && typeof code === "string") {
        max = this.itemsCollection.getSize();
        while (index < max) {
            itemCollection = this.itemsCollection.get(index);
            itemRule = itemCollection.getItemRule(code);
            if (itemRule) {
                itemCollection.removeItemRule(itemRule);
                index = max;
            }
            index += 1;
        }
    }
    return this;
};
/**
 * Add Collection Rules
 * Example:
 *          PMDesigner.modelRules.addItemRuleToCollection('bpmnActivity'
 *              [
 *                  {Object ItemRule},
 *                  {Object ItemRule},
 *                  {Object ItemRule}
 *              ]
 *          );
 * @param typeShape {String}
 * @param collectionRules {Array}
 * @returns {ModelRules}
 */
ModelRules.prototype.addCollectionRules = function (typeShape, arrayRules) {
    var max,
        i;
    if (type && arrayRules && Array.isArray(arrayRules)) {
        max = arrayRules.length;
        for (i = 0; i < max; i += 1) {
            this.addItemRuleToCollection(typeShape, arrayRules[i]);
        }
    }
    return this;
};


/**
 * Crown Class
 * @param options
 * @constructor
 */
var Corona = function (options) {
    /**
     * Call Shape Contructor
     */
    PMUI.draw.Shape.call(this, options);
    /**
     * Define parent crown
     * @type {shape}
     */
    this.parent = null;
    /**
     * Define Type Especific of the parent
     * @type {null}
     */
    this.parentType = null;
    /**
     * Define number rows
     * @type {number}
     */
    this.rows = 1;
    /**
     * Define number cols
     * @type {number}
     */
    this.cols = 1;
    /**
     * Define Items of the Crown
     * @type {PMUI.util.ArrayList}
     */
    this.itemsCrown = new PMUI.util.ArrayList();
    /**
     * Event OnMouseOut
     * @type {null}
     */
    this.eventOnMouseOut = null;
    this.init(options);
};
/**
 * Define New Object Shape
 * @type {PMUI.draw.Shape}
 */
Corona.prototype = new PMUI.draw.Shape();
/**
 * Defines the object type
 * @type {String}
 */
Corona.prototype.type = 'Crown';
/**
 * Inicializate Crown
 * @param options
 * @returns {Crown}
 */
Corona.prototype.init = function (options) {
    var config;
    if (typeof options === "object" && !jQuery.isEmptyObject(options)) {
        this.setParent(options.parent);
        this.setParentType(options.parentType);
        config = this.getConfigItems(options.parentType);
        if (config) {
            this.populateItemsCrown(config);
            this.setRows(config.rows);
            this.setCols(config.cols);
        }
    }
    return this;
};
/**
 * Get Parent Crown
 * @returns {shape|PMUI.draw.Shape|*}
 */
Corona.prototype.getParent = function () {
    return this.parent;
};
/**
 * Get Parent Type
 * @returns {null|string|*}
 */
Corona.prototype.getParentType = function () {
    return this.parentType;
};
/**
 * Get Rows
 * @returns {number}
 */
Corona.prototype.getRows = function () {
    return this.rows;
};
/**
 * Get Cols
 * @returns {number}
 */
Corona.prototype.getCols = function () {
    return this.cols;
};
/**
 * Get Items Crown
 * @returns {PMUI.util.ArrayList|*}
 */
Corona.prototype.getItemsCrown = function () {
    return this.itemsCrown;
};
/**
 * Set Parent Crown
 * @param parent
 * @returns {Corona}
 */
Corona.prototype.setParent = function (parent) {
    if (typeof parent === "object" && !jQuery.isEmptyObject(parent)) {
        this.parent = parent;
    }
    return this;
};
/**
 * Set Parent Type
 * @param parentType
 * @returns {Corona}
 */
Corona.prototype.setParentType = function (parentType) {
    if (parentType && typeof parentType === "string") {
        this.parentType = parentType;
    }
    return this;
};
/**
 * Set Number Rows
 * @param rows
 * @returns {Crown}
 */
Corona.prototype.setRows = function (rows) {
    if (rows && rows > 0) {
        this.rows = rows;
    }
    return this;
};
/**
 * Set Number Cols
 * @param cols
 * @returns {Crown}
 */
Corona.prototype.setCols = function (cols) {
    if (cols && cols > 0) {
        this.cols = cols;
    }
    return this;
};
/**
 * Resize crown
 * @returns {Corona}
 */
Corona.prototype.adjustSize = function () {
    var width,
        height,
        itemFirst,
        margin = 4;
    itemFirst = this.getItemsCrown().get(0);
    width = (itemFirst.getWidth() + margin) * this.getCols();
    height = (itemFirst.getHeight() + margin) * this.getRows();
    if (this.html) {
        this.html.style.width = width + "px";
        this.html.style.height = height + 'px';
        this.updatePosition();
    }
    return this;
};
/**
 * Changes position
 * @returns {Corona}
 */
Corona.prototype.updatePosition = function () {
    jQuery(this.html).position({
        of: jQuery(this.parent.html),
        my: "left top",
        at: "right top",
        collision: 'none'
    });
    return this;
};
/**
 * Get config Items Crown
 * @param especificType
 * @returns {*}
 */
Corona.prototype.getConfigItems = function (especificType) {
    var configDefault = PMDesigner.configCrown,
        typeShape = this.getParent().getType(),
        configCrown = configDefault[typeShape][especificType];
    return configCrown;
};
/**
 * Populate Crown from previous configuration
 * @param config
 * @returns {Corona}
 */
Corona.prototype.populateItemsCrown = function (config) {
    var order = (config && config.order) || [],
        itemsDefault = PMDesigner.modelCrown.getItemsDefault(),
        itemCrownDefault,
        itemCrown,
        max,
        i;
    if (order && Array.isArray(order) && order.length > 0) {
        max = order.length;
        for (i = 0; i < max; i += 1) {
            itemCrownDefault = itemsDefault.find("id", order[i]);
            itemCrownDefault.parent = this;
            itemCrownDefault.canvas = this.canvas;
            itemCrown = new ItemCrown(itemCrownDefault);
            this.itemsCrown.insert(itemCrown);
        }
    }
    return this;
};
Corona.prototype.isCreatedItems = function () {
    var isCreated = false;
    if (this.countChildrens() === this.getItemsCrown().getSize()) {
        isCreated = true;
    }
    return isCreated
};
/**
 * Paint Crown
 * @returns {Corona}
 */
Corona.prototype.paint = function () {
    if (!this.html) {
        this.createHTML();
    }
    return this;
};
/**
 * Create HTML
 * @returns {Corona}
 */
Corona.prototype.createHTML = function () {
    var htmlParent = this.getCanvas().html,
        htmlCrown,
        htmlRow,
        itemCrown,
        itemsAux = this.getItemsCrown().asArray(),
        cont = 0,
        i;
    this.html = null;
    htmlCrown = this.createHtmlCrown();
    while (itemsAux.length > 0) {
        if (this.getRows() > cont) {
            htmlRow = this.createHtmlRow(cont);
            for (i = 0; i < this.getCols(); i += 1) {
                if (itemsAux.length > 0) {
                    itemCrown = itemsAux.shift();
                    itemCrown = itemCrown.createHtmlItem();
                    itemCrown = itemCrown.attachListeners();
                    htmlRow.appendChild(itemCrown.html);
                } else {
                    break;
                }
            }
            htmlCrown.appendChild(htmlRow);
            cont += 1;
        }
    }
    htmlParent.appendChild(htmlCrown);
    this.html = htmlCrown;
    this.adjustSize();
    return this;
};
/**
 * Create Html Crown
 * @returns {*}
 */
Corona.prototype.createHtmlCrown = function () {
    var htmlCrown = null,
        classCrown = "crown-container";
    PMUI.draw.Shape.prototype.createHTML.call(this);
    htmlCrown = this.html;
    if (htmlCrown) {
        htmlCrown.className = classCrown;
    }
    return htmlCrown;
};
/**
 * Create Html Row
 * @param index
 * @returns {HTMLElement|*}
 */
Corona.prototype.createHtmlRow = function (index) {
    var htmlRow = null,
        classRow = "row";
    htmlRow = PMUI.createHTMLElement("div");
    htmlRow.className = classRow + " " + classRow  + "-" + index;
    return htmlRow;
};
/**
 * Show Crown
 * @returns {Corona}
 */
Corona.prototype.show = function () {
    if (this.isDirtyParentType()) {
        this.updateCrown();
    }
    if (!this.html) {
        this.createHTML();
    } else {
        jQuery(this.html).show();
        this.updatePosition();
        this.setZOrder(this.getParent().getZOrder() + 1 || 1);
    }
    return this;
};
/**
 * Hide Crown
 * @returns {Corona}
 */
Corona.prototype.hide = function () {
    if (this && this.html) {
        jQuery(this.html).hide();
    }
    return this;
};
/**
 * Destroy Crown Content
 * @returns {Corona}
 */
Corona.prototype.destroy = function () {
    if (this && this.html) {
        jQuery(this.html).empty();
        jQuery(this.html).remove();
        this.html = null;
    }
    this.getItemsCrown().clear();
    return this;
};
/**
 * Update Crown when the config changes
 * @returns {Corona}
 */
Corona.prototype.updateCrown = function () {
    var config,
        especificType = this.getParent().getEspecificType();
    this.destroy();
    this.setParentType(especificType);
    config = this.getConfigItems(especificType);
    this.populateItemsCrown(config);
    this.setRows(config.rows);
    this.setCols(config.cols);
    return this;
};
/**
 * Validate if the shape type change
 * @returns {boolean}
 */
Corona.prototype.isDirtyParentType = function () {
    var isDirty = false;
    if (this.getParent().getType() !== "PMActivity" && this.getParentType() !== this.getParent().getEspecificType()) {
        isDirty = true;
    }
    return isDirty;
};

/**
 * Item Crown Class
 * @param options
 * @constructor
 */
var ItemCrown = function (options) {
    /**
     * Call Shape Constructor
     */
    PMUI.draw.Shape.call(this, options);
    /**
     * Define Id ItemCrown
     * @type {null}
     */
    this.id = null;
    /**
     * Parent Item Crown
     * @type {null}
     */
    this.parent = null;
    /**
     * Name Item Crown
     * @type {string}
     */
    this.name = null;
    /**
     * Class Name Item Crown
     * @type {string}
     */
    this.className = null;
    /**
     * Width of the Item Crown
     * @type {number}
     */
    this.width = 22;
    /**
     * Height of the Item Crown
     * @type {number}
     */
    this.height = 22;
    /**
     * Event OnClick
     * @type {null}
     */
    this.eventOnClick = null;
    /**
     * Event OnMouseDown
     * @type {null}
     */
    this.eventOnMouseDown = null;
    /**
     * Event OnMouseUp
     * @type {null}
     */
    this.eventOnMouseUp = null;
    /**
     * Event OnMouseMove
     * @type {null}
     */
    this.eventOnMouseMove = null;
    /**
     * Event OnMouseOut
     * @type {null}
     */
    this.eventOnMouseOut = null;
    this.init(options);
};
/**
 * Define new Object Shape
 * @type {PMUI.draw.Shape}
 */
ItemCrown.prototype = new PMUI.draw.Shape();
/**
 * Defines the object type
 * @type {String}
 */
ItemCrown.prototype.type = 'ItemCrown';
/**
 * Inicialize Item Crown
 * @param options
 * @returns {ItemCrown}
 */
ItemCrown.prototype.init = function (options) {
    if (typeof options === "object") {
        this.setId(options.id);
        this.setParent(options.parent);
        this.setName(options.name);
        this.setClassName(options.className);
        this.setEventOnClick(options.eventOnClick);
        this.setEventOnMouseDown(options.eventOnMouseDown);
        this.setEventOnMouseUp(options.eventOnMouseUp);
        this.setEventOnMouseMove(options.eventOnMouseMove);
        this.setEventOnMouseOut(options.eventOnMouseOut);
    }
    return this;
};
/**
 * Get Id Item Crown
 * @returns {null}
 */
ItemCrown.prototype.getId = function () {
    return this.id;
};
/**
 * Get parent Item Crown
 * @returns {null}
 */
ItemCrown.prototype.getParent = function () {
    return this.parent;
};
/**
 * Get Name
 * @returns {null|string|*}
 */
ItemCrown.prototype.getName = function () {
    return this.name;
};
/**
 * Get Class Name Style
 * @returns {null|*}
 */
ItemCrown.prototype.getClassName = function () {
    return this.className;
};
/**
 * Get Width
 * @returns {number|*}
 */
ItemCrown.prototype.getWidth = function () {
    return this.width;
};
/**
 * Get Height
 * @returns {number|*}
 */
ItemCrown.prototype.getHeight = function () {
    return this.height;
};
/**
 * Get Function EventOnclick
 * @returns {null|*}
 */
ItemCrown.prototype.getEventOnClick = function () {
    return this.eventOnClick;
};
/**
 * Get Function EventOnMouseDown
 * @returns {null|*}
 */
ItemCrown.prototype.getEventOnMouseDown = function () {
    return this.eventOnMouseDown;
};
/**
 * Set Id ItemCrown
 * @param id
 * @returns {ItemCrown}
 */
ItemCrown.prototype.setId = function (id) {
    if (id && typeof id === "string") {
        this.id = id;
    }
    return this;
};
/**
 * Set Parent ItemCrown
 * @param parent
 * @returns {ItemCrown}
 */
ItemCrown.prototype.setParent = function (parent) {
    if (typeof parent === "object" && !jQuery.isEmptyObject(parent)) {
        this.parent = parent;
    }
    return this;
};
/**
 * Set Name
 * @param name
 * @returns {ItemCrown}
 */
ItemCrown.prototype.setName = function (name) {
    if (name && typeof name === "string") {
        this.name = name;
    }
    return this;
};
/**
 * Set ClassName Style
 * @param className
 * @returns {ItemCrown}
 */
ItemCrown.prototype.setClassName = function (className) {
    if (className && typeof className === "string") {
        this.className = className
    }
    return this;
};
/**
 * Set Width
 * @param width
 * @returns {ItemCrown}
 */
ItemCrown.prototype.setWidth = function (width) {
    if (width) {
        this.width = width;
    }
    return this;
};
/**
 * Set Height
 * @param height
 * @returns {ItemCrown}
 */
ItemCrown.prototype.setHeight = function (height) {
    if (height) {
        this.height = height;
    }
    return this;
};
/**
 * Set Function EventOnClick
 * @param func
 * @returns {ItemCrown}
 */
ItemCrown.prototype.setEventOnClick = function (func) {
    if (func && typeof func === "function") {
        this.eventOnClick = func;
    }
    return this;
};
/**
 * Set Function EventOnMouseDown
 * @param func
 * @returns {ItemCrown}
 */
ItemCrown.prototype.setEventOnMouseDown = function (func) {
    if (func && typeof func === "function") {
        this.eventOnMouseDown = func;
    }
    return this;
};
/**
 * Set Function EventOnMouseUp
 * @param func
 * @returns {ItemCrown}
 */
ItemCrown.prototype.setEventOnMouseUp = function (func) {
    if (func && typeof func === "function") {
        this.eventOnMouseUp = func;
    }
    return this;
};
/**
 * Set Function EventOnMouseMove
 * @param func
 * @returns {ItemCrown}
 */
ItemCrown.prototype.setEventOnMouseMove = function (func) {
    if (func && typeof func === "function") {
        this.eventOnMouseMove = func;
    }
    return this;
};
/**
 * Set Function EventOnMouseOut
 * @param func
 * @returns {ItemCrown}
 */
ItemCrown.prototype.setEventOnMouseOut = function (func) {
    if (func && typeof func === "function") {
        this.eventOnMouseOut = func;
    }
    return this;
};
/**
 * Create HTML Item Crown
 * @returns {*}
 */
ItemCrown.prototype.createHtmlItem = function () {
    var htmlItemCrown,
        classItemCrown = "item-crown",
        positionDefault = 'relative';
    PMUI.draw.Shape.prototype.createHTML.call(this);
    htmlItemCrown = this.html;
    htmlItemCrown.className = this.getClassName() + " " + classItemCrown;
    htmlItemCrown.title = this.getName();
    htmlItemCrown.style.position = positionDefault;
    htmlItemCrown.style.width = this.getWidth() + "px";
    htmlItemCrown.style.height = this.getHeight() + "px";
    this.html = htmlItemCrown;
    return this;
};
/**
 * Listeners
 * @returns {ItemCrown}
 */
ItemCrown.prototype.attachListeners = function () {
    var htmlItemCrown;
    htmlItemCrown = this.html;
    if (htmlItemCrown) {
        jQuery(htmlItemCrown).click(this.onClick());
        jQuery(htmlItemCrown).mousedown(this.onMouseDown());
        jQuery(htmlItemCrown).mouseup(this.onMouseUp());
        jQuery(htmlItemCrown).mousemove(this.onMouseMove());
        jQuery(htmlItemCrown).mouseout(this.onMouseOut());
    }
    this.html = htmlItemCrown;
    return this;
};
/**
 * OnClick Event
 * @returns {Function}
 */
ItemCrown.prototype.onClick = function () {
    var that = this;
    return function (e) {
        e.stopPropagation();
        e.preventDefault();
        if (that.eventOnClick) {
            that.eventOnClick(that);
        }
    };
};
/**
 * OnMouseDown Event
 * @returns {Function}
 */
ItemCrown.prototype.onMouseDown = function () {
    var that = this;
    return function (e) {
        e.stopPropagation();
        e.preventDefault();
        if (that.eventOnMouseDown) {
            that.eventOnMouseDown(that);
        }
    };
};
/**
 * OnMouseUp Event
 * @returns {Function}
 */
ItemCrown.prototype.onMouseUp = function () {
    var that = this;
    return function (e) {
        e.stopPropagation();
        e.preventDefault();
        if (that.eventOnMouseUp) {
            that.eventOnMouseUp(that);
        }
    };
};
/**
 * OnMouseMove Event
 * @returns {Function}
 */
ItemCrown.prototype.onMouseMove = function () {
    var that = this;
    return function (e) {
        e.stopPropagation();
        e.preventDefault();
        if (that.eventOnMouseMove) {
            that.eventOnMouseMove(that);
        }
    };
};
/**
 * OnMouseOut Event
 * @returns {Function}
 */
ItemCrown.prototype.onMouseOut = function () {
    var that = this;
    return function (e) {
        e.stopPropagation();
        e.preventDefault();
        if (that.eventOnMouseOut) {
            that.eventOnMouseOut(that);
        }
    };
};

/**
 * Class ModelCrown
 * @param options
 * @constructor
 */
var ModelCrown = function (options) {
    this.itemsDefault = PMUI.util.ArrayList();
    this.init(options);
};
/**
 * Initialize ModelCrown
 * @param options
 */
ModelCrown.prototype.init = function (options) {
    if (typeof options === "object" && Object.keys(options).length > 0) {
        this.setItemsDefault(options.items);
    }
    return this;
};
/**
 * Get Items ModelCrown
 * @returns {*|Array}
 */
ModelCrown.prototype.getItemsDefault = function () {
    return this.itemsDefault;
};
/**
 * Set Items ModelCrown
 * @param items {Array of the Objects}
 * @returns {ModelCrown}
 */
ModelCrown.prototype.setItemsDefault = function (items) {
    var itemCrown,
        max,
        i;
    if (items && Array.isArray(items) && items.length > 0) {
        max = items.length;
        for (i = 0; i < max; i += 1) {
            this.itemsDefault.insert(items[i]);
        }
    }
    return this;
};
/**
 * Add New Item Crown
 * Example:
 *          PMDesigner.modelCrown.addItemToCrown({
 *              id: "example-id",
 *              name: "exmaple-name".translate(),
 *              className: "name-class-css",
 *              eventOnClick: function(item) {
 *                  --Your code goes here
 *              },
 *              eventOnMouseDown: function(item) {
 *                  --Your code goes here
 *              }
 *          });
 *
 * @param itemObject {Object}
 * @returns {ModelCrown}
 */
ModelCrown.prototype.addItemToCrown = function (itemObject) {
    if (typeof itemObject === "object" && Object.keys(itemObject).length > 0) {
        this.itemsDefault.insert(itemObject);
    }
    return this;
};
/**
 * Remove ItemCrown
 * Example:
 *          PMDesigner.modelCrown.removeItemFromCrown(idItem);
 *
 * @param idItem {string}
 * @returns {ModelCrown}
 */
ModelCrown.prototype.removeItemFromCrown = function (idItem) {
    var itemCrown;
    if (idItem && typeof idItem === "string") {
        itemCrown = this.itemsDefault.find("id", idItem);
        if (itemCrown) {
            this.itemsDefault.remove(itemCrown);
        }
    }
    return this;
};
var __env = __env || {};__env.USER_GUEST = {"uid":"00000000000000000000000000000002","firstname":"Guest","lastname":"Guest","username":"guest"}; __env.pmVariable = {"regEx":"/^[a-zA-Z\\_]{1}\\w+$/"};

/*----------------------------------********---------------------------------*/
/**features-begins**/
var defaultRules = {
    'bpmn:StartEvent': [
        {
            code: '105101',
            description: 'Start Event must have an outgoing sequence flow'.translate(),
            type: 'bpmnStartEvent',
            severity: 'Error',
            criteria: function (shape, error) {
                var ref = shape.businessObject.elem;
                if (ref && !(ref.outgoing && ref.outgoing.length === 1)) {
                    //add error
                    shape.addErrorLog(error);
                }
            }
        },
        {
            code: '105102',
            description: "Can't connect start event to subprocess".translate(),
            type: 'bpmnStartEvent',
            severity: 'Error',
            criteria: function (shape, error) {
                var ref = shape.businessObject.elem,
                    refArray = ref.outgoing;
                if (ref && refArray && refArray.length === 1) {
                    if (refArray[0].targetRef.$type === "bpmn:SubProcess") {
                        //add error
                        shape.addErrorLog(error);
                    }
                }
            }
        },
        {
            code: '105103',
            description: 'Invalid Connection between elements'.translate(),
            type: 'bpmnStartEvent',
            severity: 'Error',
            criteria: function (shape, error) {
                if(!shape.ValidateConnections()) {
                    //add error
                    shape.addErrorLog(error);
                }
            }
        }
    ],
    'bpmnActivity': [
        {
            code: '105201',
            description: 'Activity must have an incoming sequence flow'.translate(),
            type: 'bpmnActivity',
            severity: 'Error',
            criteria: function (shape, error) {
                var ref = shape.businessObject.elem;
                if (ref && !(ref.incoming && ref.incoming.length > 0 )) {
                    //add error
                    shape.addErrorLog(error);
                }
            }
        },
        {
            code: '105202',
            description: 'Activity must have an outgoing sequence flow'.translate(),
            type: 'bpmnActivity',
            severity: 'Error',
            criteria: function (shape, error) {
                var ref = shape.businessObject.elem;
                if (ref && !(ref.outgoing && ref.outgoing.length === 1)) {
                    //add error
                    shape.addErrorLog(error);
                }
            }
        },
        {
            code: '105203',
            description: 'Invalid Connection between elements'.translate(),
            type: 'bpmnActivity',
            severity: 'Error',
            criteria: function (shape, error) {
                if(!shape.ValidateConnections()) {
                    //add error
                    shape.addErrorLog(error);
                }
            }
        }
    ],
    'bpmn:EndEvent': [
        {
            code: '105301',
            description: 'End event must have an incoming sequence flow'.translate(),
            type: 'bpmnEndEvent',
            severity: 'Error',
            criteria: function (shape, error) {
                var ref = shape.businessObject.elem;
                if (ref && !(ref.incoming && ref.incoming.length > 0)) {
                    //add error
                    shape.addErrorLog(error);
                }
            }
        },
        {
            code: '105302',
            description: 'Invalid Connection between elements'.translate(),
            type: 'bpmnEndEvent',
            severity: 'Error',
            criteria: function (shape, error) {
                if(!shape.ValidateConnections()) {
                    //add error
                    shape.addErrorLog(error);
                }
            }
        }
    ],
    'bpmnIntermediateEvent': [
        {
            code: '105401',
            description: 'Intermediate event must have one or more incoming sequence flow'.translate(),
            type: 'bpmnIntermediateEvent',
            severity: 'Error',
            criteria: function (shape, error) {
                var ref = shape.businessObject.elem;
                if (ref && !(ref.incoming && ref.incoming.length >= 1)) {
                    //add error
                    shape.addErrorLog(error);
                }
            }
        },
        {

            code: '105402',
            description: 'Intermediate event must have an outgoing sequence flow'.translate(),
            type: 'bpmnIntermediateEvent',
            severity: 'Error',
            criteria: function (shape, error) {
                var ref = shape.businessObject.elem;
                if (ref && !(ref.outgoing && ref.outgoing.length === 1)) {
                    //add error
                    shape.addErrorLog(error);
                }
            }
        },
        {
            code: '105403',
            description: 'Invalid Connection between elements'.translate(),
            type: 'bpmnIntermediateEvent',
            severity: 'Error',
            criteria: function (shape, error) {
                if(!shape.ValidateConnections()) {
                    //add error
                    shape.addErrorLog(error);
                }
            }
        }
    ],
    'bpmnGateway': [
        {
            code: '105501',
            description: 'Diverging Gateway must have an incoming sequence flow'.translate(),
            type: 'bpmnGateway',
            severity: 'Error',
            criteria: function (shape, error) {
                if (shape.gat_direction === 'CONVERGING') {
                    error.description = 'Converging Gateway must have two or more incoming sequence flow'.translate();
                    var ref = shape.businessObject.elem;
                    if (ref && !(ref.incoming && ref.incoming.length > 1)) {
                        shape.addErrorLog(error);
                    }
                } else {
                    var ref = shape.businessObject.elem; 
                    if (ref && ref.incoming && ref.incoming.length > 1) {
                        error.description = "Diverging gateways expect to receive only one incoming flow. ".translate()
                            + "Ignore this warning if your flow is planned to reach to this gateway by only one of the incoming flows at a time".translate();
                        shape.addErrorLog(error);
                    } else if (ref && !(ref.incoming && ref.incoming.length === 1)) {
                        //add error
                        shape.addErrorLog(error);
                    }
                }

            }
        },
        {
            code: '105502',
            description: 'Diverging Gateway must have two or more outgoing sequence flow'.translate(),
            type: 'bpmnGateway',
            severity: 'Error',
            criteria: function (shape, error) {
                if (shape.gat_direction === 'CONVERGING') {
                    error.description = 'Converging Gateway must have a outgoing sequence flow'.translate();
                    var ref = shape.businessObject.elem;
                    if (ref && !(ref.outgoing && ref.outgoing.length === 1)) {
                        shape.addErrorLog(error);
                    }
                } else {
                    var ref = shape.businessObject.elem;
                    if (ref && !(ref.outgoing && ref.outgoing.length > 1)) {
                        //add error
                        shape.addErrorLog(error);
                    }
                }
            }
        },
        {
            code: '105503',
            description: 'Invalid Connection between elements'.translate(),
            type: 'bpmnGateway',
            severity: 'Error',
            criteria: function (shape, error) {
                if(!shape.ValidateConnections()) {
                    //add error
                    shape.addErrorLog(error);
                }
            }
        }
    ],
    'bpmnSubProcess': [
        {
            code: '105601',
            description: 'SubProcess must have an incoming sequence flow'.translate(),
            type: 'bpmnSubProcess',
            severity: 'Error',
            criteria: function (shape, error) {
                var ref = shape.businessObject.elem;
                if (ref && !(ref.incoming && ref.incoming.length > 0)) {
                    //add error
                    shape.addErrorLog(error);
                }
            }
        },
        {
            code: '105602',
            description: 'SubProcess must have an outgoing sequence flow'.translate(),
            type: 'bpmnSubProcess',
            severity: 'Error',
            criteria: function (shape, error) {
                var ref = shape.businessObject.elem;
                if (ref && !(ref.outgoing && ref.outgoing.length === 1)) {
                    //add error
                    shape.addErrorLog(error);
                }
            }
        },
        {
            code: '105603',
            description: 'Invalid Connection between elements'.translate(),
            type: 'bpmnSubProcess',
            severity: 'Error',
            criteria: function (shape, error) {
                if(!shape.ValidateConnections()) {
                    //add error
                    shape.addErrorLog(error);
                }
            }
        }
    ]
};
/**features-ends**/
/*----------------------------------********---------------------------------*/
var defaultCrown = {
    items: [
        {
            id: "task",
            name: "Task".translate(),
            className: "mafe-corona-task",
            eventOnMouseDown: function (item) {
                item.canvas.canCreateShape = true;
                item.canvas.canCreateShapeType = 'TASK';
                item.canvas.canCreateShapeClass = 'mafe-toolbar-task';
                item.canvas.connectStartShape = item.parent.parent;
            },
            eventOnMouseOut: function (item) {
                if (item.canvas.canCreateShape) {
                    item.parent.hide();
                }
            }
        },
        {
            id: "gateway",
            name: 'Gateway'.translate(),
            className: 'mafe-corona-gateway-exclusive',
            eventOnClick: function (item) {
                item.parent.hide();
            },
            eventOnMouseDown: function (item) {
                item.canvas.canCreateShape = true;
                item.canvas.canCreateShapeType = 'EXCLUSIVE';
                item.canvas.canCreateShapeClass = 'mafe-toolbar-gateway-exclusive';
                item.canvas.connectStartShape = item.parent.parent;
            },
            eventOnMouseOut: function (item) {
                if (item.canvas.canCreateShape) {
                    item.parent.hide();
                }
            }
        },
        {
            id: "intermediate",
            name: 'Intermediate'.translate(),
            className: 'mafe-corona-intermediate',
            eventOnClick: function (item) {
                item.parent.hide();
            },
            eventOnMouseDown: function (item) {
                item.canvas.canCreateShape = true;
                item.canvas.canCreateShapeType = 'INTERMEDIATE_EMAIL';
                item.canvas.canCreateShapeClass = 'mafe-toolbar-intermediate-send-mesage';
                item.canvas.connectStartShape = item.parent.parent;
            },
            eventOnMouseOut: function (item) {
                if (item.canvas.canCreateShape) {
                    item.parent.hide();
                }
            }
        },
        {
            id: "end",
            name: 'End'.translate(),
            className: 'mafe-corona-end',
            eventOnClick: function (item) {
                item.parent.hide();
            },
            eventOnMouseDown: function (item) {
                item.canvas.canCreateShape = true;
                item.canvas.canCreateShapeType = 'END';
                item.canvas.canCreateShapeClass = 'mafe-toolbar-end';
                item.canvas.connectStartShape = item.parent.parent;
            },
            eventOnMouseOut: function (item) {
                if (item.canvas.canCreateShape) {
                    item.parent.hide();
                }
            }
        },
        {
            id: "flow",
            name: 'Flow'.translate(),
            className: 'mafe-corona-flow',
            eventOnClick: function (item) {
                item.parent.hide();
                item.parent.parent.canvas.hideAllFocusedLabels();
            },
            eventOnMouseDown: function (item) {
                item.canvas.canConnect = true;
                item.canvas.connectStartShape = item.parent.parent;
            }
        },
        {
            id: "properties",
            name: 'Properties'.translate(),
            className: 'mafe-corona-settings',
            eventOnClick: function (item) {
                item.parent.hide();
                PMDesigner.saveAndOpenSettings(item.parent.parent, PMDesigner.shapeProperties);
            }
        },
        {
            id: "delete",
            name: 'Delete'.translate(),
            className: 'mafe-corona-delete',
            eventOnClick: function (item) {
                PMUI.getActiveCanvas().removeElements();
                item.parent.hide();
            }
        }
    ]
};

var configCrown = {
    'PMActivity': {
        'DEFAULT': {
            order: ["task", "gateway", "intermediate", "end", "flow", "properties", "delete"],
            rows: 3,
            cols: 3
        }
    },
    'PMGateway': {
        'PARALLEL': {
            order: ["task", "gateway", "end", "flow", "delete"],
            rows: 3,
            cols: 2
        },
        'EXCLUSIVE': {
            order: ["task", "gateway", "end", "flow", "properties", "delete"],
            rows: 3,
            cols: 2
        },
        'INCLUSIVE': {
            order: ["task", "gateway", "end", "flow", "properties", "delete"],
            rows: 3,
            cols: 2
        }
    },
    'PMEvent': {
        'START_EMPTY': {
            order: ["task", "gateway", "intermediate", "flow", "delete"],
            rows: 3,
            cols: 2
        },
        'START_MESSAGECATCH': {
            order: ["task", "gateway", "intermediate", "flow", "properties", "delete"],
            rows: 3,
            cols: 2
        },
        'START_TIMER': {
            order: ["task", "gateway", "intermediate", "flow", "properties", "delete"],
            rows: 3,
            cols: 2
        },
        'START_CONDITIONAL': {
            order: ["task", "gateway", "intermediate", "flow", "delete"],
            rows: 3,
            cols: 2
        },
        'START_SIGNALCATCH': {
            order: ["task", "gateway", "intermediate", "flow", "delete"],
            rows: 3,
            cols: 2
        },
        'INTERMEDIATE_EMAIL': {
            order: ["task", "gateway", "end", "flow", "properties", "delete"],
            rows: 3,
            cols: 2
        },
        'INTERMEDIATE_MESSAGETHROW': {
            order: ["task", "gateway", "end", "flow", "properties", "delete"],
            rows: 3,
            cols: 2
        },
        'INTERMEDIATE_SIGNALTHROW': {
            order: ["task", "gateway", "end", "flow", "delete"],
            rows: 3,
            cols: 2
        },
        'INTERMEDIATE_MESSAGECATCH': {
            order: ["task", "gateway", "end", "flow", "properties", "delete"],
            rows: 3,
            cols: 2
        },
        'INTERMEDIATE_TIMER': {
            order: ["task", "gateway", "end", "flow", "properties", "delete"],
            rows: 3,
            cols: 2
        },
        'INTERMEDIATE_CONDITIONAL': {
            order: ["task", "gateway", "end", "flow", "delete"],
            rows: 3,
            cols: 2
        },
        'INTERMEDIATE_SIGNALCATCH': {
            order: ["task", "gateway", "end", "flow", "delete"],
            rows: 3,
            cols: 2
        },
        'END_EMPTY': {
            order: ["flow", "delete"],
            rows: 2,
            cols: 1
        },
        'END_EMAIL': {
            order: ["flow", "properties", "delete"],
            rows: 2,
            cols: 2
        },
        'END_MESSAGETHROW': {
            order: ["flow", "properties", "delete"],
            rows: 2,
            cols: 2
        },
        'END_ERRORTHROW': {
            order: ["flow", "delete"],
            rows: 2,
            cols: 1
        },
        'END_SIGNALTHROW': {
            order: ["flow", "delete"],
            rows: 2,
            cols: 1
        },
        'END_TERMINATETHROW': {
            order: ["flow", "delete"],
            rows: 2,
            cols: 1
        }
    },
    'PMPool': {
        'DEFAULT': {
            order: ["delete"],
            rows: 1,
            cols: 1
        }
    },
    'PMParticipant': {
        'DEFAULT': {
            order: ["flow", "delete"],
            rows: 2,
            cols: 1
        }
    },
    'PMArtifact': {
        'TEXT_ANNOTATION': {
            order: ["flow", "delete"],
            rows: 2,
            cols: 1
        },
        'GROUP': {
            order: ["delete"],
            rows: 1,
            cols: 1
        }
    },
    'PMData': {
        'DATAOBJECT': {
            order: ["flow", "delete"],
            rows: 2,
            cols: 1
        },
        'DATAINPUT': {
            order: ["flow", "delete"],
            rows: 2,
            cols: 1
        },
        'DATAOUTPUT': {
            order: ["flow", "delete"],
            rows: 2,
            cols: 1
        },
        'DATASTORE': {
            order: ["flow", "delete"],
            rows: 2,
            cols: 1
        }
    }
};
var PMDesigner = {},
    LANG,
    WORKSPACE,
    SKIN,
    DEFAULT_WINDOW_WIDTH,
    DEFAULT_WINDOW_HEIGHT,
    ENABLED_FEATURES,
    DataDictionary,
    enviromentVariables,
    resizingFrame,
    ViewTaskInformation;

PMDesigner.defaultRules = window.defaultRules ? window.defaultRules : {};
PMDesigner.defaultCrown = window.defaultCrown ? defaultCrown : {};
PMDesigner.configCrown = window.configCrown ? configCrown : {};
PMDesigner.modelRules = new ModelRules(PMDesigner.defaultRules);
PMDesigner.modelCrown = new ModelCrown(PMDesigner.defaultCrown);
PMDesigner.remoteUrl = "";
PMDesigner.moddle = new BpmnModdle();
PMDesigner.bpmnFactory = new BpmnFactory(PMDesigner.moddle);
PMDesigner.keyCodeF5 = 116;
PMDesigner.autoSave = true;
PMDesigner.shapeProperties = function (shape) {
    var typeShape = shape.type;
    switch (typeShape) {
        case "PMActivity":
            PMDesigner.activityProperties(shape);
            break;
        case "PMGateway":
            PMDesigner.gatewayProperties(shape);
            break;
        case "PMEvent":
            shape.eventProperties();
            break;
    }
};

/**
 * function to get the enviroment variables (WORKSPACE, LANG, SKIN)
 */
enviromentVariables = function (variable) {
    var url1, variables, WORKSPACE, LANG, SKIN;
    if (window.parent) {
        url1 = window.parent.location.pathname;
        variables = url1.split('/');
        WORKSPACE = variables[1];
        WORKSPACE = WORKSPACE.substring(3);
        LANG = variables[2];
        SKIN = variables[3];

        if (variable == 'WORKSPACE') {
            return WORKSPACE;
        } else if (variable == 'LANG') {
            return LANG;
        } else if (variable == 'SKIN') {
            return SKIN;
        } else {
            return null;
        }
    }
};

LANG = (typeof SYS_LANG !== "undefined") ? SYS_LANG : enviromentVariables('LANG');
WORKSPACE = (typeof SYS_SYS !== "undefined") ? SYS_SYS : enviromentVariables('WORKSPACE');
SKIN = (typeof SYS_SKIN !== "undefined") ? SYS_SKIN : enviromentVariables('SKIN');

DEFAULT_WINDOW_WIDTH = 1193;
DEFAULT_WINDOW_HEIGHT = 570;
ENABLED_FEATURES = [];

if (LANG != 'en') {
    if (typeof __TRANSLATIONMAFE != "undefined" && typeof __TRANSLATIONMAFE[LANG] != 'undefined') {
        PMUI.loadLanguage(__TRANSLATIONMAFE.en, 'en');
        PMUI.loadLanguage(__TRANSLATIONMAFE[LANG], LANG);

        PMUI.setDefaultLanguage('en');
        PMUI.setCurrentLanguage(LANG);
    }
}

PMDesigner.resizeFrame = function () {
    if (parent.document.documentElement === document.documentElement) {
        jQuery(".content").css("height", parseInt(jQuery(window).height()));
    } else {
        jQuery(".content").css("height", document.body.clientHeight);

    }
};
resizingFrame = PMDesigner.resizeFrame;
PMDesigner.applyCanvasOptions = function () {
    list = new PMUI.control.DropDownListControl({
        options: [],
        style: {
            cssClasses: [
                "mafe-dropdown-zoom"
            ]
        },
        width: 150,
        onChange: function (newValue, previous) {
            var canvas = PMDesigner.project.diagrams.find('id', newValue);
            PMUI.getActiveCanvas().getHTML().style.display = 'none';
            PMUI.setActiveCanvas(canvas);
            canvas.getHTML().style.display = 'inline';
        }
    });
    //enable to support multidiagram
    //jQuery(jQuery(".navBar li")[6]).append(list.getHTML());
    list.defineEvents();
    PMDesigner.canvasList = list;
};
//Zoom
PMDesigner.ApplyOptionsZoom = function () {
    list = new PMUI.control.DropDownListControl({
        id: '_idListZoom',
        options: [
            {
                label: "50%",
                value: 1
            },
            {
                label: "75%",
                value: 2
            },
            {
                label: "100%",
                value: 3,
                selected: true
            },
            {
                label: "125%",
                value: 4
            },
            {
                label: "150%",
                value: 5
            }
        ],
        style: {
            cssClasses: [
                "mafe-dropdown-zoom"
            ]
        },
        onChange: function (newValue, previous) {
            var i;
            newValue = parseInt(newValue, 10);
            PMUI.getActiveCanvas().applyZoom(newValue);
        }
    });

    //jQuery(jQuery(".navBar li")[4]).append(list.getHTML());
    jQuery(jQuery(".mafe-zoom-options")).append(list.getHTML());

    list.defineEvents();
};
/**
 * hides all requiered TinyControls
 */
PMDesigner.hideAllTinyEditorControls = function () {
    var control,
        i,
        max,
        j,
        mapMax,
        editor,
        controlMap = [
            'tinyeditor_fontselect',
            'tinyeditor_fontsizeselect',
            'tinyeditor_bullist',
            'tinyeditor_numlist',
            'tinyeditor_forecolor',
            'tinyeditor_backcolor'
        ];
    for (i = 0, max = tinymce.editors.length; i < max; i += 1) {
        editor = tinymce.editors[i];
        jQuery.each(editor.controlManager.controls, function (index, val) {
            if (val && jQuery.isFunction(val.hideMenu)) {
                val.hideMenu();
            }
        });
    }

};


jQuery(document).ready(function ($) {
    var setSaveButtonDisabled,
        s,
        sidebarCanvas,
        project,
        d,
        downloadLink,
        handlerExportNormal,
        handlerExportGranular,
        handler,
        validatosr,
        help,
        option,
        menu,
        elem,
        validatorLabel = "Validator".translate();
    /***************************************************
     * Defines the Process
     ***************************************************/
    if (typeof prj_uid === "undefined") {
        prj_uid = '';
    }
    if (typeof prj_readonly === "undefined") {
        prj_readonly = '';
    }
    if (typeof credentials === "undefined") {
        credentials = '';
    } else {
        credentials = RCBase64.decode(credentials);
        credentials = (credentials == '') ? "" : JSON.parse(credentials);
    }

    if (prj_readonly !== 'true') {
        $("#idContent").find(".content_controls").show();
        $(".bpmn_shapes").show();
        $('.bpmn_shapes_legend').hide();
        $("#idNavBar").show().css('height', '33px');
    }

    PMDesigner.createHTML();
    setSaveButtonDisabled = function (that) {
        if (that.isDirty()) {
            if (document.getElementsByClassName("mafe-save-process").length > 0) {
                document.getElementsByClassName("mafe-save-process")[0].removeAttribute("style");
                document.getElementsByClassName("mafe-save-process")[0].childNodes[0].style.color = "#FFF";

                var mafebuttonMenu = document.getElementsByClassName("mafe-button-menu")[0];
                mafebuttonMenu.style.backgroundColor = "#0C9778";
                mafebuttonMenu.firstChild.src = "/lib/img/caret-down-w.png";
            }
        } else {
            if (document.getElementsByClassName("mafe-save-process").length > 0) {
                document.getElementsByClassName("mafe-save-process")[0].style.backgroundColor = "#e8e8e8";
                document.getElementsByClassName("mafe-save-process")[0].style.color = "#000";
                document.getElementsByClassName("mafe-save-process")[0].childNodes[0].style.color = "#000";
                document.getElementsByClassName("mafe-save-process")[0].childNodes[0].text = "Save".translate();

                var mafebuttonMenu = document.getElementsByClassName("mafe-button-menu")[0];
                mafebuttonMenu.style.backgroundColor = "#e8e8e8";
                mafebuttonMenu.firstChild.src = "/lib/img/caret-down.png";
            }
        }
    };
    sidebarCanvas = [];
    for (s = 0; s < PMDesigner.sidebar.length; s += 1) {
        sidebarCanvas = sidebarCanvas.concat(PMDesigner.sidebar[s].getSelectors());
        jQuery(".bpmn_shapes").append(PMDesigner.sidebar[s].getHTML());
    }
    //Adding Sidebar to DOM
    firstAbsuluteX = jQuery("#div-layout-canvas").offset().left;

    project = new PMProject({
        id: prj_uid,
        name: 'Untitled Process',
        readOnly: prj_readonly === "true",
        keys: {
            access_token: credentials.access_token,
            expires_in: credentials.expires_in,
            token_type: credentials.token_type,
            scope: credentials.scope,
            refresh_token: credentials.refresh_token,
            client_id: credentials.client_id,
            client_secret: credentials.client_secret
        },
        listeners: {
            create: function (self, element) {
                var sh, i,
                    contDivergent = 0,
                    contConvergent = 0;
                //Updating the background color for connections
                jQuery(".pmui-intersection > div > div").css("background-color", "black");

                if (element.type == "Connection") {
                    ///////////****************Changing the gatDirection*******************//////////////////
                    if (element.relatedObject.srcPort.parent.gat_type === "PARALLEL" ||
                        element.relatedObject.srcPort.parent.gat_type === "INCLUSIVE" ||
                        element.relatedObject.destPort.parent.gat_type === "PARALLEL" ||
                        element.relatedObject.destPort.parent.gat_type === "INCLUSIVE") {
                        if (element.relatedObject.srcPort.parent.gat_type !== undefined) {
                            sh = element.relatedObject.srcPort.parent;
                        } else {
                            sh = element.relatedObject.destPort.parent;
                        }

                        if (sh.gat_direction === "DIVERGING") {
                            for (i = 0; i < sh.ports.asArray().length; i += 1) {
                                if (sh.ports.asArray()[i].connection.flo_element_origin_type === "bpmnActivity") {
                                    contDivergent += 1;
                                }
                                if (contDivergent > 1) {
                                    sh.gat_direction = "CONVERGING";
                                    i = sh.ports.asArray().length;
                                }
                            }
                        }
                        if (sh.gat_direction === "CONVERGING") {
                            for (i = 0; i < sh.ports.asArray().length; i += 1) {
                                if (sh.ports.asArray()[i].connection.flo_element_origin_type === "bpmnGateway") {
                                    contConvergent += 1;
                                }
                                if (contConvergent > 1) {
                                    sh.gat_direction = "DIVERGING";
                                    i = sh.ports.asArray().length;
                                }
                            }
                        }

                    }
                }
                setSaveButtonDisabled(self);
            },
            update: function (self) {
                //Updating the background color for connections
                jQuery(".pmui-intersection > div > div").css("background-color", "black");
                setSaveButtonDisabled(self);
            },
            remove: function (self) {
                setSaveButtonDisabled(self);
            },
            success: function (self, xhr, response) {
                var message;
                self.dirty = false;
                setSaveButtonDisabled(self);
                self.dirtyElements[0] = {
                    laneset: {},
                    lanes: {},
                    activities: {},
                    events: {},
                    gateways: {},
                    flows: {},
                    artifacts: {},
                    lines: {},
                    data: {},
                    participants: {},
                    startMessageEvent: {},
                    startTimerEvent: {}
                };
                self.updateIdentifiers(response);
                //if (PMDesigner.currentMsgFlash) {
                PMDesigner.msgFlash('The process was saved successfully.'.translate(), document.body, 'success', 3000, 5);
                PMDesigner.RoutingRuleSetOrder();
                //}

            },
            failure: function (self, xhr, response) {
                var message;
                if (response.error.code === 401) {
                    /*message = new PMUI.ui.FlashMessage({
                     message: "It was not possible to establish a connection with the server".translate(),
                     duration: 5000,
                     appendTo: document.body,
                     severity: 'info'
                     });
                     message.show();*/
                    //self.remoteProxy.setUrl("/"+WORKSPACE+"/oauth2/token");
                    //self.setRefreshToken();
                    //self.remoteProxy.setUrl("/api/1.0/"+WORKSPACE+"/project/"+prj_uid);
                    //self.save();
                } else {
                    PMDesigner.msgFlash('Error saving the process.'.translate(), document.body, 'error', 3000, 5);
                    self.updateIdentifiers(response);
                }
            }
        }
    });
    PMDesigner.project = project;
    //create a new restApi
    PMDesigner.restApi = RestApi.createRestApi({
        serverUrl: '/rest/v10',
        keys: PMDesigner.project.keys
    });
    systemRest = new PMRestClient({
        typeRequest: 'post',
        multipart: true,
        data: {
            calls: [{
                url: 'system/enabled-features',
                method: 'GET'
            }
            ]
        },
        functionSuccess: function (xhr, response) {
            var result = response.pop();
            ENABLED_FEATURES = result.response;
            navbarExportUpdate();
        },
        functionFailure: function (xhr, response) {
            ENABLED_FEATURES = [];
        }
    }).setBaseEndPoint('').executeRestClient();
    PMDesigner.connectValidator = new ConnectValidator();
    for (d = 0; d < PMDesigner.sidebar.length; d += 1) {
        PMDesigner.sidebar[d].activate();
        PMDesigner.sidebar[d].enableActions();
    }

    $('.bpmn_shapes_legend').hide();
    project.remoteProxy.setUrl(HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + prj_uid);
    project.load();

    /*===========================================
     =            ProcessMaker module            =
     ===========================================*/

    //Renders content controls
    PMDesigner.contentControl.show();
    new PMAction({
        selector: ".mafe-menu-eventmessages-create",
        label: {
            selector: ".mafe-menu-eventmessages-create span",
            text: "Create".translate()
        },
        execute: true,
        handler: function () {
            PMDesigner.eventMessages.create();
        }
    });

    /*-----  End of ProcessMaker module  ------*/

    /*========================================
     =            Designer buttons            =
     ========================================*/

    //Renders navBar Panel
    PMDesigner.navbarPanel.show();
    // create Zoom options
    PMDesigner.ApplyOptionsZoom();
    //the action to generate a .bpmn file with the export option.
    downloadLink = $('.mafe-button-export-bpmn-process');
    downloadLink.click(function (e) {
        PMDesigner.moddle.toXML(PMDesigner.businessObject, function (err, xmlStrUpdated) {

            setEncoded(downloadLink, PMDesigner.project.projectName + '.bpmn', xmlStrUpdated);

            // xmlStrUpdated contains new id and the added process
        });

    });
    option = $("<div class='mafe-button-menu-option'>" + "Save as".translate() + "</div>");
    /**
     * Add data tables
     */
    $('body').append('<div class="bpmn_validator"><div class="validator_header"></div><div class="validator_body"></div></div>')
    $('.validator_header').append('<h2> ' + validatorLabel + ' </h2>');
    $('.validator_header').append('<a class="validator-close" href="#"><span class="mafe-validator-close" title=""></span></a>');
    $('.validator_body').html('<table cellpadding="0" cellspacing="0" border="0" class="display" id="validator-table" width="100%"></table>');
    PMDesigner.validTable = $('#validator-table').DataTable({
        paging: false,
        scrollY: 100,
        searching: false,
        "info": false,
        scrollCollapse: true,
        "columns": [
            {
                name: 'numrow',
                "title": "#",
                width: '5%',
                render: function (data, type, row, conf) {
                    return conf.row + 1;
                }
            },
            {
                name: 'id',
                className: 'never'
            },
            {
                "title": "Type".translate(),
                width: '10%',
                name: 'severity',
                render: function (data, type, row, conf) {
                    var clasMap = {
                        Error: 'mafe-icon-error',
                        Warning: 'mafe-icon-warning'
                    };
                    if (type === 'display') {
                        return ' <i class="' + clasMap[data] + '"></i> ' + data;
                    }
                    return data;
                }
            },
            {name: 'element', "title": "Element".translate(), width: '15%'},
            {name: 'element-type', "title": "Element Type".translate(), width: '15%'},
            {name: 'description', "title": "Description".translate(), width: '45%'}
        ]
    });
    jQuery('#validator-table tbody').on('click', 'tr', function () {
        var id = PMDesigner.validTable.row(this).data()[1],
            shape;
        if ($(this).hasClass('selected')) {
            $(this).removeClass('selected');
            PMUI.getActiveCanvas().hideAllCoronas().emptyCurrentSelection();
        }
        else {
            PMDesigner.validTable.$('tr.selected').removeClass('selected');
            $(this).addClass('selected');
            PMUI.getActiveCanvas().hideAllCoronas().emptyCurrentSelection();
            shape = PMUI.getActiveCanvas().items.find('id', id);
            PMUI.getActiveCanvas().addToSelection(shape.relatedObject);
        }
    });
    PMDesigner.validTable.columns([1]).visible(false);
    /********finish datatables********/
    new PMAction({
        selector: ".mafe-validator-close",
        tooltip: "Close Validator".translate(),
        execute: true,
        handler: function () {
            $('.bpmn_validator').css('visibility', 'hidden');
            $('.mafe-toolbar-validation').css('background-color', 'rgb(233, 233, 233)');
            PMDesigner.validator = false;
        }
    });

    menu = $("<div class='mafe-button-menu-container'></div>");
    menu.append(option);
    option.on("mouseout", function (e) {
        menu.hide();
    });
    option.on("click", function (e) {
        var saveas;
        menu.hide();
        PMDesigner.project.remoteProxy.setUrl(HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id);
        PMDesigner.project.save(true);
        saveas = new SaveAs();
        saveas.open();
    });
    $(".mafe-button-menu").on("click", function (e) {
        e.stopPropagation();
        $(".mafe-save-process").append(menu);
        menu.show();
    });

    /*-----  End of Designer buttons  ------*/
    /*=================================================
     =            Full screen functionality            =
     =================================================*/
    if (parent.document.documentElement === document.documentElement) {
        elem = document.documentElement;
    } else {
        elem = parent.document.getElementById("frameMain");
    }
    PMDesigner.fullScreen = new FullScreen({
        element: elem,
        onReadyScreen: function () {
            setTimeout(function () {
                PMDesigner.resizeFrame();
            }, 500);
        },
        onCancelScreen: function () {
            setTimeout(function () {
                PMDesigner.resizeFrame();
            }, 500);
        }
    });
    /*-----  End of Full screen functionality  ------*/

    /*=============================================
     =            Shapes and Controls Box            =
     =============================================*/
    PMDesigner.cookie = {
        name: "PMDesigner",
        object: {},
        get: function (cname) {
            var name = cname + "=", i, c,
                ca = document.cookie.split(';');
            for (i = 0; i < ca.length; i += 1) {
                c = ca[i].trim();
                if (c.indexOf(name) == 0)
                    return c.substring(name.length, c.length);
            }
            return "";
        },
        remove: function (cname) {
            jQuery.each(PMDesigner.cookie.object, function (index, val) {
                if (index === cname) {
                    delete PMDesigner.cookie.object[cname];
                    PMDesigner.cookie.refresh();
                }
            });
        },
        refresh: function () {
            document.cookie = PMDesigner.cookie.name + "=" + JSON.stringify(PMDesigner.cookie.object);
        }
    };
    PMDesigner.localStorage = {
        prefix: "PM_" + WORKSPACE + "_" + prj_uid,
        object: {},
        remove: function (cname) {
            var obj;
            obj = localStorage.getItem(PMDesigner.localStorage.prefix);
            obj = (obj === null) ? {} : JSON.parse(obj);
            if (obj[cname]) {
                delete obj[cname];
                localStorage.setItem(PMDesigner.localStorage.prefix, JSON.stringify(obj));
            }
        }
    };
    if (Modernizr.localstorage) {
        var localDesigner = localStorage.getItem(PMDesigner.localStorage.prefix);
        localDesigner = (localDesigner === null) ? {} : JSON.parse(localDesigner);
        PMDesigner.panelsPosition = localDesigner;
    } else {
        if (PMDesigner.cookie.get(PMDesigner.cookie.name) !== "") {
            var positions, pLeft, pTop, html;
            positions = JSON.parse(PMDesigner.cookie.get(PMDesigner.cookie.name));
            PMDesigner.cookie.object = positions;
            PMDesigner.panelsPosition = positions;
        }
    }
    if (typeof PMDesigner.panelsPosition === "object") {
        var pst = PMDesigner.panelsPosition;
        if (pst.navbar) {
            pLeft = pst.navbar.x;
            pTop = pst.navbar.y;
            html = document.getElementsByClassName("navBar")[0];
        }
        if (pst.bpmn) {
            pLeft = 0;
            pTop = 0;
            html = document.getElementsByClassName("bpmn_shapes")[0];
            html.style.left = pLeft + "px";
            html.style.top = pTop + "px";
        }
        if (pst.controls) {
            pLeft = pst.controls.x;
            pTop = pst.controls.y;
            html = document.getElementsByClassName("content_controls")[0];
            html.style.left = pLeft + "px";
            html.style.top = pTop + "px";
            if (pTop > 503) {
                $("#idContent").find(".content_controls").css({'top': '', 'left': ''});
            }
        }
    }
    jQuery(".bpmn_shapes").draggable({
        handle: "div",
        start: function () {
        },
        drag: function () {
        },
        stop: function (event) {
            var pLeft, pTop, currentObj;
            pLeft = parseInt(event.target.style.left);
            pTop = parseInt(event.target.style.top);
            bpmn = {
                bpmn: {
                    x: pLeft,
                    y: pTop
                }
            };
            if (Modernizr.localstorage) {
                currentObj = localStorage.getItem(PMDesigner.localStorage.prefix);
                currentObj = (currentObj === null) ? {} : JSON.parse(currentObj);
                jQuery.extend(true, currentObj, bpmn);
                localStorage.setItem(PMDesigner.localStorage.prefix, JSON.stringify(currentObj));
            } else {
                jQuery.extend(true, PMDesigner.cookie.object, bpmn);
                document.cookie = PMDesigner.cookie.name + "=" + JSON.stringify(PMDesigner.cookie.object);
            }
        }
    });
    jQuery(".content_controls").draggable({
        handle: "div",
        start: function () {
        },
        drag: function () {
            jQuery("html").css("overflow", "hidden");
        },
        stop: function (event) {
            jQuery("html").css("overflow", "auto");
            if (jQuery(this).position().top > $(window).height()) {
                var x = $(window).height() - 30;
                jQuery(this).css({'top': x + 'px'});
            }
            var pLeft, pTop, currentObj;
            pLeft = parseInt(event.target.style.left);
            pTop = parseInt(event.target.style.top);
            if (pTop < 90)
                pTop = 90;
            event.target.style.setProperty("top", pTop.toString() + "px");
            controls = {
                controls: {
                    x: pLeft,
                    y: pTop
                }
            };
            if (Modernizr.localstorage) {
                currentObj = localStorage.getItem(PMDesigner.localStorage.prefix);
                currentObj = (currentObj === null) ? {} : JSON.parse(currentObj);
                jQuery.extend(true, currentObj, controls);
                localStorage.setItem(PMDesigner.localStorage.prefix, JSON.stringify(currentObj));
            } else {
                jQuery.extend(true, PMDesigner.cookie.object, controls);
                document.cookie = PMDesigner.cookie.name + "=" + JSON.stringify(PMDesigner.cookie.object);
            }
        }
    });
    /*-----  End of Shapes and Controls Box  ------*/

    //Resize window
    PMDesigner.resizeFrame();

    /*==============================================
     =            Autosave functionality            =
     ==============================================*/
    PMDesigner.project.setSaveInterval(40000);
    setInterval(function () {
        if (PMDesigner.autoSave && PMDesigner.project.isDirty() && PMDesigner.project.readOnly === false) {
            PMDesigner.project.remoteProxy.setUrl(HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + prj_uid);
            PMDesigner.msgFlash('Saving Process'.translate(), document.body, 'success', 5000, 5);
            PMDesigner.project.save(true);
        }
    }, PMDesigner.project.saveInterval);
    /*-----  End of Autosave functionality  ------*/

    //Reviewing functionalities
    if (!PMDesigner.supportBrowser("fullscreen")) {
        var li = document.getElementsByClassName("mafe-button-fullscreen");
        if (li) {
            li[0].parentElement.style.display = "none";
        }
    }
    jQuery('.mafe-zoom-options').attr('title', 'Zoom'.translate()).tooltip({tooltipClass: "mafe-action-tooltip"});
    jQuery('.mafe-toolbar-lasso').mouseover(function (e) {
        $('.mafe-toolbar-lasso').css('cursor', 'pointer');
    });
    jQuery('.mafe-toolbar-validation').mouseover(function (e) {
        $('.mafe-toolbar-validation').css('cursor', 'pointer');
    });
    jQuery('.mafe-toolbar-lasso').click(function (e) {
        if (!PMUI.getActiveCanvas().lassoEnabled) {
            $('.mafe-toolbar-lasso').css('background-color', 'rgb(207, 207, 207)');
            PMUI.getActiveCanvas().lassoEnabled = true;
        } else {
            $('.mafe-toolbar-lasso').css('background-color', 'rgb(233, 233, 233)');
            PMUI.getActiveCanvas().lassoEnabled = false;
        }
    });

    PMDesigner.helper = new IntroHelper({
        tooltipClass: 'general',
        skipLabel: 'Quit'.translate(),

        nextLabel: 'Next &#8594;'.translate(),
        prevLabel: '&#8592; Back'.translate(),
        doneLabel: 'Done'.translate(),
        steps: [
            {
                intro: '<div class="screencast"/>'
            },
            {
                element: '#idNavBar',
                intro: 'The designer bar displays the process name and is used to control the process view (zoom, full screen view), the export, undo/redo and the save button.'.translate()
            },

            {
                element: '.bpmn_shapes',
                intro: 'Drag and drop the process elements that you want to include in the process design.'.translate() +
                '<br /><img src="../../../lib/img/corona-task.png">' + ' Task: Add to include an action in your process.'.translate() +
                '<br /><img src="../../../lib/img/corona-gateway-exclusive.png"> <img src="../../../lib/img/corona-gateway-parallel.png"> <img src="../../../lib/img/corona-gateway-inclusive.png">' + ' Gateway: Selects a path or divides the process into multiple paths and joins them together.'.translate() +
                '<br /><img src="../../../lib/img/corona-start.png"> <img src="../../../lib/img/corona-start-message.png">' + ' Start Event: The process always begins with a start event.'.translate() +
                '<br /><img src="../../../lib/img/corona-intermediate-receive-message.png"> <img src="../../../lib/img/corona-intermediate-send-message.png">' + ' Intermediate Event: Used to define an event that happens in the middle of the process.'.translate() +
                '<br /><img src="../../../lib/img/corona-end.png"> <img src="../../../lib/img/corona-end-message.png">' + ' End Event: End the execution of the process.'.translate() +
                '<br /><img src="../../../lib/img/corona-pool.png">' + ' Pool: Place each process in a separate pool.'.translate() +
                '<br /><img src="../../../lib/img/corona-lane.png">' + ' Lane: Used to divide a process into different sections.'.translate()
            },
            {
                element: '#div-layout-canvas',
                intro: "In the design area you can drop the process elements and order or arrange them to design your process.".translate()
            },
            {
                element: '.content_controls',
                intro: '<p>' +
                'The process objects are used to add execution features to the current process design.'.translate() +
                '<br/>Variables: Define the process data.'.translate() +
                '<br/>Dynaforms: Create dynamic forms.'.translate() +
                '<br/>Triggers: Create scripts.'.translate() +
                '<br/>Output documents: Generate documents with process data.'.translate() +
                '<br/>DB connections: Connect to external databases.'.translate() +
                '</p>',
                position: 'left'
            },
            {
                intro: '<div class="startcoronahelp"></div><div>' + 'Select an element in the designer to display the quick toolbar with the list of the most used options available for that element.'.translate() + '</div>'
            }
        ],
        onExit: function () {
            var canvas = PMUI.getActiveCanvas();
            if (canvas && canvas.getGridLine() && canvas.getHTML()) {
                canvas.getHTML().classList.add("pmui-pmcanvas");
            }
        }
    });

    jQuery('.mafe-toolbar-validation').click(function (e) {
        if (!PMDesigner.validator) {
            $('.mafe-toolbar-validation').css('background-color', 'rgb(207, 207, 207)');
            PMDesigner.validator = true;
        } else {
            $('.bpmn_validator').css('visibility', 'hidden');
            $('.mafe-toolbar-validation').css('background-color', 'rgb(233, 233, 233)');
            PMDesigner.validator = false;
        }
    });
    if (inArray("jXsSi94bkRUcVZyRStNVExlTXhEclVadGRRcG9xbjNvTWVFQUF3cklKQVBiVT0=", ENABLED_FEATURES)) {
        $("#idNavBar").find(".mafe-button-export-process").html(
            $("#idNavBar").find(".mafe-button-export-process").text() + "  &#x25BC;"
        );
    }

    function inArray(needle, haystack) {
        var i,
            length = haystack.length;
        for (i = 0; i < length; i += 1) {
            if (haystack[i] == needle) return true;
        }
        return false;
    }
});

window.onload = function () {
    //Reset the scroll positions
    window.scrollBy(-window.scrollX, -window.scrollY);
    document.onkeydown = function (e) {
        if (e.keyCode === 8 && e.target === document.body) {
            e.stopPropagation();
            return false;
        }
    };
};
/*==================================================
 =            Components from the Panels            =
 ==================================================*/

PMDesigner.createHTML = function () {
    var minShapes = document.createElement("span"),
        minShapesLegend = document.createElement("span"),
        refreshShapes = document.createElement("span"),
        minControls = document.createElement("span"),
        processObjects = document.createElement("span"),
        refreshControls = document.createElement("span"),
        refreshNavBar = document.createElement("span");
    minShapes.id = "minShapes";
    minShapesLegend.id = "minShapesLegend";
    refreshShapes.id = "resetShapes";
    minControls.id = "minControls";
    refreshControls.id = "resetControls";
    refreshNavBar.id = "resetNavBar";
    minShapes.className = "mafe-shapes-toggle";
    minShapesLegend.className = "mafe-shapes-toggle";
    refreshShapes.className = "mafe-shapes-refresh";
    minControls.className = "mafe-shapes-toggle";
    processObjects.className = "mafe-process-object";
    refreshControls.className = "mafe-shapes-refresh";
    refreshNavBar.className = "mafe-shapes-refresh";
    minShapes.title = "Minimize".translate();
    minShapesLegend.title = "Minimize".translate();
    refreshShapes.title = "reset".translate();
    minControls.title = "Minimize".translate();
    refreshControls.title = "Reset to original position".translate();
    refreshNavBar.title = "reset".translate();

    jQuery(minShapes).tooltip({tooltipClass: "mafe-action-tooltip"});
    jQuery(minShapesLegend).tooltip({tooltipClass: "mafe-action-tooltip"});
    jQuery(refreshShapes).tooltip({tooltipClass: "mafe-action-tooltip"});
    jQuery(minControls).tooltip({tooltipClass: "mafe-action-tooltip"});
    jQuery(refreshControls).tooltip({tooltipClass: "mafe-action-tooltip"});
    jQuery(refreshNavBar).tooltip({tooltipClass: "mafe-action-tooltip"});

    refreshControls.style.backgroundPosition = '0px 0px';
    processObjects.textContent = "Process Objects".translate();

    minShapes.onclick = function () {
        var i,
            items = jQuery(".bpmn_shapes > ul");
        if (items.length > 0) {
            for (i = 0; i < items.length; i += 1) {
                if (jQuery(items[i]).css("display").toLowerCase() !== "none") {
                    jQuery(items[i]).css({
                        display: 'none'
                    });
                } else {
                    jQuery(items[i]).css({
                        display: 'block'
                    });
                }

            }
        }
    };
    minShapesLegend.onclick = function () {
        var i,
            items = jQuery(".bpmn_shapes_legend").children();
        for (i = 1; i < items.length; i += 1) {
            if (jQuery(items[i]).css("display").toLowerCase() !== "none") {
                jQuery(items[i]).css({
                    display: 'none'
                });
            } else {
                jQuery(items[i]).css({
                    display: 'block'
                });
            }
        }
    };
    refreshShapes.onclick = function () {
        jQuery(".bpmn_shapes").removeAttr('style');
        if (Modernizr.localstorage) {
            PMDesigner.localStorage.remove("bpmn");
        } else {
            PMDesigner.cookie.remove("bpmn");
        }
    };
    minControls.onclick = function () {
        var i,
            title = '',
            items = jQuery(".content_controls > ul");

        if (items.length > 0) {
            for (i = 0; i < items.length; i += 1) {
                if (jQuery(items[i]).css("display").toLowerCase() !== "none") {
                    jQuery(items[i]).css({
                        display: 'none'
                    });
                    title = "Maximize";
                    $('#minControls').removeClass('mafe-shapes-toggle');
                    $('#minControls').addClass('mafe-shapes-plus');
                } else {
                    jQuery(items[i]).css({
                        display: 'block'
                    });
                    title = "Minimize";
                    $('#minControls').removeClass('mafe-shapes-plus');
                    $('#minControls').addClass('mafe-shapes-toggle');

                }
            }
        }
        jQuery(minControls).tooltip({content: title.translate()});
    };
    refreshControls.onclick = function () {
        jQuery(".content_controls").css({
            left: "auto",
            right: "20px",
            top: "90px"
        });
        if (Modernizr.localstorage) {
            PMDesigner.localStorage.remove("controls");
        } else {
            PMDesigner.cookie.remove("controls");
        }
    };
    refreshNavBar.onclick = function () {
        jQuery(".navBar").removeAttr('style');
        if (Modernizr.localstorage) {
            PMDesigner.localStorage.remove("navbar");
        } else {
            PMDesigner.cookie.remove("navbar");
        }
    };


    jQuery(".bpmn_shapes>div").append(minShapes);
    jQuery(".bpmn_shapes>div").append(refreshShapes);
    jQuery(".content_controls>div").append(processObjects);
    jQuery(".content_controls>div").append(minControls);
    jQuery(".content_controls>div").append(refreshControls);
    jQuery(".navBar>div").append(refreshNavBar);
    jQuery(".bpmn_shapes_legend>div").append(minShapesLegend);
    jQuery(".bpmn_shapes, .content_controls").on("contextmenu", function (e) {
        e.preventDefault();
    });

    PMDesigner.applyCanvasOptions();

};

/*-----  End of Components from the Panels  ------*/

/*=====================================================
 =            Get information about browser            =
 =====================================================*/
PMDesigner.getBrowser = function () {
    var match,
        ua = navigator.userAgent.toLowerCase();
    if (ua) {
        match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
            /(webkit)[ \/]([\w.]+)/.exec(ua) ||
            /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
            /(msie) ([\w.]+)/.exec(ua) ||
            ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
            [];

        return {
            browser: match[1] || "",
            version: match[2] || "0"
        };
    }
};
/*-----  End of Get information about browser  ------*/

PMDesigner.supportBrowser = function (functionality) {
    var browser, el, module;
    functionality = functionality.toLowerCase();
    switch (functionality) {
        case "fullscreen":
            browser = PMDesigner.getBrowser();
            if ((browser.browser === "msie") && (parseInt(browser.version, 10) <= 10)) {
                try {
                    module = new ActiveXObject("WScript.Shell");
                } catch (e) {
                    module = false;
                }
            } else {
                el = document.documentElement;
                module = el.requestFullScreen || el.webkitRequestFullScreen || el.mozRequestFullScreen || el.msRequestFullScreen;
                if (!module) {
                    module = false;
                }
            }
            break;
        case "":
            break;
    }
    return module;
};

/*============================================================
 =            Leave the current page Functionality            =
 ============================================================*/
window.onbeforeunload = function (e) {
    var message;
    if ((PMDesigner.project.isDirty()
        && !PMDesigner.project.readOnly)
        || PMDesigner.project.isSave) {
        message = "There are unsaved changes, if you leave the editor some changes won't be saved.".translate();
        e = e || window.event;
        if (e) {
            e.returnValue = message;
        }
        return message;
    }
};
/*-----  End of Leave the current page Functionality  ------*/

/*=====================================================================
 =            Validating coordinates for create a new shape            =
 =====================================================================*/
PMUI.validCoordinatedToCreate = function (canvas, event, shape) {
    var position, p, width, height, createElem = true, panels = [], message;
    //navBar panel
    position = jQuery(".navBar").offset();
    width = jQuery(".navBar").width();
    height = jQuery(".navBar").height();
    element = {
        x1: position.left,
        y1: position.top,
        x2: position.left + width,
        y2: position.top + height
    };
    panels.push(element);
    //BPMN panel
    position = jQuery(".bpmn_shapes").offset();
    width = jQuery(".bpmn_shapes").width();
    height = jQuery(".bpmn_shapes").height();
    element = {
        x1: position.left,
        y1: position.top,
        x2: position.left + width,
        y2: position.top + height
    };
    if (panels.length > 0) {
        for (p = 0; p < panels.length; p += 1) {
            if (((event.pageX >= panels[p].x1) && (event.pageX <= panels[p].x2))
                && ((event.pageY >= panels[p].y1) && (event.pageY <= panels[p].y2))) {
                PMDesigner.msgFlash('Is not possible create the element in that area'.translate(), document.body, 'info', 3000, 5);
                return false;
            }
        }
    }

    return true;
};
/*-----  End of Validating coordinates for create a new shape  ------*/

PMUI.pageCoordinatesToShapeCoordinates = function (shape, e, xCoord, yCoord, customShape) {
    var coordinates,
        x = (!xCoord) ? e.pageX : xCoord,
        y = (!yCoord) ? e.pageY : yCoord,
        orgX = (!xCoord) ? e.pageX : xCoord,
        orgY = (!yCoord) ? e.pageY : yCoord,
        canvas = shape.getCanvas();
    x += canvas.getLeftScroll() - shape.getAbsoluteX() - canvas.getX();
    y += canvas.getTopScroll() - shape.getAbsoluteY() - canvas.getY();
    coordinates = new PMUI.util.Point(x, y);
    return coordinates;
};

PMDesigner.msgFlash = function (text, container, severity, duration, zorder) {
    var msg;
    if (!PMDesigner.currentMsgFlash) {
        msg = new PMUI.ui.FlashMessage({
            id: '__msgFlashMessage',
            severity: 'success'
        });
    } else {
        msg = PMDesigner.currentMsgFlash;
    }
    if (msg.html)
        jQuery(msg.html).remove();
    msg.setMessage(text || "");
    msg.setAppendTo(container || document.body);
    msg.setSeverity(severity || "success");
    msg.setDuration(duration || 3000);
    msg.setZOrder(zorder || 200);
    msg.show();
    PMDesigner.currentMsgFlash = msg;
};

PMDesigner.msgWinError = function (text) {
    var msgError;
    if (!PMDesigner.currentWinError) {
        msgError = new PMUI.ui.MessageWindow({
            id: 'showMessageWindowFailure',
            width: 490,
            windowMessageType: 'error',
            title: 'Error'.translate(),
            footerItems: [
                {
                    text: 'Ok'.translate(),
                    handler: function () {
                        msgError.close();
                    },
                    buttonType: "success"
                }
            ]
        });
    } else {
        msgError = PMDesigner.currentWinError;
    }
    msgError.setMessage(text || 'Error'.translate());
    msgError.showFooter();
    msgError.open();
    PMDesigner.currentWinError = msgError;
};

PMDesigner.msgWinWarning = function (text) {
    var msgWarning;
    if (!PMDesigner.currentWinWarning) {
        msgWarning = new PMUI.ui.MessageWindow({
            id: 'showMessageWindowWarning',
            windowMessageType: 'warning',
            width: 490,
            title: 'Warning'.translate(),
            footerItems: [{
                text: 'Ok'.translate(),
                buttonType: "success", handler: function () {
                    msgWarning.close();
                }
            }]
        });
    } else {
        msgWarning = PMDesigner.currentWinWarning;
    }
    msgWarning.setMessage(text || 'Warning'.translate());
    msgWarning.showFooter();
    msgWarning.open();
    PMDesigner.currentWinWarning = msgWarning;
};

PMDesigner.modeReadOnly = function () {
    var restClient;
    if (prj_readonly === 'true') {
        restClient = new PMRestClient({
            typeRequest: 'post',
            multipart: true,
            data: {
                calls: [{
                    url: 'cases/' + app_uid + '/tasks',
                    method: 'GET'
                }
                ]
            },
            functionSuccess: function (xhr, response) {
                var viewTaskInformation = new ViewTaskInformation();
                viewTaskInformation.setData(response[0].response);
                viewTaskInformation.setShapes();
                viewTaskInformation.showViewLegendsInformation();
            },
            functionFailure: function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            }
        });
        restClient.setBaseEndPoint('');
        restClient.executeRestClient();
    }
};

PMDesigner.reloadDataTable = function () {
    $('.bpmn_validator').css('visibility', 'visible');
};
/**
 * Disable the autosave feature
 */
PMDesigner.autoSaveValue = function (value) {
    PMDesigner.autoSave = value;
};
/**
 * Escape XML characters method.
 * There are only five:
 * "   &quot;
 * '   &apos;
 * <   &lt;
 * >   &gt;
 * &   &amp;
 * 
 * @param {string} label
 * @returns {string}
 */
PMDesigner.escapeXMLCharacters = function (label) {
    return label
            .replace(/&/g, "&amp;")
            .replace(/"/g, "&quot;")
            .replace(/'/g, "&apos;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;");
};

DataDictionary = function () {
};
DataDictionary.prototype.getColor = function (value) {
    switch (value) {
        case 'TASK_IN_PROGRESS':
            return 'red';
        case 'TASK_COMPLETED':
            return 'green';
        case 'TASK_PENDING_NOT_EXECUTED':
            return 'silver';
        case 'TASK_PARALLEL':
            return 'orange';
        default:
            return 'white';
    }
};
DataDictionary.prototype.getStatus = function (value) {
    switch (value) {
        case 'TASK_IN_PROGRESS':
            return 'Task in Progress'.translate();
        case 'TASK_COMPLETED':
            return 'Completed Task'.translate();
        case 'TASK_PENDING_NOT_EXECUTED':
            return 'Pending Task / Not Executed'.translate();
        case 'TASK_PARALLEL':
            return 'Parallel Task'.translate();
        default:
            return value;
    }
};
DataDictionary.prototype.getTasAssignType = function (value) {
    switch (value) {
        case 'BALANCED':
            return 'Balanced'.translate();
        case 'MANUAL':
            return 'Manual'.translate();
        case 'REPORT_TO':
            return 'Report toO'.translate();
        case 'EVALUATE':
            return 'Evaluate'.translate();
        case 'SELF_SERVICE':
            return 'self Service'.translate();
        case 'SELF_SERVICE_EVALUATE':
            return 'Self Service Evaluate'.translate();
        default:
            return value;
    }
};
DataDictionary.prototype.getTasType = function (value) {
    switch (value) {
        case 'NORMAL':
            return 'Normal'.translate();
        case 'SUBPROCESS':
            return 'Sub Process'.translate();
        default:
            return value;
    }
};
DataDictionary.prototype.getTasDerivation = function (value) {
    switch (value) {
        case 'NORMAL':
            return 'Normal'.translate();
        default:
            return value;
    }
};

ViewTaskInformation = function (settings) {
    ViewTaskInformation.prototype.init.call(this, settings);
};
ViewTaskInformation.prototype.init = function () {
    var that = this,
        panelButton = new PMUI.core.Panel({
            layout: 'hbox',
            items: [
                that.getButton('Information', function () {
                    that.showInformation();
                }),
                that.getButton('Delegations', function () {
                    that.showDelegations();
                }),
                that.getButton('Route', function () {
                    that.showRoute();
                })
            ]
        });
    that.windowAbstract.showFooter();
    that.windowAbstract.addItem(panelButton);
    that.windowAbstract.addItem(that.panelvertical);
};
ViewTaskInformation.prototype.dataDictionary = new DataDictionary();
ViewTaskInformation.prototype.data = null;
ViewTaskInformation.prototype.shapeData = null;
ViewTaskInformation.prototype.panelvertical = new PMUI.core.Panel({layout: 'vbox', width: 400});
ViewTaskInformation.prototype.windowAbstract = new PMUI.ui.Window({id: 'windowAbstract', width: 500, height: 350});
ViewTaskInformation.prototype.setData = function (data) {
    this.data = data;
};
ViewTaskInformation.prototype.setCursor = function (shape) {
    shape.getHTML().onmouseover = function () {
        this.style.cursor = 'pointer';
    };
    shape.getHTML().onmouseout = function () {
        this.style.cursor = '';
    };
};
ViewTaskInformation.prototype.setShapes = function () {
    var that = this,
        shape,
        diagrams,
        i,
        j,
        dt = that.data;
    for (i = 0; i < dt.length; i += 1) {
        diagrams = PMDesigner.project.diagrams.asArray();
        for (j = 0; j < diagrams.length; j += 1) {
            shape = diagrams[j].getCustomShapes().find('id', dt[i].tas_uid);
            if (typeof shape != "undefined" && shape != null) {
                shape.changeColor(that.dataDictionary.getColor(dt[i].status));
                shape.data = dt[i];
                shape.hasClick = function (event) {
                    that.setShapeData(this.data);
                    that.showInformation();
                };
                that.setCursor(shape);
            }
        }
    }
};
ViewTaskInformation.prototype.setShapeData = function (data) {
    this.shapeData = data;
};
ViewTaskInformation.prototype.addRowNewLine = function (label, value) {
    var panelhorizontal = new PMUI.core.Panel({
        layout: 'hbox'
    });
    panelhorizontal.addItem(new PMUI.ui.TextLabel({text: ''}));
    this.panelvertical.addItem(panelhorizontal);
    return panelhorizontal;
};
ViewTaskInformation.prototype.addRow = function (label, value) {
    var field1, field2, field3, panelhorizontal;

    field1 = new PMUI.ui.TextLabel({text: label.translate(), proportion: 0.3});
    field2 = new PMUI.ui.TextLabel({text: ':', proportion: 0.1});
    field3 = new PMUI.ui.TextLabel({text: value ? value + '' : '', proportion: 0.6});

    panelhorizontal = new PMUI.core.Panel({
        layout: 'hbox'
    });

    panelhorizontal.addItem(field1);
    panelhorizontal.addItem(field2);
    panelhorizontal.addItem(field3);
    this.panelvertical.addItem(panelhorizontal);
    return panelhorizontal;
};
ViewTaskInformation.prototype.clearRows = function () {
    this.panelvertical.clearItems();
};
ViewTaskInformation.prototype.showInformation = function () {
    var that = this;
    that.clearRows();
    that.addRow('Title'.translate(), that.shapeData.tas_title);
    that.addRow('Description'.translate(), that.shapeData.tas_description);
    that.addRow('Status'.translate(), that.dataDictionary.getStatus(that.shapeData.status));
    that.addRow('Type'.translate(), that.dataDictionary.getTasType(that.shapeData.tas_type));
    that.addRow('Assign type'.translate(), that.dataDictionary.getTasAssignType(that.shapeData.tas_assign_type));
    that.addRow('Routing'.translate(), that.dataDictionary.getTasDerivation(that.shapeData.tas_derivation));
    that.addRow('Start'.translate(), that.shapeData.tas_start);
    that.addRowNewLine();
    that.addRow('Last User Name'.translate(), that.shapeData.usr_username);
    that.addRow('Last User'.translate(), that.shapeData.usr_firstname + ' ' + that.shapeData.usr_lastname);

    that.windowAbstract.setTitle('Information'.translate() + ' ' + that.shapeData.tas_title);
    that.windowAbstract.open();
    that.windowAbstract.body.style.padding = '20px';
};
ViewTaskInformation.prototype.showDelegations = function () {
    var that = this, i, dt;
    that.clearRows();
    dt = that.shapeData.delegations;
    for (i = 0; i < dt.length; i += 1) {
        that.addRow('User', dt[i].usr_username);
        that.addRow('User Name', dt[i].usr_firstname + ' ' + dt[i].usr_lastname);
        that.addRow('Duration', dt[i].del_duration);
        that.addRow('Finish Date', dt[i].del_finish_date);
        that.addRow('Index', dt[i].del_index);
        that.addRow('Init Date', dt[i].del_init_date);
        that.addRow('Task Due Date', dt[i].del_task_due_date);
        that.addRowNewLine();
    }

    that.windowAbstract.setTitle('Delegations'.translate() + ' ' + that.shapeData.tas_title);
    that.windowAbstract.open();
    that.windowAbstract.body.style.padding = '20px';
};
ViewTaskInformation.prototype.showRoute = function () {
    var that = this, i, dt;
    that.clearRows();
    that.addRow('Type', that.shapeData.route.type);
    that.addRowNewLine();
    dt = that.shapeData.route.to;
    for (i = 0; i < dt.length; i += 1) {
        that.addRow('Condition', dt[i].rou_condition);
        that.addRow('Number', dt[i].rou_number);
        that.addRowNewLine();
    }

    that.windowAbstract.setTitle('Route'.translate() + ' ' + that.shapeData.tas_title);
    that.windowAbstract.open();
    that.windowAbstract.body.style.padding = '20px';
};
ViewTaskInformation.prototype.getButton = function (text, fn) {
    return new PMUI.ui.Button({
        text: text.translate(),
        width: 180,
        height: 50,
        style: {
            cssProperties: {
                marginRight: 10,
                marginBottom: 10,
                backgroundColor: '#474747',
                borderRadius: 5,
                padding: 5
            },
            cssClasses: ['mafeButton']
        },
        handler: fn
    });
};
ViewTaskInformation.prototype.showViewLegendsInformation = function () {
    var i, dt, legend, legendIcon, legendText;
    $('.bpmn_shapes_legend').show();

    i;
    dt = [
        ['red', 'Task in Progress'.translate()],
        ['green', 'Completed Task'.translate()],
        ['silver', 'Pending Task / Not Executed'.translate()],
        ['orange', 'Parallel Task'.translate()]
    ];
    for (i = 0; i < dt.length; i += 1) {
        legend = $("<div></div>");
        legendIcon = $("<div></div>").addClass("mafe-activity-task-" + dt[i][0]).addClass("icon-legend");
        legendText = $("<div>" + dt[i][1] + "</div>").addClass("text-legend");
        legend.append(legendIcon).append(legendText);
        jQuery(".bpmn_shapes_legend").append(legend);
    }

    jQuery(".bpmn_shapes_legend").draggable({
        handle: "div",
        start: function () {
        },
        drag: function (event, e, u) {
        },
        stop: function (event) {

        }
    });
};

(function () {
    /**
     * @class PMUI.menu.ContextMenu
     * Handles the context menu of designer
     * contains all the menus for elements
     *
     * @extend PMUI.util.Factory
     * @constructor
     * This method creates a new instance of this object
     * @param {Object} [settings] Constructor settings
     */
    "use strict";
    var ContextMenu = function (settings) {
        this.factory = null;
        ContextMenu.prototype.init.call(this, settings);
    };

    ContextMenu.prototype.type = 'ContextMenu';

    ContextMenu.prototype.family = 'ContextMenu';

    /**
     * Initializes the object.
     * @param  {Object} settings A JSON object with the config options.
     * @private
     */
    ContextMenu.prototype.init = function (settings) {
        jQuery.extend(true, defaults, settings);
        this.factory = new PMUI.util.Factory(defaults.factory);
    };

    /**
     * Register a new context menu object into a product.
     *
     * Usage example:
     *      @example
     *       //Remember, this is an abstract class so it shouldn't be instantiate,
     *       //anyway we are instantiating it just for this example
     *       var additionalMenu = {
     *          id: "additionalMenu",
     *          text: "New Menu",
     *          onClick: function () {
     *              PMDesigner.msgWinWarning('This is a new Menu');
     *              }
     *          };
     *       // Assuming that you're using PMDesigner.contextMenuFactory as the default contextMenuFactory
     *       PMDesigner.contextMenuFactory.registerMenu("CANVAS", additionalMenu);
     *
     * @param  {String} productName Name of the shape or product in factory.
     * @param  {Object} menu Object with menu values and actions.
     * @private
     */
    ContextMenu.prototype.registerMenu = function (productName, menu) {
        if (typeof this.factory.products[productName] === 'undefined') {
            console.log('Context Menu Warning: Cannot add Menu into: ' + productName + '. Please, review name.');
        } else {
            if (this.factory.products[productName].items instanceof Array) {
                this.factory.products[productName].items.push(menu);
            }
        }
    };

    /**
     * Removes context menu object from product.
     *
     * Usage example:
     *      @example
     *       // Assuming that you're using PMDesigner.contextMenuFactory as the default contextMenuFactory
     *       // this example removes Canvas gridLines option
     *       PMDesigner.contextMenuFactory.removeMenu("CANVAS", "id", "menuGridLines");
     *
     * @param  {String} product Name of the shape or product in factory.
     * @param  {String} id Key to look through product.
     * @param  {String} value Name of the elementValue.
     * @private
     */
    ContextMenu.prototype.removeMenu = function (product, id, value) {
        if (typeof this.factory.products[product] !== 'undefined') {
            this.factory.products[product].items = _.reject(this.factory.products[product].items, function (menuValue) {
                return menuValue[id] === value;
            });
        } else {
            console.log('Context Menu Warning: Cannot find ' + product + ' Menu. Please, review name.');
        }
    };

    /**
     * Returns specific product.
     * @param  {String} type Name of product.
     * @private
     */
    ContextMenu.prototype.getProduct = function (type) {
        return this.factory.products[type];
    };

    var menuMessages = {
            'START': {
                'TIMER': 'Please configure cron to create cases.'.translate(),
                'CONDITIONAL': 'Please configure cron to create cases in base to a condition.'.translate(),
                'SIGNALCATCH': 'Please configure cron to create cases in base to a signal.'.translate()
            },
            'INTERMEDIATE': {
                'CATCH': {
                    'TIMER': 'Please configure cron to wait for time event.'.translate(),
                    'CONDITIONAL': 'Please configure cron to wait for time condition.'.translate(),
                    'SIGNALCATCH': 'Please configure script to wait for a signal.'.translate()
                },
                'THROW': {
                    'SIGNALTHROW': 'Please configure a script to send a signal.'.translate()
                }
            },
            'END': {
                'ERRORTHROW': 'Please configure script to end with error status.'.translate(),
                'SIGNALTHROW': 'Please configure script to send a signal.'.translate(),
                'TERMINATETHROW': 'Please configure script to terminate case.'.translate()
            }
        },
        menu = {},
        rootMenu,
        elementActivite,
        typeMenu = {
            text: "Gateway Type".translate(),
            icon: "mafe-menu-properties-action",
            id: "gatewaytype",
            items: [
                {
                    id: "gatewayexclusive",
                    text: "Exclusive (XOR) Gateway".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeTypeTo('EXCLUSIVE');
                        PMDesigner.project.updateElement([]);
                    }
                },
                {
                    id: "gatewayparallel",
                    text: "Parallel (AND) Gateway".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeTypeTo('PARALLEL');
                        PMDesigner.project.updateElement([]);
                    }
                },
                {
                    id: "gatewayinclusive",
                    text: "Inclusive (OR) Gateway".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeTypeTo('INCLUSIVE');
                        PMDesigner.project.updateElement([]);
                    }
                }
            ]
        },
        endMarker = {
            text: "End Event Type".translate(),
            icon: "mafe-menu-properties-action",
            id: "result",
            items: [
                {
                    id: "endempty",
                    text: "Empty".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('EMPTY', 'Empty');
                        PMDesigner.project.updateElement([]);
                    }
                },
                {
                    id: "endemail",
                    text: "Email Message".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('EMAIL', 'Email Message');
                        PMDesigner.project.updateElement([]);
                    }
                },
                {
                    id: "endmessagethrow",
                    text: "Message".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('MESSAGETHROW', 'Message');
                        PMDesigner.project.updateElement([]);
                    }
                },
                {
                    id: "enderrorthrow",
                    text: "Error".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('ERRORTHROW', 'Error');
                        PMDesigner.project.updateElement([]);
                    }
                },
                {
                    id: "endsignalthrow",
                    text: "Signal".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('SIGNALTHROW', 'Signal');
                        PMDesigner.project.updateElement([]);
                    }
                },
                {
                    id: "endterminatethrow",
                    text: "Terminate".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('TERMINATETHROW', 'Terminate');
                        PMDesigner.project.updateElement([]);
                    }
                }
            ]
        },
        intermediateCatchMarker = {
            text: "Intermediate Event Type".translate(),
            icon: "mafe-menu-properties-action",
            id: "trigger",
            items: [
                {
                    id: "intermediatemessagecatch",
                    text: "Receive Message".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('MESSAGECATCH', 'Receive Message');
                        PMDesigner.project.updateElement([]);
                    }
                },
                {
                    id: "intermediatetimer",
                    text: "Timer".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('TIMER', 'Timer');
                        PMDesigner.project.updateElement([]);
                    }
                },
                {
                    id: "intermediateconditional",
                    text: "Conditional".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('CONDITIONAL', 'Conditional');
                        PMDesigner.project.updateElement([]);
                    }
                },
                {
                    id: "intermediatesignalcatch",
                    text: "Signal".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('SIGNALCATCH', 'Signal');
                        PMDesigner.project.updateElement([]);
                    }
                }
            ]
        },
        boundaryCatchMarker = {
            text: "Boundary Event Type".translate(),
            icon: "mafe-menu-properties-action",
            id: "eventType",
            items: [
                {
                    id: "messageCatch",
                    text: "Receive Message".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('MESSAGECATCH', 'Receive Message');
                    }
                },
                {
                    id: "boundaryTimer",
                    text: "Timer".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('TIMER', 'Timer');
                    }
                },
                {
                    id: "BoudaryConditional",
                    text: "Conditional".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('CONDITIONAL', 'Conditional');
                    }
                },
                {
                    id: "BoudarySignal",
                    text: "Signal".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('SIGNALCATCH', 'Signal');
                    }
                },
                {
                    id: "BoudaryError",
                    text: "Error".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('ERRORCATCH', 'Error');
                    }
                }
            ]
        },
        intermediateThrowMarker = {
            text: "Intermediate Event Type".translate(),
            icon: "mafe-menu-properties-action",
            id: "result",
            items: [
                {
                    id: "intermediateemail",
                    text: "Email Message".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('EMAIL', 'Email Message');
                        PMDesigner.project.updateElement([]);
                    }
                },
                {
                    id: "intermediatemessagethrow",
                    text: "Send Message".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('MESSAGETHROW', 'Send Message');
                        PMDesigner.project.updateElement([]);
                    }
                },
                {
                    id: "intermediatesignalthrow",
                    text: "Signal".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('SIGNALTHROW', 'Signal');
                        PMDesigner.project.updateElement([]);
                    }
                }
            ]
        },
        startCatchMarker = {
            text: "Start Event Type".translate(),
            icon: "mafe-menu-properties-action",
            id: "trigger",
            items: [
                {
                    id: "startempty",
                    text: "Empty".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('EMPTY', 'Empty');
                        PMDesigner.project.updateElement([]);
                    }
                },
                {
                    id: "startmessagecatch",
                    text: "Receive Message".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('MESSAGECATCH', 'Receive Message');
                        rootMenu = menuOption.getRootMenu();
                        rootMenu.getItems()[3].disable();
                        PMDesigner.project.updateElement([]);
                    }
                },
                {
                    id: "starttimer",
                    text: "Timer".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('TIMER', 'Timer');
                        rootMenu = menuOption.getRootMenu();
                        rootMenu.getItems()[3].disable();
                        PMDesigner.project.updateElement([]);
                    }
                },
                {
                    id: "startconditional",
                    text: "Conditional".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('CONDITIONAL', 'Conditional');
                        rootMenu = menuOption.getRootMenu();
                        rootMenu.getItems()[3].disable();
                        PMDesigner.project.updateElement([]);
                    }
                },
                {
                    id: "startsignalcatch",
                    text: "Signal".translate(),
                    onClick: function (menuOption) {
                        var targetElement = menuOption
                            .getMenuTargetElement();
                        targetElement.changeMarkerTo('SIGNALCATCH', 'Signal');
                        rootMenu = menuOption.getRootMenu();
                        rootMenu.getItems()[3].disable();
                        PMDesigner.project.updateElement([]);
                    }
                }
            ]
        },
        canvas = {
            id: "menuCanvas",
            width: 150,
            items: [
                {
                    id: "menuEditProcess",
                    text: "Edit Process".translate(),
                    onClick: function (menuOption) {
                        PMDesigner.propertiesProcess();
                    }
                },
                {
                    id: "menuGridLines",
                    text: "Enable Grid Lines".translate(),
                    onClick: function () {
                        var canvas = PMUI.getActiveCanvas();
                        if (canvas.toogleGridLine()) {
                            this.setText("Disable Grid Lines".translate());
                        } else {
                            this.setText("Enable Grid Lines".translate());
                        }
                    }
                }
            ],
            onShow: function (menu) {
                var canvas = PMUI.getActiveCanvas();
                if (canvas.currentConnection) {
                    canvas.currentConnection.hidePortsAndHandlers();
                }
                if (canvas.isGridLine) {
                    menu.items.find('id', 'menuGridLines').setText("Disable Grid Lines".translate());
                }

            }
        },
        task = {
            id: "menuTask",
            items: [
                {
                    id: "taskType",
                    text: "Task Type".translate(),
                    items: [
                        {
                            id: "empty",
                            text: "Empty Task".translate(),
                            onClick: function (menuOption) {
                                handlerMarkerType(menuOption);
                            }
                        },
                        {
                            id: "sendtask",
                            text: "Send Task".translate(),
                            onClick: function (menuOption) {
                                handlerMarkerType(menuOption);
                            }
                        },
                        {
                            id: "receivetask",
                            text: "Receive Task".translate(),
                            onClick: function (menuOption) {
                                handlerMarkerType(menuOption);
                            }
                        },
                        {
                            id: "usertask",
                            text: "User Task".translate(),
                            onClick: function (menuOption) {
                                handlerMarkerType(menuOption);
                            }
                        },
                        {
                            id: "servicetask",
                            text: "Service Task".translate(),
                            onClick: function (menuOption) {
                                handlerMarkerType(menuOption);
                            }
                        },
                        {
                            id: "scripttask",
                            text: "Script Task".translate(),
                            onClick: function (menuOption) {
                                handlerMarkerType(menuOption);
                            }
                        },
                        {
                            id: "manualtask",
                            text: "Manual Task".translate(),
                            onClick: function (menuOption) {
                                handlerMarkerType(menuOption);
                            }
                        },
                        {
                            id: "businessrule",
                            text: "Business Rule Task".translate(),
                            onClick: function (menuOption) {
                                handlerMarkerType(menuOption);
                            }
                        }
                    ]
                },
                {
                    id: "loopType",
                    text: "Marker Type".translate(),
                    items: [
                        {
                            id: "empty",
                            text: "None".translate(),
                            onClick: function (menuOption) {
                                handlerMarkerLoopType(menuOption);
                            }
                        },
                        {
                            id: "loop",
                            text: "Loop".translate(),
                            onClick: function (menuOption) {
                                handlerMarkerLoopType(menuOption);
                            }
                        },
                        {
                            id: "parallel",
                            text: "Parallel".translate(),
                            onClick: function (menuOption) {
                                handlerMarkerLoopType(menuOption);
                            }
                        },
                        {
                            id: "sequential",
                            text: "Sequential".translate(),
                            onClick: function (menuOption) {
                                handlerMarkerLoopType(menuOption);
                            }
                        }
                    ]
                },
                {
                    id: "menuTaskSteps",
                    text: "Steps".translate(),
                    icon: "mafe-menu-task-steps",
                    onClick: function (menuOption) {
                        var splitedID = menuOption.getMenuTargetElement().getID().split("-");
                        menuOption.parent.hide();
                        if ((splitedID && splitedID[0] === 'pmui') || PMDesigner.project.isDirty()) {
                            PMDesigner.restApi.execute({
                                data: JSON.stringify(PMDesigner.project.getDirtyObject()),
                                method: "update",
                                url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id,
                                success: function (data, textStatus, xhr) {
                                    PMDesigner.project.listeners.success(PMDesigner.project, textStatus, data);
                                    PMDesigner.project.isSave = false;
                                    //open property form
                                    PMDesigner.act_name = menuOption.getMenuTargetElement().act_name;
                                    PMDesigner.act_uid = menuOption.getMenuTargetElement().act_uid;
                                    PMDesigner.stepsTask = new stepsTask();
                                },
                                error: function (xhr, textStatus, errorThrown) {
                                    PMDesigner.project.listeners.failure(that, textStatus, xhr);
                                    PMDesigner.project.isSave = false;
                                }
                            });
                        } else {
                            PMDesigner.act_name = menuOption.getMenuTargetElement().act_name;
                            PMDesigner.act_uid = menuOption.getMenuTargetElement().act_uid;
                            PMDesigner.stepsTask = new stepsTask();
                        }
                    }
                },
                {
                    id: "menuTaskAssignedRules",
                    text: "Assignment Rules".translate(),
                    icon: "mafe-menu-users-action",
                    onClick: function (menuOption) {
                        var splitedID = menuOption.getMenuTargetElement().getID().split("-");
                        menuOption.parent.hide();
                        if ((splitedID && splitedID[0] === 'pmui') || PMDesigner.project.isDirty()) {
                            PMDesigner.restApi.execute({
                                data: JSON.stringify(PMDesigner.project.getDirtyObject()),
                                method: "update",
                                url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id,
                                success: function (data, textStatus, xhr) {
                                    PMDesigner.project.listeners.success(PMDesigner.project, textStatus, data);
                                    PMDesigner.project.isSave = false;
                                    //open property form
                                    PMDesigner.assigmentRules(menuOption.getMenuTargetElement());
                                },
                                error: function (xhr, textStatus, errorThrown) {
                                    PMDesigner.project.listeners.failure(that, textStatus, xhr);
                                    PMDesigner.project.isSave = false;
                                }
                            });
                        } else {
                            PMDesigner.assigmentRules(menuOption.getMenuTargetElement());
                        }
                    }
                },
                {
                    text: "Edit Label".translate(),
                    icon: "mafe-menu-edit-label-action",
                    onClick: function (menuOption) {
                        menuOption.parent.hide();

                        menuOption.getMenuTargetElement().label.canvas.hideAllFocusLabels();
                        menuOption.getMenuTargetElement().label.getFocus();
                    }
                },
                {
                    text: "Delete".translate(),
                    icon: "mafe-menu-delete-action",
                    onClick: function (menuOption) {
                        menuOption.parent.hide();

                        PMUI.getActiveCanvas().removeElements();
                    }
                },
                {
                    id: "menuTaskProperties",
                    text: "Properties".translate(),
                    icon: "mafe-menu-properties-action",
                    onClick: function (menuOption) {
                        var splitedID = menuOption.getMenuTargetElement().getID().split("-");
                        menuOption.parent.hide();
                        if ((splitedID && splitedID[0] === 'pmui') || PMDesigner.project.isDirty()) {
                            PMDesigner.restApi.execute({
                                data: JSON.stringify(PMDesigner.project.getDirtyObject()),
                                method: "update",
                                url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id,
                                success: function (data, textStatus, xhr) {
                                    PMDesigner.project.listeners.success(PMDesigner.project, textStatus, data);
                                    PMDesigner.project.isSave = false;
                                    //open property form
                                    PMDesigner.activityProperties(menuOption.getMenuTargetElement());
                                },
                                error: function (xhr, textStatus, errorThrown) {
                                    PMDesigner.project.listeners.failure(that, textStatus, xhr);
                                    PMDesigner.project.isSave = false;
                                }
                            });
                        } else {
                            PMDesigner.activityProperties(menuOption.getMenuTargetElement());
                        }
                    }
                }
            ],
            onShow: function (menu) {
                var targetElement = menu.getTargetElement();
                PMUI.getActiveCanvas().emptyCurrentSelection();
                PMUI.getActiveCanvas().addToSelection(targetElement);
                PMUI.getActiveCanvas().hideDragConnectHandlers();
                PMUI.getActiveCanvas().hideAllFocusLabels();
                if (PMUI.getActiveCanvas().currentConnection) {
                    PMUI.getActiveCanvas().currentConnection
                        .hidePortsAndHandlers();
                }

                if (targetElement.act_task_type == "SCRIPTTASK") {
                    menu.getItems()[1].setVisible(false);
                    menu.getItems()[2].setVisible(false);
                    menu.getItems()[3].setVisible(false);
                } else {
                    menu.getItems()[1].setVisible(true);
                    menu.getItems()[2].setVisible(true);
                    menu.getItems()[3].setVisible(true);
                }

                if (targetElement.act_task_type == "MANUALTASK" || targetElement.act_task_type == "USERTASK" || targetElement.act_task_type == "EMPTY") {
                    menu.getItems()[1].getItems()[2].setVisible(true);
                } else {
                    menu.getItems()[1].getItems()[2].setVisible(false);
                }
            }
        },
        subProcess = {
            id: "menuSubProcess",
            items: [
                {
                    text: "Edit Label".translate(),
                    icon: "mafe-menu-edit-label-action",
                    onClick: function (menuOption) {
                        menuOption.parent.hide();

                        menuOption.getMenuTargetElement().label.canvas
                            .hideAllFocusLabels();
                        menuOption.getMenuTargetElement().label.getFocus();
                    }
                },
                {
                    text: "Delete Routing Rule".translate(),
                    icon: "mafe-menu-delete-rules-action",
                    onClick: function (menuOption) {
                        var splitedID = menuOption.getMenuTargetElement().getID().split("-");
                        menuOption.parent.hide();
                        if ((splitedID && splitedID[0] === 'pmui') || PMDesigner.project.isDirty()) {
                            PMDesigner.restApi.execute({
                                data: JSON.stringify(PMDesigner.project.getDirtyObject()),
                                method: "update",
                                url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id,
                                success: function (data, textStatus, xhr) {
                                    PMDesigner.project.listeners.success(PMDesigner.project, textStatus, data);
                                    PMDesigner.project.isSave = false;
                                    //open property form
                                    PMDesigner.RoutingRuleDeleteAllFlow(menuOption.getMenuTargetElement());
                                },
                                error: function (xhr, textStatus, errorThrown) {
                                    PMDesigner.project.listeners.failure(that, textStatus, xhr);
                                    PMDesigner.project.isSave = false;
                                }
                            });
                        } else {
                            PMDesigner.RoutingRuleDeleteAllFlow(menuOption.getMenuTargetElement());
                        }
                    }
                },
                {
                    text: "Delete".translate(),
                    icon: "mafe-menu-delete-action",
                    onClick: function (menuOption) {
                        menuOption.parent.hide();

                        PMUI.getActiveCanvas().removeElements();
                    }
                },
                {
                    id: "menuSubProperties",
                    text: "Properties".translate(),
                    icon: "mafe-menu-properties-action",
                    onClick: function (menuOption) {
                        var splitedID = menuOption.getMenuTargetElement().getID().split("-");
                        menuOption.parent.hide();
                        if ((splitedID && splitedID[0] === 'pmui') || PMDesigner.project.isDirty()) {
                            PMDesigner.restApi.execute({
                                data: JSON.stringify(PMDesigner.project.getDirtyObject()),
                                method: "update",
                                url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id,
                                success: function (data, textStatus, xhr) {
                                    PMDesigner.project.listeners.success(PMDesigner.project, textStatus, data);
                                    PMDesigner.project.isSave = false;
                                    //open property form
                                    PMDesigner.propertiesSubProcess(menuOption
                                        .getMenuTargetElement(menuOption.getMenuTargetElement()));

                                },
                                error: function (xhr, textStatus, errorThrown) {
                                    PMDesigner.project.listeners.failure(that, textStatus, xhr);
                                    PMDesigner.project.isSave = false;
                                }
                            });
                        } else {
                            PMDesigner.propertiesSubProcess(menuOption
                                .getMenuTargetElement(menuOption.getMenuTargetElement()));

                        }
                    }
                }
            ],
            onShow: function (menu) {
                var targetElement = menu.getTargetElement();
                PMUI.getActiveCanvas().emptyCurrentSelection();
                PMUI.getActiveCanvas().addToSelection(targetElement);
                PMUI.getActiveCanvas().hideDragConnectHandlers();
                PMUI.getActiveCanvas().hideAllFocusLabels();
                if (PMUI.getActiveCanvas().currentConnection) {
                    PMUI.getActiveCanvas().currentConnection
                        .hidePortsAndHandlers();
                }
            }
        },
        start = {
            id: 'menuStart',
            items: [
                startCatchMarker,
                {
                    text: "Edit Label".translate(),
                    icon: "mafe-menu-edit-label-action",
                    onClick: function (menuOption) {
                        menuOption.getMenuTargetElement().label.canvas.hideAllFocusLabels();
                        menuOption.getMenuTargetElement().label.getFocus();
                    }
                },
                {
                    text: "Delete".translate(),
                    icon: "mafe-menu-delete-action",
                    onClick: function (menuOption) {
                        PMUI.getActiveCanvas().removeElements();
                    }
                },
                {
                    id: "menuStartWebEntry",
                    text: "Web Entry".translate(),
                    icon: "mafe-menu-start-message-view",
                    //visible: false,
                    onClick: function (menuOption) {
                        var splitedID = menuOption.getMenuTargetElement().getID().split("-"), webEntry;
                        menuOption.parent.hide();
                        if ((splitedID && splitedID[0] === 'pmui') || PMDesigner.project.isDirty()) {
                            PMDesigner.restApi.execute({
                                data: JSON.stringify(PMDesigner.project.getDirtyObject()),
                                method: "update",
                                url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id,
                                success: function (data, textStatus, xhr) {
                                    var webEntry;
                                    PMDesigner.project.listeners.success(PMDesigner.project, textStatus, data);
                                    PMDesigner.project.isSave = false;
                                    //open property form
                                    webEntry = new WebEntry(menuOption.getMenuTargetElement());
                                    webEntry.render();
                                },
                                error: function (xhr, textStatus, errorThrown) {
                                    PMDesigner.project.listeners.failure(that, textStatus, xhr);
                                    PMDesigner.project.isSave = false;
                                }
                            });
                        } else {
                            webEntry = new WebEntry(menuOption.getMenuTargetElement());
                            webEntry.render();
                        }
                    }
                },
                {
                    id: "menuStartProperties",
                    text: "Properties".translate(),
                    icon: "mafe-menu-start-message-view",
                    onClick: function (menuOption) {
                        var a = menuOption.getMenuTargetElement(), message_window,
                            splitedID = menuOption.getMenuTargetElement().getID().split("-");
                        switch (a.getEventMarker()) {
                            case "TIMER":
                                menuOption.parent.hide();
                                if ((splitedID && splitedID[0] === 'pmui') || PMDesigner.project.isDirty()) {
                                    PMDesigner.restApi.execute({
                                        data: JSON.stringify(PMDesigner.project.getDirtyObject()),
                                        method: "update",
                                        url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id,
                                        success: function (data, textStatus, xhr) {
                                            PMDesigner.project.listeners.success(PMDesigner.project, textStatus, data);
                                            PMDesigner.project.isSave = false;
                                            //open property form
                                            PMDesigner.timerEventProperties(menuOption.getMenuTargetElement());
                                        },
                                        error: function (xhr, textStatus, errorThrown) {
                                            PMDesigner.project.listeners.failure(that, textStatus, xhr);
                                            PMDesigner.project.isSave = false;
                                        }
                                    });
                                } else {
                                    PMDesigner.timerEventProperties(menuOption.getMenuTargetElement());
                                }
                                break;
                            default :
                                var messageEventDefinition,
                                    eventCurrent = menuOption.getMenuTargetElement();
                                if (menuMessages[eventCurrent.evn_type][eventCurrent.evn_marker] !== undefined) {
                                    message_window = new PMUI.ui.MessageWindow({
                                        id: "cancelMessageTriggers",
                                        width: 490,
                                        title: 'Information'.translate(),
                                        windowMessageType: 'info',
                                        bodyHeight: 'auto',
                                        message: menuMessages[eventCurrent.evn_type][eventCurrent.evn_marker],
                                        footerItems: [
                                            {
                                                text: 'Ok'.translate(),
                                                handler: function () {
                                                    message_window.close();
                                                },
                                                buttonType: "success"
                                            }
                                        ]
                                    });
                                    message_window.open();
                                    message_window.showFooter();
                                } else {
                                    menuOption.parent.hide();
                                    if ((splitedID && splitedID[0] === 'pmui') || PMDesigner.project.isDirty()) {
                                        PMDesigner.restApi.execute({
                                            data: JSON.stringify(PMDesigner.project.getDirtyObject()),
                                            method: "update",
                                            url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id,
                                            success: function (data, textStatus, xhr) {
                                                PMDesigner.project.listeners.success(PMDesigner.project, textStatus, data);
                                                PMDesigner.project.isSave = false;
                                                //open property form
                                                messageEventDefinition = new MessageEventDefinition(eventCurrent);
                                            },
                                            error: function (xhr, textStatus, errorThrown) {
                                                PMDesigner.project.listeners.failure(that, textStatus, xhr);
                                                PMDesigner.project.isSave = false;
                                            }
                                        });
                                    } else {
                                        messageEventDefinition = new MessageEventDefinition(eventCurrent);
                                    }
                                }
                        }
                    }
                }
            ],
            onShow: function (menu) {
                var targetElement = menu.getTargetElement(),
                    shape,
                    propertyMap = ['MESSAGECATCH', 'TIMER'];
                PMUI.getActiveCanvas().emptyCurrentSelection();
                PMUI.getActiveCanvas().addToSelection(targetElement);
                PMUI.getActiveCanvas().hideDragConnectHandlers();
                PMUI.getActiveCanvas().hideAllFocusLabels();
                if (targetElement.evn_marker == 'TIMER') {
                }
                if (PMUI.getActiveCanvas().currentConnection) {
                    PMUI.getActiveCanvas().currentConnection.hidePortsAndHandlers();
                }
                if (targetElement.evn_marker === 'MESSAGECATCH') {
                    var dt = menu.getItems();
                    for (var i = 0; i < dt.length; i += 1) {
                        if (dt[i].id === 'idReceiveMessage') {
                            dt[i].setVisible(true);
                        }
                    }
                }
                //disabled weebentry for others pmEvent no support
                if (targetElement.getPorts().asArray().length) {
                    shape = targetElement.getPorts().getFirst().getConnection().getDestPort().parent;
                    if (shape && (shape instanceof PMActivity) && targetElement.evn_marker === "EMPTY") {
                        menu.items.find('id', 'menuStartWebEntry').enable();
                    } else if (shape && (shape instanceof PMEvent) && shape.evn_marker === "EMAIL"
                        && targetElement.evn_marker === "EMPTY") {
                        menu.items.find('id', 'menuStartWebEntry').enable();
                    } else {
                        menu.items.find('id', 'menuStartWebEntry').disable();
                    }
                } else {
                    menu.items.find('id', 'menuStartWebEntry').disable();
                }

                //Enable && Disabled - Properties
                if (targetElement.evn_type === "START" && propertyMap.indexOf(targetElement.evn_marker) >= 0) {
                    menu.items.find('id', 'menuStartProperties').enable();
                } else {
                    menu.items.find('id', 'menuStartProperties').disable();
                }
            },
            onHide: function (menu) {
                var dt = menu.getItems(), i;
                for (i = 0; i < dt.length; i += 1) {
                    if (dt[i].id === 'idReceiveMessage') {
                        dt[i].setVisible(false);
                    }
                }
            }
        },
        end = {
            id: 'menuEnd',
            items: [
                endMarker,
                {
                    text: "Edit Label".translate(),
                    icon: "mafe-menu-edit-label-action",
                    onClick: function (menuOption) {
                        menuOption.getMenuTargetElement().label.canvas.hideAllFocusLabels();
                        menuOption.getMenuTargetElement().label.getFocus();
                    }
                },
                {
                    text: "Delete".translate(),
                    icon: "mafe-menu-delete-action",
                    onClick: function (menuOption) {
                        PMUI.getActiveCanvas().removeElements();
                    }
                },
                {
                    id: "menuEndProperties",
                    text: "Properties".translate(),
                    icon: "mafe-menu-delete-action",
                    onClick: function (menuOption) {
                        var a = menuOption.getMenuTargetElement(), message_window,
                            splitedID = menuOption.getMenuTargetElement().getID().split("-");
                        switch (a.getEventMarker()) {
                            case "EMAIL":
                                menuOption.parent.hide();
                                if ((splitedID && splitedID[0] === 'pmui') || PMDesigner.project.isDirty()) {
                                    PMDesigner.restApi.execute({
                                        data: JSON.stringify(PMDesigner.project.getDirtyObject()),
                                        method: "update",
                                        url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id,
                                        success: function (data, textStatus, xhr) {
                                            PMDesigner.project.listeners.success(PMDesigner.project, textStatus, data);
                                            PMDesigner.project.isSave = false;
                                            //open property form
                                            PMDesigner.emailEventProperties(menuOption.getMenuTargetElement());
                                        },
                                        error: function (xhr, textStatus, errorThrown) {
                                            PMDesigner.project.listeners.failure(that, textStatus, xhr);
                                            PMDesigner.project.isSave = false;
                                        }
                                    });
                                } else {
                                    PMDesigner.emailEventProperties(menuOption.getMenuTargetElement());
                                }
                                break;
                            default :
                                var messageEventDefinition,
                                    eventCurrent = menuOption.getMenuTargetElement();
                                if (menuMessages[eventCurrent.evn_type][eventCurrent.evn_marker] !== undefined) {
                                    message_window = new PMUI.ui.MessageWindow({
                                        id: "cancelMessageTriggers",
                                        width: 490,
                                        title: 'Information'.translate(),
                                        windowMessageType: 'info',
                                        bodyHeight: 'auto',
                                        message: menuMessages[eventCurrent.evn_type][eventCurrent.evn_marker],
                                        footerItems: [
                                            {
                                                text: 'Ok'.translate(),
                                                handler: function () {
                                                    message_window.close();
                                                },
                                                buttonType: "success"
                                            }
                                        ]
                                    });
                                    message_window.open();
                                    message_window.showFooter();
                                } else {
                                    if ((splitedID && splitedID[0] === 'pmui') || PMDesigner.project.isDirty()) {
                                        PMDesigner.restApi.execute({
                                            data: JSON.stringify(PMDesigner.project.getDirtyObject()),
                                            method: "update",
                                            url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id,
                                            success: function (data, textStatus, xhr) {
                                                PMDesigner.project.listeners.success(PMDesigner.project, textStatus, data);
                                                PMDesigner.project.isSave = false;
                                                //open property form
                                                messageEventDefinition = new MessageEventDefinition(eventCurrent);
                                            },
                                            error: function (xhr, textStatus, errorThrown) {
                                                PMDesigner.project.listeners.failure(that, textStatus, xhr);
                                                PMDesigner.project.isSave = false;
                                            }
                                        });
                                    } else {
                                        messageEventDefinition = new MessageEventDefinition(eventCurrent);
                                    }
                                }
                        }
                    }
                }
            ],
            onShow: function (menu) {
                var propertyMap = ['MESSAGETHROW', 'EMAIL'],
                    targetElement = menu.getTargetElement();
                PMUI.getActiveCanvas().emptyCurrentSelection();
                PMUI.getActiveCanvas().addToSelection(targetElement);
                PMUI.getActiveCanvas().hideDragConnectHandlers();
                PMUI.getActiveCanvas().hideAllFocusLabels();
                if (PMUI.getActiveCanvas().currentConnection) {
                    PMUI.getActiveCanvas().currentConnection.hidePortsAndHandlers();
                }


                //Enable && Disabled - Properties
                if (targetElement.evn_type === "END" && propertyMap.indexOf(targetElement.evn_marker) >= 0) {
                    menu.items.find('id', 'menuEndProperties').enable();
                } else {
                    menu.items.find('id', 'menuEndProperties').disable();
                }
            }
        },
        selection = {
            id: 'menuSelection',
            items: [
                {
                    text: "Properties".translate(),
                    icon: "mafe-menu-properties-action"
                },
                {
                    text: "Edit Label".translate(),
                    icon: "mafe-menu-edit-label-action",
                    onClick: function (menuOption) {
                        menuOption.getMenuTargetElement().label.canvas.hideAllFocusLabels();
                        menuOption.getMenuTargetElement().label.getFocus();
                    }
                },
                {
                    text: "Delete".translate(),
                    icon: "mafe-menu-delete-action"
                }
            ]
        },
        evaluation = {
            id: 'menuEvaluation',
            items: [

                {
                    text: "Edit Label".translate(),
                    icon: "mafe-menu-edit-label-action",
                    onClick: function (menuOption) {
                        menuOption.getMenuTargetElement().label.canvas.hideAllFocusLabels();
                        menuOption.getMenuTargetElement().label.getFocus();
                    }
                },
                {
                    text: "Delete".translate(),
                    icon: "mafe-menu-delete-action",
                    onClick: function (menuOption) {
                        alert("Working on this feature...");
                    }
                },
                {
                    text: "Properties".translate(),
                    icon: "mafe-menu-properties-action",
                    onClick: function (menuOption) {
                        alert("Working on this feature...");
                    }
                }
            ]
        },
        parallel = {
            id: 'menuParallel',
            items: [
                typeMenu,
                {
                    text: "Edit Label".translate(),
                    icon: "mafe-menu-edit-label-action",
                    onClick: function (menuOption) {
                        menuOption.getMenuTargetElement().label.canvas.hideAllFocusLabels();
                        menuOption.getMenuTargetElement().label.getFocus();
                    }
                },
                {
                    text: 'Delete'.translate(),
                    icon: 'mafe-menu-delete-action',
                    onClick: function (menuOption) {
                        PMUI.getActiveCanvas().removeElements();
                    }
                }
            ],
            listeners: {},
            onShow: function (menu) {
                PMUI.getActiveCanvas().emptyCurrentSelection();
                PMUI.getActiveCanvas().addToSelection(menu.getTargetElement());
                PMUI.getActiveCanvas().hideDragConnectHandlers();
                PMUI.getActiveCanvas().hideAllFocusLabels();
                if (PMUI.getActiveCanvas().currentConnection) {
                    PMUI.getActiveCanvas().currentConnection.hidePortsAndHandlers();
                }
            }
        },
        exclusive = {
            id: 'menuExclusive',
            items: [
                typeMenu,
                {
                    text: "Edit Label".translate(),
                    icon: "mafe-menu-edit-label-action",
                    onClick: function (menuOption) {
                        menuOption.getMenuTargetElement().label.canvas.hideAllFocusLabels();
                        menuOption.getMenuTargetElement().label.getFocus();
                    }
                },
                {
                    text: "Delete".translate(),
                    icon: "mafe-menu-delete-action",
                    onClick: function (menuOption) {
                        PMUI.getActiveCanvas().removeElements();
                    }
                },
                {
                    text: "Properties".translate(),
                    icon: "mafe-menu-properties-action",
                    onClick: function (menuOption) {
                        PMDesigner.RoutingRule(menuOption.getMenuTargetElement());
                    }
                }
            ],
            onShow: function (menu) {
                var element = menu.getTargetElement(), propertyOption;
                PMUI.getActiveCanvas().emptyCurrentSelection();
                PMUI.getActiveCanvas().addToSelection(element);
                PMUI.getActiveCanvas().hideDragConnectHandlers();
                PMUI.getActiveCanvas().hideAllFocusLabels();
                if (PMUI.getActiveCanvas().currentConnection) {
                    PMUI.getActiveCanvas().currentConnection.hidePortsAndHandlers();
                }
                if (menu.items) {
                    if (element && element.gat_direction) {
                        propertyOption = menu.items.find("text", "Properties");
                        if (propertyOption) {
                            if (element.gat_direction === 'CONVERGING') {
                                propertyOption.disable();
                            } else {
                                propertyOption.enable();

                            }
                        }
                    }
                }

            }
        },
        inclusive = {
            id: 'menuInclusive',
            items: [
                typeMenu,
                {
                    text: "Edit Label".translate(),
                    icon: "mafe-menu-edit-label-action",
                    onClick: function (menuOption) {
                        menuOption.getMenuTargetElement().label.canvas.hideAllFocusLabels();
                        menuOption.getMenuTargetElement().label.getFocus();
                    }
                },
                {
                    text: 'Delete'.translate(),
                    icon: 'mafe-menu-delete-action',
                    onClick: function (menuOption) {
                        PMUI.getActiveCanvas().removeElements();
                    }
                },
                {
                    text: 'Properties'.translate(),
                    icon: 'mafe-menu-properties-action',
                    onClick: function (menuOption) {
                        PMDesigner.RoutingRule(menuOption.getMenuTargetElement());
                    }
                }
            ],
            onShow: function (menu) {
                var element = menu.getTargetElement(), propertyOption;
                PMUI.getActiveCanvas().addToSelection(element);
                PMUI.getActiveCanvas().hideDragConnectHandlers();
                PMUI.getActiveCanvas().hideAllFocusLabels();

                if (menu.items) {
                    if (element && element.gat_direction) {
                        propertyOption = menu.items.find("text", "Properties");
                        if (propertyOption) {
                            if (element.gat_direction === 'CONVERGING') {
                                propertyOption.disable();
                            } else {
                                propertyOption.enable();

                            }
                        }
                    }
                }
            }
        },
        complex = {
            id: 'menuComplex',
            items: [
                typeMenu,
                {
                    text: "Edit Label".translate(),
                    icon: "mafe-menu-edit-label-action",
                    onClick: function (menuOption) {
                        menuOption.getMenuTargetElement().label.canvas.hideAllFocusLabels();
                        menuOption.getMenuTargetElement().label.getFocus();
                    }
                },
                {
                    text: 'Delete'.translate(),
                    icon: 'mafe-menu-delete-action',
                    onClick: function (menuOption) {
                        PMUI.getActiveCanvas().removeElements();
                    }
                },
                {
                    text: "Properties".translate(),
                    icon: "mafe-menu-properties-action",
                    onClick: function (menuOption) {
                        PMDesigner.complexRoutingRule(menuOption.getMenuTargetElement());
                    }
                }
            ],
            onShow: function (menu) {
                PMUI.getActiveCanvas().emptyCurrentSelection();
                PMUI.getActiveCanvas().addToSelection(menu.getTargetElement());
                if (PMUI.getActiveCanvas().currentConnection) {
                    PMUI.getActiveCanvas().currentConnection.hidePortsAndHandlers();
                }
                PMUI.getActiveCanvas().hideDragConnectHandlers();
                PMUI.getActiveCanvas().hideAllFocusLabels();
            }
        },
        group = {
            id: 'menuLine',
            items: [
                {
                    text: "Edit Label".translate(),
                    icon: "mafe-menu-edit-label-action",
                    onClick: function (menuOption) {
                        menuOption.getMenuTargetElement().label.canvas.hideAllFocusLabels();
                        menuOption.getMenuTargetElement().label.getFocus();
                    }
                },
                {
                    text: 'Delete'.translate(),
                    icon: 'mafe-menu-delete-action',
                    onClick: function (menuOption) {
                        PMUI.getActiveCanvas().removeElements();
                    }
                }
            ],
            onShow: function (menu) {
                PMUI.getActiveCanvas().emptyCurrentSelection();
                PMUI.getActiveCanvas().addToSelection(menu.getTargetElement());
                if (PMUI.getActiveCanvas().currentConnection) {
                    PMUI.getActiveCanvas().currentConnection.hidePortsAndHandlers();
                }
                PMUI.getActiveCanvas().hideDragConnectHandlers();
                PMUI.getActiveCanvas().hideAllFocusLabels();
            }
        },
        dataObject = {
            id: 'menuDataObject',
            items: [
                {
                    id: "dataType",
                    text: "Data Type".translate(),
                    items: [
                        {
                            id: "dataobject",
                            text: "Data Empty".translate(),
                            onClick: function (menuOption) {
                                menuOption.getMenuTargetElement().switchDataType('DATAOBJECT');
                            }
                        },
                        {
                            id: "datainput",
                            text: "Data Input".translate(),
                            onClick: function (menuOption) {
                                menuOption.getMenuTargetElement().switchDataType('DATAINPUT');
                            }
                        },
                        {
                            id: "dataoutput",
                            text: "Data Output".translate(),
                            onClick: function (menuOption) {
                                menuOption.getMenuTargetElement().switchDataType('DATAOUTPUT');
                            }
                        }
                    ]
                },
                {
                    text: "Edit Label".translate(),
                    icon: "mafe-menu-edit-label-action",
                    onClick: function (menuOption) {
                        menuOption.getMenuTargetElement().label.canvas.hideAllFocusLabels();
                        menuOption.getMenuTargetElement().label.getFocus();
                    }
                },
                {
                    text: 'Delete'.translate(),
                    icon: 'mafe-menu-delete-action',
                    onClick: function (menuOption) {
                        PMUI.getActiveCanvas().removeElements();
                    }
                }
            ],
            onShow: function (menu) {
                PMUI.getActiveCanvas().emptyCurrentSelection();
                PMUI.getActiveCanvas().addToSelection(menu.getTargetElement());
                if (PMUI.getActiveCanvas().currentConnection) {
                    PMUI.getActiveCanvas().currentConnection.hidePortsAndHandlers();
                }
                PMUI.getActiveCanvas().hideDragConnectHandlers();
                PMUI.getActiveCanvas().hideAllFocusLabels();
            }
        },
        dataStore = {
            id: 'menuDataObject',
            items: [
                {
                    text: "Edit Label".translate(),
                    icon: "mafe-menu-edit-label-action",
                    onClick: function (menuOption) {
                        menuOption.getMenuTargetElement().label.canvas.hideAllFocusLabels();
                        menuOption.getMenuTargetElement().label.getFocus();
                    }
                },
                {
                    text: 'Delete'.translate(),
                    icon: 'mafe-menu-delete-action',
                    onClick: function (menuOption) {
                        PMUI.getActiveCanvas().removeElements();
                    }
                }
            ],
            onShow: function (menu) {
                PMUI.getActiveCanvas().emptyCurrentSelection();
                PMUI.getActiveCanvas().addToSelection(menu.getTargetElement());
                if (PMUI.getActiveCanvas().currentConnection) {
                    PMUI.getActiveCanvas().currentConnection.hidePortsAndHandlers();
                }
                PMUI.getActiveCanvas().hideDragConnectHandlers();
                PMUI.getActiveCanvas().hideAllFocusLabels();
            }
        },
        textAnnotation = {
            id: 'menuDataObject',
            items: [
                {
                    text: "Edit Label".translate(),
                    icon: "mafe-menu-edit-label-action",
                    onClick: function (menuOption) {
                        menuOption.getMenuTargetElement().label.canvas.hideAllFocusLabels();
                        menuOption.getMenuTargetElement().label.getFocus();
                    }
                },
                {
                    text: 'Delete'.translate(),
                    icon: 'mafe-menu-delete-action',
                    onClick: function (menuOption) {
                        PMUI.getActiveCanvas().removeElements();
                    }
                }
            ],
            onShow: function (menu) {
                PMUI.getActiveCanvas().emptyCurrentSelection();
                PMUI.getActiveCanvas().addToSelection(menu.getTargetElement());
                if (PMUI.getActiveCanvas().currentConnection) {
                    PMUI.getActiveCanvas().currentConnection.hidePortsAndHandlers();
                }
                PMUI.getActiveCanvas().hideDragConnectHandlers();
                PMUI.getActiveCanvas().hideAllFocusLabels();
            }
        },
        intermediate = {
            id: 'intermediate',
            items: [
                intermediateCatchMarker,
                intermediateThrowMarker,
                {
                    text: "Edit Label".translate(),
                    icon: "mafe-menu-edit-label-action",
                    onClick: function (menuOption) {
                        menuOption.getMenuTargetElement().label.canvas.hideAllFocusLabels();
                        menuOption.getMenuTargetElement().label.getFocus();
                    }
                },
                {
                    text: 'Delete'.translate(),
                    icon: 'mafe-menu-delete-action',
                    onClick: function (menuOption) {
                        PMUI.getActiveCanvas().removeElements();
                    }
                },
                {
                    id: "intermediateProperties",
                    text: "Properties".translate(),
                    icon: "mafe-menu-delete-action",
                    onClick: function (menuOption) {
                        var a = menuOption.getMenuTargetElement(), message_window,
                            splitedID = menuOption.getMenuTargetElement().getID().split("-");
                        switch (a.getEventMarker()) {
                            case "EMAIL":
                                menuOption.parent.hide();
                                if ((splitedID && splitedID[0] === 'pmui') || PMDesigner.project.isDirty()) {
                                    PMDesigner.restApi.execute({
                                        data: JSON.stringify(PMDesigner.project.getDirtyObject()),
                                        method: "update",
                                        url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id,
                                        success: function (data, textStatus, xhr) {
                                            PMDesigner.project.listeners.success(PMDesigner.project, textStatus, data);
                                            PMDesigner.project.isSave = false;
                                            //open property form
                                            PMDesigner.emailEventProperties(menuOption.getMenuTargetElement());
                                        },
                                        error: function (xhr, textStatus, errorThrown) {
                                            PMDesigner.project.listeners.failure(that, textStatus, xhr);
                                            PMDesigner.project.isSave = false;
                                        }
                                    });
                                } else {
                                    PMDesigner.emailEventProperties(menuOption.getMenuTargetElement());
                                }
                                break;
                            case "TIMER":
                                menuOption.parent.hide();
                                if ((splitedID && splitedID[0] === 'pmui') || PMDesigner.project.isDirty()) {
                                    PMDesigner.restApi.execute({
                                        data: JSON.stringify(PMDesigner.project.getDirtyObject()),
                                        method: "update",
                                        url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id,
                                        success: function (data, textStatus, xhr) {
                                            PMDesigner.project.listeners.success(PMDesigner.project, textStatus, data);
                                            PMDesigner.project.isSave = false;
                                            //open property form
                                            PMDesigner.timerEventProperties(menuOption.getMenuTargetElement());
                                        },
                                        error: function (xhr, textStatus, errorThrown) {
                                            PMDesigner.project.listeners.failure(that, textStatus, xhr);
                                            PMDesigner.project.isSave = false;
                                        }
                                    });
                                } else {
                                    PMDesigner.timerEventProperties(menuOption.getMenuTargetElement());
                                }
                                break;
                            default :
                                var messageEventDefinition,
                                    eventCurrent = menuOption.getMenuTargetElement();
                                if (menuMessages[eventCurrent.evn_type][eventCurrent.evn_behavior][eventCurrent.evn_marker] !== undefined) {
                                    message_window = new PMUI.ui.MessageWindow({
                                        id: "cancelMessageTriggers",
                                        width: 490,
                                        title: 'Information'.translate(),
                                        windowMessageType: 'info',
                                        bodyHeight: 'auto',
                                        message: menuMessages[eventCurrent.evn_type][eventCurrent.evn_behavior][eventCurrent.evn_marker],
                                        footerItems: [
                                            {
                                                text: 'Ok'.translate(),
                                                handler: function () {
                                                    message_window.close();
                                                },
                                                buttonType: "success"
                                            }
                                        ]
                                    });
                                    message_window.open();
                                    message_window.showFooter();
                                } else {
                                    menuOption.parent.hide();
                                    if ((splitedID && splitedID[0] === 'pmui') || PMDesigner.project.isDirty()) {
                                        PMDesigner.restApi.execute({
                                            data: JSON.stringify(PMDesigner.project.getDirtyObject()),
                                            method: "update",
                                            url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id,
                                            success: function (data, textStatus, xhr) {
                                                PMDesigner.project.listeners.success(PMDesigner.project, textStatus, data);
                                                PMDesigner.project.isSave = false;
                                                //open property form
                                                messageEventDefinition = new MessageEventDefinition(eventCurrent);
                                            },
                                            error: function (xhr, textStatus, errorThrown) {
                                                PMDesigner.project.listeners.failure(that, textStatus, xhr);
                                                PMDesigner.project.isSave = false;
                                            }
                                        });
                                    } else {
                                        messageEventDefinition = new MessageEventDefinition(eventCurrent);
                                    }
                                }
                        }

                    }
                }
            ],
            onShow: function (menu) {
                var targetElement = menu.getTargetElement(),
                    propertyEnabled = ['MESSAGECATCH', 'MESSAGETHROW', 'EMAIL', 'TIMER'];

                PMUI.getActiveCanvas().emptyCurrentSelection();
                PMUI.getActiveCanvas().addToSelection(menu.getTargetElement());
                if (PMUI.getActiveCanvas().currentConnection) {
                    PMUI.getActiveCanvas().currentConnection.hidePortsAndHandlers();
                }
                PMUI.getActiveCanvas().hideDragConnectHandlers();
                PMUI.getActiveCanvas().hideAllFocusLabels();

                if (targetElement.evn_behavior === 'CATCH') {
                    targetElement.menu.getItems()[0].setVisible(true);
                    targetElement.menu.getItems()[1].setVisible(false);
                } else {
                    targetElement.menu.getItems()[0].setVisible(false);
                    targetElement.menu.getItems()[1].setVisible(true);
                }

                //Enable && Disabled - Properties
                if (propertyEnabled.indexOf(targetElement.evn_marker) >= 0) {

                    menu.items.find('id', 'intermediateProperties').enable();
                } else {
                    menu.items.find('id', 'intermediateProperties').disable();
                }
            }
        },
        boundary = {
            id: 'menuDataObject',
            items: [
                boundaryCatchMarker,
                {
                    text: "Edit Label".translate(),
                    icon: "mafe-menu-edit-label-action",
                    onClick: function (menuOption) {
                        menuOption.getMenuTargetElement().label.canvas.hideAllFocusLabels();
                        menuOption.getMenuTargetElement().label.getFocus();
                    }
                },
                {
                    text: 'Delete'.translate(),
                    icon: 'mafe-menu-delete-action',
                    onClick: function (menuOption) {
                        PMUI.getActiveCanvas().removeElements();
                    }
                }
            ],
            onShow: function (menu) {
                var targetElement = menu.getTargetElement();

                PMUI.getActiveCanvas().emptyCurrentSelection();
                PMUI.getActiveCanvas().addToSelection(menu.getTargetElement());
                if (PMUI.getActiveCanvas().currentConnection) {
                    PMUI.getActiveCanvas().currentConnection.hidePortsAndHandlers();
                }
                PMUI.getActiveCanvas().hideDragConnectHandlers();
                PMUI.getActiveCanvas().hideAllFocusLabels();

            }
        },
        participant = {
            id: 'menuDataObject',
            items: [
                {
                    text: "Edit Label".translate(),
                    icon: "mafe-menu-edit-label-action",
                    onClick: function (menuOption) {
                        menuOption.getMenuTargetElement().label.canvas.hideAllFocusLabels();
                        menuOption.getMenuTargetElement().label.getFocus();
                    }
                },
                {
                    text: 'Delete'.translate(),
                    icon: 'mafe-menu-delete-action',
                    onClick: function (menuOption) {
                        PMUI.getActiveCanvas().removeElements();
                    }
                }
            ],
            onShow: function (menu) {
                PMUI.getActiveCanvas().emptyCurrentSelection();
                PMUI.getActiveCanvas().addToSelection(menu.getTargetElement());
                if (PMUI.getActiveCanvas().currentConnection) {
                    PMUI.getActiveCanvas().currentConnection.hidePortsAndHandlers();
                }
                PMUI.getActiveCanvas().hideDragConnectHandlers();
                PMUI.getActiveCanvas().hideAllFocusLabels();
            }
        };
    var defaults = {
        factory: {
            products: {
                'CANVAS': canvas,
                'TASK': task,
                'SUB_PROCESS': subProcess,
                'START': start,
                'END': end,
                'SELECTION': selection,
                'EVALUATION': evaluation,
                'PARALLEL': parallel,
                'EXCLUSIVE': exclusive,
                'EVENTBASED': exclusive,
                'EXCLUSIVEEVENTBASED': exclusive,
                'PARALLELEVENTBASED': exclusive,
                'INCLUSIVE': inclusive,
                'COMPLEX': complex,
                'VERTICAL_LINE': group,
                'HORIZONTAL_LINE': group,
                'GROUP': group,
                'LANE': participant,
                'DATAOBJECT': dataObject,
                'DATASTORE': dataStore,
                'TEXT_ANNOTATION': textAnnotation,
                'V_LABEL': textAnnotation,
                'H_LABEL': textAnnotation,
                'INTERMEDIATE': intermediate,
                'BOUNDARY': boundary,
                'PARTICIPANT': participant,
                'POOL': participant
            },

            defaultProduct: 'START'
        }
    };

    PMDesigner.contextMenuFactory = new ContextMenu(defaults.factory);
    PMDesigner.getMenuFactory = function (type) {
        if (prj_readonly === 'true') {
            return {};
        }
        return PMDesigner.contextMenuFactory.getProduct(type);
    };
    /**
     * Change task type marker
     * @param menuOption
     */
    function handlerMarkerType(menuOption) {
        var result = true,
            tempType = menuOption.getMenuTargetElement().getTaskType();
        menuOption.parent.hide();

        menuOption.getMenuTargetElement().switchTaskType(menuOption.id.toUpperCase());

        //validate if act loop type is parallel
        if (menuOption.getMenuTargetElement().act_loop_type === "PARALLEL") {
            handlerMarkerLoopType(menuOption, tempType)
        }

        //to enable save button
        PMDesigner.project.updateElement([]);

    }

    /**
     * Change marker loop type
     * @param menuOption
     * @param type
     * @returns {boolean}
     */
    function handlerMarkerLoopType(menuOption, type) {
        var message_window,
            loopType = menuOption.id.toUpperCase(),
            taskType = menuOption.getMenuTargetElement().act_task_type;
        if (typeof type !== 'undefined' && type !== null) {
            taskType = type;
        }

        if (menuOption.getMenuTargetElement().act_loop_type === "PARALLEL") {

            message_window = new PMUI.ui.MessageWindow({
                id: "handlerMarkerType",
                width: 490,
                title: 'Parallel Marker Type'.translate(),
                windowMessageType: 'warning',
                bodyHeight: 'auto',
                message: "The configuring of multiple instances will be lost".translate(),
                footerItems: [
                    {
                        text: 'Cancel'.translate(),
                        handler: function () {
                            menuOption.getMenuTargetElement().switchLoopType('PARALLEL');
                            menuOption.getMenuTargetElement().switchTaskType(taskType);
                            message_window.close();
                            return false;
                        },
                        buttonType: "error"
                    },
                    {
                        text: 'Continue'.translate(),
                        handler: function () {
                            try {
                                if (loopType == "loop" || loopType == "sequential") {
                                    menuOption.getMenuTargetElement().switchLoopType(loopType.toUpperCase());
                                } else {
                                    menuOption.getMenuTargetElement().switchLoopType('EMPTY');
                                }
                                PMDesigner.project.updateElement([]);
                            } catch (e) {
                                console.error("loop marker error", e.message);
                            }
                            message_window.close();
                            return true;
                        },
                        buttonType: "success"
                    }

                ]
            });
            message_window.open();
            message_window.showFooter();
        } else {
            menuOption.parent.hide();
            menuOption.getMenuTargetElement().switchLoopType(loopType.toUpperCase());
            PMDesigner.project.updateElement([]);
            return true;
        }
    }

}());

(function () {
    PMDesigner.shapeFactory = function (type, options) {
        var customshape = null,
            menuShape,
            defaultOptions = null,
            canvasName,
            name,
            id,
            classEvent = "start",
            pmCanvas = this.canvas,
            corona = PMDesigner.defaultCrown,
            wildcard;

        canvasName = new IncrementNameCanvas(pmCanvas);
        name = canvasName.get(type);

        if (typeof options === 'undefined') {
            options = {};
            if (type === 'START_MESSAGE') {
                type = 'START';
                classEvent = "message";
                options.evn_marker = 'MESSAGECATCH';
            }
            if (type === 'START_TIMER') {
                type = 'START';
                classEvent = "timer";
                options.evn_marker = 'TIMER';
            }

            if (type === 'END_MESSAGE') {
                type = 'END';
                classEvent = "message";
                options.evn_marker = 'MESSAGETHROW';
            }
            if (type === 'END_EMAIL') {
                type = 'END';
                classEvent = "email";
                options.evn_marker = 'EMAIL';
                options.evn_behavior = 'THROW';
            }
            if (type === 'INTERMEDIATE_SENDMESSAGE') {
                type = 'INTERMEDIATE';
                classEvent = "sendmessage";
                options.evn_marker = 'MESSAGETHROW';
                options.evn_behavior = 'THROW';
            }
            if (type === 'INTERMEDIATE_RECEIVEMESSAGE') {
                type = 'INTERMEDIATE';
                classEvent = "receivemessage";
                options.evn_marker = 'MESSAGECATCH';
                options.evn_behavior = 'CATCH';
            }
            if (type === 'INTERMEDIATE_EMAIL') {
                type = 'INTERMEDIATE';
                classEvent = "email";
                options.evn_marker = 'EMAIL';
                options.evn_behavior = 'THROW';
            }
            if (type === 'INTERMEDIATE_TIMER') {
                type = 'INTERMEDIATE';
                classEvent = "timer";
                options.evn_marker = 'TIMER';
            }
            if (type === 'BOUNDARY_EVENT') {
                type = 'BOUNDARY';
                classEvent = "receivemessage";
                options.evn_marker = 'EMPTY';
                options.evn_behavior = 'CATCH';
            }

            switch (type) {
                case 'COMPLEX':
                case 'PARALLEL':
                case 'EXCLUSIVE':
                case 'EVENTBASED':
                case 'EXCLUSIVEEVENTBASED':
                case 'PARALLELEVENTBASED':
                case 'INCLUSIVE':
                    options.gat_type = type;
                    break;
            }
        }
        if (type === 'DATAOBJECT') {
            type = 'DATAOBJECT';
            options.dat_object_type = 'dataobject';
        }
        if (type === 'DATAINPUT') {
            type = 'DATAOBJECT';
            options.dat_object_type = 'datainput';
        }
        if (type === 'DATAOUTPUT') {
            type = 'DATAOBJECT';
            options.dat_object_type = 'dataoutput';
        }

        wildcard = [
            {
                name: 'wildcard'.translate(),
                className: 'mafe-wildcard',
                onClick: null,
                column: 2
            }
        ];

        switch (type) {
            case 'TASK':
                defaultOptions = {
                    canvas: pmCanvas,
                    width: 150,
                    height: 75,
                    act_type: 'TASK',
                    act_name: name,
                    act_task_type: 'EMPTY',
                    act_loop_type: 'EMPTY',
                    minHeight: 30,
                    minWidth: 150,
                    maxHeight: 50,
                    maxWidth: 170,
                    container: "activity",
                    labels: [
                        {
                            message: name,
                            width: 0,
                            height: 0,
                            position: {
                                location: 'center',
                                diffX: 0,
                                diffY: 0
                            },
                            attachEvents: false
                        }
                    ],
                    layers: [
                        {
                            x: 0,
                            y: 0,
                            layerName: "first-layer",
                            priority: 2,
                            visible: true,
                            style: {
                                cssClasses: [
                                    'mafe-activity-task'
                                ]
                            }
                        },
                        {
                            x: 0,
                            y: 0,
                            layerName: "second-layer",
                            priority: 2,
                            visible: false,
                            style: {
                                cssClasses: []
                            }
                        }

                    ],
                    connectAtMiddlePoints: true,
                    resizeBehavior: 'activityResize',
                    resizeHandlers: {
                        type: "Rectangle",
                        total: 8,
                        resizableStyle: {
                            cssProperties: {
                                'background-color': "rgb(0, 255, 0)",
                                'border': '1px solid black'
                            }
                        },
                        nonResizableStyle: {
                            cssProperties: {
                                'background-color': "white",
                                'border': '1px solid black'
                            }
                        }
                    },
                    "drop": {
                        type: "pmactivitydrop",
                        selectors: [
                            "#BOUNDARY_EVENT",
                            ".mafe-event-boundary",
                            ".dragConnectHandler"
                        ]
                    },
                    markers: [
                        {
                            markerType: 'USERTASK',
                            x: 10,
                            y: 10,
                            position: 0,
                            markerZoomClasses: []
                        },
                        {
                            markerType: 'EMPTY',
                            x: 10,
                            y: 10,
                            position: 4,
                            markerZoomClasses: []
                        }
                    ],
                    validatorMarker: {
                        width: 12,
                        height: 12,
                        position: 2,
                        errors: {
                            style: {
                                markerZoomClassesError: [
                                    "mafe-style-error-marker-7",
                                    "mafe-style-error-marker-10",
                                    "mafe-style-error-marker-14",
                                    "mafe-style-error-marker-17",
                                    "mafe-style-error-marker-21"
                                ],
                                markerZoomClassesWarning: [
                                    "mafe-style-warning-marker-7",
                                    "mafe-style-warning-marker-10",
                                    "mafe-style-warning-marker-14",
                                    "mafe-style-warning-marker-17",
                                    "mafe-style-warning-marker-21"
                                ]
                            }
                        }
                    },
                    corona: corona,
                    focusLabel: true
                };
                jQuery.extend(true, defaultOptions, options);
                defaultOptions.markers[0]
                    .markerZoomClasses = PMDesigner.updateMarkerLayerClasses(defaultOptions);
                defaultOptions.markers[1]
                    .markerZoomClasses = PMDesigner.updateLoopLayerClasses(defaultOptions);
                customshape = new PMActivity(defaultOptions);
                break;
            case 'SUB_PROCESS':
                defaultOptions = {
                    canvas: pmCanvas,
                    width: 150,
                    height: 75,
                    act_type: 'SUB_PROCESS',
                    act_loop_type: 'EMPTY',
                    act_name: name,
                    act_task_type: 'COLLAPSED',
                    minHeight: 30,
                    minWidth: 150,
                    maxHeight: 50,
                    maxWidth: 170,
                    container: "activity",
                    labels: [
                        {
                            message: name,
                            position: {
                                location: 'center',
                                diffX: 0,
                                diffY: 0
                            },
                            attachEvents: false
                        }
                    ],
                    layers: [
                        {
                            x: 0,
                            y: 0,
                            layerName: "first-layer",
                            priority: 2,
                            visible: true,
                            style: {
                                cssClasses: [
                                    'mafe-activity-subprocess'
                                ]
                            }
                        },
                        {
                            x: 0,
                            y: 0,
                            layerName: "second-layer",
                            priority: 2,
                            visible: false,
                            style: {
                                cssClasses: []
                            }
                        }

                    ],
                    connectAtMiddlePoints: true,
                    resizeBehavior: 'activityResize',
                    resizeHandlers: {
                        type: "Rectangle",
                        total: 8,
                        resizableStyle: {
                            cssProperties: {
                                'background-color': "rgb(0, 255, 0)",
                                'border': '1px solid black'
                            }
                        },
                        nonResizableStyle: {
                            cssProperties: {
                                'background-color': "white",
                                'border': '1px solid black'
                            }
                        }
                    },
                    "drop": {
                        type: "pmactivitydrop",
                        selectors: [
                            "#BOUNDARY_EVENT",
                            ".mafe-event-boundary",
                            ".dragConnectHandler"
                        ]
                    },
                    markers: [
                        {
                            markerType: 'COLLAPSED',
                            x: 10,
                            y: 10,
                            position: 4,
                            markerZoomClasses: [
                                "mafe-collapsed-marker-10",
                                "mafe-collapsed-marker-15",
                                "mafe-collapsed-marker-21",
                                "mafe-collapsed-marker-26",
                                "mafe-collapsed-marker-31"
                            ]
                        }
                    ],
                    validatorMarker: {
                        width: 12,
                        height: 12,
                        position: 2,
                        errors: {
                            style: {
                                markerZoomClassesError: [
                                    "mafe-style-error-marker-7",
                                    "mafe-style-error-marker-10",
                                    "mafe-style-error-marker-14",
                                    "mafe-style-error-marker-17",
                                    "mafe-style-error-marker-21"
                                ]
                            }
                        }
                    },
                    corona: corona,
                    focusLabel: true
                };
                jQuery.extend(true, defaultOptions, options);
                defaultOptions.markers[0]
                    .markerZoomClasses = PMDesigner.updateMarkerLayerClasses(defaultOptions);
                customshape = new PMActivity(defaultOptions);
                break;
            case 'START':
                defaultOptions = {
                    canvas: pmCanvas,
                    width: 33,
                    height: 33,
                    evn_type: 'START',
                    evn_name: '',
                    evn_marker: 'EMPTY',
                    evn_behavior: 'catch',
                    evn_message: 'LEAD',
                    labels: [
                        {
                            message: '',
                            visible: true,
                            width: 100,
                            position: {
                                location: 'bottom',
                                diffX: 0,
                                diffY: 13
                            }
                        }
                    ],
                    style: {
                        cssClasses: ['mafe-event-start']
                    },
                    layers: [
                        {
                            x: 0,
                            y: 0,
                            layerName: "first-layer",
                            priority: 2,
                            visible: true,
                            style: {
                                cssClasses: []
                            },
                            zoomSprites: [
                                'mafe-event-start-empty-16',
                                'mafe-event-start-empty-24',
                                'mafe-event-start-empty-33',
                                'mafe-event-start-empty-41',
                                'mafe-event-start-empty-49'
                            ]
                        },
                        {
                            x: 0,
                            y: 0,
                            layerName: "second-layer",
                            priority: 2,
                            visible: false,
                            style: {
                                cssClasses: []
                            }
                        }
                    ],
                    connectAtMiddlePoints: true,
                    resizeBehavior: 'NoResize',
                    resizeHandlers: {
                        type: "Rectangle",
                        total: 4,
                        resizableStyle: {
                            cssProperties: {
                                'background-color': "rgb(0, 255, 0)",
                                'border': '1px solid black'
                            }
                        },
                        nonResizableStyle: {
                            cssProperties: {
                                'background-color': "white",
                                'border': '1px solid black'
                            }
                        }
                    },
                    "drop": {
                        type: "pmconnection",
                        selectors: ['.dragConnectHandler']
                    },
                    validatorMarker: {
                        width: 12,
                        height: 12,
                        position: 2,
                        errors: {
                            style: {
                                markerZoomClassesError: [
                                    "mafe-style-error-marker-7",
                                    "mafe-style-error-marker-10",
                                    "mafe-style-error-marker-14",
                                    "mafe-style-error-marker-17",
                                    "mafe-style-error-marker-21"
                                ]
                            }
                        }
                    },
                    corona: corona
                };
                jQuery.extend(true, defaultOptions, options);
                defaultOptions.layers[0]
                    .zoomSprites = PMDesigner.updateLayerClasses(defaultOptions);
                customshape = new PMEvent(defaultOptions);
                break;
            case 'INTERMEDIATE':
                defaultOptions = {
                    canvas: pmCanvas,
                    width: 33,
                    height: 33,
                    evn_type: 'INTERMEDIATE',
                    evn_name: '',
                    evn_marker: 'EMPTY',
                    evn_behavior: 'CATCH',
                    labels: [
                        {
                            message: '',
                            visible: true,
                            position: {
                                location: 'bottom',
                                diffX: 0,
                                diffY: 13
                            }
                        }
                    ],
                    style: {
                        cssClasses: ['mafe-event-intermediate']
                    },
                    layers: [
                        {
                            x: 0,
                            y: 0,
                            layerName: "first-layer",
                            priority: 2,
                            visible: true,
                            style: {
                                cssClasses: []
                            },
                            zoomSprites: [
                                'mafe-event-intermediate-16',
                                'mafe-event-intermediate-24',
                                'mafe-event-intermediate-33',
                                'mafe-event-intermediate-41',
                                'mafe-event-intermediate-49'
                            ]
                        },
                        {
                            x: 0,
                            y: 0,
                            layerName: "second-layer",
                            priority: 2,
                            visible: false,
                            style: {
                                cssClasses: []
                            }
                        }
                    ],
                    connectAtMiddlePoints: true,
                    resizeBehavior: 'NoResize',
                    resizeHandlers: {
                        type: "Rectangle",
                        total: 4,
                        resizableStyle: {
                            cssProperties: {
                                'background-color': "rgb(0, 255, 0)",
                                'border': '1px solid black'
                            }
                        },
                        nonResizableStyle: {
                            cssProperties: {
                                'background-color': "white",
                                'border': '1px solid black'
                            }
                        }
                    },
                    "drop": {
                        type: "pmconnection",
                        selectors: ['.dragConnectHandler']
                    },
                    validatorMarker: {
                        width: 12,
                        height: 12,
                        position: 2,
                        errors: {
                            style: {
                                markerZoomClassesError: [
                                    "mafe-style-error-marker-7",
                                    "mafe-style-error-marker-10",
                                    "mafe-style-error-marker-14",
                                    "mafe-style-error-marker-17",
                                    "mafe-style-error-marker-21"
                                ]
                            }
                        }
                    },
                    corona: corona
                };
                jQuery.extend(true, defaultOptions, options);
                defaultOptions.layers[0]
                    .zoomSprites = PMDesigner.updateLayerClasses(defaultOptions);
                customshape = new PMEvent(defaultOptions);
                break;
            case 'BOUNDARY':
                defaultOptions = {
                    canvas: pmCanvas,
                    width: 33,
                    height: 33,
                    evn_type: 'BOUNDARY',
                    evn_name: '',
                    evn_marker: 'EMPTY',
                    evn_behavior: 'CATCH',
                    labels: [
                        {
                            message: '',
                            visible: true,
                            position: {
                                location: 'bottom',
                                diffX: 0,
                                diffY: 0
                            }
                        }
                    ],
                    style: {
                        cssClasses: ['mafe-event-boundary']
                    },
                    layers: [
                        {
                            x: 0,
                            y: 0,
                            layerName: "first-layer",
                            priority: 2,
                            visible: true,
                            style: {
                                cssClasses: []
                            },
                            zoomSprites: [
                                'mafe-event-intermediate-16',
                                'mafe-event-intermediate-24',
                                'mafe-event-intermediate-33',
                                'mafe-event-intermediate-41',
                                'mafe-event-intermediate-49'
                            ]
                        }
                    ],
                    connectAtMiddlePoints: true,
                    resizeBehavior: 'NoResize',
                    resizeHandlers: {
                        type: "Rectangle",
                        total: 4,
                        resizableStyle: {
                            cssProperties: {
                                'background-color': "rgb(0, 255, 0)",
                                'border': '1px solid black'
                            }
                        },
                        nonResizableStyle: {
                            cssProperties: {
                                'background-color': "white",
                                'border': '1px solid black'
                            }
                        }
                    },
                    "drop": {
                        type: "pmconnection",
                        selectors: ['.dragConnectHandler']
                    },
                    drag: 'nodrag'
                };
                jQuery.extend(true, defaultOptions, options);
                defaultOptions.layers[0]
                    .zoomSprites = PMDesigner.updateLayerClasses(defaultOptions);
                customshape = new PMEvent(defaultOptions);
                break;

            case 'END':
                defaultOptions = {
                    canvas: pmCanvas,
                    width: 33,
                    height: 33,
                    evn_type: 'END',
                    evn_name: '',
                    evn_marker: 'EMPTY',
                    evn_behavior: 'throw',
                    labels: [
                        {
                            message: '',
                            visible: true,
                            position: {
                                location: 'bottom',
                                diffX: 0,
                                diffY: 13
                            }
                        }
                    ],
                    style: {
                        cssClasses: ['mafe-event-end']
                    },
                    layers: [
                        {
                            x: 0,
                            y: 0,
                            layerName: "first-layer",
                            priority: 2,
                            visible: true,
                            style: {
                                cssClasses: []
                            },
                            zoomSprites: [
                                'mafe-event-end-empty-16',
                                'mafe-event-end-empty-24',
                                'mafe-event-end-empty-33',
                                'mafe-event-end-empty-41',
                                'mafe-event-end-empty-49'
                            ]
                        },
                        {
                            x: 0,
                            y: 0,
                            layerName: "second-layer",
                            priority: 2,
                            visible: false,
                            style: {
                                cssClasses: []
                            }
                        }
                    ],
                    connectAtMiddlePoints: true,
                    resizeBehavior: 'NoResize',
                    resizeHandlers: {
                        type: "Rectangle",
                        total: 4,
                        resizableStyle: {
                            cssProperties: {
                                'background-color': "rgb(0, 255, 0)",
                                'border': '1px solid black'
                            }
                        },
                        nonResizableStyle: {
                            cssProperties: {
                                'background-color': "white",
                                'border': '1px solid black'
                            }
                        }
                    },
                    "drop": {
                        type: "pmconnection",
                        selectors: ['.dragConnectHandler']
                    },
                    validatorMarker: {
                        width: 12,
                        height: 12,
                        position: 2,
                        errors: {
                            style: {
                                markerZoomClassesError: [
                                    "mafe-style-error-marker-7",
                                    "mafe-style-error-marker-10",
                                    "mafe-style-error-marker-14",
                                    "mafe-style-error-marker-17",
                                    "mafe-style-error-marker-21"
                                ]
                            }
                        }
                    },
                    corona: corona
                };
                jQuery.extend(true, defaultOptions, options);
                defaultOptions.layers[0]
                    .zoomSprites = PMDesigner.updateLayerClasses(defaultOptions);
                customshape = new PMEvent(defaultOptions);
                break;

            case 'COMPLEX':
            case 'PARALLEL':
            case 'EXCLUSIVE':
            case 'EVENTBASED':
            case 'EXCLUSIVEEVENTBASED':
            case 'PARALLELEVENTBASED':
            case 'INCLUSIVE':
                defaultOptions = {
                    labels: [
                        {
                            message: '',
                            visible: true,
                            width: 100,
                            position: {
                                location: 'bottom',
                                diffX: 0,
                                diffY: 13
                            }
                        }
                    ],
                    canvas: pmCanvas,
                    width: 41,
                    height: 41,
                    gat_type: 'EXCLUSIVE',
                    gat_name: '',
                    layers: [
                        {
                            x: 0,
                            y: 0,
                            layerName: "first-layer",
                            priority: 2,
                            visible: true,
                            style: {
                                cssClasses: []
                            },
                            zoomSprites: [
                                'mafe-gateway-exclusive-20',
                                'mafe-gateway-exclusive-30',
                                'mafe-gateway-exclusive-41',
                                'mafe-gateway-exclusive-51',
                                'mafe-gateway-exclusive-61'
                            ]
                        },
                        {
                            x: 0,
                            y: 0,
                            layerName: "second-layer",
                            priority: 2,
                            visible: false,
                            style: {
                                cssClasses: []
                            }
                        }
                    ],
                    connectAtMiddlePoints: true,
                    resizeBehavior: 'NoResize',
                    resizeHandlers: {
                        type: "Rectangle",
                        total: 4,
                        resizableStyle: {
                            cssProperties: {
                                'background-color': "rgb(0, 255, 0)",
                                'border': '1px solid black'
                            }
                        },
                        nonResizableStyle: {
                            cssProperties: {
                                'background-color': "white",
                                'border': '1px solid black'
                            }
                        }
                    },
                    "drop": {
                        type: "pmconnection",
                        selectors: ['.dragConnectHandler']
                    },
                    validatorMarker: {
                        width: 12,
                        height: 12,
                        position: 2,
                        errors: {
                            style: {
                                markerZoomClassesError: [
                                    "mafe-style-error-marker-7",
                                    "mafe-style-error-marker-10",
                                    "mafe-style-error-marker-14",
                                    "mafe-style-error-marker-17",
                                    "mafe-style-error-marker-21"
                                ]
                            }
                        }
                    },
                    corona: corona
                };
                jQuery.extend(true, defaultOptions, options);
                defaultOptions.layers[0]
                    .zoomSprites = PMDesigner.updateGatewayLayerClasses(defaultOptions);
                customshape = new PMGateway(defaultOptions);
                break;
            case 'GROUP':
                defaultOptions = {
                    art_name: name,
                    art_type: 'GROUP',
                    canvas: pmCanvas,
                    width: 200,
                    height: 100,
                    style: {
                        cssClasses: ['mafe-artifact-group']
                    },
                    layers: [
                        {
                            x: 0,
                            y: 0,
                            layerName: "background-layer",
                            priority: 1,
                            visible: true,
                            style: {
                                cssClasses: [
                                    'mafe-artifact-group'
                                ]
                            }
                        },
                        {
                            x: 0,
                            y: 0,
                            layerName: "second-layer",
                            priority: 2,
                            visible: false,
                            style: {
                                cssClasses: []
                            }
                        }
                    ],
                    connectAtMiddlePoints: true,
                    resizeBehavior: 'annotationResize',
                    resizeHandlers: {
                        type: "Rectangle",
                        total: 8,
                        resizableStyle: {
                            cssProperties: {
                                'background-color': "rgb(0, 255, 0)",
                                'border': '1px solid black'
                            }
                        }
                    },
                    labels: [
                        {
                            message: "",
                            width: 0,
                            height: 0,
                            position: {
                                location: 'top',
                                diffX: 2,
                                diffY: 0
                            },
                            attachEvents: false,
                            updateParent: true,
                            style: {
                                cssClasses: [
                                    'mafe-label-annotation'
                                ]
                            }
                        }
                    ],
                    "drop": {
                        type: "pmconnection",
                        selectors: ['.dragConnectHandler']
                    },
                    corona: corona,
                    focusLabel: true
                };
                jQuery.extend(true, defaultOptions, options);
                customshape = new PMArtifact(defaultOptions);
                break;
            case 'TEXT_ANNOTATION':
                defaultOptions = {
                    art_name: name,
                    art_type: 'TEXT_ANNOTATION',
                    canvas: pmCanvas,
                    width: 100,
                    height: 30,
                    style: {
                        cssClasses: ['mafe-artifact-annotation']
                    },
                    layers: [
                        {
                            x: 0,
                            y: 0,
                            layerName: "background-layer",
                            priority: 1,
                            visible: true,
                            style: {
                                cssClasses: [
                                    'mafe-artifact-annotation'
                                ]
                            }
                        },
                        {
                            x: 0,
                            y: 0,
                            layerName: "second-layer",
                            priority: 2,
                            visible: false,
                            style: {
                                cssClasses: []
                            }
                        }
                    ],
                    connectAtMiddlePoints: true,
                    resizeBehavior: 'annotationResize',
                    resizeHandlers: {
                        type: "Rectangle",
                        total: 8,
                        resizableStyle: {
                            cssProperties: {
                                'background-color': "rgb(0, 255, 0)",
                                'border': '1px solid black'
                            }
                        }
                    },
                    labels: [
                        {
                            message: name,
                            width: 0,
                            height: 0,
                            position: {
                                location: 'center',
                                diffX: 0,
                                diffY: 0
                            },
                            attachEvents: false,
                            updateParent: true
                        }
                    ],
                    "drop": {
                        type: "pmconnection",
                        selectors: ['.dragConnectHandler']
                    },
                    corona: corona,
                    focusLabel: true
                };
                jQuery.extend(true, defaultOptions, options);
                customshape = new PMArtifact(defaultOptions);
                break;

            case 'DATAOBJECT':
                defaultOptions = {
                    labels: [
                        {
                            message: '',
                            visible: true,
                            width: 100,
                            position: {
                                location: 'bottom',
                                diffX: 0,
                                diffY: 0
                            }
                        }
                    ],
                    canvas: pmCanvas,
                    width: 33,
                    height: 41,
                    dat_type: 'DATAOBJECT',
                    dat_object_type: "dataobject",
                    dat_name: '',
                    layers: [
                        {
                            x: 0,
                            y: 0,
                            layerName: "first-layer",
                            priority: 2,
                            visible: true,
                            style: {
                                cssClasses: []
                            },
                            zoomSprites: [
                                'mafe-dataobject-50',
                                'mafe-dataobject-75',
                                'mafe-dataobject-100',
                                'mafe-dataobject-125',
                                'mafe-dataobject-150'
                            ]
                        },
                        {
                            x: 0,
                            y: 0,
                            layerName: "second-layer",
                            priority: 2,
                            visible: false,
                            style: {
                                cssClasses: []
                            }
                        }
                    ],
                    connectAtMiddlePoints: true,
                    resizeBehavior: 'NoResize',
                    resizeHandlers: {
                        type: "Rectangle",
                        total: 4,
                        resizableStyle: {
                            cssProperties: {
                                'background-color': "rgb(0, 255, 0)",
                                'border': '1px solid black'
                            }
                        },
                        nonResizableStyle: {
                            cssProperties: {
                                'background-color': "white",
                                'border': '1px solid black'
                            }
                        }
                    },
                    "drop": {
                        type: "pmconnection",
                        selectors: ['.dragConnectHandler']
                    },
                    markers: [
                        {
                            markerType: 'USERTASK',
                            x: 10,
                            y: 10,
                            position: 0
                        }
                    ],
                    corona: corona
                };
                jQuery.extend(true, defaultOptions, options);
                defaultOptions.markers[0]
                    .markerZoomClasses = PMDesigner.updateDataMarkerLayerClasses(defaultOptions);
                customshape = new PMData(defaultOptions);
                break;

            case 'DATASTORE':
                defaultOptions = {
                    labels: [
                        {
                            message: '',
                            visible: true,
                            width: 100,
                            position: {
                                location: 'bottom',
                                diffX: 0,
                                diffY: 0
                            }
                        }
                    ],
                    canvas: pmCanvas,
                    width: 41,
                    height: 41,
                    dat_type: 'DATASTORE',
                    dat_name: '',
                    layers: [
                        {
                            x: 0,
                            y: 0,
                            layerName: "first-layer",
                            priority: 2,
                            visible: true,
                            style: {
                                cssClasses: []
                            },
                            zoomSprites: [
                                'mafe-datastore-20',
                                'mafe-datastore-30',
                                'mafe-datastore-41',
                                'mafe-datastore-51',
                                'mafe-datastore-61'
                            ]
                        },
                        {
                            x: 0,
                            y: 0,
                            layerName: "second-layer",
                            priority: 2,
                            visible: false,
                            style: {
                                cssClasses: []
                            }
                        }
                    ],
                    connectAtMiddlePoints: true,
                    resizeBehavior: 'NoResize',
                    resizeHandlers: {
                        type: "Rectangle",
                        total: 4,
                        resizableStyle: {
                            cssProperties: {
                                'background-color': "rgb(0, 255, 0)",
                                'border': '1px solid black'
                            }
                        },
                        nonResizableStyle: {
                            cssProperties: {
                                'background-color': "white",
                                'border': '1px solid black'
                            }
                        }
                    },

                    "drop": {
                        type: "pmconnection",
                        selectors: ['.dragConnectHandler']
                    },
                    corona: corona
                };
                jQuery.extend(true, defaultOptions, options);
                customshape = new PMData(defaultOptions);
                break;
            case 'PARTICIPANT':
                defaultOptions = {
                    width: 500,
                    height: 130,
                    "canvas": this,
                    "connectAtMiddlePoints": false,
                    topLeft: true,
                    connectionType: 'dotted',
                    resizeBehavior: "participantResize",
                    resizeHandlers: {
                        type: "Rectangle",
                        total: 8,
                        resizableStyle: {
                            cssProperties: {
                                'background-color': "rgb(0, 255, 0)",
                                'border': '1px solid black'
                            }
                        },
                        nonResizableStyle: {
                            cssProperties: {
                                'background-color': "white",
                                'border': '1px solid black'
                            }
                        }
                    },
                    "drop": {
                        type: "pmconnection",
                        selectors: ['.dragConnectHandler']
                    },
                    "style": {
                        cssClasses: ["mafe-pool"]

                    },
                    layers: [
                        {
                            layerName: "first-layer",
                            priority: 2,
                            visible: true,
                            style: {
                                cssProperties: {}
                            }
                        },
                        {
                            x: 0,
                            y: 0,
                            layerName: "second-layer",
                            priority: 2,
                            visible: false,
                            style: {
                                cssClasses: []
                            }
                        }
                    ],
                    labels: [
                        {
                            message: name,
                            width: 0,
                            height: 0,
                            orientation: 'vertical',
                            position: {
                                location: 'center-left',
                                diffX: 15,
                                diffY: 0
                            },
                            attachEvents: false
                        }
                    ],
                    par_name: name,
                    corona: corona,
                    focusLabel: true

                };
                jQuery.extend(true, defaultOptions, options);
                customshape = new PMParticipant(defaultOptions);
                break;
            case 'POOL':
                if (options.lns_name) {
                    name = options.lns_name;
                } else if (options.par_name) {
                    name = options.par_name;
                }
                defaultOptions = {
                    width: 700,
                    height: 200,
                    "canvas": pmCanvas,
                    "connectAtMiddlePoints": false,
                    topLeft: false,
                    connectionType: 'dotted',
                    resizeBehavior: "poolResize",
                    resizeHandlers: {
                        type: "Rectangle",
                        total: 8,
                        resizableStyle: {
                            cssProperties: {
                                'background-color': "rgb(0, 255, 0)",
                                'border': '1px solid black'
                            }
                        },
                        nonResizableStyle: {
                            cssProperties: {
                                'background-color': "white",
                                'border': '1px solid black'
                            }
                        }
                    },
                    "drop": {
                        type: "pmcontainer",
                        selectors: [
                            "#TASK",
                            "#SUB_PROCESS",
                            "#START",
                            "#START_MESSAGE",
                            "#START_TIMER",
                            "#END",
                            "#END_MESSAGE",
                            "#END_EMAIL",
                            "#INTERMEDIATE_SENDMESSAGE",
                            "#INTERMEDIATE_RECEIVEMESSAGE",
                            "#INTERMEDIATE_EMAIL",
                            "#INTERMEDIATE_TIMER",
                            "#EXCLUSIVE",
                            "#PARALLEL",
                            "#INCLUSIVE",
                            "#DATAOBJECT",
                            "#DATASTORE",
                            "#TEXT_ANNOTATION",
                            "#LANE",
                            "#GROUP",
                            ".mafe-event-start",
                            ".mafe-event-intermediate",
                            ".mafe-event-end",
                            ".pmui-pmactivity",
                            ".pmui-pmgateway",
                            ".pmui-pmdata",
                            ".mafe-artifact-annotation",
                            ".mafe-artifact-group",
                            ".port"
                        ]
                    },
                    container: "pool",
                    "style": {
                        cssClasses: ["mafe-pool"]

                    },
                    layers: [
                        {
                            layerName: "first-layer",
                            priority: 2,
                            visible: true,
                            style: {
                                cssProperties: {}
                            }
                        },
                        {
                            x: 0,
                            y: 0,
                            layerName: "second-layer",
                            priority: 2,
                            visible: false,
                            style: {
                                cssClasses: []
                            }
                        }
                    ],
                    labels: [
                        {
                            message: name,
                            width: 0,
                            height: 0,
                            orientation: 'vertical',
                            position: {
                                location: 'center-left',
                                diffX: 15,
                                diffY: 0
                            },
                            attachEvents: false
                        }
                    ],
                    lns_name: name,
                    focusLabel: true,
                    corona: corona

                };
                jQuery.extend(true, defaultOptions, options);
                customshape = new PMPool(defaultOptions);
                break;
            case 'LANE':
                defaultOptions = {
                    width: 500,
                    height: 200,
                    "canvas": pmCanvas,
                    "connectAtMiddlePoints": false,
                    topLeft: true,
                    connectionType: 'dotted',
                    resizeBehavior: "laneResize",
                    resizeHandlers: {
                        type: "Rectangle",
                        total: 8,
                        resizableStyle: {
                            cssProperties: {
                                'background-color': "rgb(0, 255, 0)",
                                'border': '1px solid black'
                            }
                        },
                        nonResizableStyle: {
                            cssProperties: {
                                'background-color': "white",
                                'border': '1px solid black'
                            }
                        }
                    },
                    "drop": {
                        type: "pmcontainer",
                        selectors: [
                            "#TASK",
                            "#SUB_PROCESS",
                            "#START",
                            "#START_MESSAGE",
                            "#START_TIMER",
                            "#END",
                            "#END_MESSAGE",
                            "#END_EMAIL",
                            "#INTERMEDIATE_SENDMESSAGE",
                            "#INTERMEDIATE_RECEIVEMESSAGE",
                            "#INTERMEDIATE_EMAIL",
                            "#INTERMEDIATE_TIMER",
                            "#EXCLUSIVE",
                            "#PARALLEL",
                            "#INCLUSIVE",
                            "#DATAOBJECT",
                            "#DATASTORE",
                            "#GROUP",
                            "#TEXT_ANNOTATION",
                            ".mafe-event-start",
                            ".mafe-event-intermediate",
                            ".mafe-event-end",
                            ".pmui-pmactivity",
                            ".pmui-pmgateway",
                            ".pmui-pmdata",
                            ".mafe-artifact-annotation",
                            ".mafe-artifact-group"
                        ]
                    },
                    container: "lane",
                    layers: [
                        {
                            layerName: "first-layer",
                            priority: 2,
                            visible: true,
                            style: {
                                cssProperties: {}
                            }
                        }

                    ],
                    labels: [
                        {
                            message: name,
                            width: 0,
                            height: 0,
                            orientation: 'vertical',
                            position: {
                                location: 'center-left',
                                diffX: 15,
                                diffY: 0
                            }
                        }
                    ],
                    lan_name: name,
                    focusLabel: true

                };
                jQuery.extend(true, defaultOptions, options);
                customshape = new PMLane(defaultOptions);
                break;

        }
        if (customshape && !pmCanvas.readOnly) {
            customshape.attachListeners();
            customshape.extendedType = type;
            menuShape = PMDesigner.getMenuFactory(type);
            customshape.getHTML();
            customshape.setContextMenu(menuShape);
        }
        return customshape;
    };
    PMDesigner.updateLayerClasses = function (options) {
        return [
            'mafe-event-' + options.evn_type.toLowerCase() + '-' + options.evn_marker.toLowerCase() + '-16',
            'mafe-event-' + options.evn_type.toLowerCase() + '-' + options.evn_marker.toLowerCase() + '-24',
            'mafe-event-' + options.evn_type.toLowerCase() + '-' + options.evn_marker.toLowerCase() + '-33',
            'mafe-event-' + options.evn_type.toLowerCase() + '-' + options.evn_marker.toLowerCase() + '-41',
            'mafe-event-' + options.evn_type.toLowerCase() + '-' + options.evn_marker.toLowerCase() + '-49'
        ];
    };
    PMDesigner.updateGatewayLayerClasses = function (options) {
        return [
            'mafe-gateway-' + options.gat_type.toLowerCase() + '-20',
            'mafe-gateway-' + options.gat_type.toLowerCase() + '-30',
            'mafe-gateway-' + options.gat_type.toLowerCase() + '-41',
            'mafe-gateway-' + options.gat_type.toLowerCase() + '-51',
            'mafe-gateway-' + options.gat_type.toLowerCase() + '-61'
        ];
    };
    PMDesigner.updateMarkerLayerClasses = function (options) {
        if (options.act_task_type !== 'EMPTY') {
            return [
                "mafe-" + options.act_task_type.toLowerCase() + "-marker-10",
                "mafe-" + options.act_task_type.toLowerCase() + "-marker-15",
                "mafe-" + options.act_task_type.toLowerCase() + "-marker-21",
                "mafe-" + options.act_task_type.toLowerCase() + "-marker-26",
                "mafe-" + options.act_task_type.toLowerCase() + "-marker-31"
            ];
        }
    };
    PMDesigner.updateLoopLayerClasses = function (options) {
        if (options.act_loop_type !== 'EMPTY') {
            return [
                "mafe-" + options.act_loop_type.toLowerCase() + "-marker-10",
                "mafe-" + options.act_loop_type.toLowerCase() + "-marker-15",
                "mafe-" + options.act_loop_type.toLowerCase() + "-marker-21",
                "mafe-" + options.act_loop_type.toLowerCase() + "-marker-26",
                "mafe-" + options.act_loop_type.toLowerCase() + "-marker-31"
            ];
        }
    };
    PMDesigner.updateDataMarkerLayerClasses = function (options) {
        var type = options.dat_object_type.toLowerCase();
        if (type !== 'dataobject') {
            return [
                "mafe-" + type + "-marker-10",
                "mafe-" + type + "-marker-15",
                "mafe-" + type + "-marker-21",
                "mafe-" + type + "-marker-26",
                "mafe-" + type + "-marker-31"
            ];
        }
    };
    /**
     * Save a process and open the settings
     * @param shape
     * @param callback
     */
    PMDesigner.saveAndOpenSettings= function (shape, callback) {
        var splitedID;

        if(shape) {
            splitedID = shape.getID().split("-");
            if ((splitedID && splitedID[0] === 'pmui') || PMDesigner.project.isDirty()) {
                PMDesigner.restApi.execute({
                    data: JSON.stringify(PMDesigner.project.getDirtyObject()),
                    method: "update",
                    url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE + "/project/" + PMDesigner.project.id,
                    success: function (data, textStatus, xhr) {
                        PMDesigner.project.listeners.success(PMDesigner.project, textStatus, data);
                        PMDesigner.project.isSave = false;
                        if(callback) {
                            callback(shape);
                        }
                    },
                    error: function (xhr, textStatus, errorThrown) {
                        PMDesigner.project.listeners.failure(that, textStatus, xhr);
                        PMDesigner.project.isSave = false;
                    }
                });
            } else {
                callback(shape);
            }
        }


    };
}());
PMDesigner.sidebar = [];

PMDesigner.sidebar.push(
    new ToolbarPanel({
        fields: [
            {
                selector: 'TASK',
                type: 'button',
                className: [
                    'mafe-designer-icon',
                    'mafe-toolbar-task'
                ],
                tooltip: "Task".translate()
            },
            {
                selector: 'SUB_PROCESS',
                type: 'button',
                className: [
                    'mafe-designer-icon',
                    'mafe-toolbar-subprocess'
                ],
                tooltip: "Sub Process".translate()
            }
        ]
    }),
    new ToolbarPanel({
        fields: [
            {
                selector: 'EXCLUSIVE',
                type: 'button',
                className: [
                    'mafe-designer-icon',
                    'mafe-toolbar-gateway-exclusive'
                ],
                tooltip: "Exclusive Gateway".translate()
            },
            {
                selector: 'PARALLEL',
                type: 'button',
                className: [
                    'mafe-designer-icon',
                    'mafe-toolbar-gateway-parallel'
                ],
                tooltip: "Parallel gateway".translate()
            },
            {
                selector: 'INCLUSIVE',
                type: 'button',
                className: [
                    'mafe-designer-icon',
                    'mafe-toolbar-gateway-inclusive'
                ],
                tooltip: "Inclusive Gateway".translate()
            }
        ]
    }),
    new ToolbarPanel({
        fields: [
            {
                selector: 'START',
                type: 'button',
                className: [
                    'mafe-designer-icon',
                    'mafe-toolbar-start'
                ],
                tooltip: "Start Event".translate()
            },
            {
                selector: 'START_TIMER',
                type: 'button',
                className: [
                    'mafe-designer-icon',
                    'mafe-toolbar-event-start-timer'
                ],
                tooltip: "Start Timer Event".translate()
            },
            {
                selector: 'INTERMEDIATE_EMAIL',
                type: 'button',
                className: [
                    'mafe-designer-icon',
                    'mafe-toolbar-intermediate-send-mesage'
                ],
                tooltip: "Intermediate Email Event".translate()
            },
            {
                selector: 'INTERMEDIATE_TIMER',
                type: 'button',
                className: [
                    'mafe-designer-icon',
                    'mafe-toolbar-event-intermediate-timer'
                ],
                tooltip: "Intermediate Timer Event".translate()
            },
            {
                selector: 'END',
                type: 'button',
                className: [
                    'mafe-designer-icon',
                    'mafe-toolbar-end'
                ],
                tooltip: "End Event".translate()
            },
            {
                selector: 'END_EMAIL',
                type: 'button',
                className: [
                    'mafe-designer-icon',
                    'mafe-toolbar-end-message'
                ],
                tooltip: "End Email Event ".translate()
            }
        ]
    }),
    new ToolbarPanel({
        fields: [
            {
                selector: 'DATAOBJECT',
                type: 'button',
                className: [
                    'mafe-designer-icon',
                    'mafe-toolbar-data-object'
                ],
                tooltip: "Data Object".translate()
            },
            {
                selector: 'DATASTORE',
                type: 'button',
                className: [
                    'mafe-designer-icon',
                    'mafe-toolbar-data-store'
                ],
                tooltip: "Data Store".translate()
            }
        ]
    }),
    new ToolbarPanel({
        fields: [
            {
                selector: 'PARTICIPANT',
                type: 'button',
                className: [
                    'mafe-designer-icon',
                    'mafe-toolbar-blackbox'
                ],
                tooltip: " Black Box Pool".translate()
            },
            {
                selector: 'POOL',
                type: 'button',
                className: [
                    'mafe-designer-icon',
                    'mafe-toolbar-pool'
                ],
                tooltip: "Pool".translate()
            },
            {
                selector: 'LANE',
                type: 'button',
                className: [
                    'mafe-designer-icon',
                    'mafe-toolbar-lane'
                ],
                tooltip: "Lane".translate()
            }
        ]
    }),
    new ToolbarPanel({
        fields: [
            {
                selector: 'GROUP',
                type: 'button',
                className: [
                    'mafe-designer-icon',
                    'mafe-toolbar-group'
                ],
                tooltip: "Group".translate()
            },
            {
                selector: 'TEXT_ANNOTATION',
                type: 'button',
                className: [
                    'mafe-designer-icon',
                    'mafe-toolbar-annotation'
                ],
                tooltip: "Text Annotation".translate()
            }
        ]
    }),
    new ToolbarPanel({
        fields: [
            {
                selector: 'LASSO',
                type: 'button',
                className: [
                    'mafe-designer-icon',
                    'mafe-toolbar-lasso'
                ],
                tooltip: "Lasso".translate()
            }

        ]
    }),
    new ToolbarPanel({
        fields: [
            {
                selector: 'enableAutosave',
                type: 'switch',
                className: [
                    'mafe-toolbar-autosave'
                ],
                tooltip: "Validate Now".translate(),
                checked: true,
                text: "Auto Save".translate(),
                checkHandler: function (value) {
                    PMDesigner.autoSaveValue(value);
                }
            }
        ]  
    })
);
ListDynaform = function () {
    this.superTitle = 'Dynaforms'.translate();
    this.tableContainerHeight = 374;
    this.buttonCreate = null;
    this.onCreate = new Function();
    this.onShowId = new Function();
    this.onEdit = new Function();
    this.onDel = new Function();
    this.loaded = false;
    this.clickedClose = true;
    Mafe.Grid.call(this);
    ListDynaform.prototype.init.call(this);
};
ListDynaform.prototype = new Mafe.Grid();
ListDynaform.prototype.init = function () {
    var that = this;
    that.buttonCreate = new PMUI.ui.Button({
        id: 'dynaformButtonNew',
        text: 'Create'.translate(),
        height: '36px',
        width: 100,
        style: {cssClasses: ['mafe-button-create']},
        handler: function (event) {
            that.onCreate(event);
        }
    });
    that.buttonCreate.defineEvents();
    that.setID('idListDynaform');
    that.setColumns([{
        id: 'copyuid',
        title: 'Show ID'.translate(),
        dataType: 'button',
        buttonLabel: 'Show ID'.translate(),
        buttonStyle: {cssClasses: ['mafe-button-show']},
        onButtonClick: function (row, grid) {
            that.onShowId(row, grid);
        }
    }, {
        id: 'dynaformGridPanelTitle',
        title: 'Title'.translate(),
        dataType: 'string',
        width: '460px',
        alignmentCell: 'left',
        sortable: true,
        columnData: 'dyn_title'
    }, {
        id: 'dynaformGridPanelEdit',
        title: '',
        dataType: 'button',
        buttonStyle: {cssClasses: ['mafe-button-edit']},
        buttonLabel: function (row, data) {
            return 'Edit'.translate();
        },
        onButtonClick: function (row, grid) {
            that.onEdit(row, grid);
        }
    }, {
        id: 'dynaformGridPanelDelete',
        title: '',
        dataType: 'button',
        buttonStyle: {cssClasses: ['mafe-button-delete']},
        buttonLabel: function (row, data) {
            return 'Delete'.translate();
        },
        onButtonClick: function (row, grid) {
            that.onDel(row, grid);
        }
    }
    ]);
};
ListDynaform.prototype.createHTML = function () {
    Mafe.Grid.prototype.createHTML.call(this);
    this.dom.toolbar.appendChild(this.buttonCreate.getHTML());
    return this.html;
};
ListDynaform.prototype.load = function () {
    var that = this;
    that.clearItems();
    (new PMRestClient({
        endpoint: 'dynaforms',
        typeRequest: 'get',
        functionSuccess: function (xhr, response) {
            that.setDataItems(response);
            that.sort('dyn_title', 'asc');
            that.loaded = true;
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);

            that.loaded = false;
        },
        messageError: 'There are problems getting the list of dynaforms, please try again.'.translate()
    })).executeRestClient();
};
ListDynaform.prototype.getSuperTitle = function () {
    return this.superTitle;
};

BlankDynaform = function () {
    this.superTitle = 'Create Blank Dynaform'.translate();
    this.onSave = new Function();
    this.onSaveOpen = new Function();
    this.onCancel = new Function();
    this.buttons = null;
    Mafe.Form.call(this);
    BlankDynaform.prototype.init.call(this);
};
BlankDynaform.prototype = new Mafe.Form();
BlankDynaform.prototype.init = function () {
    var that = this;
    that.setID('formDynaformInformation');
    that.setTitle('Dynaform Information'.translate());
    that.setItems([{
        id: 'formDynaformInformationTitle',
        pmType: 'text',
        label: 'Title'.translate(),
        value: '',
        maxLength: 255,
        placeholder: 'The Dynaform title'.translate(),
        name: 'dyn_title',
        required: true,
        controlsWidth: 303
    }, {
        id: 'formDynaformInformationDescription',
        pmType: 'textarea',
        rows: 200,
        name: 'dyn_description',
        width: 200,
        label: 'Description'.translate(),
        style: {cssClasses: ['mafe-textarea-resize']}
    }
    ]);
    that.buttons = [
        new PMUI.ui.Button({
            id: 'btnClose',
            text: 'Cancel'.translate(),
            buttonType: 'error',
            handler: function () {
                that.onCancel();
            }
        }),
        new PMUI.ui.Button({
            id: 'windowDynaformInformationSaveOpen',
            text: 'Save & Open'.translate(),
            buttonType: 'success',
            handler: function () {
                that.onSaveOpen();
            }
        }),
        new PMUI.ui.Button({
            id: 'windowDynaformInformationSave',
            text: 'Save'.translate(),
            buttonType: 'success',
            handler: function () {
                that.onSave();
            }
        })
    ];
};
BlankDynaform.prototype.getButtons = function () {
    return this.buttons;
};
BlankDynaform.prototype.getSuperTitle = function () {
    return this.superTitle;
};

Dynaform = function (settings) {
    this.listDynaform = null;
    this.blankDynaform = null;
    Mafe.Window.call(this, settings);
    Dynaform.prototype.init.call(this, settings);
};
Dynaform.prototype = new Mafe.Window();
Dynaform.prototype.init = function (settings) {
    var that = this;
    that.listDynaform = new ListDynaform();
    that.setOnBeforeCloseHandler(function () {
        this.clickedClose = true;
        if (!that.blankDynaform.isVisible()) {
            that.close();
        } else {
            that.blankDynaform.onCancel();
        }
    });
    that.listDynaform.load();
    that.listDynaform.onShowId = function (row, grid) {
        showUID(row.getData().dyn_uid);
    };
    that.listDynaform.onCreate = function () {
        that.resetView();
        that.setTitle(that.blankDynaform.getSuperTitle());
        that.blankDynaform.setVisible(true);
        that.setButtons(that.blankDynaform.getButtons());
    };
    that.listDynaform.onEdit = function (row, grid) {
        that.edit(row, grid);
    };
    that.listDynaform.onDel = function (row, grid) {
        that.del(row, grid);
    };

    that.blankDynaform = new BlankDynaform();
    that.blankDynaform.onSave = function () {
        that.saveBlank(false);
    };
    that.blankDynaform.onSaveOpen = function () {
        that.saveBlank(true);
    };
    that.blankDynaform.onCancel = function () {
        $("input,select,textarea").blur();
        var title = "";
        that.blankDynaform.loseChanges(title);
    };
    that.blankDynaform.onYesConfirmCancellation = function () {
        if (that.clickedClose) {
            that.close();
        }
        that.resetView();
        that.setTitle(that.listDynaform.getSuperTitle());
        that.listDynaform.setVisible(true);
    };

    that.addItem(that.listDynaform);
    that.addItem(that.blankDynaform);

    that.open();
    that.resetView();
    that.setTitle(that.listDynaform.getSuperTitle());
    that.listDynaform.setVisible(true);

    // hard coding dyn_title textfield because enter keypress reload the current page
    if (that.blankDynaform.getItems()[0]
        && that.blankDynaform.getItems()[0].controls[0]
        && that.blankDynaform.getItems()[0].controls[0].html) {
        $(that.blankDynaform.getItems()[0].controls[0].html).keypress(function (e) {
            if (e.which == 13) {
                e.preventDefault();
            }
        });
    }
};
Dynaform.prototype.saveBlank = function (open) {
    var that = this,
        data,
        restClient,
        flagAux;

    if (!that.blankDynaform.isValid()) {
        flagAux = that.blankDynaform.visible;
    } else {
        flagAux = that.blankDynaform.isValid();
    }

    if (flagAux) {
        if (getData2PMUI(that.blankDynaform.html).dyn_title == "") {
            return false;
        }
    }

    data = getData2PMUI(that.blankDynaform.html);
    data['dyn_version'] = 2;
    data['dyn_type'] = 'xmlform';
    restClient = new PMRestClient({
        endpoint: 'dynaform',
        typeRequest: 'post',
        data: data,
        functionSuccess: function (xhr, response) {
            if (open) {
                that.close();
                try {
                    PMUI.getActiveCanvas().emptyCurrentSelection();
                } catch (msg) {
                }
                PMDesigner.dynaformDesigner(response);
            } else {
                that.resetView();
                that.setTitle(that.listDynaform.getSuperTitle());
                that.listDynaform.setVisible(true);
                that.listDynaform.load();
            }
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        },
        messageError: 'There are problems creating the dynaform, please try again.'.translate(),
        messageSuccess: 'Dynaform saved successfully'.translate(),
        flashContainer: that.listDynaform
    });
    restClient.executeRestClient();
};
Dynaform.prototype.edit = function (row, grid) {
    this.close();
    try {
        PMUI.getActiveCanvas().emptyCurrentSelection();
    } catch (msg) {
    }
    PMDesigner.dynaformDesigner(row.getData());
};
Dynaform.prototype.del = function (row, grid) {
    var that = this,
        confirmDeletion = new Mafe.ConfirmDeletion();
    confirmDeletion.setMessage('Do you want to delete this DynaForm?'.translate());
    confirmDeletion.setTitle("Dynaform".translate());
    confirmDeletion.onDelete = function () {
        var restClient = new PMRestClient({
            endpoint: 'dynaform/' + row.getData().dyn_uid,
            typeRequest: 'remove',
            functionSuccess: function (xhr, response) {
                that.listDynaform.load();
            },
            functionFailure: function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            },
            messageError: 'There are problems deleting the dynaform, please try again.'.translate(),
            messageSuccess: 'Dynaform deleted successfully'.translate(),
            flashContainer: that.listDynaform
        });
        restClient.executeRestClient();
    };
};

(function () {
    PMDesigner.dynaform = function () {
        var dynaform = new Dynaform();
    };
    PMDesigner.dynaform.create = function () {
        var dynaform = new Dynaform();
        dynaform.resetView();
        dynaform.setTitle(dynaform.blankDynaform.getSuperTitle());
        dynaform.blankDynaform.setVisible(true);
        dynaform.setButtons(dynaform.blankDynaform.getButtons());
    };
}());

/**
 * Updates the granular export feature after enabled features are loaded.
 */
var navbarExportUpdate = function () {
    //Code export - export granular (handler)
    var handlerExportNormal = function () {
        var ws = enviromentVariables('WORKSPACE');
        if (!HTTP_SERVER_HOSTNAME) {
            HTTP_SERVER_HOSTNAME = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port : '');
        }
        location.href = HTTP_SERVER_HOSTNAME + "/api/1.0/" + ws + "/project/" + PMDesigner.project.id + "/export?access_token=" + PMDesigner.project.keys.access_token;
    };

    var handlerExportGranular = function () {
        var optionExportNormal = $("<div class='mafe-button-submenu-option normalExport'>" + "Normal".translate() + "</div>"),
            optionExportGranular = $("<div class='mafe-button-submenu-option granularExport'>" + "Custom".translate() + "</div>"),
            menuExport = $("<div class='mafe-button-menu-container sub-nav'></div>").hide(),
            $item = $("#idNavBar").find(".mafe-button-export-process").closest("li");

        menuExport.append(optionExportNormal).append(optionExportGranular);
        if (!$item.find(".mafe-button-menu-container").length) {
            $item.append(menuExport);
        }
        $(".ui-tooltip").hide();
        $(menuExport).slideToggle("slow");
        $item.on("mouseleave", function (e) {
            if ($(this).find(".mafe-button-menu-container").eq(0).is(":visible")) {
                var that = this;
                $(that).find(".mafe-button-menu-container").remove();
            }
        });

        $(".sub-nav").on("click", ".normalExport", function (event) {
            var ws = enviromentVariables('WORKSPACE'),
                locationOrigin;
            if (!window.location.origin) {
                locationOrigin = window.location.protocol + "//" + window.location.hostname +
                    (window.location.port ? ':' + window.location.port : '');
            } else if (typeof HTTP_SERVER_HOSTNAME !== 'undefined') {
                locationOrigin = HTTP_SERVER_HOSTNAME;
            } else {
                locationOrigin = window.location.origin;
            }
            location.href = locationOrigin + "/api/1.0/" + ws + "/project/" + PMDesigner.project.id + "/export?access_token=" + PMDesigner.project.keys.access_token;
        });

        $(".sub-nav").on("click", ".granularExport", function (event) {
            PMDesigner.granularProcessExport();
        });
    };
    if (inArray("jXsSi94bkRUcVZyRStNVExlTXhEclVadGRRcG9xbjNvTWVFQUF3cklKQVBiVT0=", ENABLED_FEATURES)) {
        handler = handlerExportGranular;
    } else {
        handler = handlerExportNormal;
    }

};

function inArray(needle, haystack) {
    var i,
        length = haystack.length;
    for (i = 0; i < length; i += 1) {
        if (haystack[i] == needle) return true;
    }
    return false;
}

var defaultNavbarPanelMenus = {
    array: [],
    init: function () {
        var closeVar = {
                id: 'closeButton',
                name: 'Close',
                htmlProperty: {
                    id: 'closeButton',
                    element: 'li',
                    child: [
                        {
                            element: 'a',
                            class: 'mafe-close',
                            child: [
                                {
                                    element: 'span',
                                    class: 'mafe-button-close'
                                }
                            ]
                        }

                    ]
                },
                actions: {
                    selector: ".mafe-button-close",
                    tooltip: "Close".translate(),
                    execute: true,
                    handler: function () {
                        var message_window,
                            browser = PMDesigner.getBrowser(),
                            url = parent.location.href;

                        if (PMDesigner.project.isDirty() && !PMDesigner.project.isSave) {
                            var message_window = new PMUI.ui.MessageWindow({
                                windowMessageType: 'warning',
                                width: 490,
                                bodyHeight: 'auto',
                                id: "cancelSaveSubprocPropertiesWin",
                                title: PMDesigner.project.projectName,
                                message: 'Save your changes and exit ?'.translate(),
                                footerItems: [
                                    {
                                        pmType: 'label',
                                        text: ' '
                                    }, {
                                        text: "No".translate(),
                                        handler: function () {
                                            PMDesigner.project.isClose = true;
                                            if ((navigator.userAgent.indexOf("MSIE") != -1) || (navigator.userAgent.indexOf("Trident") != -1)) {
                                                window.close();
                                            } else {
                                                parent.location.href = url;
                                            }
                                        },
                                        buttonType: "error"
                                    },

                                    {
                                        text: "Yes".translate(),
                                        handler: function () {
                                            PMDesigner.project.saveClose(true);
                                        },
                                        buttonType: "success"
                                    }
                                ]
                            });
                            message_window.open();
                            message_window.showFooter();
                        } else {
                            if ((navigator.userAgent.indexOf("MSIE") != -1) || (navigator.userAgent.indexOf("Trident") != -1)) {
                                window.close();
                            } else {
                                parent.location.href = url;
                            }
                        }
                    }
                }
            },
            helpVar = {
                id: 'helpButton',
                name: 'Help',
                htmlProperty: {
                    id: 'helpButton',
                    element: 'li',
                    child: [
                        {
                            element: 'a',
                            class: 'mafe-help',
                            child: [
                                {
                                    element: 'span',
                                    class: 'mafe-toolbar-help'
                                }
                            ]
                        }
                    ]
                },
                actions: {
                    selector: ".mafe-toolbar-help",
                    tooltip: "Help".translate(),
                    execute: true,
                    handler: function () {
                        var canvas = PMUI.getActiveCanvas();
                        PMDesigner.helper.startIntro();
                        if (canvas && canvas.getHTML()) {
                            canvas.getHTML().classList.remove("pmui-pmcanvas");
                        }
                    }
                }
            },
            saveVar = {
                id: 'saveButton',
                name: 'Save',
                htmlProperty: {
                    id: 'saveButton',
                    element: 'li',
                    class: 'mafe-save-process',
                    child: [
                        {
                            element: 'a',
                            class: 'mafe-button-save'
                        },
                        {
                            element: 'span',
                            class: 'mafe-button-menu',
                            child: [
                                {
                                    element: 'img',
                                    src: '/lib/img/caret-down.png'
                                }
                            ]
                        }
                    ]
                },
                actions: {
                    selector: ".mafe-button-save",
                    tooltip: "Save process".translate(),
                    label: {
                        text: "Save".translate()
                    },
                    execute: true,
                    handler: function () {
                        if (PMDesigner.project.isDirty() && PMDesigner.project.isSave === false) {
                            PMDesigner.project.isSave = true;
                            document.getElementsByClassName("mafe-save-process")[0].childNodes[0].text = "Saving";
                            PMDesigner.project.save(true);
                        }
                    }
                }
            },
            exportButton = {
                id: 'exportButton',
                name: 'ExportButton',
                htmlProperty: {
                    id: 'exportButton',
                    element: 'li',
                    child: [
                        {
                            element: 'a',
                            class: 'mafe-button-export-process'
                        }
                    ]
                },
                actions: {
                    selector: ".mafe-button-export-process",
                    tooltip: "Export process".translate(),
                    label: {
                        text: "Export Process".translate()
                    },
                    execute: true,
                    handler: function () {
                        handler();
                    }
                }
            },
            exportBpmnButton = {
                id: 'exportBpmnButton',
                name: 'ExportBpmn',
                htmlProperty: {
                    id: 'exportBpmnButton',
                    element: 'li',
                    child: [
                        {
                            element: 'a',
                            class: 'mafe-button-export-bpmn-process'
                        }
                    ]
                },
                actions: {
                    selector: ".mafe-button-export-bpmn-process",
                    tooltip: "Export Diagram ".translate(),
                    label: {
                        text: "Export Diagram".translate()
                    },
                    execute: false,
                    handler: function () {
                    }
                }
            },
            zoomVar = {
                id: 'zoomOptions',
                name: 'Zoom',
                htmlProperty: {
                    id: 'zoomOptions',
                    element: 'li',
                    child: [
                        {
                            element: 'span',
                            class: 'mafe-zoom-options'
                        }
                    ]
                },
                actions: {
                    id: 'zoomOptions',
                    spanclass: 'mafe-zoom-options',
                    actions: 'zoom'
                },
                aditionalAction: {
                    execute: PMDesigner.ApplyOptionsZoom()
                }
            },
            undo = {
                id: 'undoButton',
                name: 'Undo',
                htmlProperty: {
                    id: 'undoButton',
                    element: 'li',
                    class: 'mafe-undo',
                    child: [
                        {
                            element: 'b',
                            class: 'mafe-action-undo',
                            child: [
                                {
                                    element: 'span',
                                    class: 'mafe-button-undo'
                                }
                            ]
                        }
                    ]
                },
                actions: {
                    selector: ".mafe-action-undo",
                    tooltip: "Undo Action".translate(),
                    label: {
                        text: ''
                    },
                    execute: true,
                    handler: function () {
                        PMUI.getActiveCanvas().hideDragConnectHandlers();
                        PMUI.getActiveCanvas().commandStack.undo();
                    }
                }
            },
            redo = {
                id: 'redoButton',
                name: 'Redo',
                htmlProperty: {
                    id: 'redoButton',
                    element: 'li',
                    class: 'mafe-redo',
                    child: [
                        {
                            element: 'b',
                            class: 'mafe-action-redo',
                            child: [
                                {
                                    element: 'span',
                                    class: 'mafe-button-redo'
                                }
                            ]
                        }
                    ]
                },
                actions: {
                    selector: ".mafe-action-redo",
                    tooltip: "Redo Action".translate(),
                    label: {
                        text: ''
                    },
                    execute: true,
                    handler: function () {
                        PMUI.getActiveCanvas().hideDragConnectHandlers();
                        PMUI.getActiveCanvas().commandStack.redo();
                    }
                }
            },
            fullScreen = {
                id: 'fullScreenButton',
                name: 'FullScreen',
                htmlProperty: {
                    id: 'fullScreenButton',
                    element: 'li',
                    child: [
                        {
                            element: 'a',
                            class: 'mafe-button-fullscreen'
                        }
                    ]
                },
                actions: {
                    selector: ".mafe-button-fullscreen",
                    tooltip: "Full Screen".translate(),
                    execute: true,
                    handler: function () {
                        PMDesigner.fullScreen.toggle(this);
                    }
                }
            };
        navbarExportUpdate();
        this.array = [
            closeVar,
            helpVar,
            saveVar,
            exportButton,
            exportBpmnButton,
            zoomVar,
            undo,
            redo,
            fullScreen
        ];
    },
    /**
     * Get Array of Items of the NavBarPanelMenu
     * @returns {Array}
     */
    getNavBarPanelMenu: function () {
        return this.array;
    },
    /**
     * Adds one Item To NavBarPanelMenu
     * @param item
     */
    addItemToNavBarPanelMenu: function (item) {
        this.array.push(item);
    }
};

defaultNavbarPanelMenus.init();
/*----------------------------------********---------------------------------*/
/**features-begins**/
(function(){
    if(PMDesigner.sidebar) {
        PMDesigner.sidebar.push(new ToolbarPanel({
            fields: [
                {
                    selector: 'VALIDATOR',
                    type: 'button',
                    className: [
                        'mafe-designer-icon',
                        'mafe-toolbar-validation'
                    ],
                    tooltip: "Validate Now".translate(),
                    actions: {
                        selector: ".mafe-toolbar-validation",
                        tooltip: "Validator".translate(),
                        execute: true,
                        handler: function () {
                            PMDesigner.reloadDataTable();
                        }
                    }
                }
            ]
        }));
    }

})();
/**features-ends**/
/*----------------------------------********---------------------------------*/

/**
 * @class PMUI.menu.navBarPanel
 * Handles the navbar panel of designer,
 * contains all menus for content elements.
 *
 * @param {array} items Default items
 * @constructor
 */
var NavbarPanel = function (items) {
    NavbarPanel.prototype.init.call(this, items);
};

/**
 * Initializes the object.
 *
 * @param {array} items Array with default values.
 */
NavbarPanel.prototype.init = function (items) {
    var item;
    if (typeof items === 'undefined') {
        items = defaultNavbarPanelMenus.getNavBarPanelMenu();
    }
    this.items = new PMUI.util.ArrayList();
    for (item in items) {
        if (!items.hasOwnProperty(item)) {
            continue;
        }
        this.items.insert(items[item]);
    }
};

/**
 * This method renders HTML and actions into designer
 *
 */
NavbarPanel.prototype.show = function () {
    var item = null,
        i,
        max;
    if (PMDesigner.navbarPanel.items instanceof Object) {
        for (i = 0, max = PMDesigner.navbarPanel.items.getSize(); i < max; i += 1) {
            item = PMDesigner.navbarPanel.items.get(i);
            if (typeof item.htmlProperty !== "undefined") {
                PMDesigner.navbarPanel.buildHtmlElement(item.htmlProperty);
            }
            if (typeof item.aditionalAction !== "undefined") {
                item.aditionalAction;
            }
            if (typeof item.actions !== "undefined") {
                new PMAction(item.actions);
            }
        }
    } else {
        throw new Error('cannot show the elements of the List');
    }

};

/**
 * This method creates a html element button into the navBar Panel
 * @param {Object} element
 * @param {HTMLElement} before
 */
NavbarPanel.prototype.buildHtmlElement = function (element, before) {
    var ul = document.getElementById('idNavBarList'),
        htmlElement;
    if ((typeof ul !== undefined) && (ul !== null)) {
        htmlElement = this.getNodeChild(element, ul);
        if (typeof before !== "undefined") {
            before = document.getElementById(before);
            ul.insertBefore(htmlElement, before);
        } else {
            ul.appendChild(htmlElement);
        }
    }

};

/**
 * This method assembling dependent html elements to the button
 * @param {Object} nodeChild
 * @param {HTMLElement} nodePattern
 * @returns {Element}
 */
NavbarPanel.prototype.getNodeChild = function (nodeChild, nodePattern) {
    var node = document.createElement(nodeChild.element),
        i;
    if (typeof nodeChild.id !== 'undefined') {
        node.setAttribute("id", nodeChild.id);
    }
    if (nodeChild.element === 'a') {
        node.setAttribute("href", "return;");
    }
    if (typeof(nodeChild.class) !== 'undefined') {
        node.setAttribute("class", nodeChild.class);
    }
    if (typeof(nodeChild.child) !== 'undefined' && nodeChild.child instanceof Array) {
        for (i = 0; i < nodeChild.child.length; i += 1) {
            this.getNodeChild(nodeChild.child[i], node);
        }
    }
    if (typeof(nodeChild.src) !== 'undefined') {
        node.setAttribute("src", nodeChild.src);
    }
    if ((typeof nodePattern !== undefined) && (nodePattern !== null) && nodePattern.localName !== 'ul') {
        nodePattern.appendChild(node);
    }
    return node;
};

/**
 * This method removes an html element for the
 * NavBar panel array List and delete the HTML from the designer.
 * @param {String} idButton
 */
NavbarPanel.prototype.deleteHtmlElement = function (idButton) {
    var btn = document.getElementById(idButton),
        element = PMDesigner.contentControl.items.find("id", idButton),
        remove = PMDesigner.contentControl.items.remove(element);
    if (typeof btn !== 'undefined' && remove === true) {
        btn.parentNode.removeChild(btn);
    } else {
        throw new Error('Cannot find the specified button: ' + idButton + '. Please, review this');
    }
};

/**
 * NavbarPanel get an instance
 * @type {NavbarPanel}
 */
PMDesigner.navbarPanel = new NavbarPanel(defaultNavbarPanelMenus.getNavBarPanelMenu());

function defaultContentControlMenus() {
    var variableCreate = {
            id: 'variableCreate',
            name: 'Variable',
            htmlProperty: {
                id: 'variableCreate',
                element: 'li',
                child: [
                    {
                        element: 'a',
                        class: 'mafe-menu-variable'
                    },
                    {
                        element: 'a',
                        class: 'btn_create mafe-menu-variable-create',
                        child: [
                            {
                                element: 'span'
                            }
                        ]
                    }
                ]
            },
            actions: {
                type: 'button',
                selector: ".mafe-menu-variable",
                label: {
                    text: "Variables".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.variables();
                }
            },
            createAction: {
                selector: ".mafe-menu-variable-create",
                label: {
                    selector: ".mafe-menu-variable-create span",
                    text: "Create".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.variables.create();
                }
            }
        },
        messageType = {
            id: 'messageTypeCreate',
            name: 'MessageType',
            htmlProperty: {
                id: 'messageTypeCreate',
                element: 'li',
                child: [
                    {
                        element: 'a',
                        class: 'mafe-menu-messagetype'
                    },
                    {
                        element: 'a',
                        class: 'btn_create mafe-menu-messagetype-create',
                        child: [
                            {
                                element: 'span'
                            }
                        ]
                    }
                ]
            },
            actions: {
                type: 'button',
                selector: ".mafe-menu-messagetype",
                label: {
                    text: "Message Types".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.messageType();
                }
            },
            createAction: {
                selector: ".mafe-menu-messagetype-create",
                label: {
                    selector: ".mafe-menu-messagetype-create span",
                    text: "Create".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.messageType.create();
                }
            }
        },
        dynaformCreate = {
            id: 'dynaformCreate',
            name: 'Dynaform',
            htmlProperty: {
                id: 'dynaformCreate',
                element: 'li',
                child: [
                    {
                        element: 'a',
                        class: 'mafe-menu-dynaform'
                    },
                    {
                        element: 'a',
                        class: 'btn_create mafe-menu-dynaform-create',
                        child: [
                            {
                                element: 'span'
                            }
                        ]
                    }
                ]
            },
            actions: {
                type: 'button',
                selector: ".mafe-menu-dynaform",
                label: {
                    text: "Dynaforms".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.dynaform();
                }
            },
            createAction: {
                selector: ".mafe-menu-dynaform-create",
                label: {
                    selector: ".mafe-menu-dynaform-create span",
                    text: "Create".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.dynaform.create();
                }
            }
        },
        inputDocumentCreate = {
            id: 'inputDocumentCreate',
            name: 'InputDocument',
            htmlProperty: {
                id: 'inputDocumentCreate',
                element: 'li',
                child: [
                    {
                        element: 'a',
                        class: 'mafe-menu-inputdocuments'
                    },
                    {
                        element: 'a',
                        class: 'btn_create mafe-menu-inputdocuments-create',
                        child: [
                            {
                                element: 'span'
                            }
                        ]
                    }
                ]
            },
            actions: {
                type: 'button',
                selector: ".mafe-menu-inputdocuments",
                label: {
                    text: "Input Documents".translate()
                },
                execute: true,
                handler: function () {
                    var inputDocument = new InputDocument();
                    inputDocument.build();
                }
            },
            createAction: {
                selector: ".mafe-menu-inputdocuments-create",
                label: {
                    selector: ".mafe-menu-inputdocuments-create span",
                    text: "Create".translate()
                },
                execute: true,
                handler: function () {
                    var inputDocument = new InputDocument();
                    inputDocument.build();
                    inputDocument.openFormInMainWindow();
                    inputDocument.method = "POST";
                }
            }
        },
        outputDocumentCreate = {
            id: 'outputDocumentCreate',
            name: 'OutputDocument',
            htmlProperty: {
                id: 'outputDocumentCreate',
                element: 'li',
                child: [
                    {
                        element: 'a',
                        class: 'mafe-menu-outputdocuments'
                    },
                    {
                        element: 'a',
                        class: 'btn_create mafe-menu-outputdocuments-create',
                        child: [
                            {
                                element: 'span'
                            }
                        ]
                    }
                ]
            },
            actions: {
                type: 'button',
                selector: ".mafe-menu-outputdocuments",
                label: {
                    text: "Output Documents".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.output();
                }
            },
            createAction: {
                selector: ".mafe-menu-outputdocuments-create",
                label: {
                    selector: ".mafe-menu-outputdocuments-create span",
                    text: "Create".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.output();
                    PMDesigner.output.create();
                }
            }
        },
        triggerCreate = {
            id: 'triggerCreate',
            name: 'Trigger',
            htmlProperty: {
                id: 'triggerCreate',
                element: 'li',
                child: [
                    {
                        element: 'a',
                        class: 'mafe-menu-triggers'
                    },
                    {
                        element: 'a',
                        class: 'btn_create mafe-menu-triggers-create',
                        child: [
                            {
                                element: 'span'
                            }
                        ]
                    }
                ]
            },
            actions: {
                type: 'button',
                selector: ".mafe-menu-triggers",
                label: {
                    text: "Triggers".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.trigger();
                }
            },
            createAction: {
                selector: ".mafe-menu-triggers-create",
                label: {
                    selector: ".mafe-menu-triggers-create span",
                    text: "Create".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.trigger();
                    PMDesigner.trigger.create();
                }
            }
        },
        reportTableCreate = {
            id: 'reportTableCreate',
            name: 'ReportTable',
            htmlProperty: {
                id: 'reportTableCreate',
                element: 'li',
                child: [
                    {
                        element: 'a',
                        class: 'mafe-menu-reporttables'
                    },
                    {
                        element: 'a',
                        class: 'btn_create mafe-menu-reporttables-create',
                        child: [
                            {
                                element: 'span'
                            }
                        ]
                    }
                ]
            },
            actions: {
                type: 'button',
                selector: ".mafe-menu-reporttables",
                label: {
                    text: "Report Tables".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.reporttable();
                }
            },
            createAction: {
                selector: ".mafe-menu-reporttables-create",
                label: {
                    selector: ".mafe-menu-reporttables-create span",
                    text: "Create".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.reporttable();
                    PMDesigner.reporttable.create();
                }
            }
        },
        databaseCreate = {
            id: 'databaseCreate',
            name: 'Database',
            htmlProperty: {
                id: 'databaseCreate',
                element: 'li',
                child: [
                    {
                        element: 'a',
                        class: 'mafe-menu-databaseconnections'
                    },
                    {
                        element: 'a',
                        class: 'btn_create mafe-menu-databaseconnections-create',
                        child: [
                            {
                                element: 'span'
                            }
                        ]
                    }
                ]
            },
            actions: {
                type: 'button',
                selector: ".mafe-menu-databaseconnections",
                label: {
                    text: "Database Connections".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.database();
                }
            },
            createAction: {
                selector: ".mafe-menu-databaseconnections-create",
                label: {
                    selector: ".mafe-menu-databaseconnections-create span",
                    text: "Create".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.database.create();
                }
            }
        },
        templateCreate = {
            id: 'templateCreate',
            name: 'TemplateCreate',
            htmlProperty: {
                id: 'templateCreate',
                element: 'li',
                child: [
                    {
                        element: 'a',
                        class: 'mafe-menu-templates'
                    },
                    {
                        element: 'a',
                        class: 'btn_create mafe-menu-templates-create',
                        child: [
                            {
                                element: 'span'
                            }
                        ]
                    }
                ]
            },
            actions: {
                type: 'button',
                selector: ".mafe-menu-templates",
                label: {
                    text: "Templates".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.ProcessFilesManager("templates", "CREATION_NORMAL");
                }
            },
            createAction: {
                selector: ".mafe-menu-templates-create",
                label: {
                    selector: ".mafe-menu-templates-create span",
                    text: "Create".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.ProcessFilesManager.createFirst("templates", "CREATION_MORE");
                }
            }
        },
        menuPublic = {
            id: 'menuPublic',
            name: 'MenuPublic',
            htmlProperty: {
                id: 'menuPublic',
                element: 'li',
                child: [
                    {
                        element: 'a',
                        class: 'mafe-menu-public'
                    },
                    {
                        element: 'a',
                        class: 'btn_create mafe-menu-public-create',
                        child: [
                            {
                                element: 'span'
                            }
                        ]
                    }
                ]
            },
            actions: {
                type: 'button',
                selector: ".mafe-menu-public",
                label: {
                    text: "Public Files".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.ProcessFilesManager("public", "CREATION_NORMAL");
                }
            },
            createAction: {
                selector: ".mafe-menu-public-create",
                label: {
                    selector: ".mafe-menu-public-create span",
                    text: "Create".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.ProcessFilesManager.createFirst("public", "CREATION_MORE");
                }
            }
        },
        menuPermission = {
            id: 'menuPermission',
            name: 'MenuPermission',
            htmlProperty: {
                id: 'menuPermission',
                element: 'li',
                child: [
                    {
                        element: 'a',
                        class: 'mafe-menu-permissions'
                    },
                    {
                        element: 'a',
                        class: 'btn_create mafe-menu-permissions-create',
                        child: [
                            {
                                element: 'span'
                            }
                        ]
                    }
                ]
            },
            actions: {
                type: 'button',
                selector: ".mafe-menu-permissions",
                label: {
                    text: "Permissions".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.processPermissions();
                }
            },
            createAction: {
                selector: ".mafe-menu-permissions-create",
                label: {
                    selector: ".mafe-menu-permissions-create span",
                    text: "Create".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.processPermissions.create();
                }
            }
        },
        menuCaseTracker = {
            id: 'menuCaseTracker',
            name: 'CaseTracker',
            htmlProperty: {
                id: 'menuCaseTracker',
                element: 'li',
                child: [
                    {
                        element: 'a',
                        class: 'mafe-menu-casetracker'
                    }
                ]
            },
            actions: {
                type: 'button',
                selector: ".mafe-menu-casetracker",
                label: {
                    text: "Case Tracker".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.caseTracker();
                }
            }
        },
        menuSupervisor = {
            id: 'menuSupervisor',
            name: 'MenuSupervisor',
            htmlProperty: {
                id: 'menuSupervisor',
                element: 'li',
                child: [
                    {
                        element: 'a',
                        class: 'mafe-menu-supervisors'
                    }
                ]
            },
            actions: {
                type: 'button',
                selector: ".mafe-menu-supervisors",
                label: {
                    text: "Supervisors".translate()
                },
                execute: true,
                handler: function () {
                    PMDesigner.assigmentSupervisors();
                }
            }
        };

    return [
        variableCreate,
        messageType,
        dynaformCreate,
        inputDocumentCreate,
        outputDocumentCreate,
        triggerCreate,
        reportTableCreate,
        databaseCreate,
        templateCreate,
        menuPublic,
        menuPermission,
        menuCaseTracker,
        menuSupervisor
    ];
};

/**
 * @class PMUI.menu.contentControl
 * Handles the content control menu of designer,
 * contains all menus for content elements.
 *
 * @param {array} items Default items
 * @constructor
 */
var ContentControl = function (items) {
    ContentControl.prototype.init.call(this, items);
};

/**
 * Initializes the object.
 *
 * @param {array} items Array with default values.
 */
ContentControl.prototype.init = function (items) {
    this.items = new PMUI.util.ArrayList();
    if (typeof items === 'undefined') {
        items = defaultContentControlMenus();
    }
    for (var item in items) {
        if (!items.hasOwnProperty(item)) {
            continue;
        }
        this.items.insert(items[item]);
    }
};

/**
 * This method renders HTML and actions into designer
 *
 */
ContentControl.prototype.show = function () {
    var item = null,
        i,
        max;
    if (this.items instanceof Object) {
        for (i = 0, max = this.items.getSize(); i < max; i += 1) {
            item = this.items.get(i);
            if (typeof item.htmlProperty !== "undefined") {
                this.buildHtmlElement(item.htmlProperty);
            }
            if (typeof item.actions !== "undefined") {
                new PMAction(item.actions);
            }
            if (typeof item.createAction !== "undefined") {
                new PMAction(item.createAction);
            }
        }
    } else {
        throw new Error('Cannot show the elements of the List');
    }
};

/**
 * This method creates a html element button into the content
 * control panel.
 * @param {Object} element
 * @param {HTMLElement} before
 */
ContentControl.prototype.buildHtmlElement = function (element, before) {
    var ul = document.getElementById('contentControlList'),
        htmlElement;
    if ((typeof ul !== undefined) && (ul !== null)) {
        htmlElement = this.getNodeChild(element, ul);
        if (typeof before !== "undefined") {
            before = document.getElementById(before);
            ul.insertBefore(htmlElement, before);
        } else {
            ul.appendChild(htmlElement);
        }
    }

};

/**
 * This method assembling dependent html elements to the button
 * @param {Object} nodeChild
 * @param {HTMLElement} nodePattern
 * @returns {Element}
 */
ContentControl.prototype.getNodeChild = function (nodeChild, nodePattern) {
    var node = document.createElement(nodeChild.element),
        i;
    if (typeof nodeChild.id !== 'undefined') {
        node.setAttribute("id", nodeChild.id);
    }
    if (nodeChild.element === 'a') {
        node.setAttribute("href", "#");
    }
    if (typeof(nodeChild.class) !== 'undefined') {
        node.setAttribute("class", nodeChild.class);
    }
    if (typeof(nodeChild.child) !== 'undefined' && nodeChild.child instanceof Array) {
        for (i = 0; i < nodeChild.child.length; i += 1) {
            this.getNodeChild(nodeChild.child[i], node);
        }
    }
    if (typeof(nodeChild.src) !== 'undefined') {
        node.setAttribute("src", nodeChild.src);
    }
    if ((typeof nodePattern !== undefined) && (nodePattern !== null) && nodePattern.localName !== 'ul') {
        nodePattern.appendChild(node);
    }
    return node;
};

/**
 * This method removes an html element for the
 * Content Control panel array List and delete the HTML from the designer.
 * @param {String} idButton
 */
ContentControl.prototype.deleteHtmlElement = function (idButton) {
    var btn = document.getElementById(idButton),
        element = this.items.find("id", idButton),
        remove = this.items.remove(element);
    if (typeof btn !== 'undefined' && remove === true) {
        btn.parentNode.removeChild(btn);
    } else {
        throw new Error('Cannot find the specified button: ' + idButton + '. Please, review this');
    }
};

/**
 * ContentControl get an instance
 * @type {ContentControl}
 */
PMDesigner.contentControl = new ContentControl(defaultContentControlMenus());

/**
 * Input Document module
 * @param options
 * @constructor
 */
var InputDocument = function (options) {
    this.winMainInputDocument = null;
    this.externalType = false;
    this.inputDocumentOriginDataForUpdate = {};
    this.windowDialog = null;
    this.assignAccordion = null;
    this.clickedClose = true;
    this.onUpdateInputDocumentHandler = null;
    InputDocument.prototype.init.call(this, options);
};
/**
 * set close modulo InputDocument constructor
 * @param handler, the callback function
 */
InputDocument.prototype.setOnUpdateInputDocumentHandler = function (handler) {
    if (typeof handler === "function") {
        this.onUpdateInputDocumentHandler = handler;
    }
    return this;
};
/**
 * constructor
 * @param options
 */
InputDocument.prototype.init = function (options) {
    var defaults = {
        onUpdateInputDocumentHandler: null
    };
    $.extend(true, defaults, options);
    this.setOnUpdateInputDocumentHandler(defaults.onUpdateInputDocumentHandler);
    return this;
};

/**
 * Puts input document using rest proxy, to update data
 * @param inputDocumentUid
 * @param data
 */
InputDocument.prototype.inputDocumentPutRestProxy = function (inputDocumentUid, data) {
    var that = this,
        restProxy;

    restProxy = new PMRestClient({
        endpoint: "input-document/" + inputDocumentUid,
        typeRequest: 'update',
        data: data,
        functionSuccess: function (xhr, response) {
            var message;
            if (!that.externalType) {
                if (typeof flagInputDocument != 'undefined' && flagInputDocument) {
                    that.winMainInputDocument.close();
                    return;
                }
                that.inputDocumentsGetRestProxy();
                that.openGridPanelInMainWindow();
            } else {
                that.winMainInputDocument.close();
                message = new PMUI.ui.FlashMessage({
                    message: 'Input Document edited correctly.'.translate(),
                    duration: 3000,
                    severity: 'success',
                    appendTo: that.windowDialog
                });
                message.show();
            }
            if (typeof that.onUpdateInputDocumentHandler === "function") {
                that.onUpdateInputDocumentHandler(data, that);
            }
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        },
        messageSuccess: 'Input Document updated successfully'.translate(),
        flashContainer: that.grdpnlInputDocument
    });
    restProxy.executeRestClient();
};
/**
 * Deletes an input document
 * @param inputDocumentUid
 */
InputDocument.prototype.inputDocumentDeleteRestProxy = function (inputDocumentUid) {
    var that = this,
        restProxy;
    restProxy = new PMRestClient({
        endpoint: "input-document/" + inputDocumentUid,
        typeRequest: 'remove',
        functionSuccess: function (xhr, response) {
            that.inputDocumentsGetRestProxy();
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        },
        messageSuccess: 'Input Document deleted successfully'.translate(),
        flashContainer: that.grdpnlInputDocument
    });
    restProxy.executeRestClient();
};
/**
 * Creates an input document using rest proxy
 * @param data
 */
InputDocument.prototype.inputDocumentPostRestProxy = function (data) {
    var that = this,
        restProxy;
    restProxy = new PMRestClient({
        endpoint: "input-document",
        typeRequest: 'post',
        data: data,
        functionSuccess: function (xhr, response) {
            that.inputDocumentsGetRestProxy();
            that.openGridPanelInMainWindow();
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        },
        messageSuccess: 'Input Document saved successfully'.translate(),
        flashContainer: that.grdpnlInputDocument
    });
    restProxy.executeRestClient();
};
/**
 * Gets all input documents to populate the grid
 */
InputDocument.prototype.inputDocumentsGetRestProxy = function () {
    var that = this,
        restProxy = new PMRestClient({
            endpoint: 'input-documents',
            typeRequest: 'get',
            functionSuccess: function (xhr, response) {
                that.grdpnlInputDocument.setDataItems(response);
                that.grdpnlInputDocument.sort('inp_doc_title', 'asc');
                if (PMVariables.prototype.isWindowActive()) {
                    PMVariables.prototype.setInputDocumentsFromIDModule(PMUI.getPMUIObject(inp_doc_uid), response);
                }
            },
            functionFailure: function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            }
        });
    restProxy.executeRestClient();
};
/**
 * Gets an specific input document data to edit it
 * @param inputDocumentUid
 */
InputDocument.prototype.inputDocumentFormGetProxy = function (inputDocumentUid) {
    var that = this,
        restProxy;
    restProxy = new PMRestClient({
        endpoint: "input-document/" + inputDocumentUid,
        typeRequest: 'get',
        functionSuccess: function (xhr, response) {
            var data = response;
            that.method = "PUT";
            that.openFormForEditInMainWindow(data);
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        }
    });
    restProxy.executeRestClient();
};

/**
 * Open Edit form and sets to main windows
 * @param inputDocumentData
 */
InputDocument.prototype.openFormForEditInMainWindow = function (inputDocumentData) {
    var that = this;

    that.inputDocumentOriginDataForUpdate = inputDocumentData;
    that.inputDocumentUid = inputDocumentData.inp_doc_uid;
    that.frmInputDocument.getField('inp_doc_original').setValue("");
    that.winMainInputDocument.setTitle("Edit Input Document".translate());

    that.frmInputDocument.getField('inp_doc_title').setValue(inputDocumentData.inp_doc_title);
    that.frmInputDocument.getField('inp_doc_description').setValue(inputDocumentData.inp_doc_description);
    that.frmInputDocument.getField('inp_doc_form_needed').setValue(inputDocumentData.inp_doc_form_needed);
    that.frmInputDocument.getField('inp_doc_original').setValue(inputDocumentData.inp_doc_original);

    if (inputDocumentData.inp_doc_form_needed !== "VIRTUAL") {
        that.frmInputDocument.getField('inp_doc_original').setVisible(true);
    } else {
        that.frmInputDocument.getField('inp_doc_original').setVisible(false);
    }
    that.frmInputDocument.getField('inp_doc_versioning').setValue(parseInt(inputDocumentData.inp_doc_versioning + ""));
    that.frmInputDocument.getField('inp_doc_destination_path').setValue(inputDocumentData.inp_doc_destination_path);
    that.frmInputDocument.getField('inp_doc_tags').setValue(inputDocumentData.inp_doc_tags);
    that.frmInputDocument.getField('inp_doc_type_file').setValue(inputDocumentData.inp_doc_type_file);
    that.frmInputDocument.getField('inp_doc_max_filesize').setValue(inputDocumentData.inp_doc_max_filesize);
    that.frmInputDocument.getField('inp_doc_max_filesize_unit').setValue(inputDocumentData.inp_doc_max_filesize_unit);
};
/**
 * Open the grid panel and sets to mai windows
 */
InputDocument.prototype.openGridPanelInMainWindow = function () {
    this.grdpnlInputDocument.setVisible(true);
    this.frmInputDocument.setVisible(false);
    this.winMainInputDocument.setTitle("Input Documents".translate());
    this.winMainInputDocument.hideFooter();
};
/**
 * Set the option external type of input Document
 */
InputDocument.prototype.setExternalType = function (value) {
    if (typeof value === "boolean") {
        this.externalType = value;
    }
    return this;
};
/**
 * Set the option window Dialog, a window that open this class
 */
InputDocument.prototype.setWindowDialog = function (value) {
    this.windowDialog = value;
    return this;
};

InputDocument.prototype.setAssignAccordion = function (obj) {
    this.assignAccordion = obj;
};

/**
 * Open create form an sets to main windows
 */
InputDocument.prototype.openFormInMainWindow = function () {
    this.grdpnlInputDocument.setVisible(false);
    this.frmInputDocument.setVisible(true);
    this.winMainInputDocument.setTitle("Create Input Document".translate());
    this.winMainInputDocument.showFooter();
};

InputDocument.prototype.checkIfValuesAreEqual = function (initialData, finalData) {
    var key1, key2;
    if (!Object.keys(initialData).length && Object.keys(finalData).length) {
        if (finalData['inp_doc_title'] != '' || finalData['inp_doc_form_needed'] != 'VIRTUAL' || finalData['inp_doc_description'] != '' || finalData['inp_doc_versioning'] != '0' || finalData['inp_doc_destination_path'] != '') {
            return false;
        }
    }

    for (key1 in initialData) {
        for (key2 in finalData) {
            if (typeof(initialData[key1]) != "undefined" &&
                typeof(finalData[key2]) != "undefined" &&
                key1 == key2 &&
                initialData[key1] != finalData[key2]
            ) {
                //Return
                return false;
            }
        }
    }
    return true;
};

/**
 * Generate all ui components(window, form, grid, fields)
 */

InputDocument.prototype.isDirtyFormInput = function () {
    $("input,select,textarea").blur();
    var that = this, message_window;
    if (this.frmInputDocument.isVisible()) {
        if (!this.externalType) {
            if (!(this.checkIfValuesAreEqual(this.inputDocumentOriginDataForUpdate, getData2PMUI(this.frmInputDocument.html)))) {
                message_window = new PMUI.ui.MessageWindow({
                    id: "cancelMessageTriggers",
                    width: 490,
                    title: "Input Documents".translate(),
                    windowMessageType: 'warning',
                    bodyHeight: 'auto',
                    message: 'Are you sure you want to discard your changes?'.translate(),
                    footerItems: [
                        {
                            text: 'No'.translate(),
                            handler: function () {
                                message_window.close();
                            },
                            buttonType: "error"
                        }, {
                            text: 'Yes'.translate(),
                            handler: function () {
                                message_window.close();
                                if (typeof flagInputDocument != 'undefined' && flagInputDocument) {
                                    if (that.clickedClose) {
                                        message_window.close();
                                        that.winMainInputDocument.close();
                                        return;
                                    } else {
                                        that.winMainInputDocument.close();
                                        flagInputDocument = false;
                                    }
                                    return;
                                } else {
                                    if (that.clickedClose) {
                                        message_window.close();
                                        that.winMainInputDocument.close();
                                        return;
                                    } else {
                                        inputDocumentOption = "";
                                        that.openGridPanelInMainWindow();
                                    }
                                }
                            },
                            buttonType: "success"
                        }
                    ]
                });
                message_window.open();
                message_window.showFooter();
                this.inputDocumentOriginDataForUpdate = {};
            } else {
                if (that.clickedClose) {
                    this.winMainInputDocument.close();
                } else {
                    inputDocumentOption = "";
                    this.openGridPanelInMainWindow();
                }
            }
        } else {
            that.winMainInputDocument.close();
        }
    } else {
        that.winMainInputDocument.close();
    }
};

InputDocument.prototype.build = function () {
    var ID = this,
        btnSave,
        btnCancel,
        winMainInputDocument,
        frmInputDocument,
        grdpnlInputDocument,
        btnNew,
        inp_doc_destination_path,
        inp_doc_tags,
        that = this;

    btnSave = new PMUI.ui.Button({
        id: "btnSave",
        text: "Save".translate(),
        handler: function () {
            var flagAux, data;

            if (!ID.frmInputDocument.isValid()) {
                flagAux = ID.frmInputDocument.visible;
            } else {
                flagAux = ID.frmInputDocument.isValid();
            }

            if (flagAux) {
                if (getData2PMUI(ID.frmInputDocument.html).inp_doc_title == "") {
                    return false;
                }
            }

            if (ID.frmInputDocument.getField("inp_doc_max_filesize").getValue() != "") {
                if (!/^\+?(0|[1-9]\d*)$/.test(ID.frmInputDocument.getField("inp_doc_max_filesize").getValue())) {
                    return false;
                }
            }

            //validation because getData method do not work in IE
            if (navigator.userAgent.indexOf("MSIE") !== -1 || navigator.userAgent.indexOf("Trident") !== -1) {
                data = getData2PMUI(that.frmInputDocument.html);
            } else {
                data = that.frmInputDocument.getData();
            }

            data["inp_doc_versioning"] = parseInt(data["inp_doc_versioning"]);

            switch (that.method) {
                case "POST":
                    that.inputDocumentPostRestProxy(data);
                    break;
                case "PUT":
                    that.inputDocumentPutRestProxy(that.inputDocumentUid, data);
                    break;
            }
        },
        buttonType: 'success'
    });

    btnCancel = new PMUI.ui.Button({
        id: "btnCancel",
        text: "Cancel".translate(),
        handler: function () {
            that.clickedClose = false;
            that.isDirtyFormInput();
        },
        buttonType: 'error'
    });
    btnNew = new PMUI.ui.Button({
        id: "btnNew",
        text: "Create".translate(),
        height: "36px",
        width: 100,
        style: {
            cssClasses: [
                'mafe-button-create'
            ]
        },
        handler: function () {
            that.openFormInMainWindow();
            that.method = "POST";
            that.frmInputDocument.reset();
        }
    });

    /* form panel*/
    this.frmInputDocument = new PMUI.form.Form({
        id: "frmInputDocument",
        width: 900,
        visibleHeader: false,
        items: [
            {
                pmType: "text",
                label: "Title".translate(),
                id: "inputDocTitle",
                name: "inp_doc_title",
                valueType: 'string',
                maxLength: 200,
                required: true,
                controlsWidth: 300
            },
            {
                pmType: "dropdown",
                name: "inp_doc_form_needed",
                id: "inputDocDocumentType",
                label: "Document Type".translate(),
                value: "VIRTUAL",
                controlsWidth: 130,
                options: [
                    {
                        value: "VIRTUAL",
                        label: "Digital".translate()
                    },
                    {
                        value: "REAL",
                        label: "Printed".translate()
                    },
                    {
                        value: "VREAL",
                        label: "Digital/Printed".translate()
                    }
                ],
                onChange: function (newValue, prevValue) {
                    var fields = that.frmInputDocument.getFields();
                    if (newValue != "VIRTUAL") {
                        fields[2].setVisible(true);
                    } else {
                        fields[2].setVisible(false);
                    }
                }
            },
            {
                pmType: "dropdown",
                id: "inputDocDocumentType",
                name: "inp_doc_original",
                label: "Format".translate(),
                value: "ORIGINAL",
                controlsWidth: 105,
                visible: false,
                options: [
                    {
                        value: "ORIGINAL",
                        label: "Original".translate()
                    },
                    {
                        value: "COPYLEGAL",
                        label: "Legal Copy".translate()
                    },
                    {
                        value: "COPY",
                        label: "Copy".translate()
                    }
                ]
            },
            {
                pmType: "textarea",
                id: "inputDocDescription",
                name: "inp_doc_description",
                label: "Description".translate(),
                controlsWidth: 380,
                rows: 100,
                style: {cssClasses: ['mafe-textarea-resize']}
            },
            {
                pmType: "dropdown",
                id: "inputDocEnableVersioning",
                name: "inp_doc_versioning",
                label: "Enable Versioning".translate(),
                value: 0,
                controlsWidth: 75,
                options: [
                    {
                        value: 0,
                        label: "NO".translate(),
                        selected: true
                    },
                    {
                        value: 1,
                        label: "YES".translate()
                    }
                ]
            }
        ],
        buttonPanelPosition: 'top'
    });
    inp_doc_destination_path = new CriteriaField({
        id: "inputDocDestinationPath",
        name: "inp_doc_destination_path",
        valueType: "string",
        label: "Destination Path".translate(),
        maxLength: 200,
        controlsWidth: 380
    });

    inp_doc_tags = new CriteriaField({
        id: "inputDocTags",
        name: "inp_doc_tags",
        valueType: "string",
        label: "Tags".translate(),
        maxLength: 200,
        value: "INPUT",
        controlsWidth: 380
    });

    inp_doc_allowed = new PMUI.field.TextField({
        label: "Allowed file extensions (Use .* to allow any extension)".translate(),
        id: "inputDocAllowedFileExtensions",
        name: "inp_doc_type_file",
        valueType: 'string',
        maxLength: 200,
        required: true,
        value: ".*",
        controlsWidth: 380

    });

    inp_doc_maximum = new PMUI.field.TextField({
        label: "Maximum file size (Use zero if unlimited)".translate(),
        id: "inputDocMaximumFileSize",
        name: "inp_doc_max_filesize",
        valueType: 'string',
        maxLength: 200,
        required: true,
        value: "0",
        controlsWidth: 380,
        validators: [
            {
                pmType: "regexp",
                criteria: /^\d*$/,
                errorMessage: "Please enter a positive integer value".translate()
            }
        ]

    });

    inp_doc_maximum_unit = new PMUI.field.DropDownListField({
        id: "inputDocUnit",
        name: "inp_doc_max_filesize_unit",
        label: "Unit".translate(),
        value: "ORIGINAL",
        controlsWidth: 105,
        visible: true,
        options: [
            {
                value: "KB",
                label: "KB".translate()
            },
            {
                value: "MB",
                label: "MB".translate()
            }
        ]
    });

    that.frmInputDocument.addItem(inp_doc_destination_path);
    that.frmInputDocument.addItem(inp_doc_tags);
    that.frmInputDocument.addItem(inp_doc_allowed);
    that.frmInputDocument.addItem(inp_doc_maximum);
    that.frmInputDocument.addItem(inp_doc_maximum_unit);

    /*grid panel*/

    this.grdpnlInputDocument = new PMUI.grid.GridPanel({
        id: "grdpnlInputDocument",
        pageSize: 10,
        width: "96%",
        style: {
            cssClasses: ["mafe-gridPanel"]
        },
        filterPlaceholder: 'Search ...'.translate(),
        emptyMessage: 'No records found'.translate(),
        nextLabel: 'Next'.translate(),
        previousLabel: 'Previous'.translate(),
        tableContainerHeight: 374,
        customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
            return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
        },
        columns: [
            {
                id: 'grdpnlInputDocumentButtonShow',
                dataType: "button",
                title: "",
                buttonLabel: "Show ID".translate(),
                buttonStyle: {
                    cssClasses: [
                        'mafe-button-show'
                    ]
                },
                onButtonClick: function (row, grid) {
                    var data = row.getData();
                    showUID(data.inp_doc_uid);
                }
            },
            {
                columnData: "inp_doc_title",
                title: "Title".translate(),
                width: "607px",
                alignment: "left",
                sortable: true,
                alignmentCell: 'left'
            },
            {
                id: 'grdpnlInputDocumentButtonEdit',
                dataType: "button",
                title: "",
                buttonLabel: "Edit".translate(),
                buttonStyle: {
                    cssClasses: [
                        'mafe-button-edit'
                    ]
                },
                onButtonClick: function (row, grid) {
                    var data = row.getData();
                    that.inputDocumentOriginDataForUpdate = {};
                    that.openFormInMainWindow();
                    that.inputDocumentFormGetProxy(data.inp_doc_uid);

                }
            },
            {
                id: 'grdpnlInputDocumentButtonDelete',
                dataType: "button",
                title: "",
                buttonLabel: "Delete".translate(),
                buttonStyle: {
                    cssClasses: [
                        'mafe-button-delete'
                    ]
                },
                onButtonClick: function (row, grid) {
                    var data = row.getData(),
                        confirmWindow;
                    confirmWindow = new PMUI.ui.MessageWindow({
                        id: "inputMessageWindowWarning",
                        windowMessageType: 'warning',
                        width: 490,
                        bodyHeight: 'auto',
                        title: "Input Documents".translate(),
                        message: "Do you want to delete this Input Document?".translate(),
                        footerItems: [
                            {
                                id: 'confirmWindowButtonNo',
                                text: "No".translate(),
                                visible: true,
                                handler: function () {
                                    confirmWindow.close();
                                },
                                buttonType: "error"
                            },
                            {
                                id: 'confirmWindowButtonYes',
                                text: "Yes".translate(),
                                visible: true,
                                handler: function () {
                                    that.inputDocumentDeleteRestProxy(data.inp_doc_uid);
                                    confirmWindow.close();
                                    confirmWindow.close();
                                },
                                buttonType: "success"
                            }
                        ]
                    });
                    confirmWindow.open();
                    confirmWindow.dom.titleContainer.style.height = "17px";
                    confirmWindow.showFooter();
                }
            }
        ],
        dataItems: null
    });
    /* main windows */
    this.winMainInputDocument = new PMUI.ui.Window({
        id: "winMainInputDocument",
        title: "Input Documents".translate(),
        height: DEFAULT_WINDOW_HEIGHT,
        width: DEFAULT_WINDOW_WIDTH,
        buttonPanelPosition: 'bottom',
        footerAlign: 'right',
        visibleFooter: true,
        footerItems: [
            btnCancel,
            btnSave
        ],
        onBeforeClose: function () {
            that.clickedClose = true;
            that.isDirtyFormInput();
        }
    });
    // add form and grid to windows
    this.winMainInputDocument.addItem(this.grdpnlInputDocument);
    this.winMainInputDocument.addItem(this.frmInputDocument);
    this.winMainInputDocument.open();
    //custom css
    this.frmInputDocument.getField("inp_doc_type_file").html.style.padding = "6px 0";
    this.frmInputDocument.getField("inp_doc_type_file").html.style.float = "left";
    this.frmInputDocument.getField("inp_doc_max_filesize").html.style.padding = "6px 0";
    this.frmInputDocument.getField("inp_doc_max_filesize").html.style.float = "left";
    this.frmInputDocument.getField("inp_doc_max_filesize_unit").html.style.padding = "6px 0";
    this.frmInputDocument.getField("inp_doc_max_filesize_unit").html.style.float = "left";
    this.grdpnlInputDocument.setVisible(true);
    this.frmInputDocument.setVisible(false);
    this.winMainInputDocument.hideFooter();
    /* insert create button to grid*/
    this.grdpnlInputDocument.dom.toolbar.appendChild(btnNew.getHTML());
    btnNew.defineEvents();
    this.inputDocumentsGetRestProxy();
};

(function () {

    var loadValuesStartTimer, openForm, updateStatus;

    PMDesigner.startTimer = function (element) {

        var startTimer = element,
            restClient,
            loadServerData,
            listUsers,
            itemsDaly,
            itemsWeekly,
            loadOptionsRadio,
            itemsMonthly,
            itemsOneTime,
            itemsEvery,
            showProperties,
            updateCaseScheduler,
            buttonCancel,
            loadUsers,
            loadDataForm,
            formCreateCaseScheduler,
            schedulerListWindow,
            buttonSave,
            dataForm;

        loadValuesStartTimer = function ($flag) {
            listUsers = [];
            dataForm = [];
            restClient = new PMRestClient({
                typeRequest: 'post',
                multipart: true,
                data: {
                    calls: [
                        {
                            url: 'activity/' + startTimer.ports.get(0).connection.flo_element_dest + '/assignee/all',
                            method: 'GET'
                        },
                        {
                            url: 'case-scheduler/' + startTimer.evn_uid,
                            method: 'GET'
                        }
                    ]
                },
                functionSuccess: function (xhr, response) {
                    listUsers = response[0].response;
                    dataForm = response[1].response;
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                },
                messageError: [null, 'There are problems loading the Start Timer, please try again.'.translate()]
            });
            restClient.executeRestClient();
            if ($flag) {
                return listUsers.length;
            }
            return true;
        };

        updateCaseScheduler = function (data) {
            var restProxy = new PMRestClient({
                endpoint: 'case-scheduler/' + startTimer.evn_uid,
                typeRequest: 'update',
                data: data,
                functionSuccess: function (xhr, response) {
                    formCreateCaseScheduler.reset();
                    schedulerListWindow.close();
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                },
                messageSuccess: 'Start Timer updated successfully'.translate(),
                messageError: 'There are problems updating the Start Timer, please try again.'.translate(),
                flashContainer: document.body
            });
            restProxy.executeRestClient();
        };

        updateStatus = function () {
            var status = (typeof dataForm.sch_state !== 'undefined') ? ((dataForm.sch_state === 'ACTIVE') ? 'INACTIVE' : 'ACTIVE') : 'INACTIVE';
            dataForm.sch_state = status;
            updateCaseScheduler(dataForm);
        };

        //Items for the form Create and edit Case Scheduler
        itemsDaly = [
            {
                pmType: 'panel',
                layout: 'hbox',
                items: [
                    {
                        id: 'startDate',
                        pmType: 'datetime',
                        label: 'Start date'.translate(),
                        value: '',
                        returnFormat: 'yy-mm-dd',
                        required: true,
                        dateFormat: 'yy mm dd',
                        dateTime: false,
                        name: 'startDate',
                        valueType: 'date',
                        labelWidth: '26%'
                    },
                    {
                        id: 'endDate',
                        pmType: 'datetime',
                        label: 'End date'.translate(),
                        value: '',
                        returnFormat: 'yy-mm-dd',
                        required: false,
                        dateFormat: 'yy mm dd',
                        dateTime: false,
                        name: 'endDate',
                        valueType: 'date'
                    }
                ]
            },
            {
                id: 'execttime',
                pmType: 'text',
                label: 'Execution time'.translate(),
                value: '',
                required: true,
                name: 'execttime',
                placeholder: '(HH:MM) Format 24 hrs.'.translate(),
                valueType: 'string',
                validators: [{
                    pmType: 'regexp',
                    criteria: /^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/,
                    errorMessage: 'Please enter a valid hour.'.translate()
                }
                ],
                helper: 'Example: 1:00, 14:30, 00:00'
            }
        ];

        itemsWeekly = [
            {
                pmType: 'panel',
                layout: 'hbox',
                items: [
                    {
                        id: 'startDate',
                        pmType: 'datetime',
                        label: 'Start date'.translate(),
                        value: '',
                        returnFormat: 'yy-mm-dd',
                        required: true,
                        dateFormat: 'yy mm dd',
                        dateTime: false,
                        name: 'startDate',
                        valueType: 'date',
                        labelWidth: '26%'
                    },
                    {
                        id: 'endDate',
                        pmType: 'datetime',
                        label: 'End date'.translate(),
                        value: '',
                        returnFormat: 'yy-mm-dd',
                        required: false,
                        dateFormat: 'yy mm dd',
                        dateTime: false,
                        name: 'endDate',
                        valueType: 'date'
                    }
                ]
            },
            {
                id: 'execttime',
                pmType: 'text',
                label: 'Execution time'.translate(),
                value: '',
                required: true,
                name: 'execttime',
                placeholder: '(HH:MM) Format 24 hrs.'.translate(),
                valueType: 'string',
                validators: [{
                    pmType: 'regexp',
                    criteria: /^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/,
                    errorMessage: 'Please enter a valid hour.'.translate()
                }
                ],
                helper: 'Example: 1:00, 14:30, 00:00'
            },
            {
                id: 'daysoftheweek',
                pmType: 'checkbox',
                label: 'Select the day(s) of the week below'.translate(),
                value: '',
                name: 'daysoftheweek',
                required: false,
                controlPositioning: 'horizontal',
                maxDirectionOptions: 3,
                options: [
                    {
                        id: 'monday',
                        label: 'Monday'.translate(),
                        value: '1'
                    },
                    {
                        id: 'tuesday',
                        label: 'Tuesday'.translate(),
                        value: '2'
                    },
                    {
                        id: 'wednesday',
                        label: 'Wednesday'.translate(),
                        value: '3'
                    },
                    {
                        id: 'thursday',
                        label: 'Thursday'.translate(),
                        value: '4'
                    },
                    {
                        id: 'friday',
                        label: 'Friday'.translate(),
                        value: '5'
                    },
                    {
                        id: 'saturday',
                        label: 'Saturday'.translate(),
                        value: '6'
                    },
                    {
                        id: 'sunday',
                        label: 'Sunday'.translate(),
                        value: '7'
                    }
                ]
            }
        ];

        loadOptionsRadio = function (newVal) {
            var paneldaysofMonth = formCreateCaseScheduler.getItems()[1].getItems()[2].getItems()[1];
            paneldaysofMonth.setVisible(true);
            if (newVal === 'dayofmonth') {
                paneldaysofMonth.getItems()[0].setVisible(true);
                paneldaysofMonth.getItems()[1].setVisible(false);
            } else if (newVal === 'day') {
                paneldaysofMonth.getItems()[0].setVisible(false);
                paneldaysofMonth.getItems()[1].setVisible(true);
            }
        };

        itemsMonthly = [
            {
                pmType: 'panel',
                layout: 'hbox',
                items: [
                    {
                        id: 'startDate',
                        pmType: 'datetime',
                        label: 'Start date'.translate(),
                        value: '',
                        returnFormat: 'yy-mm-dd',
                        required: true,
                        dateFormat: 'yy mm dd',
                        datetime: false,
                        name: 'startDate',
                        valueType: 'date',
                        labelWidth: '26%'
                    },
                    {
                        id: 'endDate',
                        pmType: 'datetime',
                        label: 'End date'.translate(),
                        value: '',
                        returnFormat: 'yy-mm-dd',
                        required: false,
                        dateFormat: 'yy mm dd',
                        datetime: false,
                        name: 'endDate',
                        valueType: 'date'
                    }
                ]
            },
            {
                id: 'execttime',
                pmType: 'text',
                label: 'Execution time'.translate(),
                value: '',
                required: true,
                name: 'execttime',
                placeholder: '(HH:MM) Format 24 hrs.'.translate(),
                valueType: 'string',
                controlsWidth: 580,
                validators: [{
                    pmType: 'regexp',
                    criteria: /^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/,
                    errorMessage: 'Please enter a valid hour.'.translate()
                }
                ],
                helper: 'Example: 1:00, 14:30, 00:00'
            },
            {
                pmType: 'panel',
                id: 'panelDays',
                layout: 'hbox',
                items: [
                    {
                        id: 'options',
                        pmType: 'radio',
                        label: '',
                        value: '',
                        name: 'options',
                        required: false,
                        controlPositioning: 'vertical',
                        maxDirectionOptions: 4,
                        options: [
                            {
                                id: 'dayMonth',
                                label: 'Day of month'.translate(),
                                value: 'dayofmonth'
                            },
                            {
                                id: 'day',
                                label: 'The day'.translate(),
                                value: 'day'
                            }
                        ],
                        onChange: function (newVal, oldVal) {
                            loadOptionsRadio(newVal);
                        },
                        labelWidth: '46%'
                    },
                    {
                        pmType: 'panel',
                        id: 'paneldaysofMonth',
                        layout: 'vbox',
                        items: [
                            {
                                id: 'dayoftheMonth',
                                pmType: 'text',
                                label: '',
                                value: '',
                                placeholder: 'Day of the month (example: 1)'.translate(),
                                required: false,
                                name: 'dayMonth',
                                valueType: 'string'
                            },
                            {
                                pmType: 'panel',
                                id: 'panelmonth',
                                layout: 'hbox',
                                items: [
                                    {
                                        id: 'first',
                                        pmType: 'dropdown',
                                        label: '',
                                        value: '',
                                        required: false,
                                        name: 'first',
                                        valueType: 'string',
                                        options: [
                                            {
                                                label: 'First'.translate(),
                                                value: '1'
                                            },
                                            {
                                                label: 'Second'.translate(),
                                                value: '2'
                                            },
                                            {
                                                label: 'Third'.translate(),
                                                value: '3'
                                            },
                                            {
                                                label: 'Fourth'.translate(),
                                                value: '4'
                                            },
                                            {
                                                label: 'Last'.translate(),
                                                value: '5'
                                            }
                                        ],
                                        controlsWidth: 100
                                    },
                                    {
                                        id: 'day',
                                        pmType: 'dropdown',
                                        label: '',
                                        value: '',
                                        required: false,
                                        name: 'day',
                                        valueType: 'string',
                                        options: [
                                            {
                                                label: 'Monday'.translate(),
                                                value: '1'
                                            },
                                            {
                                                label: 'Tuesday'.translate(),
                                                value: '2'
                                            },
                                            {
                                                label: 'Wednesday'.translate(),
                                                value: '3'
                                            },
                                            {
                                                label: 'Thursday'.translate(),
                                                value: '4'
                                            },
                                            {
                                                label: 'Friday'.translate(),
                                                value: '5'
                                            },
                                            {
                                                label: 'Saturday'.translate(),
                                                value: '6'
                                            },
                                            {
                                                label: 'Sunday'.translate(),
                                                value: '7'
                                            }
                                        ],
                                        controlsWidth: 100
                                    }
                                ]
                            }
                        ]
                    }
                ]
            },
            {
                id: 'months',
                pmType: 'checkbox',
                label: 'Of the month(s)'.translate(),
                value: '',
                name: 'months',
                required: false,
                controlPositioning: 'horizontal',
                maxDirectionOptions: 4,
                options: [
                    {
                        id: 'jan',
                        label: 'Jan'.translate(),
                        value: '1'
                    },
                    {
                        id: 'feb',
                        label: 'Feb'.translate(),
                        value: '2'
                    },
                    {
                        id: 'mar',
                        label: 'Mar'.translate(),
                        value: '3'
                    },
                    {
                        id: 'apr',
                        label: 'Apr'.translate(),
                        value: '4'
                    },
                    {
                        id: 'may',
                        label: 'May'.translate(),
                        value: '5'
                    },
                    {
                        id: 'jun',
                        label: 'Jun'.translate(),
                        value: '6'
                    },
                    {
                        id: 'jul',
                        label: 'Jul'.translate(),
                        value: '7'
                    },
                    {
                        id: 'aug',
                        label: 'Aug'.translate(),
                        value: '8'
                    },
                    {
                        id: 'sep',
                        label: 'Sep'.translate(),
                        value: '9'
                    },
                    {
                        id: 'oct',
                        label: 'Oct'.translate(),
                        value: '10'
                    },
                    {
                        id: 'nov',
                        label: 'Nov'.translate(),
                        value: '11'
                    },
                    {
                        id: 'dec',
                        label: 'Dec'.translate(),
                        value: '12'
                    }
                ]
            }
        ];

        itemsOneTime = [{
            id: 'execttime',
            pmType: 'text',
            label: 'Execution time'.translate(),
            value: '',
            required: true,
            name: 'execttime',
            placeholder: '(HH:MM) Format 24 hrs.'.translate(),
            valueType: 'string',
            validators: [{
                pmType: 'regexp',
                criteria: /^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/,
                errorMessage: 'Please enter a valid hour.'.translate()
            }
            ],
            helper: 'Example: 1:00, 14:30, 00:00'
        }];

        itemsEvery = [{
            id: 'execttime',
            pmType: 'text',
            label: 'Execute every Hour(s)'.translate(),
            value: '',
            required: true,
            name: 'execttime',
            valueType: 'string',
            placeholder: '(HH:MM) Format 24 hrs.'.translate(),
            validators: [{
                pmType: 'regexp',
                criteria: /^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/,
                errorMessage: 'Please enter a valid hour.'.translate()
            }
            ],
            helper: 'Example: 1:00, 14:30, 00:00'
        }];

        showProperties = function (newValue) {
            switch (newValue) {
                case 'daily':
                    formCreateCaseScheduler.getItems()[1].clearItems();
                    formCreateCaseScheduler.getItems()[1].setVisible(true);
                    formCreateCaseScheduler.getItems()[1].setItems(itemsDaly);
                    break;
                case 'weekly':
                    formCreateCaseScheduler.getItems()[1].clearItems();
                    formCreateCaseScheduler.getItems()[1].setVisible(true);
                    formCreateCaseScheduler.getItems()[1].setItems(itemsWeekly);
                    break;
                case 'monthly':
                    formCreateCaseScheduler.getItems()[1].clearItems();
                    formCreateCaseScheduler.getItems()[1].setVisible(true);
                    formCreateCaseScheduler.getItems()[1].setItems(itemsMonthly);
                    var paneldaysofMonth = formCreateCaseScheduler.getItems()[1].getItems()[2].getItems()[1];
                    paneldaysofMonth.setVisible(false);
                    formCreateCaseScheduler.getItems()[1].getItems()[2].getItems()[0].hideColon();
                    paneldaysofMonth.getItems()[0].hideColon();
                    paneldaysofMonth.getItems()[1].getItems()[0].hideColon();
                    paneldaysofMonth.getItems()[1].getItems()[1].hideColon();
                    break;
                case 'oneTime':
                    formCreateCaseScheduler.getItems()[1].clearItems();
                    formCreateCaseScheduler.getItems()[1].setVisible(true);
                    formCreateCaseScheduler.getItems()[1].setItems(itemsOneTime);
                    break;
                case 'every':
                    formCreateCaseScheduler.getItems()[1].clearItems();
                    formCreateCaseScheduler.getItems()[1].setVisible(true);
                    formCreateCaseScheduler.getItems()[1].setItems(itemsEvery);
                    validateKeysField(formCreateCaseScheduler.getField('execttime').getControls()[0].getHTML(), ['isnumber', 'iscolon']);
                    break;
            }
        };

        //Form to Edit and create the Case Scheduler
        formCreateCaseScheduler = new PMUI.form.Form({
            id: 'formCreateCaseScheduler',
            border: false,
            visibleHeader: false,
            width: '925px',
            name: 'formcreate',
            title: '',
            items: [
                {
                    id: 'panelProperties',
                    pmType: 'panel',
                    layout: 'vbox',
                    fieldset: true,
                    height: '350px',
                    legend: 'Properties'.translate(),
                    items: [
                        {
                            id: 'state',
                            pmType: 'dropdown',
                            label: 'Status'.translate(),
                            name: 'state',
                            required: true,
                            value: '',
                            controlsWidth: 150,
                            options: [
                                {
                                    value: 'ACTIVE',
                                    label: 'Active'.translate()
                                },
                                {
                                    value: 'INACTIVE',
                                    label: 'Inactive'.translate()
                                }
                            ]
                        },
                        {
                            id: 'username',
                            pmType: 'dropdown',
                            label: 'User'.translate(),
                            name: 'username',
                            required: true,
                            controlsWidth: 300,
                            value: '',
                            options: [],
                            onChange: function (newValue, prevValue) {
                            }
                        },
                        {
                            id: 'name',
                            pmType: 'text',
                            label: 'Name'.translate(),
                            value: startTimer.evn_name,
                            required: true,
                            name: 'name',
                            visible: false,
                            valueType: 'string'
                        },
                        {
                            id: 'performTask',
                            pmType: 'dropdown',
                            label: 'Perform this task'.translate(),
                            name: 'performTask',
                            required: true,
                            value: '',
                            controlsWidth: 300,
                            options: [
                                {
                                    label: '- Select -'.translate(),
                                    value: '',
                                    disabled: true,
                                    selected: true
                                },
                                {
                                    value: 'daily',
                                    label: 'Daily'.translate()
                                },
                                {
                                    value: 'weekly',
                                    label: 'Weekly'.translate()
                                },
                                {
                                    value: 'monthly',
                                    label: 'Monthly'.translate()
                                },
                                {
                                    value: 'oneTime',
                                    label: 'One Time Only'.translate()
                                },
                                {
                                    value: 'every',
                                    label: 'Every'.translate()
                                }
                            ],
                            onChange: function (newValue, prevValue) {
                                showProperties(newValue);
                            }
                        }
                    ]
                },
                {
                    id: 'panelSelectDate',
                    pmType: 'panel',
                    layout: 'vbox',
                    fieldset: true,
                    visible: false,
                    height: '350px',
                    legend: 'Select the date and time for case(s) to be initiated.'.translate(),
                    items: []
                }
            ]
        });

        buttonCancel = new PMUI.ui.Button({
            id: 'cancelSchedulerButton',
            text: 'Cancel'.translate(),
            buttonType: 'error',
            handler: function (event) {
                if (formCreateCaseScheduler.isDirty()) {
                    var message_window = new PMUI.ui.MessageWindow({
                        windowMessageType: 'warning',
                        width: 490,
                        bodyHeight: 'auto',
                        id: 'cancelMessageStartTimer',
                        title: 'Start Timer Event'.translate(),
                        message: 'Are you sure you want to discard your changes?'.translate(),
                        footerItems: [
                            {
                                text: 'No'.translate(),
                                handler: function () {
                                    message_window.close();
                                },
                                buttonType: "error"
                            },
                            {
                                text: 'Yes'.translate(),
                                handler: function () {
                                    message_window.close();
                                    schedulerListWindow.close();
                                },
                                buttonType: "success"
                            }
                        ]
                    });
                    message_window.open();
                    message_window.showFooter();
                } else {
                    formCreateCaseScheduler.reset();
                    schedulerListWindow.close();
                }
            }
        });

        //Window Buttons
        buttonSave = new PMUI.ui.Button({
            id: 'saveSchedulerButton',
            text: 'Save'.translate(),
            height: 31,
            buttonType: 'success',
            handler: function (event) {
                if (formCreateCaseScheduler.isValid()) {
                    var dataFormCreate = formCreateCaseScheduler.getData();

                    var dataToSend = {
                        sch_del_user_name: dataFormCreate.username,
                        sch_name: startTimer.evn_name,
                        tas_uid: startTimer.ports.get(0).connection.flo_element_dest,
                        sch_start_time: '',
                        sch_start_date: '',
                        sch_week_days: '',
                        sch_start_day: '',
                        sch_start_day_opt_1: '',
                        sch_start_day_opt_2: '',
                        sch_months: '',
                        sch_end_date: '',
                        sch_repeat_every: '',
                        sch_state: (dataFormCreate.state !== '') ? dataFormCreate.state : 'ACTIVE',
                        sch_option: ''
                    };

                    var perform = dataFormCreate.performTask;
                    switch (perform) {
                        case 'daily':
                            dataToSend.sch_option = '1';
                            dataToSend.sch_start_time = dataFormCreate.execttime;
                            dataToSend.sch_start_date = dataFormCreate.startDate;
                            dataToSend.sch_end_date = dataFormCreate.endDate;
                            break;
                        case 'weekly':
                            var formdays = eval(dataFormCreate.daysoftheweek);
                            var days = '';
                            for (i = 0; i < formdays.length; i += 1) {
                                if (i !== (formdays.length - 1)) {
                                    days += formdays[i] + '|';
                                } else {
                                    days += formdays[i];
                                }
                            }

                            dataToSend.sch_option = '2';
                            dataToSend.sch_start_time = dataFormCreate.execttime;
                            dataToSend.sch_start_date = dataFormCreate.startDate;
                            dataToSend.sch_end_date = dataFormCreate.endDate;
                            dataToSend.sch_week_days = days;
                            break;
                        case 'monthly':
                            dataToSend.sch_option = '3';
                            dataToSend.sch_start_time = dataFormCreate.execttime;
                            dataToSend.sch_start_date = dataFormCreate.startDate;
                            dataToSend.sch_end_date = dataFormCreate.endDate;
                            if (dataFormCreate.options === 'dayofmonth') {
                                var formmonths = eval(dataFormCreate.months);
                                var months = '';
                                for (i = 0; i < formmonths.length; i += 1) {
                                    if (i !== (formmonths.length - 1)) {
                                        months += formmonths[i] + '|';
                                    } else {
                                        months += formmonths[i];
                                    }
                                }
                                dataToSend.sch_start_day = '1'; //Day of month
                                dataToSend.sch_start_day_opt_1 = dataFormCreate.dayMonth;//1 to 31 - day of the month
                            } else if (dataFormCreate.options === 'day') {
                                var opt2 = dataFormCreate.first + '|' + dataFormCreate.day;
                                var formmonths = eval(dataFormCreate.months);
                                var months = '';
                                for (i = 0; i < formmonths.length; i += 1) {
                                    if (i !== (formmonths.length - 1)) {
                                        months += formmonths[i] + '|';
                                    } else {
                                        months += formmonths[i];
                                    }
                                }
                                dataToSend.sch_start_day = '2'; //Day of month
                                dataToSend.sch_start_day_opt_2 = opt2;//1 to 31 - day of the month

                            }
                            dataToSend.sch_months = months;
                            break;
                        case 'oneTime':
                            dataToSend.sch_option = '4';
                            dataToSend.sch_start_time = dataFormCreate.execttime;
                            break;
                        case 'every':
                            dataToSend.sch_option = '5';
                            dataToSend.sch_repeat_every = timeToDecimal(dataFormCreate.execttime).toFixed(2);
                            break;
                    }
                    updateCaseScheduler(dataToSend);
                }
            }
        });

        //load users
        loadUsers = function () {
            var field = formCreateCaseScheduler.getField('username');
            field.clearOptions();
            for (var i = 0; i < listUsers.length; i += 1) {
                field.addOption({
                    value: listUsers[i].aas_username,
                    label: listUsers[i].aas_name + ' ' + listUsers[i].aas_lastname
                });
            }
        };

        loadDataForm = function () {
            var loadTime,
                option,
                daysVal,
                monthsVal,
                monthsop,
                days,
                i,
                startTime,
                finallyST,
                dataEdit;

            loadTime = function (dataEdit, dataForm) {
                var starDate,
                    startTime,
                    finallyST,
                    endDate;
                starDate = dataForm.sch_start_date ? dataForm.sch_start_date.split(' ') : [''];
                endDate = dataForm.sch_end_date ? dataForm.sch_end_date.split(' ') : [''];

                startTime = dataForm.sch_start_time.split(' ');
                startTime = startTime[1].split(':');
                finallyST = startTime[0] + ':' + startTime[1];
                dataEdit[4].setValue(starDate[0]);
                dataEdit[5].setValue(endDate[0]);
                dataEdit[6].setValue(finallyST);
            };

            dataEdit = formCreateCaseScheduler.getFields();

            dataEdit[0].setValue(dataForm.sch_state);
            dataEdit[1].setValue(dataForm.sch_del_user_name);
            option = 'daily';
            switch (dataForm.sch_option) {
                case '1':
                    option = 'daily';
                    dataEdit[3].setValue(option);
                    showProperties(option);
                    dataEdit = formCreateCaseScheduler.getFields();
                    loadTime(dataEdit, dataForm);
                    break;
                case '2':
                    option = 'weekly';
                    dataEdit[3].setValue(option);
                    showProperties(option);
                    dataEdit = formCreateCaseScheduler.getFields();
                    loadTime(dataEdit, dataForm);

                    daysVal = "[\"";
                    days = dataForm.sch_week_days.split('|');
                    for (i = 0; i < days.length; i += 1) {
                        if (i !== (days.length - 1)) {
                            daysVal += days[i] + "\",\"";
                        } else {
                            daysVal += days[i] + "\"]";
                        }
                    }
                    dataEdit[7].setValue(daysVal);
                    break;
                case '3':
                    option = 'monthly';
                    dataEdit[3].setValue(option);
                    showProperties(option);
                    dataEdit = formCreateCaseScheduler.getFields();
                    loadTime(dataEdit, dataForm);
                    days = [];
                    if (dataForm.sch_start_day !== '') {
                        days = dataForm.sch_start_day.split('|');
                    }

                    if (days[0] === '1') {
                        dataEdit[7].setValue('dayofmonth');
                        loadOptionsRadio('dayofmonth');
                        dataEdit[8].setValue(days[1]);
                    } else if (days[0] === '2') {
                        dataEdit[7].setValue('day');
                        loadOptionsRadio('day');
                        dataEdit[9].setValue(days[1]);
                        dataEdit[10].setValue(days[2]);
                    }
                    monthsVal = "[\"";
                    monthsop = dataForm.sch_months.split('|');
                    for (i = 0; i < monthsop.length; i += 1) {
                        if (i !== (monthsop.length - 1)) {
                            monthsVal += monthsop[i] + "\",\"";
                        } else {
                            monthsVal += monthsop[i] + "\"]";
                        }
                    }
                    dataEdit[11].setValue(monthsVal);
                    break;
                case '4':
                    option = 'oneTime';
                    dataEdit[3].setValue(option);
                    showProperties(option);
                    dataEdit = formCreateCaseScheduler.getFields();
                    startTime = dataForm.sch_start_time.split(' ');
                    startTime = startTime[1].split(':');
                    finallyST = startTime[0] + ':' + startTime[1];
                    dataEdit[4].setValue(finallyST);
                    break;
                case '5':
                    option = 'every';
                    dataEdit[3].setValue(option);
                    showProperties(option);
                    dataEdit = formCreateCaseScheduler.getFields();
                    dataEdit[4].setValue(decimalToTime(parseFloat(dataForm.sch_repeat_every)));
                    break;
            }
        };

        //Main window Case Scheduler
        schedulerListWindow = new PMUI.ui.Window({
            id: 'schedulerListWindow',
            title: 'Start Timer Event'.translate(),
            height: DEFAULT_WINDOW_HEIGHT,
            width: DEFAULT_WINDOW_WIDTH,
            buttonPanelPosition: 'top',
            buttons: [
                buttonSave,
                {pmType: 'label', text: 'or'},
                buttonCancel
            ]
        });

        openForm = function () {
            schedulerListWindow.addItem(formCreateCaseScheduler);
            schedulerListWindow.open();
            loadUsers();
            loadDataForm();
            applyStyleWindowForm(schedulerListWindow);
            schedulerListWindow.showFooter();
            schedulerListWindow.defineEvents();
        };
    };

    PMDesigner.startTimer.openForm = function (element) {
        openForm();
    };

    PMDesigner.startTimer.validate = function (element) {
        if (element.ports.isEmpty()) {
            PMDesigner.msgFlash('Must connect to a Task'.translate(), document.body, 'error', 3000, 5);
            return false;
        }
        PMDesigner.startTimer(element);
        if (loadValuesStartTimer(true) === 0) {
            PMDesigner.msgFlash('The task doesn\'t have assigned users'.translate(), document.body, 'info', 3000, 5);
            return false;
        }
        return true;
    };

}());
(
    function () {
        var dataBaseConnectionOption,
            winFrmDataBaseConnectionShow,
            frmDataBaseConnection,
            closeClicked = true;

        PMDesigner.database = function (event) {
            var winGrdpnlDataBaseConnection,
                panelTest,
                grdpnlDataBaseConnection,
                flagError,
                titleOld,
                btnNew,
                btnTestConnection,
                btnCreate,
                btnCancel,
                testShow,
                isDirtyFrmDataBaseConnection,
                showForm,
                listDBConnection,
                refreshGridPanelInMainWindow,
                dataBaseConnectionsGetRestProxy,
                dataBaseConnectionPostTestRestProxy,
                dataBaseConnectionGetRestProxy,
                dataBaseConnectionPutRestProxy,
                dataBaseConnectionDeleteRestProxy,
                dataBaseConnectionPostRestProxy,
                cboEngineSetOptionsRestProxy,
                showEncoderOptions,
                hideEncoderOptions,
                cboConnectionTypeOracleSetOptions,
                cboEncodeSetOptionsRestProxy,
                winFrmDataBaseConnectionShow,
                cboEngine,
                txtUID,
                cboEncode,
                txtTns,
                txtServer,
                txtDataBaseName,
                disableAllItems,
                txtUsername,
                txtPassword,
                txtPort,
                btnBack,
                txtDescription,
                cboConnectionTypeOracle,
                dataBaseConnectionData;

            disableAllItems = function () {
                winGrdpnlDataBaseConnection.getItems()[0].setVisible(false);
                winGrdpnlDataBaseConnection.getItems()[1].setVisible(false);
                winGrdpnlDataBaseConnection.getItems()[2].setVisible(false);

                txtUID.setVisible(false);
                btnTestConnection.setVisible(false);
                btnBack.setVisible(false);
                btnCreate.setVisible(false);
                btnCancel.setVisible(false);
            };

            testShow = function (testData) {
                var msg = "", titleSummary, style, i, flag;
                flagError = 0;
                disableAllItems();
                titleOld = winGrdpnlDataBaseConnection.getTitle();
                winGrdpnlDataBaseConnection.setTitle("Testing Server Connection".translate());
                winGrdpnlDataBaseConnection.getItems()[2].setVisible(true);

                for (i = 0; i <= testData.length - 1; i += 1) {
                    flag = (typeof(testData[i].error) != "undefined") ? 1 : 0;
                    if (flag != 1) {
                        msg = msg + "<img src = \"/images/select-icon.png\" width=\"17\" height=\"17\" style=\"margin-right: 0.9em;\" />" + testData[i].test + "<br />";
                    } else {
                        msg = msg + "<img src = \"/images/error.png\" width=\"21\" height=\"21\" style=\"margin-right: 0.6em;\" />" + testData[i].error + "<br />";
                    }

                    if (typeof(testData[i].error) != "undefined" && flagError == 0) {
                        flagError = 1;
                    }
                }
                if (flag == 0) {
                    btnCreate.setVisible(true);
                    btnBack.setVisible(true);
                } else {
                    btnBack.setVisible(true);
                }

                titleSummary = "<div style=\"margin: 1em 1em 0.5em 1em; padding: 0 2em 0 2em; border: 2px solid #AFC5D0; background:#D7ECF1; font: bold 1em arial; text-align: center; width: " + (DEFAULT_WINDOW_WIDTH - 22) + ";\">Testing Database Server configuration</div>";

                style = $('#panelTest').attr("style");
                titleSummary = titleSummary + "<div style=\"margin-left: 0.5em; padding: 0.5em; height: 235px;\">" + msg + "</div>";
                $('#panelTest').empty();
                style = style + ' background: #FFFFFF; font: normal 0.8em arial;';
                $('#panelTest').attr("style", style);
                $('#panelTest').append(titleSummary);
            };

            isDirtyFrmDataBaseConnection = function () {
                var message_window;
                $("input,select,textarea").blur();
                if (frmDataBaseConnection.isVisible()) {
                    if (frmDataBaseConnection.isDirty()) {
                        message_window = new PMUI.ui.MessageWindow({
                            id: "cancelMessageTriggers",
                            windowMessageType: 'warning',
                            width: 490,
                            title: "Database Connections".translate(),
                            message: 'Are you sure you want to discard your changes?'.translate(),
                            footerItems: [
                                {
                                    text: "No".translate(),
                                    handler: function () {
                                        message_window.close();
                                    },
                                    buttonType: "error"
                                },
                                {
                                    text: "Yes".translate(),
                                    handler: function () {
                                        message_window.close();
                                        dataBaseConnectionOption = "";
                                        if (closeClicked) {
                                            winGrdpnlDataBaseConnection.close();
                                        }
                                        refreshGridPanelInMainWindow(false);
                                    },
                                    buttonType: "success"
                                }
                            ],
                            title: 'Confirm'.translate()
                        });
                        message_window.open();
                        message_window.showFooter();
                    } else {
                        if (closeClicked) {
                            winGrdpnlDataBaseConnection.close();
                        } else {
                            dataBaseConnectionOption = "";
                            refreshGridPanelInMainWindow(false);
                        }
                    }
                } else {
                    winGrdpnlDataBaseConnection.close();
                }
            };
            showForm = function () {
                disableAllItems();
                winGrdpnlDataBaseConnection.setTitle(titleOld);
                winGrdpnlDataBaseConnection.getItems()[1].setVisible(true);
                btnTestConnection.setVisible(true);
                btnCancel.setVisible(true);
            }

            refreshGridPanelInMainWindow = function (load) {
                disableAllItems();
                winGrdpnlDataBaseConnection.hideFooter();
                dataBaseConnectionOption = "";
                winGrdpnlDataBaseConnection.getItems()[0].setVisible(true);
                winGrdpnlDataBaseConnection.setTitle("Database Connections".translate());
                load = load != null ? load : true;
                if (load) {
                    dataBaseConnectionsGetRestProxy(grdpnlDataBaseConnection);
                }
            };

            dataBaseConnectionsGetRestProxy = function (grdpnl) {
                var restProxy = new PMRestClient({
                    endpoint: "database-connections",
                    typeRequest: "get",
                    functionSuccess: function (xhr, response) {
                        listDBConnection = response;
                        grdpnl.setDataItems(listDBConnection);
                        grdpnl.sort('dbs_database_name', 'asc');
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    }
                });
                restProxy.executeRestClient();
            };

            dataBaseConnectionPostTestRestProxy = function (data) {
                var restProxy = new PMRestClient({
                    endpoint: "database-connection/test",
                    typeRequest: "post",
                    data: data,
                    functionSuccess: function (xhr, response) {
                        testShow(response);
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    }
                });
                restProxy.executeRestClient();
            };

            dataBaseConnectionGetRestProxy = function (dataBaseConnectionUid) {
                var restProxy = new PMRestClient({
                    endpoint: "database-connection/" + dataBaseConnectionUid,
                    typeRequest: "get",
                    functionSuccess: function (xhr, response) {
                        var data = response;
                        dataBaseConnectionOption = "PUT";
                        winFrmDataBaseConnectionShow("PUT", data);
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    }
                });
                restProxy.executeRestClient();
            };

            dataBaseConnectionPutRestProxy = function (dataBaseConnectionUid, data) {
                var restProxy = new PMRestClient({
                    endpoint: "database-connection/" + dataBaseConnectionUid,
                    typeRequest: "update",
                    data: data,
                    functionSuccess: function (xhr, response) {
                        refreshGridPanelInMainWindow();
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    },
                    messageSuccess: 'Database connection edited successfully'.translate(),
                    flashContainer: grdpnlDataBaseConnection
                });

                restProxy.executeRestClient();
            };

            dataBaseConnectionDeleteRestProxy = function (dataBaseConnectionUid) {
                var restProxy = new PMRestClient({
                    endpoint: "database-connection/" + dataBaseConnectionUid,
                    typeRequest: "remove",
                    functionSuccess: function (xhr, response) {
                        refreshGridPanelInMainWindow();
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    },
                    messageSuccess: 'Database connection deleted successfully'.translate(),
                    flashContainer: grdpnlDataBaseConnection
                });
                restProxy.executeRestClient();
            };

            dataBaseConnectionPostRestProxy = function (data) {
                var restProxy = new PMRestClient({
                    endpoint: "database-connection",
                    typeRequest: "post",
                    data: data,
                    functionSuccess: function (xhr, response) {
                        refreshGridPanelInMainWindow();
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);

                        refreshGridPanelInMainWindow();
                    },
                    messageError: 'An unexpected error while deleting the DB Connection, please try again later.'.translate(),
                    messageSuccess: 'Database connection saved successfully'.translate(),
                    flashContainer: grdpnlDataBaseConnection
                });

                restProxy.executeRestClient();
            };

            cboEngineSetOptionsRestProxy = function (cboEngine, cboEncode) {
                cboEngine.clearOptions();
                var restProxy = new PMRestClient({
                    typeRequest: "get",
                    functionSuccess: function (xhr, response) {
                        var data = response,
                            i,
                            arrayOptions = [];
                        for (i = 0; i <= data.length - 1; i += 1) {
                            arrayOptions.push(
                                {
                                    value: data[i].id,
                                    label: data[i].name
                                }
                            );
                        }
                        cboEngine.setOptions(arrayOptions);
                        cboEngine.setValue(arrayOptions[0].value);
                        cboEncodeSetOptionsRestProxy(cboEngine.getValue(), cboEncode);
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    }
                });

                restProxy.setBaseEndPoint("system/db-engines");
                restProxy.executeRestClient();
            };

            hideEncoderOptions = function () {
                cboEncode.setVisible(true);
                cboConnectionTypeOracle.setVisible(true);
            };

            showEncoderOptions = function () {
                cboEncode.setVisible(true);
                cboConnectionTypeOracle.setVisible(false);
            };
            cboConnectionTypeOracleSetOptions = function (cboConnectionTypeOracle) {
                var arrayEnconde = [],
                    i,
                    arrayOptions = [];
                cboConnectionTypeOracle.clearOptions();

                arrayEnconde = [
                    {"value": "NORMAL", "text": "Normal"},
                    {"value": "TNS", "text": "TNS"}
                ];

                for (i = 0; i <= arrayEnconde.length - 1; i += 1) {
                    arrayOptions.push(
                        {
                            value: arrayEnconde[i].value,
                            label: arrayEnconde[i].text
                        }
                    );
                }

                cboConnectionTypeOracle.setOptions(arrayOptions);
            };
            cboEncodeSetOptionsRestProxy = function (selectedCboEngineValue, cboEncode) {
                var arrayEnconde = [],
                    arrayNewEnconde,
                    i,
                    portsDefault,
                    arrayOptions,
                    encode = selectedCboEngineValue;
                cboEncode.clearOptions();

                arrayEnconde["mysql"] = [
                    {"value": "big5", "text": "big5 - Big5 Traditional Chinese"},
                    {"value": "dec8", "text": "dec8 - DEC West European"},
                    {"value": "cp850", "text": "cp850 - DOS West European"},
                    {"value": "hp8", "text": "hp8 - HP West European"},
                    {"value": "koi8r", "text": "koi8r - KOI8-R Relcom Russian"},
                    {"value": "latin1", "text": "latin1 - cp1252 West European"},
                    {"value": "latin2", "text": "latin2 - ISO 8859-2 Central European"},
                    {"value": "swe7", "text": "swe7 - 7bit Swedish"},
                    {"value": "ascii", "text": "ascii - US ASCII"},
                    {"value": "ujis", "text": "ujis - EUC-JP Japanese"},
                    {"value": "sjis", "text": "sjis - Shift-JIS Japanese"},
                    {"value": "hebrew", "text": "hebrew - ISO 8859-8 Hebrew"},
                    {"value": "tis620", "text": "tis620 - TIS620 Thai"},
                    {"value": "euckr", "text": "euckr - EUC-KR Korean"},
                    {"value": "koi8u", "text": "koi8u - KOI8-U Ukrainian"},
                    {"value": "gb2312", "text": "gb2312 - GB2312 Simplified Chinese"},
                    {"value": "greek", "text": "greek - ISO 8859-7 Greek"},
                    {"value": "cp1250", "text": "cp1250 - Windows Central European"},
                    {"value": "gbk", "text": "gbk - GBK Simplified Chinese"},
                    {"value": "latin5", "text": "latin5 - ISO 8859-9 Turkish"},
                    {"value": "armscii8", "text": "armscii8 - ARMSCII-8 Armenian"},
                    {"value": "utf8", "text": "utf8 - UTF-8 Unicode"},
                    {"value": "ucs2", "text": "ucs2 - UCS-2 Unicode"},
                    {"value": "cp866", "text": "cp866 - DOS Russian"},
                    {"value": "keybcs2", "text": "keybcs2 - DOS Kamenicky Czech-Slovak"},
                    {"value": "macce", "text": "macce - Mac Central European"},
                    {"value": "macroman", "text": "macroman - Mac West European"},
                    {"value": "cp852", "text": "cp852 - DOS Central European"},
                    {"value": "latin7", "text": "atin7 - ISO 8859-13 Baltic"},
                    {"value": "cp1251", "text": "cp1251 - Windows Cyrillic"},
                    {"value": "cp1256", "text": "cp1256  - Windows Arabic"},
                    {"value": "cp1257", "text": "cp1257  - Windows Baltic"},
                    {"value": "binary", "text": "binary  - Binary pseudo charset"},
                    {"value": "geostd8", "text": "geostd8 - GEOSTD8 Georgian"},
                    {"value": "cp932", "text": "cp932] - SJIS for Windows Japanese"},
                    {"value": "eucjpms", "text": "eucjpms - UJIS for Windows Japanese"}
                ];

                arrayEnconde["pgsql"] = [
                    {"value": "BIG5", "text": "BIG5"},
                    {"value": "EUC_CN", "text": "EUC_CN"},
                    {"value": "EUC_JP", "text": "EUC_JP"},
                    {"value": "EUC_KR", "text": "EUC_KR"},
                    {"value": "EUC_TW", "text": "EUC_TW"},
                    {"value": "GB18030", "text": "GB18030"},
                    {"value": "GBK", "text": "GBK"},
                    {"value": "ISO_8859_5", "text": "ISO_8859_5"},
                    {"value": "ISO_8859_6", "text": "ISO_8859_6"},
                    {"value": "ISO_8859_7", "text": "ISO_8859_7"},
                    {"value": "ISO_8859_8", "text": "ISO_8859_8"},
                    {"value": "JOHAB", "text": "JOHAB"},
                    {"value": "KOI8", "text": "KOI8"},
                    {"value": "selected", "text": "LATIN1"},
                    {"value": "LATIN2", "text": "LATIN2"},
                    {"value": "LATIN3", "text": "LATIN3"},
                    {"value": "LATIN4", "text": "LATIN4"},
                    {"value": "LATIN5", "text": "LATIN5"},
                    {"value": "LATIN6", "text": "LATIN6"},
                    {"value": "LATIN7", "text": "LATIN7"},
                    {"value": "LATIN8", "text": "LATIN8"},
                    {"value": "LATIN9", "text": "LATIN9"},
                    {"value": "LATIN10", "text": "LATIN10"},
                    {"value": "SJIS", "text": "SJIS"},
                    {"value": "SQL_ASCII", "text": "SQL_ASCII"},
                    {"value": "UHC", "text": "UHC"},
                    {"value": "UTF8", "text": "UTF8"},
                    {"value": "WIN866", "text": "WIN866"},
                    {"value": "WIN874", "text": "WIN874"},
                    {"value": "WIN1250", "text": "WIN1250"},
                    {"value": "WIN1251", "text": "WIN1251"},
                    {"value": "WIN1252", "text": "WIN1252"},
                    {"value": "WIN1256", "text": "WIN1256"},
                    {"value": "WIN1258", "text": "WIN1258"}
                ];

                arrayEnconde["mssql"] = [
                    {"value": "utf8", "text": "utf8 - UTF-8 Unicode"}
                ];

                arrayEnconde["oracle"] = [
                    {"value": "UTF8", "text": "UTF8 - Unicode 3.0 UTF-8 Universal character set, CESU-8 compliant"},
                    {"value": "UTFE", "text": "UTFE - EBCDIC form of Unicode 3.0 UTF-8 Universal character set"},
                    {"value": "AL16UTF16", "text": "AL16UTF16 - Unicode 3.1 UTF-16 Universal character set"},
                    {"value": "AL32UTF8", "text": "AL32UTF8 - Unicode 3.1 UTF-8 Universal character set"}
                ];

                arrayEnconde["sqlsrv"] = [
                    {"value": "utf8", "text": "utf8 - UTF-8 Unicode"}
                ];

                arrayNewEnconde = (typeof(arrayEnconde[encode]) != "undefined") ? arrayEnconde[encode] : [];
                arrayOptions = [];

                for (i = 0; i <= arrayNewEnconde.length - 1; i += 1) {
                    arrayOptions.push(
                        {
                            value: arrayNewEnconde[i].value,
                            label: arrayNewEnconde[i].text
                        }
                    );
                }

                cboEncode.setOptions(arrayOptions);
                cboEncode.setValue('utf8');

                portsDefault = ["3306", "5432", "1433", "1521"];
                switch (encode) {
                    case "mysql":
                        txtPort.setValue(portsDefault[0]);
                        showEncoderOptions();
                        break;
                    case "pgsql":
                        txtPort.setValue(portsDefault[1]);
                        showEncoderOptions();
                        break;
                    case "mssql":
                        txtPort.setValue(portsDefault[2]);
                        showEncoderOptions();
                        break;
                    case "oracle":
                        txtPort.setValue(portsDefault[3]);
                        hideEncoderOptions();
                        break;
                }
            };

            winFrmDataBaseConnectionShow = function (option, data) {
                disableAllItems();
                dataBaseConnectionData = data;
                frmDataBaseConnection.reset();
                cboEngineSetOptionsRestProxy(cboEngine, cboEncode);
                cboConnectionTypeOracleSetOptions(cboConnectionTypeOracle);
                winGrdpnlDataBaseConnection.getItems()[1].setVisible(true);
                btnTestConnection.setVisible(true);
                btnCancel.setVisible(true);
                winGrdpnlDataBaseConnection.showFooter();

                switch (option) {
                    case "POST":
                        winGrdpnlDataBaseConnection.setTitle("Create Database Connection".translate());
                        frmDataBaseConnection.setTitle("");
                        txtTns.setVisible(false);
                        txtTns.setRequired(false);
                        txtServer.setVisible(true);
                        txtServer.setRequired(true);
                        txtDataBaseName.setVisible(true);
                        txtDataBaseName.setRequired(true);
                        txtPort.setVisible(true);
                        txtPort.setRequired(true);
                        txtPort.setValue("3306");
                        break;
                    case "PUT":
                        winGrdpnlDataBaseConnection.setTitle("Edit Database Connection".translate());
                        frmDataBaseConnection.setTitle("");
                        txtUID.setVisible(true);
                        txtUID.setValue(dataBaseConnectionData.dbs_uid);
                        cboEngine.setValue(dataBaseConnectionData.dbs_type);
                        cboEncodeSetOptionsRestProxy(dataBaseConnectionData.dbs_type, cboEncode);
                        cboEncode.setValue(dataBaseConnectionData.dbs_encode);
                        txtServer.setValue(dataBaseConnectionData.dbs_server);
                        txtDataBaseName.setValue(dataBaseConnectionData.dbs_database_name);
                        txtUsername.setValue(dataBaseConnectionData.dbs_username);
                        txtPassword.setValue(dataBaseConnectionData.dbs_password);
                        txtPort.setValue(dataBaseConnectionData.dbs_port);
                        txtDescription.setValue(dataBaseConnectionData.dbs_description);

                        if (dataBaseConnectionData.dbs_type == "oracle") {
                            cboConnectionTypeOracle.setValue(dataBaseConnectionData.dbs_connection_type);
                            cboConnectionTypeOracle.setVisible(true);

                            if (dataBaseConnectionData.dbs_connection_type == "TNS" && dataBaseConnectionData.dbs_tns != "") {
                                txtTns.setValue(dataBaseConnectionData.dbs_tns);
                                txtTns.setVisible(true);
                                txtTns.setRequired(true);

                                txtServer.setValue("");
                                txtServer.setVisible(false);
                                txtServer.setRequired(false);

                                txtDataBaseName.setValue("");
                                txtDataBaseName.setVisible(false);
                                txtDataBaseName.setRequired(false);

                                txtPort.setValue("");
                                txtPort.setVisible(false);
                                txtPort.setRequired(false);
                            } else {
                                txtTns.setValue("");
                                txtTns.setVisible(false);
                                txtTns.setRequired(false);

                                txtServer.setVisible(true);
                                txtServer.setRequired(true);

                                txtDataBaseName.setVisible(true);
                                txtDataBaseName.setRequired(true);

                                txtPort.setVisible(true);
                                txtPort.setRequired(true);
                            }
                        } else {
                            txtTns.setValue("");
                            txtTns.setVisible(false);
                            txtTns.setRequired(false);

                            txtServer.setVisible(true);
                            txtServer.setRequired(true);

                            txtDataBaseName.setVisible(true);
                            txtDataBaseName.setRequired(true);

                            txtPort.setVisible(true);
                            txtPort.setRequired(true);
                        }
                        break;
                }
                frmDataBaseConnection.setFocus();
            };

            txtUID = new PMUI.field.TextField({
                id: "txtUID",
                name: "txtUID",
                valueType: "string",
                controlsWidth: 300,
                label: "UID".translate(),
                maxLength: 200,
                readOnly: true,
                visible: false
            });

            cboEngine = new PMUI.field.DropDownListField({
                id: "cboEngine",
                name: "cboEngine",
                label: "Engine".translate(),
                options: null,
                controlsWidth: 150,
                onChange: function (newValue, prevValue) {
                    if (cboEngine.getValue() == "oracle") {
                        cboConnectionTypeOracleSetOptions(cboConnectionTypeOracle);
                    } else {
                        txtTns.setVisible(false);
                        txtTns.setRequired(false);

                        txtServer.setVisible(true);
                        txtServer.setRequired(true);

                        txtDataBaseName.setVisible(true);
                        txtDataBaseName.setRequired(true);

                        txtPort.setVisible(true);
                        txtPort.setRequired(true);
                    }

                    cboEncodeSetOptionsRestProxy(cboEngine.getValue(), cboEncode);
                }
            });

            cboEncode = new PMUI.field.DropDownListField({
                id: "cboEncode",
                name: "cboEncode",
                label: "Encode".translate(),
                options: null,
                controlsWidth: 300
            });

            cboConnectionTypeOracle = new PMUI.field.DropDownListField({
                id: "cboConnectionTypeOracle",
                name: "cboConnectionTypeOracle",
                label: "Select Connection Type".translate(),
                options: null,
                controlsWidth: 300,

                onChange: function (newValue, prevValue) {
                    if (cboConnectionTypeOracle.getValue() != "NORMAL") {
                        txtTns.setVisible(true);
                        txtTns.setRequired(true);

                        txtServer.setVisible(false);
                        txtServer.setRequired(false);

                        txtDataBaseName.setVisible(false);
                        txtDataBaseName.setRequired(false);

                        txtPort.setVisible(false);
                        txtPort.setRequired(false);
                    } else {
                        txtTns.setVisible(false);
                        txtTns.setRequired(false);

                        txtServer.setVisible(true);
                        txtServer.setRequired(true);

                        txtDataBaseName.setVisible(true);
                        txtDataBaseName.setRequired(true);

                        txtPort.setValue("1521");
                        txtPort.setVisible(true);
                        txtPort.setRequired(true);
                    }
                }
            });

            txtTns = new PMUI.field.TextField({
                id: "txtTns",
                name: "txtTns",
                valueType: "txtTns",
                controlsWidth: 300,
                label: "TNS".translate(),
                maxLength: 200,
                visible: false,
                required: true
            });

            txtServer = new PMUI.field.TextField({
                id: "txtServer",
                name: "txtServer",
                valueType: "string",
                controlsWidth: 300,
                label: "Server".translate(),
                maxLength: 200,
                required: true
            });

            txtDataBaseName = new PMUI.field.TextField({
                id: "txtDataBaseName",
                name: "txtDataBaseName",
                valueType: "string",
                controlsWidth: 300,
                label: "Database Name".translate(),
                maxLength: 200,
                required: true
            });

            txtUsername = new PMUI.field.TextField({
                id: "txtUsername",
                name: "txtUsername",
                valueType: "string",
                controlsWidth: 300,
                label: "Username".translate(),
                maxLength: 200,
                required: true
            });

            txtPassword = new PMUI.field.PasswordField({
                id: "txtPassword",
                name: "txtPassword",
                valueType: "string",
                controlsWidth: 300,
                label: "Password".translate(),
                maxLength: 200
            });

            txtPort = new PMUI.field.TextField({
                id: "txtPort",
                name: "txtPort",
                valueType: "string",
                controlsWidth: 300,
                label: "Port".translate(),
                value: "3306",
                controlsWidth: 300,
                maxLength: 200,
                required: true
            });

            txtDescription = new PMUI.field.TextAreaField({
                id: "txtDescription",
                name: "txtDescription",
                valueType: "string",
                controlsWidth: 500,
                label: "Description".translate(),
                height: "200px",
                style: {cssClasses: ['mafe-textarea-resize']}
            });

            btnTestConnection = new PMUI.ui.Button({
                id: "btnTestConnection",
                text: "Test Connection".translate(),
                buttonType: 'success',
                handler: function () {
                    var data;
                    if (frmDataBaseConnection.isValid()) {
                        if (cboEngine.getValue() == "oracle") {
                            data = {
                                dbs_type: cboEngine.getValue(),
                                dbs_encode: cboEncode.getValue(),
                                dbs_connection_type: cboConnectionTypeOracle.getValue(),
                                dbs_tns: txtTns.getValue(),
                                dbs_server: txtServer.getValue(),
                                dbs_database_name: txtDataBaseName.getValue(),
                                dbs_username: txtUsername.getValue(),
                                dbs_password: getData2PMUI(frmDataBaseConnection.html).txtPassword,
                                dbs_port: parseInt(getData2PMUI(frmDataBaseConnection.html).txtPort),
                                dbs_description: txtDescription.getValue()
                            };
                        } else {
                            data = {
                                dbs_type: cboEngine.getValue(),
                                dbs_encode: cboEncode.getValue(),
                                dbs_server: txtServer.getValue(),
                                dbs_database_name: txtDataBaseName.getValue(),
                                dbs_username: txtUsername.getValue(),
                                dbs_password: getData2PMUI(frmDataBaseConnection.html).txtPassword,
                                dbs_port: parseInt(getData2PMUI(frmDataBaseConnection.html).txtPort),
                                dbs_description: txtDescription.getValue()
                            };
                        }
                        dataBaseConnectionPostTestRestProxy(data);
                    }
                }
            });

            btnCreate = new PMUI.ui.Button({
                id: "btnCreate",
                text: "Save".translate(),
                buttonType: "success",
                handler: function () {
                    var data;
                    if (cboEngine.getValue() == "oracle") {
                        data = {
                            dbs_type: cboEngine.getValue(),
                            dbs_encode: cboEncode.getValue(),
                            dbs_connection_type: cboConnectionTypeOracle.getValue(),
                            dbs_tns: txtTns.getValue(),
                            dbs_server: txtServer.getValue(),
                            dbs_database_name: txtDataBaseName.getValue(),
                            dbs_username: txtUsername.getValue(),
                            dbs_password: getData2PMUI(frmDataBaseConnection.html).txtPassword,
                            dbs_port: parseInt(getData2PMUI(frmDataBaseConnection.html).txtPort),
                            dbs_description: txtDescription.getValue()
                        };
                    } else {
                        data = {
                            dbs_type: cboEngine.getValue(),
                            dbs_encode: cboEncode.getValue(),
                            dbs_server: txtServer.getValue(),
                            dbs_database_name: txtDataBaseName.getValue(),
                            dbs_username: txtUsername.getValue(),
                            dbs_password: txtPassword.getValue(),
                            dbs_port: parseInt(txtPort.getValue()),
                            dbs_description: txtDescription.getValue()
                        };
                    }

                    switch (dataBaseConnectionOption) {
                        case "POST":
                            if (frmDataBaseConnection.isValid()) {
                                dataBaseConnectionPostRestProxy(data);
                            } else {
                                return;
                            }
                            break;
                        case "PUT":
                            if (frmDataBaseConnection.isValid()) {
                                dataBaseConnectionPutRestProxy(dataBaseConnectionData.dbs_uid, data);
                            } else {
                                return;
                            }
                            break;
                    }
                }
            });

            btnCancel = new PMUI.ui.Button({
                id: "btnCancel",
                text: "Cancel".translate(),
                buttonType: 'error',
                handler: function () {
                    closeClicked = false;
                    isDirtyFrmDataBaseConnection();
                }
            });

            btnBack = new PMUI.ui.Button({
                id: "btnBack",
                text: "Back".translate(),
                handler: function () {
                    showForm();
                }
            });

            frmDataBaseConnection = new PMUI.form.Form({
                id: "frmDataBaseConnection",
                width: DEFAULT_WINDOW_WIDTH - 70,
                items: [
                    txtUID,
                    cboEngine,
                    cboEncode,
                    cboConnectionTypeOracle,
                    txtTns,
                    txtServer,
                    txtDataBaseName,
                    txtUsername,
                    txtPassword,
                    txtPort,
                    txtDescription
                ]
            });

            txtPort.controls[0].onKeyUp = function () {
                var txtPortfinalValue,
                    txtPortValue = getData2PMUI(frmDataBaseConnection.html).txtPort;
                if (/\D/.test(txtPortValue)) {
                    if (isNaN(parseInt(txtPortValue))) {
                        txtPortfinalValue = "";
                    } else {
                        txtPortfinalValue = parseInt(txtPortValue);
                    }
                    txtPort.setValue(txtPortfinalValue);
                }
            };

            btnNew = new PMUI.ui.Button({
                id: "btnNew",
                text: "Create".translate(),
                height: "36px",
                width: 100,
                style: {
                    cssClasses: [
                        'mafe-button-create'
                    ]
                },
                handler: function () {
                    dataBaseConnectionOption = "POST";
                    winFrmDataBaseConnectionShow("POST", {});
                }
            });

            grdpnlDataBaseConnection = new PMUI.grid.GridPanel({
                id: "grdpnlDataBaseConnection",
                filterable: true,
                pageSize: 10,
                width: "96%",
                style: {
                    cssClasses: ["mafe-gridPanel"]
                },
                filterPlaceholder: 'Search ...'.translate(),
                emptyMessage: 'No records found'.translate(),
                nextLabel: 'Next'.translate(),
                previousLabel: 'Previous'.translate(),
                tableContainerHeight: 374,
                customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
                    return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
                },
                columns: [
                    {
                        id: 'grdpnlDataBaseConnectionButtonShow',
                        dataType: "button",
                        title: "",
                        buttonLabel: "Show ID".translate(),
                        buttonStyle: {
                            cssClasses: [
                                'mafe-button-show'
                            ]
                        },
                        width: "100px",
                        onButtonClick: function (row, grid) {
                            showUID(row.getData().dbs_uid);
                        }
                    },
                    {
                        columnData: "dbs_type",
                        title: "Type".translate(),
                        width: "100px",
                        sortable: true,
                        alignmentCell: 'left'
                    },
                    {
                        columnData: "dbs_server",
                        title: "Server".translate(),
                        width: "155px",
                        alignmentCell: 'left'
                    },
                    {
                        columnData: "dbs_database_name",
                        title: "Database Name".translate(),
                        width: "160px",
                        sortable: true,
                        alignmentCell: 'left'
                    },
                    {
                        columnData: "dbs_description",
                        title: "Description".translate(),
                        width: "200px",
                        alignmentCell: 'left'
                    },
                    {
                        id: 'grdpnlDataBaseConnectionButtonEdit',
                        dataType: "button",
                        title: "",
                        buttonLabel: "Edit".translate(),
                        buttonStyle: {
                            cssClasses: [
                                'mafe-button-edit'
                            ]
                        },
                        onButtonClick: function (row, grid) {
                            dataBaseConnectionGetRestProxy(row.getData().dbs_uid);
                        }
                    },
                    {
                        id: 'grdpnlDataBaseConnectionButtonDelete',
                        dataType: "button",
                        title: "",
                        buttonLabel: "Delete".translate(),
                        buttonStyle: {
                            cssClasses: [
                                'mafe-button-delete'
                            ]
                        },
                        onButtonClick: function (row, grid) {
                            var data = row.getData();
                            var msgWarning = new PMUI.ui.MessageWindow({
                                id: "msgWarning",
                                title: 'Database Connections'.translate(),
                                windowMessageType: 'warning',
                                width: 490,
                                message: "Do you want to delete this DB Connection?".translate(),
                                buttons: [
                                    {
                                        text: "No".translate(),
                                        handler: function () {
                                            msgWarning.close();
                                        },
                                        buttonType: "error"
                                    },
                                    {
                                        text: "Yes".translate(),
                                        handler: function () {
                                            dataBaseConnectionDeleteRestProxy(data.dbs_uid);
                                            msgWarning.close();
                                        },
                                        buttonType: "success"
                                    }
                                ]
                            });
                            msgWarning.open();
                            msgWarning.showFooter();
                        }
                    }
                ],
                dataItems: null
            });

            panelTest = new PMUI.core.Panel({
                id: 'panelTest',
                height: DEFAULT_WINDOW_HEIGHT - 71,
                display: 'inline-block'
            });

            winGrdpnlDataBaseConnection = new PMUI.ui.Window({
                id: "winGrdpnlDataBaseConnection",
                title: "Database Connections".translate(),
                width: DEFAULT_WINDOW_WIDTH,
                height: DEFAULT_WINDOW_HEIGHT,
                buttonPanelPosition: "bottom",
                buttons: [btnCancel, btnBack, btnTestConnection, btnCreate],
                onBeforeClose: function () {
                    closeClicked = true;
                    isDirtyFrmDataBaseConnection();
                }
            });

            winGrdpnlDataBaseConnection.addItem(grdpnlDataBaseConnection);
            winGrdpnlDataBaseConnection.addItem(frmDataBaseConnection);
            winGrdpnlDataBaseConnection.addItem(panelTest);

            refreshGridPanelInMainWindow();
            if (typeof listDBConnection !== "undefined") {
                winGrdpnlDataBaseConnection.open();
                frmDataBaseConnection.panel.html.style.padding = "10px";
                $('#grdpnlDataBaseConnection .pmui-textcontrol').css({'margin-top': '5px', width: '250px'});
                winGrdpnlDataBaseConnection.defineEvents();
                applyStyleWindowForm(winGrdpnlDataBaseConnection);
                winGrdpnlDataBaseConnection.footer.html.style.textAlign = 'right';
                $(btnNew.getHTML()).css({float: "right"})
                grdpnlDataBaseConnection.dom.toolbar.appendChild(btnNew.getHTML());
                btnNew.defineEvents();

                disableAllItems();
                winGrdpnlDataBaseConnection.getItems()[0].setVisible(true);
            }
        };

        PMDesigner.database.create = function () {
            PMDesigner.database();
            dataBaseConnectionOption = "POST";
            winFrmDataBaseConnectionShow("POST", {});
        };
    }()
);

(function () {
    var openTinyInMainWindow,
        dataOutPutDocument,
        openFormInMainWindow,
        messageRequired;

    PMDesigner.output = function (event) {
        var winMainOutputDocument, formOutput, rowData, updateOutPut, restClient, isDirtyFormOutput, clickedClose = true, that = this,
            setDataRow,
            clearDataRow,
            getGridOutput,
            disableAllItems,
            deleteDataRow,
            updateOutput,
            refreshGridPanelInMainWindow,
            openGridPanelInMainWindow,
            openFormForEditInMainWindow,
            editorTiny,
            outputFormDocPdfSecurityOpen,
            docMargin,
            password,
            outputFormDocPdfSecurityOwner,
            outputFormDocPdfSecurityEnabled,
            btnCloseWindowOutputDoc,
            btnSaveWindowOutputDoc,
            btnCancelTiny,
            newButtonOutput,
            gridOutput,
            winMainOutputDocument,
            btnSaveTiny,
            listOutputDocs,
            headerSettings,
            footerSettings,
            changeType,
            setMinValue,
            getFieldById;
    
        /**
         * Get field by id string, if not found return null.
         * @param {string} id
         * @returns {object}
         */
        getFieldById = function (id) {
            var fields = formOutput.getFields();
            for (var i in fields) {
                if (fields[i].id === id) {
                    return fields[i];
                }
            }
            return null;
        };

        setDataRow = function (row) {
            dataOutPutDocument = row.getData();
            rowData = row;
        };

        clearDataRow = function () {
            dataOutPutDocument = '';
            rowData = '';
        };

        isDirtyFormOutput = function () {
            var message_window;
            $("input,select,textarea").blur();
            if (formOutput.isVisible()) {
                if (formOutput.isDirty()) {
                    message_window = new PMUI.ui.MessageWindow({
                        id: "cancelMessageTriggers",
                        width: 490,
                        title: "Output Documents".translate(),
                        windowMessageType: "warning",
                        bodyHeight: 'auto',
                        message: 'Are you sure you want to discard your changes?'.translate(),
                        footerItems: [
                            {
                                text: "No".translate(),
                                handler: function () {
                                    message_window.close();
                                },
                                buttonType: "error"
                            },
                            {
                                text: "Yes".translate(),
                                handler: function () {
                                    clearDataRow();
                                    message_window.close();
                                    if (clickedClose) {
                                        tinymce.EditorManager.execCommand('mceRemoveControl', true, 'tinyeditor');
                                        winMainOutputDocument.close();
                                    } else {
                                        clearDataRow();
                                        openGridPanelInMainWindow();
                                    }

                                },
                                buttonType: "success"
                            }
                        ]
                    });
                    message_window.open();
                    message_window.showFooter();
                } else {
                    clearDataRow();
                    if (clickedClose) {
                        tinymce.EditorManager.execCommand('mceRemoveControl', true, 'tinyeditor');
                        winMainOutputDocument.close()
                    } else {
                        openGridPanelInMainWindow();
                    }
                }
            } else {
                winMainOutputDocument.close();
            }
        };
        getGridOutput = function () {
            var restClientGet = new PMRestClient({
                endpoint: 'output-documents',
                typeRequest: 'get',
                functionSuccess: function (xhr, response) {
                    listOutputDocs = response;
                    gridOutput.setDataItems(listOutputDocs);
                    gridOutput.sort('out_doc_title', 'asc');
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                },
                messageError: "There are problems getting the output documents, please try again.".translate()
            });
            restClientGet.executeRestClient();
        };

        disableAllItems = function () {
            winMainOutputDocument.hideFooter();
            formOutput.reset();

            winMainOutputDocument.getItems()[0].setVisible(false);
            winMainOutputDocument.getItems()[1].setVisible(false);
            for (var i = 0; i <= winMainOutputDocument.getItems()[1].getItems().length - 1; i += 1) {
                winMainOutputDocument.getItems()[1].getItems()[i].setVisible(false);
            }
            btnSaveWindowOutputDoc.setVisible(false);
            btnCloseWindowOutputDoc.setVisible(false);
            btnSaveTiny.setVisible(false);
            btnCancelTiny.setVisible(false);

            winMainOutputDocument.footer.getItems()[2].setVisible(false);
        };

        refreshGridPanelInMainWindow = function () {
            disableAllItems();
            winMainOutputDocument.getItems()[0].setVisible(true);
            winMainOutputDocument.setTitle("Output Documents".translate());
            getGridOutput();
        };

        openGridPanelInMainWindow = function () {
            disableAllItems();
            winMainOutputDocument.getItems()[0].setVisible(true);
            winMainOutputDocument.setTitle("Output Documents".translate());
            $(winMainOutputDocument.body).removeClass("pmui-background");
        };

        openFormInMainWindow = function () {
            disableAllItems();
            winMainOutputDocument.showFooter();
            winMainOutputDocument.getItems()[1].setVisible(true);
            for (var i = 0; i < winMainOutputDocument.getItems()[1].getItems().length; i += 1) {
                if (winMainOutputDocument.getItems()[1].getItems()[i].type !== "PMTinyField") {
                    winMainOutputDocument.getItems()[1].getItems()[i].setVisible(true);
                }
            }
            btnSaveWindowOutputDoc.setVisible(true);
            btnCloseWindowOutputDoc.setVisible(true);
            winMainOutputDocument.footer.getItems()[2].setVisible(true);
            password.setVisible(false);
            headerSettings.setVisible(false);
            footerSettings.setVisible(false);
            footerSettings.getField('total_number_page_footer').disable();
            headerSettings.getField('total_number_page_header').disable();
            winMainOutputDocument.setTitle("Create Output Document".translate());
            winMainOutputDocument.setHeight(520);
            formOutput.panel.style.addProperties({padding: '20px 10px'});
            formOutput.setFocus();
        };

        openFormForEditInMainWindow = function (outputDocumentData) {
            disableAllItems();
            winMainOutputDocument.showFooter();
            btnSaveWindowOutputDoc.setVisible(true);
            btnCloseWindowOutputDoc.setVisible(true);
            winMainOutputDocument.footer.getItems()[1].setVisible(false);
            formOutput.setWidth(700);
            winMainOutputDocument.getItems()[1].setVisible(true);
            winMainOutputDocument.setTitle("Edit Output Document".translate());
            $(winMainOutputDocument.body).addClass("pmui-background");
            for (var i = 0; i < winMainOutputDocument.getItems()[1].getItems().length; i += 1) {
                if (winMainOutputDocument.getItems()[1].getItems()[i].type !== "PMTinyField") {
                    winMainOutputDocument.getItems()[1].getItems()[i].setVisible(true);
                }
            }

            password.setVisible(false);
            headerSettings.setVisible(false);
            footerSettings.setVisible(false);
            if (dataOutPutDocument != '' && dataOutPutDocument != undefined) {
                var dataEdit = formOutput.getFields();
                getFieldById('outputDocTitle').setValue(dataOutPutDocument['out_doc_title']);
                getFieldById('outputDocFilenameGenerated').setValue(dataOutPutDocument['out_doc_filename']);
                getFieldById('outputDocDescription').setValue(dataOutPutDocument['out_doc_description']);
                getFieldById('outputDocReportGenerator').setValue(dataOutPutDocument['out_doc_report_generator']);
                getFieldById('outputDocMedia').setValue(dataOutPutDocument['out_doc_media']);
                getFieldById('outputDocOrientation').setValue(dataOutPutDocument['out_doc_landscape']);
                getFieldById('outputDocMarginLeft').setValue(dataOutPutDocument['out_doc_left_margin']);
                getFieldById('outputDocMarginRight').setValue(dataOutPutDocument['out_doc_right_margin']);
                getFieldById('outputDocMarginTop').setValue(dataOutPutDocument['out_doc_top_margin']);
                getFieldById('outputDocMarginBottom').setValue(dataOutPutDocument['out_doc_bottom_margin']);
                getFieldById('outputDocToGenerate').setValue(dataOutPutDocument['out_doc_generate']);

                //Set data in header settings
                if (dataOutPutDocument['out_doc_header'] !== "[]") {
                    getFieldById('enableHeader').setValue(dataOutPutDocument['out_doc_header'].enableHeader ? '["1"]' : '0');
                    dataOutPutDocument['out_doc_header'].enableHeader ? headerSettings.setVisible(true) : headerSettings.setVisible(false);
                    getFieldById('headerTitle').setValue(dataOutPutDocument['out_doc_header'].title);
                    getFieldById('fontSizeHeader').setValue(dataOutPutDocument['out_doc_header'].titleFontSize);
                    getFieldById('positionXTitleHeader').setValue(dataOutPutDocument['out_doc_header'].titleFontPositionX);
                    getFieldById('positionYTitleHeader').setValue(dataOutPutDocument['out_doc_header'].titleFontPositionY);
                    getFieldById('headerLogo').setValue(dataOutPutDocument['out_doc_header'].logo);
                    getFieldById('logoWidthHeader').setValue(dataOutPutDocument['out_doc_header'].logoWidth);
                    getFieldById('positionXLogoHeader').setValue(dataOutPutDocument['out_doc_header'].logoPositionX);
                    getFieldById('positionYLogoHeader').setValue(dataOutPutDocument['out_doc_header'].logoPositionY);
                    getFieldById('pageNumberHeader').setValue(dataOutPutDocument['out_doc_header'].pageNumber ? '["1"]' : '0');
                    getFieldById('paginationTitleHeader').setValue(dataOutPutDocument['out_doc_header'].pageNumberTitle);
                    getFieldById('totalNumberPageHeader').setValue(dataOutPutDocument['out_doc_header'].pageNumberTotal ? '["1"]' : '0');
                    getFieldById('positionXNumberHeader').setValue(dataOutPutDocument['out_doc_header'].pageNumberPositionX);
                    getFieldById('positionYNumberHeader').setValue(dataOutPutDocument['out_doc_header'].pageNumberPositionY);
                    if (getFieldById('pageNumberHeader').getValue() === '["1"]') {
                        getFieldById('paginationTitleHeader').enable();
                        getFieldById('totalNumberPageHeader').enable();
                        getFieldById('positionXNumberHeader').enable();
                        getFieldById('positionYNumberHeader').enable();
                    } else {
                        getFieldById('paginationTitleHeader').disable();
                        getFieldById('totalNumberPageHeader').disable();
                        getFieldById('positionXNumberHeader').disable();
                        getFieldById('positionYNumberHeader').disable();
                    }
                }
                //Set data in footer settings
                if (dataOutPutDocument['out_doc_header'] !== "[]") {
                    getFieldById('enableFooter').setValue(dataOutPutDocument['out_doc_footer'].enableFooter ? '["1"]' : '0');
                    dataOutPutDocument['out_doc_footer'].enableFooter ? footerSettings.setVisible(true) : footerSettings.setVisible(false);
                    getFieldById('footerTitle').setValue(dataOutPutDocument['out_doc_footer'].title);
                    getFieldById('fontSizeFooter').setValue(dataOutPutDocument['out_doc_footer'].titleFontSize);
                    getFieldById('positionXTitleFooter').setValue(dataOutPutDocument['out_doc_footer'].titleFontPositionX);
                    getFieldById('positionYTitleFooter').setValue(dataOutPutDocument['out_doc_footer'].titleFontPositionY);
                    getFieldById('footerLogo').setValue(dataOutPutDocument['out_doc_footer'].logo);
                    getFieldById('logoWidthFooter').setValue(dataOutPutDocument['out_doc_footer'].logoWidth);
                    getFieldById('positionXLogoFooter').setValue(dataOutPutDocument['out_doc_footer'].logoPositionX);
                    getFieldById('positionYLogoFooter').setValue(dataOutPutDocument['out_doc_footer'].logoPositionY);
                    getFieldById('pageNumerFooter').setValue(dataOutPutDocument['out_doc_footer'].pageNumber ? '["1"]' : '0');
                    getFieldById('paginationTitleFooter').setValue(dataOutPutDocument['out_doc_footer'].pageNumberTitle);
                    getFieldById('totalNumberPageFooter').setValue(dataOutPutDocument['out_doc_footer'].pageNumberTotal ? '["1"]' : '0');
                    getFieldById('positionXNumberFooter').setValue(dataOutPutDocument['out_doc_footer'].pageNumberPositionX);
                    getFieldById('positionYNumberFooter').setValue(dataOutPutDocument['out_doc_footer'].pageNumberPositionY);
                    if (getFieldById('pageNumerFooter').getValue() === '["1"]') {
                        getFieldById('paginationTitleFooter').enable();
                        getFieldById('totalNumberPageFooter').enable();
                        getFieldById('positionXNumberFooter').enable();
                        getFieldById('positionYNumberFooter').enable();
                    } else {
                        getFieldById('paginationTitleFooter').disable();
                        getFieldById('totalNumberPageFooter').disable();
                        getFieldById('positionXNumberFooter').disable();
                        getFieldById('positionYNumberFooter').disable();
                    }
                }

                if (dataOutPutDocument["out_doc_generate"] != "DOC") {
                    getFieldById('outputDocDPFSecurity').setVisible(true);
                } else {
                    getFieldById('outputDocDPFSecurity').setVisible(false);
                }

                getFieldById('outputDocDPFSecurity').setValue(dataOutPutDocument['out_doc_pdf_security_enabled']);
                if (dataOutPutDocument['out_doc_pdf_security_enabled'] != 0) {
                    password.setVisible(true);
                }
                getFieldById('outputFormDocPdfSecurityOpen').setValue(dataOutPutDocument['out_doc_pdf_security_open_password']);
                getFieldById('outputFormDocPdfSecurityOwner').setValue(dataOutPutDocument['out_doc_pdf_security_owner_password']);

                dataOutPutDocument['out_doc_pdf_security_permissions'] = dataOutPutDocument['out_doc_pdf_security_permissions'].split("|");
                getFieldById('outputFormDocPdfSecurityPermissions').setValue(JSON.stringify(dataOutPutDocument['out_doc_pdf_security_permissions']));

                getFieldById('outputDocEnableVersioning').setValue(dataOutPutDocument['out_doc_versioning']);
                getFieldById('outputDocDestinationPath').setValue(dataOutPutDocument['out_doc_destination_path']);
                getFieldById('outputDocTags').setValue(dataOutPutDocument['out_doc_tags']);
                getFieldById('outputDocGenerateFileLink').setValue(dataOutPutDocument["out_doc_open_type"]);
            }
            winMainOutputDocument.setHeight(520);
            formOutput.panel.style.addProperties({padding: '20px 10px'});
            formOutput.setFocus();
        };

        openTinyInMainWindow = function (outputDocumentData) {
            //Fix for IE11
            var isIe11 = /Trident\/7\.0;.*rv\s*\:?\s*11/.test(navigator.userAgent);

            if (isIe11) {
                tinyMCE.isGecko = false;
            }

            //Set TinyMCE
            disableAllItems();
            winMainOutputDocument.showFooter();
            tinyEditorField = 13;
            formOutput.setVisible(true);
            formOutput.getItems()[tinyEditorField].setVisible(true);
            formOutput.setWidth(890);
            btnSaveTiny.setVisible(true);
            btnCancelTiny.setVisible(true);
            if (!editorTiny.isInitialized) {
                editorTiny.createHTML();
                editorTiny.setParameterTiny();
                editorTiny.isInitialized = true;
            } else {
                tinyMCE.execCommand('mceFocus', false, 'tinyeditor');
            }
            var dataEdit = formOutput.getFields();
            winMainOutputDocument.setTitle("Edit Output Document".translate());
            if (dataOutPutDocument != '' && dataOutPutDocument != undefined) {
                dataOutPutDocument['out_doc_template'] = (dataOutPutDocument['out_doc_template'] != null) ? dataOutPutDocument['out_doc_template'] : ' ';
                dataEdit[47].setValue(dataOutPutDocument['out_doc_template']);
                dataEdit[47].setValueTiny(dataOutPutDocument['out_doc_template']);
                dataEdit[47].setHeight(425);

                formOutput.getItems()[13].setVisible(false)
                dataEdit[47].setVisible(true);
            }
            formOutput.panel.style.addProperties({padding: '0px 10px'});
            winMainOutputDocument.setHeight(520);
            if (!editorTiny.isInitialized)
                tinymce.execCommand('mceFocus', false, 'tinyeditor');
        };

        deleteDataRow = function () {
            confirmWindow = new PMUI.ui.MessageWindow({
                id: "outputMessageWindowWarning",
                windowMessageType: 'warning',
                bodyHeight: 'auto',
                width: 490,
                title: "Output Documents".translate(),
                message: "Do you want to delete this Output Document?".translate(),
                footerItems: [
                    {
                        id: 'confirmWindowButtonNo',
                        text: "No".translate(),
                        visible: true,
                        handler: function () {
                            confirmWindow.close();
                        },
                        buttonType: "error"
                    }, {
                        id: 'confirmWindowButtonYes',
                        text: "Yes".translate(),
                        visible: true,
                        handler: function () {
                            var restClient;
                            confirmWindow.close();
                            restClient = new PMRestClient({
                                endpoint: "output-document/" + dataOutPutDocument.out_doc_uid,
                                typeRequest: 'remove',
                                functionSuccess: function (xhr, response) {
                                    refreshGridPanelInMainWindow();
                                },
                                functionFailure: function (xhr, response) {
                                    PMDesigner.msgWinError(response.error.message);
                                },
                                messageError: "There are problems deleting the OutputDocument, please try again.".translate(),
                                messageSuccess: 'Output Document deleted successfully'.translate(),
                                flashContainer: gridOutput
                            });
                            restClient.executeRestClient();
                        },
                        buttonType: "success"
                    },
                ]
            });
            confirmWindow.open();
            confirmWindow.dom.titleContainer.style.height = "17px";
            confirmWindow.showFooter();
        };

        updateOutput = function (data) {
            dataOutPutDocument = '';
            var restClientUpdate = new PMRestClient({
                endpoint: "output-document/" + data.out_doc_uid,
                typeRequest: 'update',
                data: data,
                functionSuccess: function (xhr, response) {
                    dataOutPutDocument = data;
                    refreshGridPanelInMainWindow();
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                },
                messageError: "There are problems updating the OutputDocument, please try again.".translate(),
                messageSuccess: 'Output Document edited successfully'.translate(),
                flashContainer: gridOutput
            });
            restClientUpdate.executeRestClient();
        };

        editorTiny = new PMTinyField({
            id: 'outputEditorTiny',
            theme: "advanced",
            plugins: "advhr,advimage,advlink,advlist,autolink,autoresize,contextmenu,directionality,emotions,example,example_dependency,fullpage,fullscreen,iespell,inlinepopups,insertdatetime,layer,legacyoutput,lists,media,nonbreaking,noneditable,pagebreak,paste,preview,print,save,searchreplace,style,tabfocus,table,template,visualblocks,visualchars,wordcount,xhtmlxtras,pmSimpleUploader,pmVariablePicker,style",
            mode: "specific_textareas",
            editorSelector: "tmceEditor",
            widthTiny: DEFAULT_WINDOW_WIDTH - 58,
            heightTiny: DEFAULT_WINDOW_HEIGHT - 100,
            directionality: 'ltr',
            verifyHtml: false,
            themeAdvancedButtons1: "pmSimpleUploader,|,pmVariablePicker,|,bold,italic,underline,|,justifyleft,justifycenter,justifyright,justifyfull,|,fontselect,fontsizeselect,|,cut,copy,paste,|,bullist,numlist,|,outdent,indent,blockquote",
            themeAdvancedButtons2: "tablecontrols,|,undo,redo,|,link,unlink,image,|,forecolor,backcolor,styleprops,|,hr,removeformat,visualaid,|,sub,sup,|,ltr,rtl,|,code",
            popupCss: "/js/tinymce/jscripts/tiny_mce/themes/advanced/skins/default/dialogTinyBpmn.css",
            contentCss: "/css/fonts.css,/fonts/styles.php",
            themeAdvancedFonts: tcPdfFonts,
            skin: "o2k7",
            skin_variant: "silver"
        });

        docMargin = new PMUI.form.FormPanel({
            fieldset: true,
            layout: 'hbox',
            legend: "Margin".translate(),
            items: [
                new PMUI.form.FormPanel({
                    fieldset: true,
                    layout: "box",
                    proportion: 0.6,
                    padding: "5px 5px",
                    items: [
                        new PMUI.form.FormPanel({
                            fieldset: true,
                            height: 200,
                            width: 170,
                            borderWidth : "3px",
                        })
                    ]
                }),
                {
                    pmType: "panel",
                    layout: 'vbox',
                    items: [
                        {
                            id: 'outputDocMarginLeft',
                            pmType: "text",
                            label: "Left".translate(),
                            required: true,
                            value: 20,
                            name: "out_doc_left_margin",
                            controlsWidth: 50,
                            labelWidth: '35%'
                        }, {
                            id: 'outputDocMarginRight',
                            pmType: "text",
                            label: "Right".translate(),
                            required: true,
                            value: 20,
                            name: "out_doc_right_margin",
                            controlsWidth: 50,
                            labelWidth: '35%'
                        }
                    ]
                },
                {
                    pmType: "panel",
                    layout: 'vbox',
                    proportion: 1.5,
                    items: [
                        {
                            id: 'outputDocMarginTop',
                            pmType: "text",
                            label: "Top".translate(),
                            required: true,
                            value: 20,
                            name: "out_doc_top_margin",
                            controlsWidth: 50,
                            labelWidth: '27%'
                        }, {
                            id: 'outputDocMarginBottom',
                            pmType: "text",
                            label: "Bottom".translate(),
                            required: true,
                            value: 20,
                            name: "out_doc_bottom_margin",
                            controlsWidth: 50,
                            labelWidth: '27%'
                        }
                    ]
                }
            ]
        });

        //Field "Open Password - Owner Password"
        outputFormDocPdfSecurityOpen = new PMUI.field.PasswordField({
            id: "outputFormDocPdfSecurityOpen",
            name: "out_doc_pdf_security_open_password",
            value: "",
            label: "Open Password ".translate(),
            required: true,
            controlsWidth: 300
        });

        outputFormDocPdfSecurityOwner = new PMUI.field.PasswordField({
            id: "outputFormDocPdfSecurityOwner",
            name: "out_doc_pdf_security_owner_password",
            value: "",
            label: "Owner Password ".translate(),
            required: true,
            controlsWidth: 300

        });

        password = new PMUI.form.FormPanel({
            width: 500,
            height: 130,
            fieldset: true,
            visible: false,
            legend: "",
            items: [
                {
                    pmType: "panel",
                    layout: 'vbox',
                    items: [
                        outputFormDocPdfSecurityOpen,
                        outputFormDocPdfSecurityOwner
                    ]
                },
                {
                    pmType: "panel",
                    layout: 'vbox',
                    items: [
                        {
                            id: 'outputFormDocPdfSecurityPermissions',
                            pmType: 'checkbox',
                            label: "Allowed Permissions".translate(),
                            value: '',
                            name: 'out_doc_pdf_security_permissions',
                            required: false,
                            controlPositioning: 'horizontal',
                            separator: "|",
                            maxDirectionOptions: 4,
                            options: [
                                {
                                    id: 'monday',
                                    label: "print".translate(),
                                    value: 'print'
                                },
                                {
                                    id: 'monday',
                                    label: "modify".translate(),
                                    value: 'modify'
                                },
                                {
                                    id: 'monday',
                                    label: "copy".translate(),
                                    value: 'copy'
                                },
                                {
                                    id: 'monday',
                                    label: "forms".translate(),
                                    value: 'forms'
                                }

                            ]
                        }
                    ]
                }
            ],
            layout: "vbox"
        });

        /**
         * Header Settings
         */
        headerSettings = new PMUI.form.FormPanel({
            fieldset: true,
            layout: 'vbox',
            name: "header_settings",
            legend: "Header Settings".translate(),
            items: [
                {
                    pmType: "panel",
                    layout: 'hbox',
                    items: [
                        new CriteriaField({
                            id: 'headerTitle',
                            pmType: "text",
                            name: 'header_title',
                            label: "Header Title".translate(),
                            labelWidth: '26%',
                            controlsWidth: 285,
                            required: false,
                            proportion: 3
                        }),
                        {
                            id: 'fontSizeHeader',
                            pmType: "text",
                            label: "Font Size".translate(),
                            required: false,
                            value: 8,
                            name: "font_size_header",
                            controlsWidth: 50,
                            labelWidth: '51%',
                            proportion: 1.2,
                            onChange: function (newVal, oldVal) {
                                if (newVal <= 7 || newVal >= 73) {
                                    this.setValue(oldVal);
                                }
                            }
                        },
                        {
                            id: 'positionXTitleHeader',
                            pmType: "text",
                            label: "Position X".translate(),
                            required: false,
                            value: 0,
                            name: "position_x_title_header",
                            controlsWidth: 50,
                            labelWidth: '62%'
                        },
                        {
                            id: 'positionYTitleHeader',
                            pmType: "text",
                            label: "Position Y".translate(),
                            required: false,
                            value: 0,
                            name: "position_y_title_header",
                            controlsWidth: 50,
                            labelWidth: '62%'
                        },
                    ]
                },
                {
                    pmType: "panel",
                    layout: 'hbox',
                    items: [
                        new CriteriaField({
                            id: 'headerLogo',
                            pmType: "text",
                            name: 'header_logo',
                            label: "Header Logo".translate(),
                            labelWidth: '26%',
                            controlsWidth: 285,
                            required: false,
                            proportion: 3
                        }),
                        {
                            id: 'logoWidthHeader',
                            pmType: "text",
                            label: "Logo Width".translate(),
                            required: false,
                            value: 0,
                            name: "logo_width_header",
                            controlsWidth: 50,
                            labelWidth: '51%',
                            proportion: 1.2
                        },
                        {
                            id: 'positionXLogoHeader',
                            pmType: "text",
                            label: "Position X".translate(),
                            required: false,
                            value: 0,
                            name: "position_x_logo_header",
                            controlsWidth: 50,
                            labelWidth: '62%'
                        },
                        {
                            id: 'positionYLogoHeader',
                            pmType: "text",
                            label: "Position Y".translate(),
                            required: false,
                            value: 0,
                            name: "position_y_logo_header",
                            controlsWidth: 50,
                            labelWidth: '62%'
                        },
                    ]
                },
                {
                    pmType: "panel",
                    layout: "hbox",
                    items: [
                        new PMUI.form.FormPanel({
                            fieldset: false,
                            layout: 'hbox',
                            legend: "Header Settings".translate(),
                            proportion: 3,
                            fontSize: 10,
                            padding: 0,
                            items: [
                                new SwitchField({
                                    id: 'pageNumberHeader',
                                    labelWidth: "50%",
                                    label: "Page Number".translate(),
                                    name: "page_number_header",
                                    value: '1',
                                    controlsWidth: 54,
                                    proportion: 0.45,
                                    controlPositioning: 'vertical',
                                    options: [
                                        {
                                            id: 'pageNumberOptionHeader',
                                            disabled: false,
                                            value: '1',
                                            selected: false
                                        }
                                    ],
                                    onChange: function (newVal, oldVal) {
                                        if (newVal === '["1"]') {
                                            headerSettings.getField('pagination_title_header').updateDisabled(false);
                                            headerSettings.getField('total_number_page_header').enable();
                                            getFieldById('totalNumberPageHeader').enable();
                                            getFieldById('positionXNumberHeader').enable();
                                            getFieldById('positionYNumberHeader').enable();
                                        } else {
                                            headerSettings.getField('pagination_title_header').updateDisabled(true);
                                            headerSettings.getField('total_number_page_header').disable();
                                            getFieldById('totalNumberPageHeader').disable();
                                            getFieldById('positionXNumberHeader').disable();
                                            getFieldById('positionYNumberHeader').disable();
                                        }
                                    }
                                }),
                                new CriteriaField({
                                    id: 'paginationTitleHeader',
                                    pmType: "text",
                                    name: 'pagination_title_header',
                                    label: "Pagination Title".translate(),
                                    labelWidth: '27%',
                                    controlsWidth: 170,
                                    required: false,
                                    disabled: true
                                }),
                            ]
                        }),
                        new SwitchField({
                            id: 'totalNumberPageHeader',
                            labelWidth: "60%",
                            label: "Total Number of Pages".translate(),
                            name: "total_number_page_header",
                            value: '1',
                            controlPositioning: 'vertical',
                            controlsWidth: 54,
                            proportion: 1.2,
                            options: [
                                {
                                    id: 'totalNumberPageOptionHeader',
                                    disabled: false,
                                    value: '1',
                                    selected: false
                                }
                            ],
                            onChange: function (newVal, oldVal) {
                            }
                        }),
                        {
                            id: 'positionXNumberHeader',
                            pmType: "text",
                            label: "Position X".translate(),
                            required: false,
                            value: 0,
                            name: "position_x_number_header",
                            controlsWidth: 50,
                            labelWidth: '62%',
                            disabled: true
                        },
                        {
                            id: 'positionYNumberHeader',
                            pmType: "text",
                            label: "Position Y".translate(),
                            required: false,
                            value: 0,
                            name: "position_y_number_header",
                            controlsWidth: 50,
                            labelWidth: '62%',
                            disabled: true
                        },
                    ]
                }
            ]
        });

        /**
         * Footer settings
         */
        footerSettings = new PMUI.form.FormPanel({
            fieldset: true,
            layout: 'vbox',
            name: "footer_settings",
            legend: "Footer Settings".translate(),
            items: [
                {
                    pmType: "panel",
                    layout: 'hbox',
                    items: [
                        new CriteriaField({
                            id: 'footerTitle',
                            pmType: "text",
                            name: 'footer_title',
                            label: "Footer Title".translate(),
                            labelWidth: '26%',
                            controlsWidth: 285,
                            required: false,
                            proportion: 3
                        }),
                        {
                            id: 'fontSizeFooter',
                            pmType: "text",
                            label: "Font Size".translate(),
                            required: false,
                            value: 8,
                            name: "font_size_footer",
                            controlsWidth: 50,
                            labelWidth: '51%',
                            proportion: 1.2,
                            onChange: function (newVal, oldVal) {
                                if (newVal <= 7 || newVal >= 73) {
                                    this.setValue(oldVal);
                                }
                            }
                        },
                        {
                            id: 'positionXTitleFooter',
                            pmType: "text",
                            label: "Position X".translate(),
                            required: false,
                            value: 0,
                            name: "position_x_title_footer",
                            controlsWidth: 50,
                            labelWidth: '62%'
                        },
                        {
                            id: 'positionYTitleFooter',
                            pmType: "text",
                            label: "Position Y".translate(),
                            required: false,
                            value: 0,
                            name: "position_y_title_footer",
                            controlsWidth: 50,
                            labelWidth: '62%'
                        },
                    ]
                },
                {
                    pmType: "panel",
                    layout: 'hbox',
                    items: [
                        new CriteriaField({
                            id: 'footerLogo',
                            pmType: "text",
                            name: 'footer_logo',
                            label: "Footer Logo".translate(),
                            labelWidth: '26%',
                            controlsWidth: 285,
                            required: false,
                            proportion: 3
                        }),
                        {
                            id: 'logoWidthFooter',
                            pmType: "text",
                            label: "Logo Width".translate(),
                            required: false,
                            value: 0,
                            name: "logo_width_footer",
                            controlsWidth: 50,
                            labelWidth: '51%',
                            proportion: 1.2
                        },
                        {
                            id: 'positionXLogoFooter',
                            pmType: "text",
                            label: "Position X".translate(),
                            required: false,
                            value: 0,
                            name: "position_x_logo_footer",
                            controlsWidth: 50,
                            labelWidth: '62%'
                        },
                        {
                            id: 'positionYLogoFooter',
                            pmType: "text",
                            label: "Position Y".translate(),
                            required: false,
                            value: 0,
                            name: "position_y_logo_footer",
                            controlsWidth: 50,
                            labelWidth: '62%'
                        },
                    ]
                },
                {
                    pmType: "panel",
                    layout: "hbox",
                    items: [
                        new PMUI.form.FormPanel({
                            fieldset: false,
                            layout: 'hbox',
                            legend: "Footer Settings".translate(),
                            proportion: 3,
                            fontSize: 10,
                            padding: 0,
                            items: [
                                new SwitchField({
                                    id: 'pageNumerFooter',
                                    labelWidth: "50%",
                                    label: "Page Number".translate(),
                                    name: "page_number_footer",
                                    value: '1',
                                    controlsWidth: 54,
                                    proportion: 0.45,
                                    controlPositioning: 'vertical',
                                    options: [
                                        {
                                            id: 'pageNumberOptionFooter',
                                            disabled: false,
                                            value: '1',
                                            selected: false
                                        }
                                    ],
                                    onChange: function (newVal, oldVal) {
                                        if (newVal === '["1"]') {
                                            footerSettings.getField('pagination_title_footer').updateDisabled(false);
                                            footerSettings.getField('total_number_page_footer').enable();
                                            getFieldById('totalNumberPageFooter').enable();
                                            getFieldById('positionXNumberFooter').enable();
                                            getFieldById('positionYNumberFooter').enable();
                                        } else {
                                            footerSettings.getField('pagination_title_footer').updateDisabled(true);
                                            footerSettings.getField('total_number_page_footer').disable();
                                            getFieldById('totalNumberPageFooter').disable();
                                            getFieldById('positionXNumberFooter').disable();
                                            getFieldById('positionYNumberFooter').disable();
                                        }
                                    }
                                }),
                                new CriteriaField({
                                    id: 'paginationTitleFooter',
                                    pmType: "text",
                                    name: 'pagination_title_footer',
                                    label: "Pagination Title".translate(),
                                    labelWidth: '27%',
                                    controlsWidth: 170,
                                    required: false,
                                    disabled: true
                                }),
                            ]
                        }),
                        new SwitchField({
                            id: 'totalNumberPageFooter',
                            labelWidth: "60%",
                            label: "Total Number of Pages".translate(),
                            name: "total_number_page_footer",
                            value: '1',
                            controlPositioning: 'vertical',
                            controlsWidth: 54,
                            proportion: 1.2,
                            options: [
                                {
                                    id: 'totalNumberPageOptionFooter',
                                    disabled: false,
                                    value: '1',
                                    selected: false
                                }
                            ],
                            onChange: function (newVal, oldVal) {
                            }
                        }),
                        {
                            id: 'positionXNumberFooter',
                            pmType: "text",
                            label: "Position X".translate(),
                            required: false,
                            value: 0,
                            name: "position_x_number_footer",
                            controlsWidth: 50,
                            labelWidth: '62%',
                            disabled: true
                        },
                        {
                            id: 'positionYNumberFooter',
                            pmType: "text",
                            label: "Position Y".translate(),
                            required: false,
                            value: 0,
                            name: "position_y_number_footer",
                            controlsWidth: 50,
                            labelWidth: '62%',
                            disabled: true
                        },
                    ]
                }
            ]
        });

        /**
         * Change the type of control
         */
        changeType = function () {
            headerSettings.getField('font_size_header').getControl().getHTML().type = "number";
            headerSettings.getField('position_x_title_header').getControl().getHTML().type = "number";
            headerSettings.getField('position_y_title_header').getControl().getHTML().type = "number";
            headerSettings.getField('logo_width_header').getControl().getHTML().type = "number";
            headerSettings.getField('position_x_logo_header').getControl().getHTML().type = "number";
            headerSettings.getField('position_y_logo_header').getControl().getHTML().type = "number";
            headerSettings.getField('position_x_number_header').getControl().getHTML().type = "number";
            headerSettings.getField('position_y_number_header').getControl().getHTML().type = "number";
            footerSettings.getField('font_size_footer').getControl().getHTML().type = "number";
            footerSettings.getField('position_x_title_footer').getControl().getHTML().type = "number";
            footerSettings.getField('position_y_title_footer').getControl().getHTML().type = "number";
            footerSettings.getField('logo_width_footer').getControl().getHTML().type = "number";
            footerSettings.getField('position_x_logo_footer').getControl().getHTML().type = "number";
            footerSettings.getField('position_y_logo_footer').getControl().getHTML().type = "number";
            footerSettings.getField('position_x_number_footer').getControl().getHTML().type = "number";
            footerSettings.getField('position_y_number_footer').getControl().getHTML().type = "number";
        };

        /**
         * Set a minimum value to avoid entering negative numbers
         */
        setMinValue = function () {
            headerSettings.getField('position_x_title_header').getControl().getHTML().min = "0";
            headerSettings.getField('position_y_title_header').getControl().getHTML().min = "0";
            headerSettings.getField('logo_width_header').getControl().getHTML().min = "0";
            headerSettings.getField('position_x_logo_header').getControl().getHTML().min = "0";
            headerSettings.getField('position_y_logo_header').getControl().getHTML().min = "0";
            headerSettings.getField('position_x_number_header').getControl().getHTML().min = "0";
            headerSettings.getField('position_y_number_header').getControl().getHTML().min = "0";
            footerSettings.getField('position_x_title_footer').getControl().getHTML().min = "0";
            footerSettings.getField('position_y_title_footer').getControl().getHTML().min = "0";
            footerSettings.getField('logo_width_footer').getControl().getHTML().min = "0";
            footerSettings.getField('position_x_logo_footer').getControl().getHTML().min = "0";
            footerSettings.getField('position_y_logo_footer').getControl().getHTML().min = "0";
            footerSettings.getField('position_x_number_footer').getControl().getHTML().min = "0";
            footerSettings.getField('position_y_number_footer').getControl().getHTML().min = "0";
        };

        //Field "PDF security"
        outputFormDocPdfSecurityEnabled = new PMUI.field.DropDownListField({
            id: "outputDocDPFSecurity",
            name: "out_doc_pdf_security_enabled",
            label: "PDF security".translate(),
            labelWidth: "27%",
            valueType: "number",
            visible: false,

            options: [
                {
                    value: 0,
                    label: "Disabled".translate(),
                    selected: true
                },
                {
                    value: 1,
                    label: "Enabled".translate()

                }
            ],

            controlsWidth: 100,

            onChange: function (newValue, prevValue) {
                var visible = true;

                if (newValue == 0) {
                    visible = false;

                    outputFormDocPdfSecurityOpen.setValue("");
                    outputFormDocPdfSecurityOwner.setValue("");
                }

                password.setVisible(visible);
            }
        });

        //the form is 700px width, but with the tiny grows to 890
        formOutput = new PMUI.form.Form({
            id: 'outputForm',
            name: 'outputForm',
            fieldset: true,
            title: "",
            visibleHeader: false,
            width: DEFAULT_WINDOW_WIDTH - 43,
            items: [
                {
                    id: 'outputDocTitle',
                    pmType: "text",
                    name: 'out_doc_title',
                    label: "Title".translate(),
                    labelWidth: '27%',
                    controlsWidth: 300,
                    required: true
                },
                new CriteriaField({
                    id: 'outputDocFilenameGenerated',
                    pmType: "text",
                    name: 'out_doc_filename',
                    label: "Filename generated".translate(),
                    labelWidth: '27%',
                    controlsWidth: 300,
                    required: true
                }),
                {
                    id: 'outputDocDescription',
                    pmType: "textarea",
                    name: 'out_doc_description',
                    label: "Description".translate(),
                    labelWidth: '27%',
                    controlsWidth: 500,
                    rows: 100,
                    style: {cssClasses: ['mafe-textarea-resize']}
                },
                {
                    id: 'outputDocReportGenerator',
                    pmType: "dropdown",
                    name: 'out_doc_report_generator',
                    label: "Report Generator".translate(),
                    labelWidth: '27%',
                    require: true,
                    controlsWidth: 165,
                    options: [
                        {
                            label: "TCPDF".translate(),
                            value: "TCPDF"
                        },
                        {
                            label: "HTML2PDF (Old Version)".translate(),
                            value: "HTML2PDF"
                        }
                    ],
                    value: "TCPDF"
                },
                {
                    id: 'outputDocMedia',
                    pmType: "dropdown",
                    name: 'out_doc_media',
                    label: "Media".translate(),
                    labelWidth: '27%',
                    controlsWidth: 165,
                    options: [
                        {label: "Letter".translate(), value: "Letter"},
                        {label: "Legal".translate(), value: "Legal"},
                        {label: "Executive".translate(), value: "Executive"},
                        {label: "B5".translate(), value: "B5"},
                        {label: "Folio".translate(), value: "Folio"},
                        {label: "A0Oversize".translate(), value: "A0Oversize"},
                        {label: "A0".translate(), value: "A0"},
                        {label: "A1".translate(), value: "A1"},
                        {label: "A2".translate(), value: "A2"},
                        {label: "A3".translate(), value: "A3"},
                        {label: "A4".translate(), value: "A4"},
                        {label: "A5".translate(), value: "A5"},
                        {label: "A6".translate(), value: "A6"},
                        {label: "A7".translate(), value: "A7"},
                        {label: "A8".translate(), value: "A8"},
                        {label: "A9".translate(), value: "A9"},
                        {label: "A10", value: "A10"},
                        {label: "Screenshot640".translate(), value: "SH640"},
                        {label: "Screenshot800".translate(), value: "SH800"},
                        {label: "Screenshot1024".translate(), value: "SH1024"}
                    ]
                },
                {
                    id: 'outputDocOrientation',
                    pmType: "dropdown",
                    name: 'out_doc_landscape',
                    labelWidth: '27%',
                    label: "Orientation".translate(),
                    controlsWidth: 165,
                    options: [
                        {
                            label: "Portrait".translate(),
                            selected: true,
                            value: 0
                        },
                        {
                            label: "Landscape".translate(),
                            value: 1
                        }
                    ],
                    valueType: 'number'
                },
                docMargin,
                new SwitchField({
                    id: 'enableHeader',
                    labelWidth: "6%",
                    label: "Header".translate(),
                    name: "enableHeader",
                    value: '0',
                    controlsWidth: 54,
                    controlPositioning: 'vertical',
                    options: [
                        {
                            id: 'enableHeader',
                            disabled: false,
                            value: '1',
                            selected: false
                        }
                    ],
                    onChange: function (newVal, oldVal) {
                        if (newVal === '["1"]') {
                            headerSettings.setVisible(true);
                        } else {
                            headerSettings.setVisible(false);
                        }
                    }
                }),
                headerSettings,
                new SwitchField({
                    id: 'enableFooter',
                    labelWidth: "6%",
                    label: "Footer".translate(),
                    name: "enableFooter",
                    value: '0',
                    controlsWidth: 54,
                    controlPositioning: 'vertical',
                    options: [
                        {
                            id: 'enableFooter',
                            disabled: false,
                            value: '1',
                            selected: false
                        }
                    ],
                    onChange: function (newVal, oldVal) {
                        if (newVal === '["1"]') {
                            footerSettings.setVisible(true);
                        } else {
                            footerSettings.setVisible(false);
                        }
                    }
                }),
                footerSettings,
                {
                    id: 'outputDocToGenerate',
                    pmType: "dropdown",
                    name: 'out_doc_generate',
                    controlsWidth: 70,
                    labelWidth: '27%',
                    label: "Output Document to Generate".translate(),
                    options: [
                        {
                            label: "Both".translate(),
                            value: "BOTH"
                        },
                        {
                            label: "Doc".translate(),
                            value: "DOC"
                        },
                        {
                            label: "Pdf".translate(),
                            value: "PDF"
                        }
                    ],
                    value: "BOTH",
                    onChange: function (newValue, prevValue) {
                        if (newValue == "DOC") {
                            outputFormDocPdfSecurityEnabled.setVisible(false);
                            outputFormDocPdfSecurityEnabled.setValue(0);
                            password.setVisible(false);
                            outputFormDocPdfSecurityOpen.setValue("");
                            outputFormDocPdfSecurityOwner.setValue("");
                        } else {
                            outputFormDocPdfSecurityEnabled.setVisible(true);
                        }
                    }
                },
                outputFormDocPdfSecurityEnabled,
                password,
                {
                    id: 'outputDocEnableVersioning',
                    pmType: "dropdown",
                    name: "out_doc_versioning",
                    controlsWidth: 70,
                    labelWidth: '27%',
                    label: 'Enable versioning'.translate(),
                    options: [
                        {
                            label: "Yes".translate(),
                            value: 1
                        },
                        {
                            label: "No".translate(),
                            selected: true,
                            value: 0
                        }
                    ],
                    valueType: 'number'
                },
                new CriteriaField({
                    id: 'outputDocDestinationPath',
                    pmType: "text",
                    name: "out_doc_destination_path",
                    labelWidth: '27%',
                    label: "Destination Path".translate(),
                    controlsWidth: 340
                }),
                new CriteriaField({
                    id: 'outputDocTags',
                    pmType: "text",
                    name: "out_doc_tags",
                    labelWidth: '27%',
                    label: "Tags".translate(),
                    controlsWidth: 340
                }),
                {
                    id: "outputDocGenerateFileLink",
                    name: "cboByGeneratedFile",
                    pmType: "dropdown",
                    controlsWidth: 155,
                    labelWidth: "27%",
                    label: "By clicking on the generated file link".translate(),

                    options: [
                        {
                            value: 0,
                            label: "Open the file".translate()
                        },
                        {
                            label: "Download the file".translate(),
                            value: 1,
                            selected: true
                        }
                    ],

                    valueType: "number"
                }
            ],
            style: {
                cssProperties: {
                    marginLeft: '20px'
                }
            }
        });

        formOutput.style.addProperties({marginLeft: '20px'});
        gridOutput = new PMUI.grid.GridPanel({
            id: 'gridOutput',
            pageSize: 10,
            width: "96%",
            style: {
                cssClasses: ["mafe-gridPanel"]
            },
            filterPlaceholder: 'Search ...'.translate(),
            emptyMessage: 'No records found'.translate(),
            nextLabel: 'Next'.translate(),
            previousLabel: 'Previous'.translate(),
            tableContainerHeight: 374,
            customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
                return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
            },
            columns: [
                {
                    id: 'gridOutputButtonShow',
                    title: '',
                    dataType: 'button',
                    buttonLabel: 'Show ID'.translate(),
                    columnData: "out_doc_uid",
                    buttonStyle: {
                        cssClasses: [
                            'mafe-button-show'
                        ]
                    },
                    onButtonClick: function (row, grid) {
                        var data = row.getData();
                        showUID(data.out_doc_uid);
                    }
                },
                {
                    title: 'Title'.translate(),
                    dataType: 'string',
                    width: '392px',
                    alignment: "left",
                    columnData: "out_doc_title",
                    sortable: true,
                    alignmentCell: 'left'
                },
                {
                    title: 'Type'.translate(),
                    dataType: 'string',
                    width: '100px',
                    alignmentCell: 'left',
                    columnData: "out_doc_type",
                    sortable: true
                },
                {
                    id: 'gridOutputButtonEdit',
                    title: '',
                    dataType: 'button',
                    buttonStyle: {
                        cssClasses: [
                            'mafe-button-edit'
                        ]
                    },
                    buttonLabel: 'Edit'.translate(),
                    onButtonClick: function (row, grid) {
                        messageRequired.hide();
                        setDataRow(row);
                        openFormForEditInMainWindow();
                    }
                },
                {
                    id: 'gridOutputButtonProperties',
                    title: '',
                    dataType: 'button',
                    buttonStyle: {
                        cssClasses: [
                            'mafe-button-properties'
                        ]
                    },
                    buttonLabel: 'Open Editor'.translate(),
                    onButtonClick: function (row, grid) {
                        setDataRow(row);
                        openTinyInMainWindow(row);
                    }
                },
                {
                    id: 'gridOutputButtonDelete',
                    title: '',
                    dataType: 'button',
                    buttonStyle: {
                        cssClasses: [
                            'mafe-button-delete'
                        ]
                    },
                    buttonLabel: 'Delete'.translate(),
                    onButtonClick: function (row, grid) {
                        setDataRow(row);
                        deleteDataRow();
                    }
                }
            ]
        });

        /**
         * Filter data of header settings
         * @param {Array} data
         * @return {Array}
         */
        setDataHeaderSettings = function (data) {
            var headerData = {
                "logo": data.header_logo,
                "logoWidth": data.logo_width_header,
                "logoPositionX": data.position_x_logo_header,
                "logoPositionY": data.position_y_logo_header,
                "title": data.header_title,
                "titleFontSize": data.font_size_header,
                "titleFontPositionX": data.position_x_title_header,
                "titleFontPositionY": data.position_y_title_header,
                "pageNumber": headerSettings.getField('page_number_header').value === '["1"]',
                "pageNumberTitle": headerSettings.getField('pagination_title_header').value,
                "pageNumberTotal": headerSettings.getField('total_number_page_header').value === '["1"]',
                "pageNumberPositionX": headerSettings.getField('position_x_number_header').value,
                "pageNumberPositionY": headerSettings.getField('position_y_number_header').value,
                "enableHeader": data.enableHeader === '["1"]'
            };
            //it is necessary to clean the data because it is already in the json
            delete data.header_logo;
            delete data.logo_width_header;
            delete data.position_x_logo_header;
            delete data.position_y_logo_header;
            delete data.header_title;
            delete data.font_size_header;
            delete data.position_x_title_header;
            delete data.position_y_title_header;
            delete data.page_number_header;
            delete data.pagination_title_header;
            delete data.total_number_page_header;
            delete data.position_x_number_header;
            delete data.position_y_number_header;
            delete data.enableHeader;
            return headerData;
        };

        /**
         * Filter data of footer settings
         * @param {Array} data
         * @return {Array}
         */
         setDataFooterSettings = function (data) {
            var footerData = {
                "logo": data.footer_logo,
                "logoWidth": data.logo_width_footer,
                "logoPositionX": data.position_x_logo_footer,
                "logoPositionY": data.position_y_logo_footer,
                "title": data.footer_title,
                "titleFontSize": data.font_size_footer,
                "titleFontPositionX": data.position_x_title_footer,
                "titleFontPositionY": data.position_y_title_footer,
                "pageNumber": footerSettings.getField('page_number_footer').value === '["1"]',
                "pageNumberTitle": footerSettings.getField('pagination_title_footer').value,
                "pageNumberTotal": footerSettings.getField('total_number_page_footer').value === '["1"]',
                "pageNumberPositionX": footerSettings.getField('position_x_number_footer').value,
                "pageNumberPositionY": footerSettings.getField('position_y_number_footer').value,
                "enableFooter": data.enableFooter === '["1"]'
            };
            //it is necessary to clean the data because it is already in the json
            delete data.footer_logo;
            delete data.logo_width_footer;
            delete data.position_x_logo_footer;
            delete data.position_y_logo_footer;
            delete data.footer_title;
            delete data.font_size_footer;
            delete data.position_x_title_footer;
            delete data.position_y_title_footer;
            delete data.page_number_footer;
            delete data.pagination_title_footer;
            delete data.total_number_page_footer;
            delete data.position_x_number_footer;
            delete data.position_y_number_footer;
            delete data.enableFooter;
            return footerData;
        };

        btnSaveWindowOutputDoc = new PMUI.ui.Button({
            id: 'btnSaveWindowOutputDoc',
            text: "Save".translate(),
            handler: function () {
                var itemOutPut;
                if ((navigator.userAgent.indexOf("MSIE") != -1) || (navigator.userAgent.indexOf("Trident") != -1)) {
                    itemOutPut = getData2PMUI(formOutput.html);
                } else {
                    itemOutPut = formOutput.getData();
                }
                if (itemOutPut.out_doc_title != "" && itemOutPut.out_doc_filename != "") {

                    itemOutPut['out_doc_type'] = "HTML";

                    var items = jQuery.parseJSON(itemOutPut['out_doc_pdf_security_permissions']);
                    itemOutPut['out_doc_pdf_security_permissions'] = '';
                    for (var i = 0; i < items.length; i += 1) {
                        itemOutPut['out_doc_pdf_security_permissions'] += (i == 0) ? items[i] : '|' + items[i];
                    }

                    itemOutPut["out_doc_landscape"] = parseInt(itemOutPut["out_doc_landscape"]);
                    itemOutPut["out_doc_pdf_security_enabled"] = parseInt(itemOutPut["out_doc_pdf_security_enabled"]);
                    itemOutPut["out_doc_versioning"] = parseInt(itemOutPut["out_doc_versioning"]);
                    itemOutPut["out_doc_open_type"] = parseInt(getData2PMUI(formOutput.html).cboByGeneratedFile);
                    itemOutPut["out_doc_header"] = setDataHeaderSettings(itemOutPut);
                    itemOutPut["out_doc_footer"] = setDataFooterSettings(itemOutPut);
                    if (dataOutPutDocument != '' && dataOutPutDocument != undefined) {
                        itemOutPut['out_doc_uid'] = dataOutPutDocument.out_doc_uid;
                        restClient = new PMRestClient({
                            endpoint: "output-document/" + dataOutPutDocument.out_doc_uid,
                            typeRequest: 'update',
                            data: itemOutPut,
                            functionSuccess: function (xhr, response) {
                                dataOutPutDocument = itemOutPut;
                                refreshGridPanelInMainWindow();
                            },
                            functionFailure: function (xhr, response) {
                                PMDesigner.msgWinError(response.error.message);
                            },
                            messageError: "There are problems updating the OutputDocument, please try again.".translate(),
                            messageSuccess: 'Output Document edited successfully'.translate(),
                            flashContainer: gridOutput
                        });
                        restClient.executeRestClient();
                    } else {
                        if (1 === parseInt(itemOutPut.out_doc_pdf_security_enabled) && (itemOutPut.out_doc_pdf_security_open_password.trim() === "" || itemOutPut.out_doc_pdf_security_owner_password.trim() === "")) {
                            password.getItems()[0].getItems()[0].isValid();
                            password.getItems()[0].getItems()[1].isValid();
                            return false;
                        }
                        itemOutPut['out_doc_uid'] = '';
                        restClient = new PMRestClient({
                            endpoint: "output-document",
                            typeRequest: 'post',
                            data: itemOutPut,
                            functionSuccess: function (xhr, response) {
                                dataOutPutDocument = itemOutPut;
                                refreshGridPanelInMainWindow();
                            },
                            functionFailure: function (xhr, response) {
                                PMDesigner.msgWinError(response.error.message);
                            },
                            messageError: "There are problems saved the OutputDocument, please try again.".translate(),
                            messageSuccess: 'Output Document saved successfully'.translate(),
                            flashContainer: gridOutput
                        });
                        restClient.executeRestClient();
                    }
                    clearDataRow();
                } else {
                    formOutput.getField("out_doc_title").isValid();
                    formOutput.getField("out_doc_filename").isValid();
                }
            },
            buttonType: 'success'
        });

        btnCloseWindowOutputDoc = new PMUI.ui.Button({
            id: 'btnCloseWindowOutputDoc',
            text: "Cancel".translate(),
            handler: function () {
                clickedClose = false;
                isDirtyFormOutput();
            },
            buttonType: 'error'
        });

        newButtonOutput = new PMUI.ui.Button({
            id: 'outputButtonNew',
            text: 'Create'.translate(),
            height: "36px",
            width: 100,
            style: {
                cssClasses: [
                    'mafe-button-create'
                ]
            },
            handler: function () {
                clearDataRow();
                openFormInMainWindow();
            }
        });

        btnCancelTiny = new PMUI.ui.Button({
            id: 'btnCloseTiny',
            text: 'Cancel'.translate(),
            handler: function () {
                /*if (typeof dataOutPutDocument['externalType'] != 'undefined' && dataOutPutDocument['externalType']) {
                 winMainOutputDocument.close();
                 return;
                 }*/
                PMDesigner.hideAllTinyEditorControls();
                clickedClose = false;
                isDirtyFormOutput();
            },
            buttonType: 'error'
        });

        btnSaveTiny = new PMUI.ui.Button({
            id: 'btnSaveTiny',
            text: 'Save'.translate(),
            handler: function () {
                PMDesigner.hideAllTinyEditorControls();
                dataOutPutDocument['out_doc_template'] = tinyMCE.activeEditor.getContent();
                updateOutput(dataOutPutDocument);
                if (typeof dataOutPutDocument['externalType'] != 'undefined' && dataOutPutDocument['externalType']) {
                    winMainOutputDocument.close();
                    return;
                }
                clearDataRow();
                refreshGridPanelInMainWindow();
            },
            buttonType: 'success'
        });

        winMainOutputDocument = new PMUI.ui.Window({
            id: "winMainOutputDocument",
            title: "Output Documents".translate(),
            height: DEFAULT_WINDOW_HEIGHT,
            width: DEFAULT_WINDOW_WIDTH,
            buttonPanelPosition: "bottom",
            onBeforeClose: function () {
                PMDesigner.hideAllTinyEditorControls();
                clickedClose = true;
                isDirtyFormOutput();
            },
            footerItems: [
                btnCancelTiny,
                btnSaveTiny,
                btnCloseWindowOutputDoc,
                btnSaveWindowOutputDoc]
        });

        formOutput.addItem(editorTiny);
        formOutput.footer.setVisible(false);

        winMainOutputDocument.addItem(gridOutput);
        winMainOutputDocument.addItem(formOutput);

        refreshGridPanelInMainWindow();

        validateKeysField(docMargin.getField('out_doc_left_margin').getControls()[0].getHTML(), ['isbackspace', 'isnumber']);
        validateKeysField(docMargin.getField('out_doc_right_margin').getControls()[0].getHTML(), ['isbackspace', 'isnumber']);
        validateKeysField(docMargin.getField('out_doc_top_margin').getControls()[0].getHTML(), ['isbackspace', 'isnumber']);
        validateKeysField(docMargin.getField('out_doc_bottom_margin').getControls()[0].getHTML(), ['isbackspace', 'isnumber']);
        changeType();
        setMinValue();

        if (typeof listOutputDocs !== "undefined") {
            winMainOutputDocument.open();
            $('#gridOutput .pmui-textcontrol').css({'margin-top': '5px', width: '250px'});
            messageRequired = $(document.getElementById("requiredMessage"));
            applyStyleWindowForm(winMainOutputDocument);

            editorTiny.isInitialized = false;
            winMainOutputDocument.footer.html.style.textAlign = 'right';

            gridOutput.dom.toolbar.appendChild(newButtonOutput.getHTML());
            newButtonOutput.defineEvents();
            winMainOutputDocument.defineEvents();
            disableAllItems();
            winMainOutputDocument.getItems()[0].setVisible(true);
        }
    };

    PMDesigner.output.showTiny = function (uid) {
        getItemdOutput = function () {
            var restClientGet = new PMRestClient({
                endpoint: 'output-document/' + uid,
                typeRequest: 'get',
                functionSuccess: function (xhr, response) {
                    dataOutPutDocument = response;
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                },
                messageError: "There are problems getting the output documents, please try again.".translate()
            });
            restClientGet.executeRestClient();
        };
        getItemdOutput();
        dataOutPutDocument['externalType'] = true;
        openTinyInMainWindow(dataOutPutDocument);
    };

    PMDesigner.output.create = function () {
        openFormInMainWindow();
    };
}());

(
    function () {
        var iframeRT,
            reportWindow,
            pathSrc;

        PMDesigner.reporttable = function (event) {
            reportWindow = new PMUI.ui.Window({
                id: 'reportTableWindow',
                title: "Report Tables".translate(),
                width: DEFAULT_WINDOW_WIDTH,
                height: DEFAULT_WINDOW_HEIGHT
            });

            pathSrc = window.parent.location;

            iframeRT = new PMIframe({
                id: 'reporTableIframe',
                src: window.location.href.split("/")[0] + "//" + pathSrc.host + "/sys" + WORKSPACE + "/" + LANG + "/" + SKIN + "/pmTables?PRO_UID=" + PMDesigner.project.id + "&flagProcessmap=1",
                width: DEFAULT_WINDOW_WIDTH,
                height: DEFAULT_WINDOW_HEIGHT - 36,
                scrolling: 'no',
                frameborder: '0'
            });

            reportWindow.addItem(iframeRT);
            reportWindow.open();
            reportWindow.setBodyPadding(0);
        };

        PMDesigner.reporttable.create = function () {
            pathSrc = window.parent.location;
            reportWindow.clearItems();
            iframeRT = new PMIframe({
                id: 'reporTableIframe',
                src: window.location.href.split("/")[0] + "//" + pathSrc.host + "/sys" + WORKSPACE + "/" + LANG + "/" + SKIN + "/pmTables/edit?PRO_UID=" + PMDesigner.project.id + "&tableType=report&flagProcessmap=1",
                width: DEFAULT_WINDOW_WIDTH,
                height: DEFAULT_WINDOW_HEIGHT - 36,
                scrolling: 'no',
                frameborder: '0'
            });
            reportWindow.addItem(iframeRT);
            reportWindow.open();
            reportWindow.setBodyPadding(0);
        }

    }()
);
(function () {
    $ctrlSpaceMessage = $("<p class='ctrlSpaceMessage'>" + "Press".translate() + " <strong>ctrl+space</strong> " + "to get the function list".translate() + ".</p>");
    var btnCopyImport, openFormCustom, formEditTriggerCustom;
    var triggerOriginDataForUpdate = {};
    PMDesigner.trigger = function (event) {
        var listTriggers,
            clickedClose = true,
            message_ErrorWindow,
            flagChanges,
            isDirtyFormTrigger,
            gridListTrigger,
            getListTrigger,
            addNumberTriggerWizard,
            newTriggerOptions,
            allTreesItems,
            buttonNew,
            buttonCopy,
            buttonWizard,
            openListTriggers,
            openFormWizard,
            openFormWizardEditMode,
            openFormCopy,
            openFormEditCode,
            openNewTriggerOptions,
            listProjects,
            getListProjects,
            triggerSelectedData,
            formCopyTrigger,
            codMirr,
            formEditTriggerWizard,
            sepInputs,
            labelInputs,
            paramPanel,
            sepOutputs,
            labelOutputs,
            returnPanel,
            parameterItems,
            returnItems,
            treeNewTrigger,
            accordionNewTrigger,
            updateCustom,
            checkIfValuesAreEqual,
            buttonSave,
            copyButton,
            applyButton,
            btnEditSourceCode,
            buttonCancel,
            editCode,
            formEditCode,
            triggerWindow,
            formcustom;

        message_ErrorWindow = new PMUI.ui.MessageWindow({
            id: "errorMessage",
            width: 490,
            windowMessageType: 'error',
            title: "Triggers".translate(),
            message: "This is an error message.".translate(),
            footerItems: [
                {
                    text: 'OK'.translate(),
                    handler: function () {
                        message_ErrorWindow.close();
                    },
                    buttonType: "success"
                }
            ]
        });

        isDirtyFormTrigger = function () {
            $("input,select,textarea").blur();
            $("div.pmui-window-body.pmui-background").css('overflow', '');
            var formcustom = triggerWindow.getItems()[1];
            var formwizard = triggerWindow.getItems()[2];
            var formcopy = triggerWindow.getItems()[3];
            var formeditcode = triggerWindow.getItems()[4];
            var accordionnewtrigger = triggerWindow.getItems()[5];
            var flag = false;

            var finalData = {
                tri_uid: getData2PMUI(formEditTriggerCustom.html).uid,
                tri_title: getData2PMUI(formEditTriggerCustom.html).title,
                tri_description: getData2PMUI(formEditTriggerCustom.html).description,
                tri_webbot: getData2PMUI(formEditTriggerCustom.html).code
            };

            if (formcustom.isVisible() == true) {
                flag = (checkIfValuesAreEqual(triggerOriginDataForUpdate, finalData)) ? false : true;
            } else if (formwizard.isVisible() == true) {
                flag = formEditTriggerWizard.isDirty();
            } else if (formcopy.isVisible() == true) {
                flag = formCopyTrigger.isDirty();
                if (!flag) {
                    $('.ctrlSpaceMessage').remove();
                }
            } else if (formeditcode.isVisible() == true) {
                flag = formeditcode.isDirty();
                var flagFormCustom = (checkIfValuesAreEqual(triggerOriginDataForUpdate, finalData)) ? false : true;
                if (!flag && formeditcode.getItems()[0].controls[0].value != "" || flagFormCustom) {
                    flag = true;
                }
            }

            if (flag == true) {
                var message_window = new PMUI.ui.MessageWindow({
                    id: "cancelMessageTriggers",
                    title: "Triggers".translate(),
                    windowMessageType: 'warning',
                    width: 490,
                    message: 'Are you sure you want to discard your changes?'.translate(),
                    footerItems: [
                        {
                            text: "No".translate(),
                            handler: function () {
                                message_window.close();
                            },
                            buttonType: "error"
                        }, {
                            text: "Yes".translate(),
                            handler: function () {
                                triggerOriginDataForUpdate = {};
                                var formcustom = triggerWindow.getItems()[1];
                                var formwizard = triggerWindow.getItems()[2];
                                var formcopy = triggerWindow.getItems()[3];
                                var formeditcode = triggerWindow.getItems()[4];

                                if (formcustom.isVisible() == true) {
                                    var uidInForm = formEditTriggerCustom.getFields()[0].getValue();
                                    formEditTriggerCustom.reset();
                                    openListTriggers();
                                } else if (formwizard.isVisible() == true) {
                                    var uidInForm = formEditTriggerWizard.getFields()[0].getValue();
                                    formEditTriggerWizard.reset();
                                    openListTriggers();
                                } else if (formcopy.isVisible() == true) {
                                    $('.ctrlSpaceMessage').remove();
                                    formCopyTrigger.reset();
                                    openListTriggers();
                                } else if (formeditcode.isVisible() == true) {
                                    formeditcode.reset();
                                    openFormCustom("Edit".translate());
                                }
                                message_window.close();
                                if (clickedClose) {
                                    triggerWindow.close();
                                }
                            },
                            buttonType: "success"
                        }
                    ]
                });
                message_window.open();
                message_window.showFooter();
            } else {
                var formcustom = triggerWindow.getItems()[1];
                var formwizard = triggerWindow.getItems()[2];
                var formcopy = triggerWindow.getItems()[3];
                var formeditcode = triggerWindow.getItems()[4];
                var accordionnewtrigger = triggerWindow.getItems()[5];

                if (formcustom.isVisible() == true) {
                    var uidInForm = formEditTriggerCustom.getFields()[0].getValue();
                    formEditTriggerCustom.reset();
                    openListTriggers();
                } else if (formwizard.isVisible() == true) {
                    var uidInForm = formEditTriggerWizard.getFields()[0].getValue();
                    formEditTriggerWizard.reset();
                    openListTriggers();
                } else if (formcopy.isVisible() == true) {
                    formCopyTrigger.reset();
                    openListTriggers();
                } else if (formeditcode.isVisible() == true) {
                    formeditcode.reset();
                    openFormCustom("Edit".translate());
                } else if (accordionnewtrigger.isVisible() == true) {
                    openListTriggers();
                }
                if (clickedClose) {
                    triggerWindow.close();
                }
            }
        };
        //GRID List Case Scheduler
        gridListTrigger = new PMUI.grid.GridPanel({
            id: "listTriggers",
            pageSize: 10,
            width: "96%",
            style: {
                cssClasses: ["mafe-gridPanel"]
            },
            filterPlaceholder: 'Search ...'.translate(),
            nextLabel: 'Next'.translate(),
            previousLabel: 'Previous'.translate(),
            tableContainerHeight: 374,
            customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
                return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
            },
            columns: [
                {
                    id: 'listTriggersButtonShow',
                    title: '',
                    dataType: 'button',
                    searchable: false,
                    buttonLabel: 'Show ID'.translate(),
                    buttonStyle: {
                        cssClasses: [
                            'mafe-button-show'
                        ]
                    },
                    id: 'copyuid',
                    onButtonClick: function (row, grid) {
                        var dataRow = row.getData();
                        messageWindow = new PMUI.ui.MessageWindow({
                            id: 'dynaformMessageWindowUID',
                            windowMessageType: 'info',
                            width: 490,
                            title: "Triggers".translate(),
                            message: dataRow.tri_uid,
                            footerItems: [
                                {
                                    text: 'OK'.translate(),
                                    handler: function () {
                                        messageWindow.close();
                                    },
                                    buttonType: "success"
                                }
                            ]
                        });
                        messageWindow.setTitle("ID".translate());
                        messageWindow.open();
                        messageWindow.showFooter();
                        $(messageWindow.dom.icon).removeClass();
                    }
                },
                {
                    title: 'Title'.translate(),
                    id: "title",
                    dataType: 'string',
                    width: "407px",
                    visible: true,
                    columnData: "tri_title",
                    searcheable: true,
                    sortable: true,
                    alignmentCell: 'left'
                },
                {
                    title: 'Type'.translate(),
                    id: "type",
                    dataType: 'string',
                    width: "225px",
                    visible: true,
                    alignmentCell: 'left',
                    columnData: function (data) {
                        if (typeof(data.tri_param) == "object" && typeof(data.tri_param.params) == "object") {
                            var wizardData = data.tri_param.params;

                            return wizardData.LIBRARY_NAME;
                        } else {
                            return "Custom";
                        }
                    },
                    searcheable: true,
                    sortable: true
                },
                {
                    id: "editButton",
                    title: '',
                    dataType: 'button',
                    messageTooltip: "Edit".translate(),
                    buttonLabel: "Edit".translate(),
                    buttonStyle: {
                        cssClasses: [
                            'mafe-button-edit'
                        ]
                    },
                    onButtonClick: function (row, grid) {
                        triggerOriginDataForUpdate = {};
                        var data = row.getData();
                        triggerOriginDataForUpdate = data;
                        var fields;
                        if (data.tri_param == "") {

                            openFormCustom("Edit".translate());
                            formEditTriggerCustom.showFooter();

                            var codeMirrorControl, cmControlCopyTrigger;
                            codeMirrorControl = formEditTriggerCustom.getItems()[1].controls[0].cm;

                            if (codeMirrorControl != undefined) {
                                codeMirrorControl.setSize(580, 160);
                            }
                            formEditTriggerCustom.getItems()[1].setHeight(170);
                            fields = formEditTriggerCustom.getItems()[0];
                            fields.getItems()[0].setValue(data.tri_uid);
                            fields.getItems()[1].setValue(data.tri_title);
                            fields.getItems()[2].setValue(data.tri_description);
                            formEditTriggerCustom.getItems()[1].setValue(data.tri_webbot);

                            codeMirrorControl.setValue(data.tri_webbot);
                            codeMirrorControl.refresh();
                        } else {
                            openFormWizardEditMode();

                            var wizardData = data.tri_param.params;

                            var lib = wizardData.LIBRARY_CLASS;
                            lib = lib.split('.');
                            lib = lib[1];
                            fields = formEditTriggerWizard.getFields();
                            fields[0].setValue(data.tri_uid);
                            fields[1].setValue(lib);
                            fields[2].setValue(wizardData.PMFUNTION_NAME);
                            fields[3].setValue(wizardData.LIBRARY_NAME + " (" + wizardData.PMFUNTION_LABEL + ")");
                            fields[4].setValue(data.tri_title);
                            fields[5].setValue(data.tri_description);
                            fields[6].setValue(data.tri_webbot);

                            var inputItems = [];
                            var outputItems = [];

                            restClient = new PMRestClient({
                                endpoint: "trigger-wizard/" + lib + "/" + wizardData.PMFUNTION_NAME,
                                typeRequest: 'get',
                                functionSuccess: function (xhr, response) {
                                    if (response.fn_params.input != undefined) {
                                        for (j = 0; j < response.fn_params.input.length; j++) {
                                            var nameInput = response.fn_params.input[j].name;

                                            var inp = new CriteriaField({
                                                id: nameInput,
                                                pmType: "text",
                                                name: nameInput,
                                                label: response.fn_params.input[j].label,
                                                value: wizardData[nameInput].toString(),
                                                controlsWidth: 400,
                                                labelWidth: '27%',
                                                helper: response.fn_params.input[j].description,
                                                required: response.fn_params.input[j].name === "unpauseDate" ? true : response.fn_params.input[j].required
                                            });

                                            inputItems.push(inp);
                                        }
                                    }
                                    if (response.fn_params.output != undefined) {
                                        for (k = 0; k < response.fn_params.output.length; k++) {
                                            var nameOutput = response.fn_params.output[k].name;

                                            var out = new CriteriaField({
                                                id: nameOutput,
                                                pmType: "text",
                                                name: nameOutput,
                                                label: response.fn_params.output[k].label,
                                                value: wizardData.TRI_ANSWER.toString(),
                                                controlsWidth: 400,
                                                labelWidth: '27%',
                                                helper: response.fn_params.output[k].description,
                                                required: response.fn_params.output[k].required
                                            });

                                            outputItems.push(out);
                                        }
                                    }
                                    triggerWindow.setTitle("Edit".translate() + " " + wizardData.LIBRARY_NAME);
                                    if (inputItems.length > 0) {
                                        formEditTriggerWizard.getItems()[1].setVisible(true);
                                        formEditTriggerWizard.getItems()[2].setVisible(true);
                                        formEditTriggerWizard.getItems()[3].setVisible(true);
                                        formEditTriggerWizard.getItems()[3].clearItems();
                                        formEditTriggerWizard.getItems()[3].setItems(inputItems);
                                    } else {
                                        formEditTriggerWizard.getItems()[1].setVisible(false);
                                        formEditTriggerWizard.getItems()[2].setVisible(false);
                                        formEditTriggerWizard.getItems()[3].setVisible(false);
                                        formEditTriggerWizard.getItems()[3].clearItems();
                                    }
                                    if (outputItems.length > 0) {
                                        formEditTriggerWizard.getItems()[4].setVisible(true);
                                        formEditTriggerWizard.getItems()[5].setVisible(true);
                                        formEditTriggerWizard.getItems()[6].setVisible(true);
                                        formEditTriggerWizard.getItems()[6].clearItems();
                                        formEditTriggerWizard.getItems()[6].setItems(outputItems);
                                    } else {
                                        formEditTriggerWizard.getItems()[4].setVisible(false);
                                        formEditTriggerWizard.getItems()[5].setVisible(false);
                                        formEditTriggerWizard.getItems()[6].setVisible(false);
                                        formEditTriggerWizard.getItems()[6].clearItems();
                                    }
                                    labelInputs.dom.labelTextContainer.style.display = "none";
                                    labelOutputs.dom.labelTextContainer.style.display = "none";
                                },
                                functionFailure: function (xhr, response) {
                                    PMDesigner.msgWinError(response.error.message);
                                },
                                messageError: "There are problems getting the triggers wizard list, please try again.".translate()
                            });
                            restClient.executeRestClient();
                        }
                    }
                },
                {
                    id: "deleteButton",
                    title: '',
                    dataType: 'button',
                    messageTooltip: "Delete".translate(),
                    buttonLabel: "Delete".translate(),
                    buttonStyle: {
                        cssClasses: [
                            'mafe-button-delete'
                        ]
                    },
                    onButtonClick: function (row, grid) {
                        var questionWindow, dataRow = row.getData();

                        questionWindow = new PMUI.ui.MessageWindow({
                            id: "questionDeleteWindow",
                            title: "Triggers".translate(),
                            windowMessageType: 'warning',
                            width: 490,
                            message: "Do you want to delete this Trigger?".translate(),
                            footerItems: [
                                {
                                    id: 'questionDeleteWindowButtonNo',
                                    text: "No".translate(),
                                    visible: true,
                                    handler: function () {
                                        questionWindow.close();
                                    },
                                    buttonType: "error"
                                }, {
                                    id: 'questionDeleteWindowButtonYes',
                                    text: "Yes".translate(),
                                    visible: true,
                                    handler: function () {
                                        questionWindow.close();
                                        restClient = new PMRestClient({
                                            endpoint: "trigger/" + dataRow.tri_uid,
                                            typeRequest: 'remove',
                                            functionSuccess: function (xhr, response) {
                                                grid.removeItem(row);
                                            },
                                            functionFailure: function (xhr, response) {
                                                PMDesigner.msgWinError(response.error.message);
                                            },
                                            messageSuccess: "Trigger deleted successfully".translate(),
                                            flashContainer: gridListTrigger
                                        });
                                        restClient.executeRestClient();
                                    },
                                    buttonType: "success"
                                },
                            ]
                        });

                        questionWindow.open();
                        questionWindow.dom.titleContainer.style.height = "17px";
                        questionWindow.showFooter();
                    }
                }
            ],
            dataItems: listTriggers,
            onRowClick: function (row, data) {

            }
        });

        getListTrigger = function () {
            var restClient = new PMRestClient({
                endpoint: 'triggers',
                typeRequest: 'get',
                functionSuccess: function (xhr, response) {
                    listTriggers = response;
                    gridListTrigger.setDataItems(listTriggers);
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                },
                messageError: "There are problems getting the Triggers list, please try again.".translate()
            });
            restClient.executeRestClient();
        };

        addNumberTriggerWizard = function (value, position) {
            coutTriggers = document.createElement("span");
            coutTriggers.textContent = value;
            headerAccordion = triggerWindow.getItems()[5].getItems()[position].header.html;
            headerAccordion.appendChild(coutTriggers);
            coutTriggers.style.float = "right";
            coutTriggers.style.color = "white";
            coutTriggers.style.fontSize = "13px";
            coutTriggers.style.marginRight = "6px";
            coutTriggers.style.marginTop = "3px";
        };

        newTriggerOptions = function () {

            var acItemCustom = accordionNewTrigger.getItems()[0];
            var acItemCopy = accordionNewTrigger.getItems()[1];
            acItemCustom.setIconClosed('pmcustomtrigger');
            acItemCustom.setIconExpanded('pmcustomtrigger');
            acItemCopy.setIconClosed('pmcopytrigger');
            acItemCopy.setIconExpanded('pmcopytrigger');

            var itemCustom = jQuery("#custom > .pmui-accordion-item-header > span")[1];
            itemCustom.style.width = "0px";
            var itemCopy = jQuery("#copy > .pmui-accordion-item-header > span")[1];
            itemCopy.style.width = "0px";

            var numOfItems = accordionNewTrigger.getItems();

            var lengthLibFunctions = [];
            if (numOfItems.length <= 2) {
                restClient = new PMRestClient({
                    endpoint: "trigger-wizards",
                    typeRequest: 'get',
                    functionSuccess: function (xhr, response) {
                        for (i = 0; i < response.length; i++) {
                            var functTree = response[i].lib_functions;
                            lengthLibFunctions[i] = functTree.length;
                            var newTreeItems = [];
                            for (h = 0; h < functTree.length; h++) {
                                newTreeItems.push({
                                    label: functTree[h].fn_label,
                                    id: functTree[h].fn_name,
                                    onClick: function () {
                                        for (s = 0; s < response.length; s++) {
                                            for (p = 0; p < response[s].lib_functions.length; p++) {
                                                if (response[s].lib_functions[p].fn_name == this.id) {
                                                    var fn = response[s].lib_functions[p].fn_params;
                                                    var fnLabel = response[s].lib_functions[p].fn_label;
                                                    var fnName = response[s].lib_functions[p].fn_name;
                                                    var libName = response[s].lib_name;
                                                    var libTitle = response[s].lib_title;
                                                    p = response[s].lib_functions.length;
                                                    s = response.length - 1;
                                                }
                                            }
                                        }
                                        var inputItems = [];
                                        if (fn.input != undefined) {
                                            for (j = 0; j < fn.input.length; j++) {
                                                var val = "";
                                                if (fn.input[j].type == "array") {
                                                    val = "array('')";
                                                }
                                                var inp = new CriteriaField({
                                                    id: fn.input[j].name,
                                                    pmType: "text",
                                                    name: fn.input[j].name,
                                                    label: fn.input[j].label,
                                                    controlsWidth: 400,
                                                    labelWidth: '27%',
                                                    value: val,
                                                    helper: fn.input[j].description,
                                                    required: fn.input[j].name === "unpauseDate" ? true : fn.input[j].required
                                                });

                                                inputItems.push(inp);
                                            }
                                        }

                                        var outputItems = [];
                                        if (fn.output != undefined) {
                                            for (k = 0; k < fn.output.length; k++) {
                                                var out = new CriteriaField({
                                                    id: fn.output[k].name,
                                                    pmType: "text",
                                                    name: fn.output[k].name,
                                                    label: fn.output[k].label,
                                                    controlsWidth: 400,
                                                    labelWidth: '27%',
                                                    helper: fn.output[k].description,
                                                    required: fn.output[k].required
                                                });

                                                outputItems.push(out);
                                            }
                                        }
                                        formEditTriggerWizard.reset();
                                        formEditTriggerWizard.setTitle(fnLabel + ' (' + fnName + ')');
                                        formEditTriggerWizard.getItems()[0].getItems()[1].setValue(libName);
                                        formEditTriggerWizard.getItems()[0].getItems()[2].setValue(fnName);
                                        formEditTriggerWizard.getItems()[0].getItems()[3].setValue(libTitle + ' (' + fnLabel + ')');

                                        if (inputItems.length > 0) {
                                            formEditTriggerWizard.getItems()[1].setVisible(true);
                                            formEditTriggerWizard.getItems()[2].setVisible(true);
                                            formEditTriggerWizard.getItems()[3].setVisible(true);
                                            formEditTriggerWizard.getItems()[3].clearItems();
                                            formEditTriggerWizard.getItems()[3].setItems(inputItems);
                                        } else {
                                            formEditTriggerWizard.getItems()[1].setVisible(false);
                                            formEditTriggerWizard.getItems()[2].setVisible(false);
                                            formEditTriggerWizard.getItems()[3].setVisible(false);
                                            formEditTriggerWizard.getItems()[3].clearItems();
                                        }
                                        if (outputItems.length > 0) {
                                            formEditTriggerWizard.getItems()[4].setVisible(true);
                                            formEditTriggerWizard.getItems()[5].setVisible(true);
                                            formEditTriggerWizard.getItems()[6].setVisible(true);
                                            formEditTriggerWizard.getItems()[6].clearItems();
                                            formEditTriggerWizard.getItems()[6].setItems(outputItems);
                                        } else {
                                            formEditTriggerWizard.getItems()[4].setVisible(false);
                                            formEditTriggerWizard.getItems()[5].setVisible(false);
                                            formEditTriggerWizard.getItems()[6].setVisible(false);
                                            formEditTriggerWizard.getItems()[6].clearItems();
                                        }
                                        openFormWizard();
                                        $('.pmui-pmseparatorlinefield .pmui-field-label').css({display: 'none'});
                                        triggerWindow.setTitle("Create".translate() + " " + libTitle);
                                    }
                                });
                            }
                            labelInputs.dom.labelTextContainer.style.display = "none";
                            labelOutputs.dom.labelTextContainer.style.display = "none";
                            allTreesItems[i] = newTreeItems;

                            var arrayTriggerWizards = [
                                "pmFunctions",
                                "pmSugar",
                                "pmTalend",
                                "pmTrAlfresco",
                                "pmTrSharepoint"
                            ];

                            if (arrayTriggerWizards.indexOf(response[i].lib_name) == -1) {
                                $("head").append("<style type=\"text/css\">." + response[i].lib_name + " {background: url(" + response[i].lib_icon + ") no-repeat 50% 50%;}</style>");
                            }

                            //Accordion
                            var newAccordionItem = [];
                            newAccordionItem = {
                                iconClass: response[i].lib_name,
                                id: response[i].lib_name,
                                title: response[i].lib_title,
                                height: 'auto'
                            };
                            accordionNewTrigger.addItem(newAccordionItem);
                        }
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    },
                    messageError: "There are problems getting the triggers wizard list, please try again.".translate()
                });
                restClient.executeRestClient();
            }

            openNewTriggerOptions();
            var accordion = triggerWindow.getItems()[5].getItems();
            accordion[0].setVisible(false);
            accordion[1].setVisible(false);
            for (i = 0; i < accordion.length; i++) {
                accordion[i].header.html.style.backgroundColor = "#FFFFFF";
                if (i > 1) {
                    addNumberTriggerWizard(lengthLibFunctions[i - 2], (i));
                }
            }
        };

        allTreesItems = [];
        getListTrigger();

        //Button NEW Trigger
        buttonNew = new PMUI.ui.Button({
            id: 'newTriggerButton',
            text: "Create".translate(),
            height: "36px",
            width: 100,
            handler: function (event) {
                openFormCustom("Create".translate());
                formEditTriggerCustom.getField("code").dom.labelTextContainer.style.width = "23.8%";
                $('.pmui-pmseparatorlinefield .pmui-field-label').css({display: 'none'});
                formEditTriggerCustom.showFooter();

                var codeMirrorControl, cmControlCopyTrigger;
                codeMirrorControl = formEditTriggerCustom.getItems()[1].controls[0].cm;
                formEditTriggerCustom.getItems()[1].html.style.padding = "10px";
                if (codeMirrorControl !== undefined) {
                    codeMirrorControl.setSize(580, 150);
                }
                formEditTriggerCustom.getItems()[1].setHeight(170);

                formEditTriggerCustom.reset();

                fields = formEditTriggerCustom.getItems()[0];
                fields.getItems()[0].setValue("");
                fields.getItems()[1].setValue("");
                fields.getItems()[2].setValue("");
                formEditTriggerCustom.getItems()[1].setValue("");
                formEditTriggerCustom.getItems()[1].controls[0].cm.setValue("");
                codeMirrorControl.refresh();
            },
            style: {
                cssClasses: [
                    'mafe-button-create'
                ]
            }
        });

        //Button COPY Trigger
        buttonCopy = new PMUI.ui.Button({
            id: 'copyTriggerButton',
            text: "Copy".translate(),
            height: "36px",
            width: 100,
            handler: function (event) {
                getListProjects();
                openFormCopy();
                formCopyTrigger.getFields()[1].clearOptions();
                $("#Code .CodeMirror.cm-s-default.CodeMirror-wrap").css({'border': '1px solid #c0c0c0'});

                var cmControlCopyTrigger = formCopyTrigger.getFields()[4].controls[0].cm;
                cmControlCopyTrigger.setValue("");
                if (cmControlCopyTrigger !== undefined) {
                    cmControlCopyTrigger.setSize(650, 140);
                }
                cmControlCopyTrigger.refresh();
            },
            style: {
                cssClasses: [
                    'mafe-button-create'
                ]
            }
        });

        //Button WIZARD Trigger
        buttonWizard = new PMUI.ui.Button({
            id: 'wizardTriggerButton',
            text: "Wizard".translate(),
            height: "36px",
            width: 100,
            handler: function (event) {
                newTriggerOptions();
            },
            style: {
                cssClasses: [
                    'mafe-button-create'
                ]
            }
        });

        openListTriggers = function () {
            triggerWindow.getItems()[0].setVisible(true);
            triggerWindow.getItems()[1].setVisible(false);
            triggerWindow.getItems()[2].setVisible(false);
            triggerWindow.getItems()[3].setVisible(false);
            triggerWindow.getItems()[4].setVisible(false);
            triggerWindow.getItems()[5].setVisible(false);
            triggerWindow.setTitle("Triggers".translate());
            triggerWindow.hideFooter();
            $(triggerWindow.body).removeClass("pmui-background");
        };

        openFormCustom = function (typeTitle) {
            triggerWindow.getItems()[0].setVisible(false);
            triggerWindow.getItems()[1].setVisible(true);
            triggerWindow.getItems()[2].setVisible(false);
            triggerWindow.getItems()[3].setVisible(false);
            triggerWindow.getItems()[4].setVisible(false);
            triggerWindow.getItems()[5].setVisible(false);
            triggerWindow.footer.getItems()[0].setVisible(true); //button Cancel
            triggerWindow.footer.getItems()[1].setVisible(true); //button Save
            triggerWindow.footer.getItems()[2].setVisible(false); //Edit Source Code
            triggerWindow.footer.getItems()[3].setVisible(false); //button Aply
            triggerWindow.footer.getItems()[4].setVisible(false); //button Copy/import

            triggerWindow.setTitle(typeTitle + " Custom Trigger".translate());
            triggerWindow.showFooter();
            $(triggerWindow.body).addClass("pmui-background");
            formEditTriggerCustom.setFocus();

            $('.CodeMirror.cm-s-default')[0].style.border = "1px solid #c0c0c0";
            if (formEditTriggerCustom.getItems()[0].getItems()[1].visible == false) {
                var fields = formEditTriggerCustom.getItems()[0];
                fields.getItems()[1].setVisible(true);
                fields.getItems()[2].setVisible(true);
                formEditTriggerCustom.footer.getItems()[1].setVisible(true);
            }
            formEditTriggerCustom.getFields()[4].dom.labelTextContainer.style.marginLeft = "10px";
            formEditTriggerCustom.setAlignmentButtons('left');

            $("#code").after($ctrlSpaceMessage.css({
                "margin": "5px 0 0 0",
                "text-align": "center",
                "width": "655px",
                "padding": ""
            }));
        };

        openFormWizard = function () {
            triggerWindow.getItems()[0].setVisible(false);
            triggerWindow.getItems()[1].setVisible(false);
            triggerWindow.getItems()[2].setVisible(true);
            triggerWindow.getItems()[3].setVisible(false);
            triggerWindow.getItems()[4].setVisible(false);
            triggerWindow.getItems()[5].setVisible(false);
            triggerWindow.footer.getItems()[0].setVisible(true); //button Cancel
            triggerWindow.footer.getItems()[1].setVisible(true); //button Save
            triggerWindow.footer.getItems()[2].setVisible(false); //Edit Source Code
            triggerWindow.footer.getItems()[3].setVisible(false); //button Aply
            triggerWindow.footer.getItems()[4].setVisible(false); //button Copy/import
            triggerWindow.showFooter();
            $(triggerWindow.body).addClass("pmui-background");
            formEditTriggerWizard.setFocus();
        };

        openFormWizardEditMode = function () {
            triggerWindow.getItems()[0].setVisible(false);
            triggerWindow.getItems()[1].setVisible(false);
            triggerWindow.getItems()[2].setVisible(true);
            triggerWindow.getItems()[3].setVisible(false);
            triggerWindow.getItems()[4].setVisible(false);
            triggerWindow.getItems()[5].setVisible(false);
            triggerWindow.footer.getItems()[0].setVisible(true); //button Cancel
            triggerWindow.footer.getItems()[1].setVisible(true); //button Save
            triggerWindow.footer.getItems()[2].setVisible(true); //Edit Source Code
            triggerWindow.footer.getItems()[3].setVisible(false); //button Aply
            triggerWindow.footer.getItems()[4].setVisible(false); //button Copy/import
            triggerWindow.showFooter();
            $(triggerWindow.body).addClass("pmui-background");
            formEditTriggerWizard.setFocus();
        };


        openFormCopy = function () {
            triggerWindow.getItems()[0].setVisible(false);
            triggerWindow.getItems()[1].setVisible(false);
            triggerWindow.getItems()[2].setVisible(false);
            triggerWindow.getItems()[3].setVisible(true);
            triggerWindow.getItems()[4].setVisible(false);
            triggerWindow.getItems()[5].setVisible(false);
            triggerWindow.setTitle("Copy Trigger".translate());
            triggerWindow.footer.getItems()[0].setVisible(true); //button Cancel
            triggerWindow.footer.getItems()[1].setVisible(false); //button Save
            triggerWindow.footer.getItems()[2].setVisible(false); //Edit Source Code
            triggerWindow.footer.getItems()[3].setVisible(false); //button Aply
            triggerWindow.footer.getItems()[4].setVisible(true); //button Copy/import
            triggerWindow.showFooter();
            $(triggerWindow.body).addClass("pmui-background");
            formCopyTrigger.setFocus();

            $(".pmui-field").css("float", "left");
            $('#Code .pmui.pmui-pmcodemirrorcontrol').append($ctrlSpaceMessage.css({
                "margin-top": "10px",
                "text-align": "",
                "width": "655px",
                "padding": ""
            }));
        };

        openFormEditCode = function () {
            triggerWindow.getItems()[0].setVisible(false);
            triggerWindow.getItems()[1].setVisible(false);
            triggerWindow.getItems()[2].setVisible(false);
            triggerWindow.getItems()[3].setVisible(false);
            triggerWindow.getItems()[4].setVisible(true);
            triggerWindow.getItems()[5].setVisible(false);
            triggerWindow.setTitle("Editor".translate());
            triggerWindow.footer.getItems()[0].setVisible(true); //button Cancel
            triggerWindow.footer.getItems()[1].setVisible(false); //button Save
            triggerWindow.footer.getItems()[2].setVisible(false); //Edit Source Code
            triggerWindow.footer.getItems()[3].setVisible(true); //button Aply
            triggerWindow.footer.getItems()[4].setVisible(false); //button Copy/import
            triggerWindow.showFooter();
            $(triggerWindow.body).addClass("pmui-background");

            $($('#codeEditor .CodeMirror.cm-s-default.CodeMirror-wrap')[0]).after($ctrlSpaceMessage.css({
                "padding": "5px 0 0 10px",
                "text-align": ""
            }));

        };

        openNewTriggerOptions = function () {
            triggerWindow.getItems()[0].setVisible(false);
            triggerWindow.getItems()[1].setVisible(false);
            triggerWindow.getItems()[2].setVisible(false);
            triggerWindow.getItems()[3].setVisible(false);
            triggerWindow.getItems()[4].setVisible(false);
            triggerWindow.getItems()[5].setVisible(true);
            triggerWindow.setTitle("Create Predefined Trigger".translate());
            triggerWindow.footer.getItems()[0].setVisible(true); //button Cancel
            triggerWindow.footer.getItems()[1].setVisible(false); //button Save
            triggerWindow.footer.getItems()[2].setVisible(false); //Edit Source Code
            triggerWindow.footer.getItems()[3].setVisible(false); //button Aply
            triggerWindow.footer.getItems()[4].setVisible(false); //button Copy/import
            triggerWindow.showFooter();
            $(triggerWindow.body).removeClass("pmui-background");
        };

        //obtaning the list of process to show it on the copy/import of a trigger dropdown
        listProjects = [];
        getListProjects = function () {
            restClient = new PMRestClient({
                typeRequest: 'get',
                functionSuccess: function (xhr, response) {
                    listProjects = [];
                    formCopyTrigger.getFields()[0].clearOptions();
                    listProjects[0] = {
                        label: "- Select a process -".translate(),
                        value: "",
                        disabled: true,
                        selected: true
                    };
                    for (i = 0; i < response.length; i++) {
                        listProjects.push({
                            label: response[i].prj_name,
                            value: response[i].prj_uid
                        });
                    }
                    formCopyTrigger.getFields()[0].setOptions(listProjects);
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                },
                messageError: "There are problems getting the list of projects.".translate()
            });
            restClient.setBaseEndPoint('projects');
            restClient.executeRestClient();
        };

        //form Copy/Import Trigger
        triggerSelectedData;
        formCopyTrigger = new PMUI.form.Form({
            id: "formCopyTriggers",
            border: true,
            width: '890px',
            height: 'auto',
            title: "",
            visibleHeader: false,
            items: [
                {
                    pmType: 'dropdown',
                    id: "processField",
                    label: 'Process'.translate(),
                    name: 'prj_uid',
                    required: true,
                    controlsWidth: 300,
                    value: "",
                    options: listProjects,
                    onChange: function (newValue, prevValue) {
                        var formFields = formCopyTrigger.getFields();
                        formFields[2].setValue("");
                        formFields[3].setValue("");
                        formFields[4].controls[0].cm.setValue("");

                        var uidProj = newValue, myForm = this.form;

                        //obtaning the Trigger list of the process SELECTED
                        restClient = new PMRestClient({
                            typeRequest: 'get',
                            functionSuccess: function (xhr, response) {
                                triggerSelectedData = response;
                                var listTriggersCopy = [];
                                listTriggersCopy[0] = {
                                    label: "- Select a trigger -".translate(),
                                    value: "",
                                    disabled: true,
                                    selected: true
                                };

                                for (i = 0; i < response.length; i++) {
                                    listTriggersCopy.push({
                                        label: response[i].tri_title,
                                        value: response[i].tri_uid
                                    });
                                }
                                formFields[1].setOptions(listTriggersCopy);
                            },
                            functionFailure: function (xhr, response) {
                                PMDesigner.msgWinError(response.error.message);
                            },
                            messageError: "There are problems getting the list of triggers.".translate()
                        });
                        restClient.setBaseEndPoint("project/" + uidProj + "/triggers");
                        restClient.executeRestClient();
                    }
                },
                {
                    pmType: 'dropdown',
                    id: "triggerField",
                    label: 'Trigger'.translate(),
                    name: 'tri_uid',
                    required: true,
                    controlsWidth: 300,
                    value: "",
                    options: [],
                    onChange: function (newValue, prevValue) {
                        var formFields = formCopyTrigger.getFields();
                        formFields[2].setValue("");
                        formFields[3].setValue("");
                        formFields[4].controls[0].cm.setValue("");

                        var uidTri = newValue;

                        for (i = 0; i < triggerSelectedData.length; i++) {
                            if (triggerSelectedData[i].tri_uid == uidTri) {
                                formFields[2].setValue(triggerSelectedData[i].tri_title);
                                formFields[3].setValue(triggerSelectedData[i].tri_description);
                                formFields[4].controls[0].cm.setValue(triggerSelectedData[i].tri_webbot);
                            }
                        }
                    }
                },
                {
                    pmType: "text",
                    id: "triTitle",
                    label: "Title of the new trigger".translate(),
                    placeholder: "Insert the title of the new trigger".translate(),
                    name: "tri_title",
                    required: true,
                    valueType: 'string',
                    controlsWidth: 300,
                    style: {
                        cssProperties: {
                            'float': 'left'
                        }
                    }
                },
                {
                    pmType: "textarea",
                    id: "triDescription",
                    rows: 80,
                    name: 'tri_description',
                    label: "Description of the new trigger".translate(),
                    valueType: 'string',
                    controlsWidth: 652,
                    style: {
                        cssClasses: ['mafe-textarea-resize'],
                        cssProperties: {'float': 'left'}
                    }
                }
            ]
        });
        codMirr = new PMCodeMirrorField();
        codMirr.setLabel("Code".translate());
        codMirr.setID("Code");
        codMirr.setName('tri_webbot');
        CodeMirror.commands.autocomplete = function (cm) {
            CodeMirror.showHint(cm, CodeMirror.phpHint);
        };
        formCopyTrigger.addItem(codMirr);

        //Form to Edit the trigger with the WIZARD (we used a Form because we need buttons to save the changes)
        formEditTriggerWizard = new PMUI.form.Form({
            id: "formEditTriggerWizard",
            border: true,
            visibleHeader: false,
            name: "formwizard",
            width: 926,
            title: "New Trigger".translate(),
            items: [
                {
                    id: "panelDetailsWizard",
                    pmType: "panel",
                    layout: 'vbox',
                    fieldset: false,
                    height: 'auto',
                    legend: "DETAILS".translate(),
                    items: [
                        {
                            id: "uid",
                            pmType: "text",
                            label: "ID".translate(),
                            value: "",
                            name: "uid",
                            visible: false,
                            valueType: 'string'
                        },
                        {
                            id: "libName",
                            pmType: "text",
                            label: "",
                            value: "",
                            name: "libName",
                            visible: false,
                            valueType: 'string'
                        },
                        {
                            id: "fnName",
                            pmType: "text",
                            label: "",
                            value: "",
                            name: "fnName",
                            visible: false,
                            valueType: 'string'
                        },
                        {
                            id: "type",
                            pmType: "text",
                            label: "Type".translate(),
                            value: "WIZARD",
                            controlsWidth: 450,
                            labelWidth: '27%',
                            readOnly: true,
                            name: "type",
                            valueType: 'string'
                        },
                        {
                            id: "titleDetailsWiz",
                            pmType: "text",
                            label: "Title".translate(),
                            value: "",
                            controlsWidth: 450,
                            labelWidth: '27%',
                            name: "titleDetails",
                            valueType: 'string',
                            required: true
                        },
                        {
                            id: "descriptionDetailsWiz",
                            pmType: "textarea",
                            label: "Description".translate(),
                            value: "",
                            rows: 90,
                            width: '300px',
                            controlsWidth: 450,
                            labelWidth: '27%',
                            name: "descriptionDetails",
                            valueType: 'string',
                            style: {cssClasses: ['mafe-textarea-resize']}
                        },
                        {
                            id: "webbot",
                            pmType: "text",
                            label: "Webbot".translate(),
                            value: "",
                            name: "webbot",
                            controlsWidth: 300,
                            labelWidth: '27%',
                            visible: false,
                            valueType: 'string'
                        }
                    ]
                }
            ]
        });

        //adding a separator Inputs line
        sepInputs = new PMSeparatorLineField({
            controlHeight: '1px',
            controlColor: "#CDCDCD",
            controlsWidth: "890px",
            marginLeft: '0%'
        });
        formEditTriggerWizard.addItem(sepInputs);

        //adding a label Inputs
        labelInputs = new PMLabelField({
            text: "PARAMETERS".translate(),
            textMode: "plain",
            style: {
                cssProperties: {
                    color: "#AEAEAE",
                    'font-weight': 'bold'
                }
            },
            controlsWidth: 885
        });
        formEditTriggerWizard.addItem(labelInputs);

        //adding the Inputs Items (PARAMETERS) Panel
        paramPanel = new PMUI.form.FormPanel({
            id: "panelParametersWizard",
            layout: 'vbox',
            fieldset: false,
            height: 'auto',
            visible: false,
            legend: "__PARAMETERS__".translate(),
            items: []
        });
        formEditTriggerWizard.addItem(paramPanel);

        //adding a separator Outputs line
        sepOutputs = new PMSeparatorLineField({
            controlHeight: 1,
            controlColor: "#CDCDCD",
            controlsWidth: "890px",
            marginLeft: '0%'
        });
        formEditTriggerWizard.addItem(sepOutputs);

        //adding a label Outputs
        labelOutputs = new PMLabelField({
            text: "RETURN VALUE".translate(),
            textMode: "plain",
            style: {
                cssProperties: {
                    color: "#AEAEAE",
                    'font-weight': 'bold'
                }
            },
            controlsWidth: 885
        });

        formEditTriggerWizard.addItem(labelOutputs);

        //adding the Output Items (Returns) Panel
        returnPanel = new PMUI.form.FormPanel({
            id: "panelReturnValWizard",
            layout: 'vbox',
            fieldset: false,
            height: 'auto',
            visible: false,
            legend: "RETURN VALUE".translate(),
            items: []
        });
        formEditTriggerWizard.addItem(returnPanel);


        //treePanel New Trigger PMF
        treeNewTrigger = new PMUI.panel.TreePanel({
            id: "treeNewTrigger",
            width: 'auto',
            height: 'auto',
            style: {
                cssClasses: ['pmtrigger'],
                cssProperties: {
                    'margin-top': '5px',
                    'margin-bottom': '4px'
                }
            },
            items: []
        });

        //Acordion Panel New Triggers Options
        accordionNewTrigger = new PMUI.panel.AccordionPanel({
            id: "accordionNewTrigger",
            width: 885,
            height: 'auto',
            borderWidth: "0px",
            hiddenTitle: true,
            style: {
                cssProperties: {
                    'margin-left': '30px'
                }
            },
            items: [
                {
                    iconClass: "",
                    id: 'custom',
                    title: "Custom Trigger".translate(),
                    height: '26px',
                    body: "",
                    style: {
                        cssProperties: {
                            "background-color": "#FDFDFD"
                        }
                    }
                },
                {
                    iconClass: "",
                    id: 'copy',
                    title: "Copy Trigger".translate(),
                    height: '26px',
                    body: "",
                    style: {
                        cssProperties: {
                            "background-color": "#FDFDFD"
                        }
                    }
                }
            ],
            listeners: {
                select: function (obj, event) {

                    if (obj.id == "custom") {
                        openFormCustom("Create".translate());
                        $('.pmui-pmseparatorlinefield .pmui-field-label').css({display: 'none'});
                        formEditTriggerCustom.showFooter();

                        var codeMirrorControl, cmControlCopyTrigger;
                        codeMirrorControl = formEditTriggerCustom.getItems()[1].controls[0].cm;

                        if (codeMirrorControl != undefined) {
                            codeMirrorControl.setSize(580, 150); //CodeMirror Size
                        }
                        formEditTriggerCustom.getItems()[1].setHeight(170);

                        formEditTriggerCustom.reset();

                        fields = formEditTriggerCustom.getItems()[0];
                        fields.getItems()[0].setValue("");
                        fields.getItems()[1].setValue("");
                        fields.getItems()[2].setValue("");
                        formEditTriggerCustom.getItems()[1].setValue("");
                        formEditTriggerCustom.getItems()[1].controls[0].cm.setValue("");

                    } else if (obj.id == "copy") {

                        getListProjects();
                        openFormCopy();
                        $($('#Code .CodeMirror.cm-s-default.CodeMirror-wrap')[1]).css({'border': '1px solid #c0c0c0'});
                        $('.pmui-pmseparatorlinefield .pmui-field-label').css({display: 'none'});

                        var cmControlCopyTrigger = formCopyTrigger.getFields()[4].controls[0].cm;
                        if (cmControlCopyTrigger != undefined) {
                            cmControlCopyTrigger.setSize(650, 140); //CodeMirror in formCopyTrigger Size
                        }
                    } else {
                        if (treeNewTrigger.getItems() == "" || treeNewTrigger.id != obj.id) {
                            var acItems = accordionNewTrigger.getItems();
                            var accordionActualItem = accordionNewTrigger.getItem(obj.id);

                            for (i = 0; i < acItems.length; i++) {
                                if (acItems[i].id == accordionActualItem.id) {
                                    var positionActualAcItem = i - 2;
                                }
                            }

                            treeNewTrigger.setItems(allTreesItems[positionActualAcItem]);
                            treeNewTrigger.setID(obj.id);

                            accordionActualItem.setBody(treeNewTrigger);
                            treeNewTrigger.defineEvents();
                            applyStyleTreePanel(treeNewTrigger, false);
                        }
                    }
                }
            }
        });

        updateCustom = function (dataToSend, triggerUid) {
            var restClient = new PMRestClient({
                endpoint: "trigger/" + triggerUid,
                typeRequest: 'update',
                data: dataToSend,
                functionSuccess: function (xhr, response) {
                    formEditTriggerCustom.setDirty(false);
                    getListTrigger();
                    openListTriggers();
                    $('.pmui-pmseparatorlinefield .pmui-field-label').css({display: 'none'});
                },
                functionFailure: function (xhr, response) {
                    var msg = response.error.message;
                    var arrayMatch = [];

                    if ((arrayMatch = /^[\w\s]+\:\s*(.*)$/i.exec(msg))) {
                        msg = arrayMatch[1];
                    }

                    PMDesigner.msgWinError(msg);
                },
                messageSuccess: "Trigger updated successfully".translate(),
                flashContainer: gridListTrigger
            });
            restClient.executeRestClient();
        };

        checkIfValuesAreEqual = function (initialData, finalData) {
            if (typeof(initialData['tri_uid']) == "undefined" && finalData['tri_uid'] == '') {
                if (finalData['tri_description'] != '' || finalData['tri_title'] != '' || finalData['tri_webbot'] != '') {
                    return false;
                }
            }
            for (var key1 in initialData) {
                for (var key2 in finalData) {
                    if (typeof(initialData[key1]) != "undefined" &&
                        typeof(finalData[key2]) != "undefined" &&
                        key1 == key2 &&
                        initialData[key1] != finalData[key2]
                    ) {
                        return false;
                    }
                }
            }
            return true;
        }

        //Buttons Save and Cancel for the 'formEditTriggerCustom' and 'formEditTriggerWizard'
        buttonSave = new PMUI.ui.Button({
            id: 'saveTriggerButton',
            text: "Save".translate(),
            handler: function (event) {
                triggerOriginDataForUpdate = {};
                var formcustom = triggerWindow.getItems()[1],
                    formwizard = triggerWindow.getItems()[2],
                    formTriggerData,
                    restClient,
                    dataToSend,
                    formTriggerData,
                    inputFields,
                    message_window,
                    outputV,
                    outputFields,
                    inputV,
                    dataToSend,
                    flagEdit;
                if (formcustom.isVisible() == true) {
                    valuesCustom=formcustom.getItems()[0];
                    if (!formEditTriggerCustom.isValid( )) {
                        flagEdit = formEditTriggerCustom.visible;
                    } else {
                        flagEdit = formEditTriggerCustom.isValid();
                    }
                    if (flagEdit) {
                        if ((navigator.userAgent.indexOf("MSIE") != -1) || (navigator.userAgent.indexOf("Trident") != -1)) {
                            formTriggerData = getData2PMUI(formEditTriggerCustom.html);
                        } else {
                            formTriggerData = formEditTriggerCustom.getData();
                        }
                        if (formTriggerData.title.trim() === "") {
                            return false;
                        }
                        dataToSend = {
                            tri_title: formTriggerData.title,
                            tri_description: formTriggerData.description,
                            tri_webbot: formTriggerData.code,
                            tri_param: ""
                        };

                        if (formTriggerData.uid === "") {
                            restClient = new PMRestClient({
                                endpoint: 'trigger',
                                typeRequest: 'post',
                                data: dataToSend,
                                functionSuccess: function (xhr, response) {
                                    formEditTriggerCustom.setDirty(false);
                                    getListTrigger();
                                    formEditTriggerCustom.getItems()[0].getItems()[0].setValue(response.tri_uid);
                                    openListTriggers();
                                    $('.pmui-pmseparatorlinefield .pmui-field-label').css({display: 'none'});
                                },
                                functionFailure: function (xhr, response) {
                                    var msg = response.error.message,
                                        arrayMatch = [];

                                    if ((arrayMatch = /^[\w\s]+\:\s*(.*)$/i.exec(msg))) {
                                        msg = arrayMatch[1];
                                    }

                                    PMDesigner.msgWinError(msg);
                                },
                                messageSuccess: "New Trigger saved successfully".translate(),
                                flashContainer: gridListTrigger
                            });
                            restClient.executeRestClient();
                        } else {
                            if (formTriggerData.wizzard != "") {
                                if (formEditTriggerCustom.isDirty()) {
                                    message_window = new PMUI.ui.MessageWindow({
                                        id: "wizzardToCustomTriggerWin",
                                        windowMessageType: 'warning',
                                        title: 'Triggers'.translate(),
                                        width: 490,
                                        message: 'Do you want to save the changes? This Trigger will be saved like a custom Trigger.'.translate(),
                                        footerItems: [
                                            {
                                                text: "No".translate(),
                                                handler: function () {
                                                    message_window.close();
                                                    openFormWizardEditMode();
                                                    $('.pmui-pmseparatorlinefield .pmui-field-label').css({display: 'none'});
                                                    formEditTriggerCustom.reset();
                                                },
                                                buttonType: "error"
                                            },
                                            {
                                                text: "Yes".translate(),
                                                handler: function () {
                                                    message_window.close();
                                                    updateCustom(dataToSend, formTriggerData.uid);
                                                },
                                                buttonType: "success"
                                            }
                                        ]

                                    });
                                    message_window.open();
                                    message_window.showFooter();
                                } else {
                                    openListTriggers();
                                    $('.pmui-pmseparatorlinefield .pmui-field-label').css({display: 'none'});
                                }
                            } else {
                                updateCustom(dataToSend, formTriggerData.uid);
                            }
                        }
                    }
                } else if (formwizard.isVisible() == true) {
                    if (formEditTriggerWizard.isValid() || ((navigator.userAgent.indexOf("MSIE") != -1) || (navigator.userAgent.indexOf("Trident") != -1))) {
                        if ((navigator.userAgent.indexOf("MSIE") != -1) || (navigator.userAgent.indexOf("Trident") != -1)) {
                            formTriggerData = getData2PMUI(formEditTriggerWizard.html);
                        } else {
                            formTriggerData = formEditTriggerWizard.getData();
                        }

                        inputV = {};
                        inputFields = formEditTriggerWizard.getItems()[3];
                        for (i = 0; i < inputFields.getItems().length; i++) {
                            inputV[inputFields.getItems()[i].name] = formTriggerData[inputFields.getItems()[i].name];
                        }

                        outputV = {};
                        outputFields = formEditTriggerWizard.getItems()[6];
                        for (j = 0; j < outputFields.getItems().length; j++) {
                            outputV[outputFields.getItems()[j].name] = formTriggerData[outputFields.getItems()[j].name];
                        }

                        dataToSend = {
                            tri_title: formTriggerData.titleDetails,
                            tri_description: formTriggerData.descriptionDetails,
                            tri_type: "SCRIPT",
                            tri_params: {
                                input: inputV,
                                output: outputV
                            }
                        };

                        if (formTriggerData.uid === "") {
                            restClient = new PMRestClient({
                                endpoint: 'trigger-wizard/' + formTriggerData.libName + '/' + formTriggerData.fnName,
                                typeRequest: 'post',
                                data: dataToSend,
                                functionSuccess: function (xhr, response) {
                                    formEditTriggerWizard.setDirty(false);
                                    getListTrigger();
                                    formEditTriggerWizard.getFields()[0].setValue(response.tri_uid);
                                    openListTriggers();
                                },
                                functionFailure: function (xhr, response) {
                                    PMDesigner.msgWinError(response.error.message);
                                },
                                messageSuccess: "New Trigger saved successfully".translate(),
                                flashContainer: gridListTrigger
                            });
                            restClient.executeRestClient();
                        } else {
                            restClient = new PMRestClient({
                                endpoint: 'trigger-wizard/' + formTriggerData.libName + '/' + formTriggerData.fnName + '/' + formTriggerData.uid,
                                typeRequest: 'update',
                                data: dataToSend,
                                functionSuccess: function (xhr, response) {
                                    formEditTriggerWizard.setDirty(false);
                                    getListTrigger();
                                    openListTriggers();
                                },
                                functionFailure: function (xhr, response) {
                                    PMDesigner.msgWinError(response.error.message);
                                },
                                messageSuccess: "Trigger updated successfully".translate(),
                                flashContainer: gridListTrigger
                            });
                            restClient.executeRestClient();
                        }
                    }
                }
            },
            buttonType: 'success'
        });

        //Buttons Save and Cancel for the 'formEditTriggerCustom' and 'formEditTriggerWizard'
        copyButton = new PMUI.ui.Button({
            id: 'copyAndSaveButton',
            text: "Copy Trigger".translate(),
            buttonType: 'success',
            handler: function (event) {
                var data,
                    dataToSend,
                    restClient;
                if (formCopyTrigger.isValid()) {
                    formCopyTrigger.setDirty(false);

                    data = formCopyTrigger.getData();
                    dataToSend = {
                        tri_title: getData2PMUI(formCopyTrigger.html).tri_title.trim(),
                        tri_description: data.tri_description,
                        tri_webbot: data.tri_webbot,
                        tri_param: ""
                    };
                    restClient = new PMRestClient({
                        endpoint: 'trigger',
                        typeRequest: 'post',
                        data: dataToSend,
                        functionSuccess: function (xhr, response) {
                            getListTrigger();
                            formCopyTrigger.reset();
                            openListTriggers();
                        },
                        functionFailure: function (xhr, response) {
                            PMDesigner.msgWinError(response.error.message);
                        },
                        messageSuccess: "New Trigger saved successfully".translate(),
                        flashContainer: gridListTrigger
                    });
                    restClient.executeRestClient();
                }
            }
        });

        //Buttons Apply for the Code Editor
        applyButton = new PMUI.ui.Button({
            id: 'applyButton',
            text: "Apply".translate(),
            buttonType: 'success',
            handler: function (event) {

                $("div.pmui-window-body.pmui-background").css('overflow', '');
                if (formEditCode.isValid()) {
                    var typeTitle = "Edit".translate();
                    if (triggerWindow.getItems()[1].getData().uid == "") {
                        typeTitle = "Create".translate();
                    }
                    openFormCustom(typeTitle);
                    formEditCode.setDirty(false);

                    var editCode = formEditCode.getItems()[0].controls[0].cm;
                    editCodeValue = editCode.getValue();

                    var codeMirrorCustom = formEditTriggerCustom.getItems()[1].controls[0].cm;

                    if (codeMirrorCustom != undefined) {
                        codeMirrorCustom.setSize(580, 150); //CodeMirror Size
                    }
                    codeMirrorCustom.setValue(editCodeValue);
                    formEditTriggerCustom.getItems()[1].setValue(editCodeValue);

                    editCode.setValue("");
                    editCodeValue = "";

                    formEditCode.getItems()[0].setValue("");
                }
            }
        });

        //Button Edit Source Code (this is showed just when the Trigger Wizzard is opened in edition mode)
        btnEditSourceCode = new PMUI.ui.Button({
            id: 'btnEditSourceCode',
            text: "Edit Source Code".translate(),
            buttonType: 'success',
            handler: function (event) {

                var data = formEditTriggerWizard.getData();
                var fields;
                openFormCustom("Edit".translate());
                formEditTriggerCustom.showFooter();

                var codeMirrorControl;
                codeMirrorControl = formEditTriggerCustom.getItems()[1].controls[0].cm;

                if (codeMirrorControl !== undefined) {
                    codeMirrorControl.setSize(650, 280);
                }
                formEditTriggerCustom.getItems()[1].setHeight(170);

                fields = formEditTriggerCustom.getItems()[0];
                fields.getItems()[0].setValue(data.uid);
                fields.getItems()[1].setValue(data.titleDetails);
                fields.getItems()[2].setValue(data.descriptionDetails);
                fields.getItems()[3].setValue(data.webbot);
                fields.getItems()[3].setVisible(false);
                formEditTriggerCustom.getItems()[1].setValue(data.webbot);

                codeMirrorControl.setValue(data.webbot);

                formEditTriggerCustom.getItems()[1].setHeight(300);
                fields.getItems()[1].setVisible(false);
                fields.getItems()[2].setVisible(false);
                formEditTriggerCustom.footer.getItems()[1].setVisible(false);
                formEditTriggerCustom.footer.getItems()[0].style.addProperties({'margin-right': "10px"});
                formEditTriggerCustom.footer.style.addProperties({width: '880px'});
                codeMirrorControl.refresh();
            }
        });

        buttonCancel = new PMUI.ui.Button({
            id: 'cancelTriggerButton',
            text: "Cancel".translate(),
            buttonType: 'error',
            handler: function (event) {
                clickedClose = false;
                isDirtyFormTrigger();
            }
        });

        //Form to Edit the Custom trigger (we used a Form because we need buttons to save the changes)
        formEditTriggerCustom = new PMUI.form.Form({
            id: "formEditTriggerCustom",
            border: true,
            visibleHeader: false,
            width: '900px',
            height: "365px",
            name: "formcustom",
            title: "Custom Trigger".translate(),
            items: [
                {
                    id: "panelDetailsCustom",
                    pmType: "panel",
                    layout: 'vbox',
                    fieldset: false,
                    height: '380px',
                    legend: "DETAILS".translate(),
                    items: [
                        {
                            id: "uid",
                            pmType: "text",
                            label: "ID".translate(),
                            value: "",
                            name: "uid",
                            readOnly: true,
                            visible: false,
                            valueType: 'string'
                        },
                        {
                            id: "title",
                            pmType: "text",
                            label: "Title".translate(),
                            value: "",
                            required: true,
                            name: "title",
                            valueType: 'string'
                        },
                        {
                            id: "description",
                            pmType: "textarea",
                            rows: 70,
                            name: 'description',
                            label: "Description".translate(),
                            valueType: 'string',
                            style: {cssClasses: ['mafe-textarea-resize']}
                        },
                        {
                            id: "wizzard",
                            pmType: "text",
                            label: "Wizzard".translate(),
                            value: "",
                            name: "wizzard",
                            readOnly: true,
                            visible: false,
                            valueType: 'string'
                        }
                    ]
                }
            ],
            buttons: [
                {
                    text: "@@",
                    id: "selectPickerButton",
                    handler: function () {
                        var picker = new VariablePicker();
                        picker.open({
                            success: function (variable) {
                                var codeMirror = formEditTriggerCustom.getItems()[1].controls[0].cm;
                                var cursorPos = codeMirror.getCursor();
                                codeMirror.replaceSelection(variable);
                                codeMirror.setCursor(cursorPos.line, cursorPos.ch);
                            }
                        });
                    },
                    style: {
                        cssProperties: {
                            "margin-left": '208px',
                            "background": "rgb(30, 145, 209)",
                            "border": "0px solid rgb(30, 145, 209)"
                        }
                    }
                }, {
                    text: "Open Editor".translate(),
                    id: "openEditorButton",
                    handler: function () {
                        openFormEditCode();
                        formEditCode.showFooter();

                        var codeMirrorCustom = formEditTriggerCustom.getItems()[1].controls[0].cm;
                        var value = codeMirrorCustom.getValue();

                        var codeMirrorCopy = formEditCode.getItems()[0].controls[0].cm;

                        if (codeMirrorCopy != undefined) {
                            codeMirrorCopy.setSize(810, 315); //CodeMirror Size
                            $($('#codeEditor .CodeMirror.cm-s-default.CodeMirror-wrap')[0]).css({'border': '1px solid #c0c0c0'});

                        }

                        formEditCode.getItems()[0].setHeight(325);

                        codeMirrorCopy.setValue(value);
                        formEditCode.getItems()[0].setValue(value);
                        formEditCode.setDirty(false);

                        $('#codeEditor .pmui-field-label').hide();
                        $('#formEditCode').children().last().css({
                            'margin-top': '-352px',
                            'border': '0px',
                            'margin-right': '-15px'
                        }).find('a').css('padding', '10px 5px');
                        $($('#codeEditor .CodeMirror.cm-s-default.CodeMirror-wrap')[0]).css({
                            'margin-left': '10px',
                            'width': '850px',
                            'height': '360px'
                        });
                        $('#triggerWindow .pmui-window-body').css('overflow', 'hidden');
                        codeMirrorCopy.refresh();
                    },
                    style: {
                        cssProperties: {
                            "margin-right": 2
                        }
                    }
                }
            ]
        });
        cd = new PMCodeMirrorField({
            labelWidth: '23.8%'
        });
        cd.setLabel("Code".translate());
        formEditTriggerCustom.addItem(cd);
        formEditTriggerCustom.getItems()[1].setName('code');
        formEditTriggerCustom.getItems()[1].setID('code');

        formEditCode = new PMUI.form.Form({
            id: "formEditCode",
            border: true,
            visibleHeader: false,
            width: '925px',
            name: "formeditcode",
            title: "Editor".translate(),
            items: [],
            buttons: [
                {
                    text: "@@",
                    id: "selectPickerButton",
                    handler: function () {
                        var picker = new VariablePicker();
                        picker.open({
                            success: function (variable) {
                                var codeMirror = formEditCode.getItems()[0].controls[0].cm;
                                var cursorPos = codeMirror.getCursor();
                                codeMirror.replaceSelection(variable);
                                codeMirror.setCursor(cursorPos.line, cursorPos.ch);
                            }
                        });
                    },
                    style: {
                        cssProperties: {
                            "margin-left": '229px',
                            "background": "rgb(45, 62, 80)",
                            "border": "1px solid rgb(45, 62, 80)"
                        }
                    }
                }
            ]
        });
        editCode = new PMCodeMirrorField({
            labelWidth: '9%'
        });
        editCode.setLabel("Code".translate());
        formEditCode.addItem(editCode);
        formEditCode.getItems()[0].setName('codeEditor');
        formEditCode.getItems()[0].setID('codeEditor');

        //Trigger Window
        triggerWindow = new PMUI.ui.Window({
            id: "triggerWindow",
            title: "Triggers".translate(),
            width: DEFAULT_WINDOW_WIDTH,
            height: DEFAULT_WINDOW_HEIGHT,
            footerItems: [
                buttonCancel,
                buttonSave,
                btnEditSourceCode,
                applyButton,
                copyButton
            ],
            buttonPanelPosition: "bottom",
            footerAling: "right",
            onBeforeClose: function () {
                clickedClose = true;
                isDirtyFormTrigger();
            }
        });


        triggerWindow.addItem(gridListTrigger);
        triggerWindow.addItem(formEditTriggerCustom);
        triggerWindow.addItem(formEditTriggerWizard);
        triggerWindow.addItem(formCopyTrigger);
        triggerWindow.addItem(formEditCode);
        triggerWindow.addItem(accordionNewTrigger);

        if (typeof listTriggers !== "undefined") {
            triggerWindow.open();
            codMirr.dom.labelTextContainer.style.width = "23.8%";
            codMirr.html.style.padding = "10px";
            formEditTriggerCustom.panel.html.style.padding = "10px 10px";
            $('#listTriggers .pmui-textcontrol').css({'margin-top': '5px', width: '250px'});
            applyStyleWindowForm(triggerWindow);
            triggerWindow.hideFooter();
            openListTriggers();

            gridListTrigger.dom.toolbar.appendChild(buttonNew.getHTML());
            buttonNew.defineEvents();

            gridListTrigger.dom.toolbar.appendChild(buttonCopy.getHTML());
            buttonCopy.defineEvents();

            gridListTrigger.dom.toolbar.appendChild(buttonWizard.getHTML());
            buttonWizard.defineEvents();

            triggerWindow.defineEvents();
            gridListTrigger.sort('tri_title', 'asc');
            formEditTriggerCustom.panel.style.addProperties({'overflow': 'hidden'});

            formcustom = triggerWindow.getItems()[1];
            valuesCustom=formcustom.getItems()[0];
        }
    };

    PMDesigner.trigger.create = function () {
        var codeMirrorControl, cmControlCopyTrigger;
        openFormCustom("Create".translate());
        formEditTriggerCustom.getField("code").dom.labelTextContainer.style.width = "23.8%";
        $('.pmui-pmseparatorlinefield .pmui-field-label').css({display: 'none'});
        formEditTriggerCustom.showFooter();

        codeMirrorControl = formEditTriggerCustom.getItems()[1].controls[0].cm;
        formEditTriggerCustom.getItems()[1].html.style.padding = "10px";
        if (codeMirrorControl !== undefined) {
            codeMirrorControl.setSize(580, 150);
            codeMirrorControl.refresh();
        }
        codeMirrorControl.refresh();
        formEditTriggerCustom.getItems()[1].setHeight(170);
        formEditTriggerCustom.reset();
    };
}());
/**
 * new stepTask module
 */
var stepsTask = function (activity) {
    this.mainWindow = null;
    this.secondaryWindow = null;
    this.stepsAssignTree = null;
    this.mainContainer = null;
    this.labelsPanel = null;
    this.stepsMainContainer = null;
    this.stepsAssignAccordion = null;
    this.confirmWindow = null;
    this.elementAccordionOpen = null;
    this.groupType = null;
    this.groupLabel = null;
    this.stepsType = null;
    this.stepsAssigned = null;
    stepsTask.prototype.initialize.call(this, activity);
};
/**
 * initialization method steps settings, constants are created.
 * components are created
 * the data is loaded
 * styles are customized
 * It extends behaviors and events
 */
stepsTask.prototype.initialize = function () {
    this.groupType = ['DYNAFORM', 'INPUT_DOCUMENT', 'OUTPUT_DOCUMENT', 'EXTERNAL'];
    this.groupLabel = ['Dynaform (s)'.translate(), 'Input Document (s)'.translate(), 'OutPut Document (s)'.translate(), 'External (s)'.translate()];
    this.stepsType = {
        "DYNAFORM": "Dynaform".translate(),
        "INPUT_DOCUMENT": "Input Document".translate(),
        "OUTPUT_DOCUMENT": "Output Document".translate(),
        "EXTERNAL": "External".translate()
    };
    this.stepsAssigned = new PMUI.util.ArrayList();
    this.elementAccordionOpen = new PMUI.util.ArrayList();
    this.createWidgets();
    this.mainWindow.addItem(this.mainContainer);
    this.mainWindow.open();
    this.loadInitialData();
    this.customStyles();
    this.elementsAccordionOpenFixed();
    this.addEventSortableInAccordionElements();
    this.addEventSortableInTreePanelElements();
};
/**
 * the components are created PMUI UI
 * - confirmWindow
 * - mainWindow
 * - labelsPanel
 * - stepsAssignTree
 * - stepsAssignAccordion
 * - stepsMainContainer
 * - mainContainer
 * - secondaryWindow
 */
stepsTask.prototype.createWidgets = function () {
    var that = this;
    this.confirmWindow = new PMUI.ui.MessageWindow({
        id: 'confirmWindowDeleteAcceptedValue',
        windowMessageType: 'warning',
        width: 490,
        bodyHeight: 'auto',
        title: '',
        message: '',
        footerItems: [
            {
                id: 'confirmWindow-footer-no',
                text: 'No'.translate(),
                visible: true,
                buttonType: "error"
            }, {
                id: 'confirmWindow-footer-yes',
                text: 'Yes'.translate(),
                visible: true,
                buttonType: "success"
            }
        ],
        visibleFooter: true
    });
    this.mainWindow = new PMUI.ui.Window({
        id: "stepsForTask",
        title: "Steps for task".translate(),
        height: DEFAULT_WINDOW_HEIGHT,
        width: DEFAULT_WINDOW_WIDTH
    });
    this.labelsPanel = new PMUI.core.Panel({
        width: DEFAULT_WINDOW_WIDTH,
        proportion: 0.08,
        layout: "hbox",
        items: [
            new PMUI.field.TextAnnotationField({
                text: 'Available Elements'.translate(),
                text_Align: 'center',
                proportion: 1.4
            }),
            new PMUI.field.TextAnnotationField({
                text: 'Assigned Elements (Drop here)'.translate(),
                text_Align: 'center',
                proportion: 1.3
            }),
            new PMUI.ui.Button({
                buttonType: "link",
                "text": "Expand all".translate(),
                id: "expand-button",
                proportion: 0.6,
                handler: function () {
                    var items, i, item, buttonAfected;
                    items = that.stepsAssignAccordion.getItems();
                    buttonAfected = that.labelsPanel.getItem("collapse-button");
                    buttonAfected.setDisabled(false);
                    this.setDisabled(true);
                    that.elementAccordionOpen.clear();
                    for (i = 0; i < items.length; i += 1) {
                        item = items[i];
                        item.expand();
                        that.elementAccordionOpen.insert(item);
                    }
                }
            }),
            new PMUI.ui.Button({
                buttonType: "link",
                "text": "Collapse all".translate(),
                id: "collapse-button",
                proportion: 0.7,
                disabled: true,
                handler: function () {
                    var items, i, item, buttonAfected;
                    buttonAfected = that.labelsPanel.getItem("expand-button");
                    buttonAfected.setDisabled(false);
                    this.setDisabled(true);
                    items = that.stepsAssignAccordion.getItems();
                    for (i = 0; i < items.length; i += 1) {
                        item = items[i];
                        that.elementAccordionOpen.remove(item);
                        item.collapse();
                    }
                }
            })
        ]
    });
    this.stepsAssignTree = new PMUI.panel.TreePanel({
        id: 'stepsAssignTree',
        proportion: 0.5,
        height: 475,
        filterable: true,
        autoBind: true,
        filterPlaceholder: 'Search ...'.translate(),
        emptyMessage: 'No records found'.translate(),
        nodeDefaultSettings: {
            labelDataBind: 'obj_title',
            autoBind: true,
            collapsed: false,
            itemsDataBind: 'items',
            childrenDefaultSettings: {
                labelDataBind: 'obj_title',
                autoBind: true
            },
            behavior: 'drag'
        },
        style: {
            cssProperties: {
                margin: '0px 0px 0px 0px',
                float: 'left',
                overflow: 'auto'
            },
            cssClasses: ['mafe-border-panel']
        }
    });
    this.stepsAssignAccordion = new PMUI.panel.AccordionPanel({
        id: 'stepsAssignAccordion',
        multipleSelection: true,
        hiddenTitle: true,
        proportion: 1.5,
        style: {
            cssProperties: {
                margin: '0px 0px 0px 0px'
            },
            cssClasses: ['mafe-border-panel']
        },
        listeners: {
            select: function (accordionItem, event) {
                var buttonExpand, buttonCollapse, itemsAccod;
                itemsAccod = that.stepsAssignAccordion.items;
                if (accordionItem.collapsed) {
                    if (that.elementAccordionOpen.indexOf(accordionItem) > -1) {
                        that.elementAccordionOpen.remove(accordionItem);
                    }
                } else {
                    if (that.elementAccordionOpen.indexOf(accordionItem) === -1) {
                        that.elementAccordionOpen.insert(accordionItem);
                    }
                }
                buttonCollapse = that.labelsPanel.getItem("collapse-button");
                buttonExpand = that.labelsPanel.getItem("expand-button");
                if (that.elementAccordionOpen.getSize() === 0) {
                    buttonExpand.setDisabled(false);
                    buttonCollapse.setDisabled(true);
                } else if (that.elementAccordionOpen.getSize() === itemsAccod.getSize()) {
                    buttonExpand.setDisabled(true);
                    buttonCollapse.setDisabled(false);
                } else {
                    buttonExpand.setDisabled(false);
                    buttonCollapse.setDisabled(false);
                }
            }
        }
    });
    this.stepsMainContainer = new PMUI.core.Panel({
        id: "stepsMainContainer",
        width: DEFAULT_WINDOW_WIDTH,
        height: DEFAULT_WINDOW_HEIGHT - 45,
        layout: 'hbox',
        height: 475,
        items: [
            this.stepsAssignTree,
            this.stepsAssignAccordion
        ]
    });
    this.mainContainer = new PMUI.core.Panel({
        id: "mainContainer",
        width: DEFAULT_WINDOW_WIDTH,
        height: DEFAULT_WINDOW_HEIGHT - 45,
        layout: 'vbox',
        items: [
            this.labelsPanel,
            this.stepsMainContainer
        ]
    });
    this.secondaryWindow = new PMUI.ui.Window({
        visibleFooter: true,
        title: 'Trigger'.translate(),
        footerAlign: 'right',
        footerItems: [
            {
                text: "@@",
                id: "secondaryWindow-criteria",
                handler: function () {
                },
                style: {
                    cssProperties: {
                        "background": "rgb(45, 62, 80)",
                        "border": "1px solid rgb(45, 62, 80)"
                    },
                    cssClasses: ["mafe-button-condition-trigger"]
                }
            }, {
                id: 'secondaryWindow-cancel',
                text: 'Cancel'.translate(),
                buttonType: 'error',
                height: 31,
                style: {
                    cssClasses: ["mafe-button-condition-trigger"]
                },
                handler: function () {
                }
            }, {
                id: 'secondaryWindow-save',
                text: 'Save'.translate(),
                buttonType: 'success',
                height: 31,
                style: {
                    cssClasses: ["mafe-button-condition-trigger"]
                },
                handler: function () {
                }
            }
        ]
    });
};
/**
 * This method loads the initial data module steps,for the components:
 * - stepsAssignTree
 * - stepsAssignAccordion
 */
stepsTask.prototype.loadInitialData = function () {
    this.loadTreePanelData(this.getTreePanelData());
    this.loadAccordionItems(this.getAccordionData());
};
/**
 * This method loads the options to stepsAssignAccordion
 * @param {Array} response An array where each element can be a {Element} object or a JSON object
 */
stepsTask.prototype.loadAccordionItems = function (response) {
    var firstResp = [],
        secondResp = [],
        i,
        item,
        assigmentConfig,
        firstRes = 0,
        secondRes = 1;
    if (jQuery.isArray(response) && response.length) {
        if (typeof response[firstRes] === "object") {
            firstResp = response[firstRes].response ? response[firstRes].response : [];
        }
        if (typeof response[secondRes] === "object") {
            secondResp = response[secondRes].response ? response[secondRes].response : [];
        }
    }
    if (firstResp.length) {
        for (i = 0; i < firstResp.length; i += 1) {
            item = this.createAccordionItem(firstResp[i], true, true);
            this.stepsAssignAccordion.addItem(item);
            item.dataItem = firstResp[i];
            this.customAccordionItemButtons(item.html, firstResp[i], item);
        }
    }
    assigmentConfig = {
        step_type_obj: "Assignment".translate(),
        triggers: secondResp,
        st_type: "ASSIGNMENT",
        obj_title: "Assignment".translate(),
        step_uid_obj: "Assignment"
    };
    item = this.createAccordionItem(assigmentConfig);
    this.stepsAssignAccordion.addItem(item);
    item.dataItem = assigmentConfig;
    assigmentConfig = {
        step_type_obj: "Routing".translate(),
        triggers: secondResp,
        obj_title: "Routing".translate(),
        st_type: 'ROUTING',
        step_uid_obj: "Routing"
    };
    item = this.createAccordionItem(assigmentConfig);
    this.stepsAssignAccordion.addItem(item);
    item.dataItem = assigmentConfig;
    this.stepsAssignAccordion.defineEvents();
};
/**
 * This method creates an element for stepsAssignAccordion
 * @param {data} It is an object with the settings to create the element
 * @returns {PMUI.item.AccordionItem}
 */
stepsTask.prototype.createAccordionItem = function (data) {
    var that = this,
        gridBefore,
        gridAfter,
        beforeTitle,
        afterTitle,
        i,
        textLabel;
    if (this.stepsType[data.step_type_obj]) {
        textLabel = this.stepsType[data.step_type_obj];
    } else {
        textLabel = data.step_type_obj;
    }
    beforeTitle = new PMUI.field.TextAnnotationField({
        text: 'Before'.translate() + ' ' + textLabel,
        proportion: 0.5,
        text_Align: 'left'
    });
    afterTitle = new PMUI.field.TextAnnotationField({
        text: 'After'.translate() + ' ' + textLabel,
        proportion: 0.5,
        text_Align: 'left',
        visible: data.st_type === "ASSIGNMENT" ? false : true
    });

    gridBefore = new PMUI.grid.GridPanel({
        behavior: 'dragdropsort',
        filterable: false,
        visibleHeaders: false,
        data: data.triggers,
        st_type: 'BEFORE',
        step_uid: data.step_uid,
        visibleFooter: false,
        width: '96%',
        emptyMessage: 'No records found'.translate(),
        style: {
            cssClasses: ['mafe-gridPanel']
        },
        customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
            return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
        },
        columns: [
            {
                title: '',
                dataType: 'string',
                alignmentCell: 'center',
                columnData: "st_position",
                width: 20
            }, {
                title: 'Before Output Document'.translate(),
                dataType: 'string',
                alignmentCell: 'left',
                columnData: 'tri_title',
                width: 360
            }, {
                title: '',
                dataType: 'button',
                buttonLabel: function (row, data) {
                    return data.st_condition === '' ? 'Condition'.translate() : 'Condition *'.translate();
                },
                buttonStyle: {cssClasses: ['mafe-button-edit']},
                onButtonClick: function (row, grid) {
                    var data = row.getData();
                    that.editCondition(grid.step_uid, data.tri_uid, data.st_type, row);
                }
            }, {
                title: '',
                dataType: 'button',
                buttonLabel: 'Edit'.translate(),
                buttonStyle: {cssClasses: ['mafe-button-edit']},
                onButtonClick: function (row, grid) {
                    var data = row.getData(),
                        restClient;
                    restClient = new PMRestClient({
                        endpoint: 'trigger/' + data.tri_uid,
                        typeRequest: 'get',
                        functionSuccess: function (xhr, response) {
                            that.editTrigger(response.tri_webbot, response.tri_uid);
                        },
                        functionFailure: function (xhr, response) {
                            PMDesigner.msgWinError(response.error.message);
                        }
                    });
                    restClient.executeRestClient();
                }
            }, {
                title: '',
                dataType: 'button',
                buttonLabel: 'Remove'.translate(),
                buttonStyle: {cssClasses: ['mafe-button-delete']},
                onButtonClick: function (row, grid) {
                    that.removeTrigger(row, grid);
                }
            }
        ],
        onDrop: function (container, draggableItem, index) {
            var receiveData = draggableItem.getData();
            if (draggableItem instanceof PMUI.item.TreeNode) {
                that.receiveTreeNodeItem(receiveData, this, index);
            } else {
                that.receiveRowItem(receiveData, this, index, draggableItem);
            }
            that.updateIndexToGrid(this);
            return false;
        },
        onSort: function (container, item, index) {
            var receiveData = item.getData();
            that.sortableRowHandler(receiveData, this, index);
            that.updateIndexToGrid(this);
        }
    });
    if (data.st_type !== "ROUTING" && data.st_type !== "ASSIGNMENT") {
        gridBefore.st_type = 'BEFORE';
    } else if (data.st_type === "ROUTING") {
        gridBefore.st_type = "BEFORE_ROUTING";
    } else {
        gridBefore.st_type = "BEFORE_ASSIGNMENT";
    }
    gridBefore.step_uid = data.step_uid;
    gridBefore.clearItems();
    if (jQuery.isArray(data.triggers)) {
        for (i = 0; i < data.triggers.length; i += 1) {
            if (gridBefore.st_type === data.triggers[i].st_type) {
                gridBefore.addDataItem({
                    st_condition: data.triggers[i].st_condition,
                    st_position: data.triggers[i].st_position,
                    st_type: data.triggers[i].st_type,
                    tri_description: data.triggers[i].tri_description,
                    tri_title: data.triggers[i].tri_title,
                    tri_uid: data.triggers[i].tri_uid,
                    obj_title: data.triggers[i].tri_title,
                    obj_uid: data.triggers[i].tri_uid
                });
            }
        }
    }
    gridAfter = new PMUI.grid.GridPanel({
        behavior: 'dragdropsort',
        filterable: false,
        visibleHeaders: false,
        data: data.triggers,
        visibleFooter: false,
        width: '96%',
        visible: data.st_type === "ASSIGNMENT" ? false : true,
        emptyMessage: 'No records found'.translate(),
        style: {
            cssClasses: ['mafe-gridPanel']
        },
        customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
            return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
        },
        columns: [
            {
                title: '',
                dataType: 'string',
                alignmentCell: 'center',
                columnData: 'st_position',
                width: 20
            }, {
                title: 'Before Output Document'.translate(),
                dataType: 'string',
                alignmentCell: 'left',
                columnData: 'tri_title',
                width: 360
            }, {
                title: '',
                dataType: 'button',
                buttonLabel: function (row, data) {
                    return data.st_condition === '' ? 'Condition'.translate() : 'Condition *'.translate();
                },
                buttonStyle: {cssClasses: ['mafe-button-edit']},
                onButtonClick: function (row, grid) {
                    var data = row.getData();
                    that.editCondition(grid.step_uid, data.tri_uid, data.st_type, row);
                }
            }, {
                title: '',
                dataType: 'button',
                buttonLabel: 'Edit'.translate(),
                buttonStyle: {cssClasses: ['mafe-button-edit']},
                onButtonClick: function (row, grid) {
                    var data = row.getData(),
                        restClient;
                    restClient = new PMRestClient({
                        endpoint: 'trigger/' + data.tri_uid,
                        typeRequest: 'get',
                        functionSuccess: function (xhr, response) {
                            that.editTrigger(response.tri_webbot, response.tri_uid);
                        },
                        functionFailure: function (xhr, response) {
                            PMDesigner.msgWinError(response.error.message);
                        }
                    });
                    restClient.executeRestClient();
                }
            }, {
                title: '',
                dataType: 'button',
                buttonLabel: 'Remove'.translate(),
                buttonStyle: {cssClasses: ['mafe-button-delete']},
                onButtonClick: function (row, grid) {
                    that.removeTrigger(row, grid);
                }
            }
        ],
        onDrop: function (container, draggableItem, index) {
            var receiveData = draggableItem.getData();
            if (draggableItem instanceof PMUI.item.TreeNode) {
                that.receiveTreeNodeItem(receiveData, this, index);
            } else {
                that.receiveRowItem(receiveData, this, index, draggableItem);
            }
            that.updateIndexToGrid(this);
            return false;
        },
        onSort: function (container, item, index) {
            var receiveData = item.getData();
            that.sortableRowHandler(receiveData, this, index);
            that.updateIndexToGrid(this);
        }
    });
    if (data.st_type !== "ROUTING" && data.st_type !== "ASSIGNMENT") {
        gridAfter.st_type = 'AFTER';
    } else if (data.st_type == "ROUTING") {
        gridAfter.st_type = "AFTER_ROUTING";
    } else {
        gridAfter.st_type = "AFTER_ASSIGNMENT";
    }
    gridAfter.step_uid = data.step_uid;
    if (jQuery.isArray(data.triggers)) {
        for (i = 0; i < data.triggers.length; i += 1) {
            if (gridAfter.st_type === data.triggers[i].st_type) {
                gridAfter.addDataItem({
                    st_condition: data.triggers[i].st_condition,
                    st_position: data.triggers[i].st_position,
                    st_type: data.triggers[i].st_type,
                    tri_description: data.triggers[i].tri_description,
                    tri_title: data.triggers[i].tri_title,
                    tri_uid: data.triggers[i].tri_uid,
                    obj_title: data.triggers[i].tri_title,
                    obj_uid: data.triggers[i].tri_uid
                });
            }
        }
    }
    var accordionItem = new PMUI.item.AccordionItem({
        id: 'id' + data.step_uid_obj,
        dataStep: data,
        closeable: true,
        body: new PMUI.core.Panel({
            layout: 'vbox',
            items: [
                beforeTitle,
                gridBefore,
                afterTitle,
                gridAfter
            ]
        })
    });
    if (this.stepsType[data.step_type_obj]) {
        accordionItem.setTitle(data.step_position + ".  " + data.obj_title + ' (' + this.stepsType[data.step_type_obj] + ')');
        this.stepsAssigned.insert(accordionItem);
    } else {
        accordionItem.setTitle((this.stepsAssignAccordion.items.getSize() + 1) + ". " + data.obj_title);
    }
    return accordionItem;
};
/**
 * styles that can not be handled with the library are customized PMUI
 * @chainable
 */
stepsTask.prototype.customStyles = function () {
    this.mainWindow.body.style.overflow = "hidden";
};
/**
 * run the endpoint 'activity/{activity_id}/available-steps' to get
 * dynaforms, output document, input Document and external, Unassigned or Availables
 * @returns {Array}
 */
stepsTask.prototype.getStepAvailables = function () {
    var resp = [];
    restClient = new PMRestClient({
        typeRequest: 'post',
        multipart: true,
        data: {
            calls: [
                {
                    url: 'activity/' + PMDesigner.act_uid + '/available-steps',
                    method: 'GET'
                }
            ]
        },
        functionSuccess: function (xhr, response) {
            if (jQuery.isArray(response)) {
                resp = response[0] ? response[0].response : [];
            }
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        }
    });
    restClient.executeRestClient();
    return resp;
};
/**
 * run the endpoint 'activity/{activity_id}/available-steps' to get all Availables
 * triggres and dynaforms Unassigned or Availables
 * @returns {Array}
 */
stepsTask.prototype.getTreePanelData = function () {
    var resp = [];
    restClient = new PMRestClient({
        typeRequest: 'post',
        multipart: true,
        data: {
            calls: [{
                url: 'activity/' + PMDesigner.act_uid + '/available-steps',
                method: 'GET'
            }, {
                url: 'triggers',
                method: 'GET'
            }
            ]
        },
        functionSuccess: function (xhr, response) {
            resp = response;
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        }
    });
    restClient.executeRestClient();
    return resp;
}
/**
 * this method loads the data to stepsAssignTree
 * @param response, the answer is an array containing all the elements
 * that will be loaded into the step stepsAssignTree
 * @chainable
 */
stepsTask.prototype.loadTreePanelData = function (response) {
    var that = this,
        data,
        i,
        j,
        type,
        label,
        items,
        labelTrigger,
        dataTree = [],
        treeNode;
    data = response[1].response;
    labelTrigger = 'Trigger (s)'.translate();
    if (data.length === 0) {
        dataTree.push({
            obj_title: labelTrigger,
            items: [this.notItemConfig()]
        });
    } else {
        items = [];
        for (i = 0; i < data.length; i += 1) {
            items.push({
                obj_title: data[i]['tri_title'],
                obj_type: data[i]['tri_type'],
                obj_uid: data[i]['tri_uid']
            });
        }
        dataTree.push({
            obj_title: labelTrigger,
            items: items,
            id: "TRIGGER"
        });
    }
    data = response[0].response;
    type = this.groupType;
    label = this.groupLabel;
    items = [];
    for (i = 0; i < type.length; i += 1) {
        items = [];
        for (j = 0; j < data.length; j += 1) {
            if (type[i] === data[j].obj_type) {
                items.push({
                    obj_title: data[j]['obj_title'],
                    obj_type: data[j]['obj_type'],
                    obj_uid: data[j]['obj_uid']
                });
            }
        }
        if (items.length === 0) {
            dataTree.push({
                obj_title: label[i].translate(),
                items: [this.notItemConfig()],
                behavior: '',
                id: type[i]
            });
        } else {
            dataTree.push({
                obj_title: label[i].translate(),
                items: items,
                behavior: 'drag',
                id: type[i]
            });
        }
    }
    this.stepsAssignTree.clearItems();
    for (i = 0; i < dataTree.length; i += 1) {
        this.stepsAssignTree.addDataItem(dataTree[i]);
        treeNode = this.stepsAssignTree.getItem(i);
        treeNode.setID(dataTree[i].id);
        this.updateIndexPosition(treeNode);
    }
    return this;
};
/**
 * run the endpoint 'activity/{activity_id}/steps' and 'activity/{activity_id}/step/triggers'
 * to get all triggres and dynaforms assigned
 * @returns {Array}
 */
stepsTask.prototype.getAccordionData = function () {
    var resp = [],
        restClient = new PMRestClient({
            typeRequest: 'post',
            multipart: true,
            data: {
                calls: [
                    {
                        url: 'activity/' + PMDesigner.act_uid + '/steps',
                        method: 'GET'
                    }, {
                        url: 'activity/' + PMDesigner.act_uid + '/step/triggers',
                        method: 'GET'
                    }
                ]
            },
            functionSuccess: function (xhr, response) {
                resp = response;
            },
            functionFailure: function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            },
            messageError: 'There are problems getting the Steps, please try again.'.translate()
        });
    restClient.executeRestClient();
    return resp;
};
/**
 * checks whether a trigger is already assigned in a grid
 * @param grid, is instanceof PMUI.grid.Grid, in conducting the search
 * @param tri_uid, search parameter in the rows of the grid
 * @returns {boolean}
 */
stepsTask.prototype.isTriggerAssigned = function (grid, tri_uid) {
    var data, i, exist = false;
    data = grid.getData();
    if (grid && jQuery.isArray(data)) {
        for (i = 0; i < data.length; i += 1) {
            if (data[i].tri_uid === tri_uid) {
                exist = true;
                break;
            }
        }
    }
    return exist;
};
/**
 * retorna el tipo de de step, para la ejecucion de "endpoint"
 * @param st_type, this a step type, the accepted parameters are:
 *  - BEFORE_ASSIGNMENT
 *  - BEFORE_ROUTING
 *  - AFTER_ROUTING
 *  - BEFORE
 *  - AFTER
 * @returns {string}
 */
stepsTask.prototype.getStepType = function (st_type) {
    var value;
    switch (st_type) {
        case 'BEFORE_ASSIGNMENT':
            value = 'before-assignment';
            break;
        case 'BEFORE_ROUTING':
            value = 'before-routing';
            break;
        case 'AFTER_ROUTING':
            value = 'after-routing';
            break;
        case 'BEFORE':
            value = 'before';
            break;
        case 'AFTER':
            value = 'after';
            break;
        default:
            value = '';
            break;
    }
    return value;
};
/**
 * This method is executed when editing a "trigger" in a row of the grid.
 * secondary window opens with the current configuration of the trigger
 * @param trigger, is the return value when is update 'trigger' action  in the enpoint
 * @param triggerID, is the id of the trigger to update
 * @chainable
 */
stepsTask.prototype.editTrigger = function (trigger, triggerID) {
    var codeMirror,
        saveButton,
        cancelButton,
        criteriaButton,
        that = this;
    this.resetSecondaryWindow();
    codeMirror = new PMCodeMirror({
        id: "codeMirror"
    });
    CodeMirror.commands.autocomplete = function (cm) {
        CodeMirror.showHint(cm, CodeMirror.phpHint);
    };
    codeMirror.setValue(trigger);
    this.secondaryWindow.setWidth(DEFAULT_WINDOW_WIDTH);
    this.secondaryWindow.setHeight(DEFAULT_WINDOW_HEIGHT);
    this.secondaryWindow.setTitle("Trigger".translate());
    saveButton = this.secondaryWindow.footer.getItem("secondaryWindow-save");
    cancelButton = this.secondaryWindow.footer.getItem("secondaryWindow-cancel");
    criteriaButton = this.secondaryWindow.footer.getItem("secondaryWindow-criteria");
    if (saveButton) {
        saveButton.setHandler(function () {
            var restClient = new PMRestClient({
                endpoint: 'trigger/' + triggerID,
                typeRequest: 'update',
                data: {
                    tri_param: '',
                    tri_webbot: codeMirror.getValue()
                },
                messageError: 'There are problems updating the trigger, please try again.'.translate(),
                messageSuccess: 'Trigger updated correctly'.translate(),
                flashContainer: that.mainWindow,
                functionSuccess: function () {
                    that.secondaryWindow.close();
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            });
            restClient.executeRestClient();
        });
    }
    if (cancelButton) {
        cancelButton.setHandler(function () {
            that.secondaryWindow.close();
        });
    }
    if (criteriaButton) {
        criteriaButton.setVisible(true);
        criteriaButton.setHandler(function () {
            var picker = new VariablePicker();
            picker.open({
                success: function (variable) {
                    var cursorPos,
                        codemirror;
                    codemirror = codeMirror.cm;
                    cursorPos = codemirror.getCursor();
                    codemirror.replaceSelection(variable);
                    codemirror.setCursor(cursorPos.line, cursorPos.ch);
                }
            });
        });
    }
    this.secondaryWindow.open();
    this.secondaryWindow.addItem(codeMirror);
    codeMirror.cm.setSize(this.secondaryWindow.getWidth(), 380);
    $(".CodeMirror.cm-s-default.CodeMirror-wrap").after($ctrlSpaceMessage.css({
        "padding-left": "10px",
        "margin": "3px 0px 0px 0px"
    }));
    $(".pmui-window-body").css("overflow", "hidden");
    codeMirror.cm.refresh();
};
/**
 * edit the selected trigger condition
 * @param stepID, It is the id of the step to upgrade
 * @param triggerID, is the id of the trigger to update
 * @param stepType, It is the kind of step to update
 * @param row, PMUI.grid.GridPanelRow, is the row affected
 */
stepsTask.prototype.editCondition = function (stepID, triggerID, stepType, row) {
    var saveButton,
        cancelButton,
        criteriaButton,
        form,
        dataRow,
        that = this;
    dataRow = row.getData();
    this.resetSecondaryWindow();
    this.secondaryWindow.setWidth(500);
    this.secondaryWindow.setHeight(350);
    this.secondaryWindow.setTitle('Condition Trigger'.translate());
    this.secondaryWindow.setTitle("Trigger".translate());
    form = new PMUI.form.Form({
        id: 'idFormEditCondition',
        width: 500,
        title: 'Condition Trigger'.translate(),
        visibleHeader: false,
        items: [
            new CriteriaField({
                id: 'st_condition',
                pmType: 'textarea',
                name: 'st_condition',
                valueType: 'string',
                label: 'Condition'.translate(),
                placeholder: 'Insert a condition'.translate(),
                rows: 150,
                controlsWidth: 250,
                renderType: 'textarea',
                value: dataRow.st_condition
            })
        ]
    });
    this.secondaryWindow.addItem(form);
    saveButton = this.secondaryWindow.footer.getItem("secondaryWindow-save");
    cancelButton = this.secondaryWindow.footer.getItem("secondaryWindow-cancel");
    criteriaButton = this.secondaryWindow.footer.getItem("secondaryWindow-criteria");
    if (saveButton) {
        saveButton.setHandler(function () {
            var data,
                restClient;
            data = form.getData();
            data.st_type = stepType;
            restClient = new PMRestClient({
                endpoint: 'activity/' + PMDesigner.act_uid + '/step/' + ((typeof(stepID) != "undefined") ? stepID + "/" : "") + 'trigger/' + triggerID,
                typeRequest: 'update',
                data: data,
                messageError: 'There are problems update the Step Trigger, please try again.'.translate(),
                functionSuccess: function (xhr, response) {
                    dataRow.st_condition = data.st_condition;
                    row.setData(dataRow);
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            });
            restClient.executeRestClient();
            that.secondaryWindow.close();
        });
    }
    if (cancelButton) {
        cancelButton.setHandler(function () {
            that.secondaryWindow.close();
        });
    }
    if (criteriaButton) {
        criteriaButton.setVisible(false);
        criteriaButton.handler = null;
    }
    this.secondaryWindow.open();
};
/**
 * eliminates the elements of the secondary window
 * @chainable
 */
stepsTask.prototype.resetSecondaryWindow = function () {
    var i, items;
    if (this.secondaryWindow && this.secondaryWindow.items.getSize() > 0) {
        items = this.secondaryWindow.items;
        for (i = 0; i < items.getSize(); i += 1) {
            this.secondaryWindow.removeItem(items.get(i));
        }
    }
};
/**
 * It establishes a PMUI.util.ArrayList that stores the
 * elements of "stepsAssignAccordion" that are open
 * @chainable
 */
stepsTask.prototype.elementsAccordionOpenFixed = function () {
    var i,
        accordionItems;
    if (this.stepsAssignAccordion) {
        accordionItems = this.stepsAssignAccordion.getItems();
        if ($.isArray(accordionItems)) {
            for (i = 0; i < accordionItems.length; i += 1) {
                if (!accordionItems[i].collapsed) {
                    this.elementAccordionOpen.insert(accordionItems[i]);
                }
            }
        }
    }
};
/**
 * It is an extension to add the "sortable" event "stepAssignAccordion".
 * when a node "treePanel" is added to stop runs and is where you choose if it's a sort or aggregation.
 * @chainable
 */
stepsTask.prototype.addEventSortableInAccordionElements = function () {
    var tagContainer,
        newIndex,
        index,
        treeNodeObject,
        treeNodeData,
        that = this;
    if (this.stepsAssignAccordion && this.stepsAssignAccordion.html) {
        tagContainer = this.stepsAssignAccordion.body;
        $(tagContainer).sortable({
            items: '>div:not(#idAssignment,#idRouting)',
            placeholder: 'steps-placeholder',
            receive: function (event, ui) {
                var item = ui ? ui.item : null;
                if (item && item instanceof jQuery && item.length) {
                    treeNodeObject = PMUI.getPMUIObject(item.get(0));
                    treeNodeData = treeNodeObject.getData();
                }
            },
            stop: function (event, ui) {
                var itemClone = ui ? ui.item : null,
                    accordionItems,
                    accordionItem,
                    dataEdited,
                    restClientMultipart,
                    restClient;
                var newIndex = ui.item.index();
                accordionItems = that.stepsAssignAccordion.getItems();
                if (itemClone && itemClone instanceof jQuery && itemClone.length) {
                    if (treeNodeObject) {
                        itemClone.remove();
                        if (newIndex + 1 > accordionItems.length) {
                            newIndex = that.stepsAssigned.getSize();
                        }
                        restClient = new PMRestClient({
                            endpoint: 'activity/' + PMDesigner.act_uid + '/step',
                            typeRequest: 'post',
                            data: {
                                step_type_obj: treeNodeData.obj_type,
                                step_uid_obj: treeNodeData.obj_uid,
                                step_condition: '',
                                step_position: newIndex + 1,
                                step_mode: 'EDIT'
                            },
                            functionSuccess: function (xhr, response) {
                                var item, buttonAfected, treeNode;
                                that.stepsAssignTree.removeItem(treeNodeObject);
                                treeNode = that.stepsAssignTree.items.find("id", response.step_type_obj);
                                if (treeNode.items.getSize() === 0) {
                                    treeNode.addDataItem(that.notItemConfig());
                                }
                                response.obj_description = '';
                                response.obj_title = treeNodeData.obj_title;
                                response.triggers = [];
                                item = that.createAccordionItem(response, true, true);
                                item.dataItem = response;
                                if (that.stepsAssignAccordion.items.getSize() === 2) {
                                    that.stepsAssignAccordion.addItem(item, 0);
                                } else {
                                    that.stepsAssignAccordion.addItem(item, newIndex);
                                }
                                that.stepsAssignAccordion.defineEvents();
                                that.customAccordionItemButtons(item.html, response, item);
                                that.updateItemIndexToAccordion();
                                that.addEventSortableInAccordionElements();
                                that.addEventSortableInTreePanelElements();
                                buttonAfected = that.labelsPanel.getItem("expand-button");
                                buttonAfected.setDisabled(false);
                            },
                            functionFailure: function (xhr, response) {
                                PMDesigner.msgWinError(response.error.message);
                            },
                            messageError: 'An unexpected error while assigning the step, please try again later.'.translate(),
                            messageSuccess: 'Step assigned successfully.'.translate(),
                            flashContainer: that.stepsAssignAccordion.getParent()
                        });
                        restClient.executeRestClient();
                    } else {
                        accordionItem = PMUI.getPMUIObject(ui.item.get(0));
                        index = that.stepsAssignAccordion.items.indexOf(accordionItem);
                        if (newIndex !== index) {
                            that.stepsAssignAccordion.items.remove(accordionItem);
                            that.stepsAssignAccordion.items.insertAt(accordionItem, newIndex);
                            dataEdited = {
                                step_position: newIndex + 1,
                                step_uid: accordionItem.dataItem.step_uid,
                                step_type_obj: accordionItem.dataItem.step_type_obj,
                                step_uid_obj: accordionItem.dataItem.step_uid_obj
                            };
                            restClientMultipart = new PMRestClient({
                                endpoint: 'activity/' + PMDesigner.act_uid + '/step/' + accordionItem.dataItem.step_uid,
                                typeRequest: 'update',
                                data: dataEdited,
                                functionSuccess: function (xhr, response) {
                                    that.updateItemIndexToAccordion();
                                },
                                functionFailure: function (xhr, response) {
                                    PMDesigner.msgWinError(response.error.message);
                                },
                                messageError: 'An unexpected error while editing the step, please try again later.'.translate(),
                                messageSuccess: 'Step editing successfully.'.translate(),
                                flashContainer: this.mainWindow
                            });
                            restClientMultipart.executeRestClient();
                        }
                    }
                }
            },
            start: function (e, ui) {
                newIndex = ui.item.index();
            }
        });
    }
};
/**
 * It is an extension to add the "sortable" event "stepsAssignTree".
 * when choosing a node treePanel and you want to add to the accordion or the grid
 * @chainable
 */
stepsTask.prototype.addEventSortableInTreePanelElements = function () {
    var items = this.stepsAssignTree.getItems(),
        connect,
        i,
        sw,
        that = this,
        nodeItems;
    for (i = 0; i < items.length; i += 1) {
        nodeItems = items[i].getItems();
        if (nodeItems.length && nodeItems[0].getData().obj_type) {
            sw = items[i].getItems()[0].getData().obj_type === "SCRIPT";
            connect = sw ? ".pmui-gridpanel-tbody" : ".pmui-accordion-panel-body";
            $(items[i].html).find('ul').find('>li').draggable({
                appendTo: document.body,
                revert: "invalid",
                helper: "clone",
                cursor: "move",
                zIndex: 1000,
                connectToSortable: connect,
                start: function (e) {
                    var i, nodeTag, node, nodeData, accordionItems, item;
                    nodeTag = e.target;
                    node = PMUI.getPMUIObject(nodeTag);
                    nodeData = node.getData();
                    accordionItems = that.stepsAssignAccordion.getItems();
                    $(that.stepsAssignAccordion.body).hide();
                    if (nodeData.obj_type !== "SCRIPT") {
                        for (i = 0; i < accordionItems.length; i += 1) {
                            item = accordionItems[i];
                            item.collapse();
                        }
                    }
                    $(that.stepsAssignAccordion.body).show();
                },
                stop: function () {
                    var i = 0, max;
                    if (that.elementAccordionOpen) {
                        max = that.elementAccordionOpen.getSize();
                        for (i = 0; i < max; i += 1) {
                            that.elementAccordionOpen.get(i).expand();
                        }
                    }
                }
            });
        } else {
            $(nodeItems[0].html).draggable("disable");
        }
    }
};
/**
 * add custom buttons on the head of an element of stepsAssignAccordion
 * are three buttons
 * properties
 * edit
 * remove
 * @param html, is the html of the header accordion item
 * @param step, the data of the step selected
 */
stepsTask.prototype.customAccordionItemButtons = function (html, step, accordionItem) {
    var propertiesStep,
        editStep,
        removeStep,
        $html,
        containerButtons,
        that = this,
        title;
    if (html) {
        $html = jQuery(html.getElementsByClassName("pmui-accordion-item-header"));
        title = step.obj_title + ' (' + step.step_type_obj + ')';
        $html.find(".pmui-accordion-item-title").get(0).title = title;
        containerButtons = $('<div></div>');
        containerButtons.addClass("propertiesTask-accordionItem");
        propertiesStep = $('<a>' + 'Properties'.translate() + '</a>');
        propertiesStep.addClass("mafe-button-edit propertiesTask-accordionButton");
        editStep = $('<a>' + 'Edit'.translate() + '</a>');
        editStep.addClass("mafe-button-edit propertiesTask-accordionButton");
        removeStep = $('<a>' + 'Remove'.translate() + '</a>');
        removeStep.addClass("mafe-button-delete propertiesTask-accordionButton");

        propertiesStep.click(function (e) {
            e.preventDefault();
            e.stopPropagation();
            that.propertiesStepShow(step);
            return false;
        });

        editStep.click(function (e) {
            e.preventDefault();
            e.stopPropagation();
            that.editStepShow(step, accordionItem);
            return false;
        });

        removeStep.click(function (e) {
            e.preventDefault();
            e.stopPropagation();
            that.removeStepShow(step);
            return false;
        });
        containerButtons.append(propertiesStep);
        containerButtons.append(editStep);
        containerButtons.append(removeStep);
        $html.append(containerButtons);
    }
};
/**
 * opens the properties of the selected step with the current settings
 * @param step, is the data of selected step
 * @chainable
 */
stepsTask.prototype.propertiesStepShow = function (step) {
    var form,
        saveButton,
        cancelButton,
        criteriaButton,
        that = this;
    this.resetSecondaryWindow();
    this.secondaryWindow.setWidth(520);
    this.secondaryWindow.setHeight(370);
    this.secondaryWindow.setTitle('Step Properties'.translate());
    form = new PMUI.form.Form({
        id: 'stepsEditCondition',
        width: 500,
        title: 'Condition Trigger'.translate(),
        visibleHeader: false,
        items: [
            {
                id: 'step_mode',
                pmType: 'radio',
                label: 'Mode'.translate(),
                value: '',
                visible: step.step_type_obj === "DYNAFORM" ? true : false,
                name: 'step_mode',
                options: [
                    {
                        id: 'modeEdit',
                        label: 'Edit'.translate(),
                        value: 'EDIT',
                        selected: true
                    }, {
                        id: 'modeView',
                        label: 'View'.translate(),
                        value: 'VIEW'
                    }
                ]
            },
            new CriteriaField({
                id: 'step_condition',
                pmType: 'textarea',
                name: 'step_condition',
                valueType: 'string',
                label: 'Condition'.translate(),
                placeholder: 'Insert a condition'.translate(),
                rows: 150,
                controlsWidth: 250,
                renderType: 'textarea'
            })
        ]
    });
    this.secondaryWindow.addItem(form);

    var restClient = new PMRestClient({
        endpoint: 'activity/' + PMDesigner.act_uid + '/step/' + step.step_uid,
        typeRequest: 'get',
        functionSuccess: function (xhr, response) {
            form.getField('step_mode').setValue(response.step_mode);
            form.getField('step_condition').setValue(response.step_condition);
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        }
    });
    restClient.executeRestClient();
    saveButton = this.secondaryWindow.footer.getItem("secondaryWindow-save");
    cancelButton = this.secondaryWindow.footer.getItem("secondaryWindow-cancel");
    criteriaButton = this.secondaryWindow.footer.getItem("secondaryWindow-criteria");
    if (saveButton) {
        saveButton.setHandler(function () {
            var restClient;
            if (form.isValid()) {
                restClient = new PMRestClient({
                    endpoint: 'activity/' + PMDesigner.act_uid + '/step/' + step.step_uid,
                    typeRequest: 'update',
                    data: form.getData(),
                    functionSuccess: function () {
                        that.secondaryWindow.close();
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    },
                    messageError: 'There are problems update the Step Trigger, please try again.'.translate()
                });
                restClient.executeRestClient();
            }
        });
    }
    if (cancelButton) {
        cancelButton.setHandler(function () {
            that.secondaryWindow.close();
        });
    }
    if (criteriaButton) {
        criteriaButton.handler = null;
        criteriaButton.setVisible(false);
    }
    this.secondaryWindow.open();
};
/**
 * opens the step of the selected step with the current settings
 * @param step, is the data of selected step
 * @chainable
 */
stepsTask.prototype.editStepShow = function (step, accordioItem) {
    var inputDocument,
        that = this;
    switch (step.step_type_obj) {
        case 'DYNAFORM':
            var restProxy = new PMRestClient({
                endpoint: 'dynaform/' + step.step_uid_obj,
                typeRequest: 'get',
                functionSuccess: function (xhr, response) {
                    var old = PMUI.activeCanvas,
                        formDesigner;
                    PMUI.activeCanvas = false;
                    formDesigner = PMDesigner.dynaformDesigner(response);
                    formDesigner.onHide = function () {
                        var assignedDynaform,
                            i,
                            data,
                            title;
                        assignedDynaform = that.getStepsAssignedByCriteria("DYNAFORM");
                        if (jQuery.isArray(assignedDynaform)) {
                            for (i = 0; i < assignedDynaform.length; i += 1) {
                                data = assignedDynaform[i];
                                if (typeof data === "object") {
                                    if (data.step_uid === step.step_uid) {
                                        title = data.step_position + ". " + data.obj_title;
                                        title = title + ' (' + that.stepsType["DYNAFORM"] + ')';
                                        accordioItem.setTitle(title);
                                        accordioItem.dataItem = data;
                                    }
                                }
                            }
                        }
                        PMUI.activeCanvas = old;
                    };
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            });
            restProxy.executeRestClient();
            break;
        case 'OUTPUT_DOCUMENT':
            PMDesigner.output();
            PMDesigner.output.showTiny(step.step_uid_obj);
            break;
        case 'INPUT_DOCUMENT':
            inputDocument = new InputDocument({
                onUpdateInputDocumentHandler: function (data, inputDoc) {
                    var position, title;
                    position = accordioItem.dataItem.step_position;
                    title = position + ". " + data.inp_doc_title;
                    title = title + ' (' + that.stepsType["INPUT_DOCUMENT"] + ')';
                    accordioItem.dataItem.obj_title = data.inp_doc_title;
                    accordioItem.setTitle(title);
                    inputDoc.winMainInputDocument.close();
                }
            });
            inputDocument.build();
            inputDocument.openFormInMainWindow();
            inputDocument.inputDocumentFormGetProxy(step.step_uid_obj);
            break;
    }
};
/**
 * the window opens for confirmation of the removal step
 * @param step, the current step to remove
 * @chainable
 */
stepsTask.prototype.removeStepShow = function (step) {
    var title,
        yesButton,
        noButton,
        that = this,
        restClient;
    if (this.stepsType[step.step_type_obj] !== undefined) {
        title = "Step {0} ( {1} )".translate([step.obj_title, this.stepsType[step.step_type_obj]]);
        this.confirmWindow.setTitle(title);
    } else {
        this.confirmWindow.setTitle("Step " + step.step_type_obj.capitalize());
    }
    this.confirmWindow.setMessage("Do you want to remove the step '{0}'?".translate([step.obj_title]));
    yesButton = this.confirmWindow.footer.getItem("confirmWindow-footer-yes");
    noButton = this.confirmWindow.footer.getItem("confirmWindow-footer-no");
    if (yesButton) {
        yesButton.setHandler(function () {
            restClient = new PMRestClient({
                endpoint: 'activity/' + PMDesigner.act_uid + '/step/' + step.step_uid,
                typeRequest: 'remove',
                functionSuccess: function (xhr, response) {
                    that.removingStepTask(step, response);
                    that.confirmWindow.close();
                    that.updateItemIndexToAccordion();
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                },
                messageError: 'An unexpected error while deleting the step, please try again later.'.translate(),
                messageSuccess: 'Step removed successfully'.translate(),
                flashContainer: that.mainWindow.getParent()
            });
            restClient.executeRestClient();
        });
    }
    if (noButton) {
        noButton.setHandler(function () {
            that.confirmWindow.close();
        });
    }
    this.confirmWindow.open();
};
/**
 * eliminates the step of step Assign Accordion
 * @param step, the current step to remove
 * @param response, data from the endpoint
 */
stepsTask.prototype.removingStepTask = function (step, response) {
    var stepObject,
        stepAvailable,
        treeNodeObject,
        stepAvailables,
        i,
        itemsTreeNode = [],
        items = [];
    stepObject = this.stepsAssignAccordion.getItem("id" + step.step_uid_obj);
    this.elementAccordionOpen.remove(stepObject);
    this.stepsAssigned.remove(stepObject);
    this.stepsAssignAccordion.removeItem(stepObject);
    if (stepObject) {
        stepAvailable = this.getStepAvailables();
        stepAvailables = this.getAvailablesStepsByCriteria(step.step_type_obj, stepAvailable);
        for (i = 0; i < stepAvailables.length; i += 1) {
            items.push({
                obj_title: stepAvailables[i]['obj_title'],
                obj_type: stepAvailables[i]['obj_type'],
                obj_uid: stepAvailables[i]['obj_uid']
            });
        }
        treeNodeObject = this.stepsAssignTree.getItem(step.step_type_obj);
        itemsTreeNode = treeNodeObject.getItems();
        for (i = 0; i < itemsTreeNode.length; i += 1) {
            treeNodeObject.removeItem(itemsTreeNode[i]);
        }
        treeNodeObject.clearItems();
        treeNodeObject.setDataItems(items);
        this.updateIndexPosition(treeNodeObject);
        this.addEventSortableInTreePanelElements();
        this.addEventSortableInAccordionElements();
    }
};
/**
 * get the steps is not assigned by a criterion
 * @param criteria, It is the filter criteria search
 * @param stepAvailable, all steps Unassigned
 * @returns {Array}, filtered items
 */
stepsTask.prototype.getAvailablesStepsByCriteria = function (criteria, stepAvailable) {
    var items = [],
        i;
    if (jQuery.isArray(stepAvailable)) {
        for (i = 0; i < stepAvailable.length; i += 1) {
            if (stepAvailable[i].obj_type === criteria) {
                items.push(stepAvailable[i]);
            }
        }
    }
    return items;
};
/**
 * This method is executed when an element stepsAssignTree, is assigned in a grid
 * @param receiveData, data of the droppable item
 * @param grid, the affected grid
 * @param index, the index position row
 * @returns {stepsTask}
 */
stepsTask.prototype.receiveTreeNodeItem = function (receiveData, grid, index) {
    var restClient, that = this, message;
    if (that.isTriggerAssigned(grid, receiveData.obj_uid)) {
        message = new PMUI.ui.FlashMessage({
            message: 'Trigger is assigned.'.translate(),
            duration: 3000,
            severity: 'error',
            appendTo: that.mainWindow
        });
        message.show();
        return;
    }
    restClient = new PMRestClient({
        endpoint: grid.step_uid === undefined ?
        'activity/' + PMDesigner.act_uid + '/step/trigger' :
        'activity/' + PMDesigner.act_uid + '/step/' + grid.step_uid + '/trigger',
        typeRequest: 'post',
        data: {
            tri_uid: receiveData.obj_uid,
            st_type: grid.st_type,
            st_condition: '',
            st_position: index + 1
        },
        functionSuccess: function (xhr, response) {
            grid.addDataItem({
                st_condition: '',
                st_position: index + 1,
                st_type: grid.st_type,
                tri_description: '',
                tri_title: receiveData.obj_title,
                tri_uid: receiveData.obj_uid,
                obj_title: receiveData.obj_title,
                obj_uid: receiveData.obj_uid
            }, index);
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        }
    });
    restClient.executeRestClient();
    return this;
};
/**
 * This method is executed when a row is drop in another grid
 * @param receiveData, data of the droppable item
 * @param grid, the affected grid
 * @param index, the index position row
 * @param draggableItem
 * @returns {*}
 */
stepsTask.prototype.receiveRowItem = function (receiveData, grid, index, draggableItem) {
    var receiveParent = draggableItem.getParent(),
        message,
        restClient,
        that = this;
    if (this.isTriggerAssigned(grid, receiveData.obj_uid)) {
        message = new PMUI.ui.FlashMessage({
            message: 'Trigger is assigned.'.translate(),
            duration: 3000,
            severity: 'error',
            appendTo: that.mainWindow
        });
        index = receiveParent.items.indexOf(draggableItem);
        receiveParent.items.remove(draggableItem);
        receiveParent.addItem(draggableItem, index);
        message.show();
        return false;
    }
    restClient = new PMRestClient({
        typeRequest: 'post',
        multipart: true,
        data: {
            calls: [
                {
                    url: grid.step_uid === undefined ?
                    'activity/' + PMDesigner.act_uid + '/step/trigger' :
                    'activity/' + PMDesigner.act_uid + '/step/' + grid.step_uid + '/trigger',
                    method: 'POST',
                    data: {
                        tri_uid: receiveData.obj_uid,
                        st_type: grid.st_type,
                        st_condition: receiveData.st_condition,
                        st_position: index + 1
                    }
                }, {
                    url: receiveParent.step_uid === undefined ?
                    'activity/' + PMDesigner.act_uid + '/step/trigger/' + receiveData.obj_uid + '/' + that.getStepType(receiveParent.st_type) :
                    'activity/' + PMDesigner.act_uid + '/step/' + receiveParent.step_uid + '/trigger/' + receiveData.obj_uid + '/' + receiveParent.st_type.toLowerCase(),
                    method: 'DELETE'
                }
            ]
        },
        functionSuccess: function (xhr, response) {
            var data;
            data = receiveData;
            if (data.hasOwnProperty("st_type")) {
                data.st_type = grid.st_type;
                grid.addDataItem(receiveData, index);
            }
            receiveParent.removeItem(draggableItem);
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        },
        flashContainer: that.mainWindow,
        messageError: [
            'An unexpected error while assigning the trigger, please try again later.'.translate()
        ],
        messageSuccess: [
            'Trigger assigned successfully.'.translate()
        ]
    });
    restClient.executeRestClient();
    return this;
};
/**
 * This method is executed when a row is sorted in the grid
 * @param receiveData, data of the droppable item
 * @param grid, the affected grid
 * @param index, the new index position row
 * @returns {stepsTask}
 */
stepsTask.prototype.sortableRowHandler = function (receiveData, grid, index) {
    var that = this,
        restClient;
    restClient = new PMRestClient({
        endpoint: grid.step_uid === undefined ?
        'activity/' + PMDesigner.act_uid + "/step/trigger/" + receiveData.tri_uid :
        'activity/' + PMDesigner.act_uid + "/step/" + grid.step_uid + "/trigger/" + receiveData.tri_uid,
        typeRequest: 'update',
        data: {
            st_type: receiveData.st_type,
            st_condition: receiveData.st_condition,
            st_position: index + 1
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        },
        flashContainer: that.mainWindow,
        messageError: 'An unexpected error while assigning the trigger, please try again later.'.translate(),
        messageSuccess: 'Trigger assigned successfully.'.translate()
    });
    restClient.executeRestClient();
    return this;
};
/**
 * This method eliminates the list of triggers trigger an assigned step
 * @param row, the row affected or selected
 * @param grid, It is affected or grid to remove selected row
 */
stepsTask.prototype.removeTrigger = function (row, grid) {
    var message = 'Do you want to remove the trigger "',
        messageData = row.getData().tri_title ? row.getData().tri_title : "",
        yesButton,
        noButton,
        that = this,
        restClient;
    message = message + messageData + '"?';
    this.confirmWindow.setMessage(message.translate());
    yesButton = this.confirmWindow.footer.getItem("confirmWindow-footer-yes");
    if (yesButton) {
        yesButton.setHandler(function () {
            restClient = new PMRestClient({
                endpoint: grid.step_uid === undefined ?
                'activity/' + PMDesigner.act_uid + '/step/trigger/' + row.getData().tri_uid + '/' + that.getStepType(row.getData().st_type) :
                'activity/' + PMDesigner.act_uid + '/step/' + grid.step_uid + '/trigger/' + row.getData().tri_uid + '/' + that.getStepType(row.getData().st_type),
                typeRequest: 'remove',
                functionSuccess: function (xhr, response) {
                    grid.removeItem(row);
                    that.confirmWindow.close();
                    that.updateIndexToGrid(grid);
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                },
                flashContainer: that.mainWindow,
                messageError: 'An unexpected error while deleting the trigger, please try again later.'.translate(),
                messageSuccess: 'Trigger removed successfully'.translate()
            });
            restClient.executeRestClient();
        });
    }
    noButton = this.confirmWindow.footer.getItem("confirmWindow-footer-no");
    if (noButton) {
        noButton.setHandler(function () {
            that.confirmWindow.close();
        });
    }
    this.confirmWindow.open();
};
/**
 * updates indexes of elements selected grid
 * @param grid, It is affected or grid to remove selected row
 * @returns {stepsTask}
 */
stepsTask.prototype.updateIndexToGrid = function (grid) {
    var cell, rows, i, row;
    if (grid) {
        rows = grid.getItems();
        if (jQuery.isArray(rows)) {
            for (i = 0; i < rows.length; i += 1) {
                row = rows[i];
                cell = row.cells.find("columnData");
                if (cell) {
                    cell.setContent(i + 1);
                }
            }
        }
    }
    return this;
};
/**
 * get the steps assigned by a search criterion
 * @param criteria, search filter, after running the endpoint getAccordionData method
 * @returns {Array}, response with criteria
 */
stepsTask.prototype.getStepsAssignedByCriteria = function (criteria) {
    var allAssigned,
        i,
        elements,
        j,
        resp,
        response = [];
    allAssigned = this.getAccordionData();
    if (jQuery.isArray(allAssigned)) {
        for (i = 0; i < allAssigned.length; i += 1) {
            resp = allAssigned[i];
            if (typeof resp === "object") {
                elements = resp.response ? resp.response : [];
                for (j = 0; j < elements.length; j += 1) {
                    data = elements[j];
                    if (typeof data === "object") {
                        if (data.step_type_obj && data.step_type_obj === criteria) {
                            response.push(data);
                        }
                    }
                }
            }
        }
    }
    return response;
};
/**
 * updates indexes of elements assigned
 * @returns {stepsTask}
 */
stepsTask.prototype.updateItemIndexToAccordion = function () {
    var title,
        i,
        item,
        dataItem,
        items = this.stepsAssignAccordion.items,
        position,
        max;
    max = items.getSize();
    for (i = 0; i < max; i += 1) {
        item = items.get(i);
        position = items.indexOf(item);
        dataItem = item.dataItem;
        title = (position + 1) + ".  " + dataItem.obj_title;
        if (this.stepsType[dataItem.step_type_obj]){
            title = title + ' (' + this.stepsType[dataItem.step_type_obj] + ')';
        }
        item.dataItem.step_position = i + 1;
        item.setTitle(title);
    }
    return this;
};
/**
 * add tooltip in treeNode elements
 * @returns {stepsTask}
 */
stepsTask.prototype.updateIndexPosition = function (treeNode) {
    var items, i, item, $item, text, data;
    if (treeNode && treeNode.html) {
        items = treeNode.getItems();
        if (jQuery.isArray(items)) {
            for (i = 0; i < items.length; i += 1) {
                item = items[i];
                if (item.html) {
                    $item = $(item.html);
                    data = item.getData();
                    text = $item.find("a").get(0);
                    text.title = data.obj_title;
                }
            }
        }
    }
    return this;
};
/**
 * return the not items config.
 * @returns {{obj_title: *, obj_uid: string, id: string}}
 */
stepsTask.prototype.notItemConfig = function () {
    var config = {
        obj_title: 'N/A'.translate(),
        obj_uid: '',
        id: "notItem"
    };
    return config;
};
(
    function () {
        var processPermissionsSetForm;
        PMDesigner.processPermissions = function (event) {
            var PROCESS_PERMISSIONS_OPTION = "",
                PROCESS_PERMISSIONS_UID = "",
                arrayCboGroup = [],
                arrayCboUser = [],
                winGrdpnlProcessPermissions,
                grdpnlProcessPermissions,
                frmProcessPermissions,
                processPermissionsData,
                btnCreate, btnSave, btnCancel,
                loadDataFromServerToFields,
                disableAllItems,
                listProcessPermissions,
                isDirty2,
                isDirtyFormProcessPermission,
                refreshGridPanelInMainWindow,
                processPermissionsGetRestProxy,
                processPermissionsPostRestProxy,
                processPermissionGetRestProxy,
                processPermissionsPutRestProxy,
                cboTargetCboOriginTaskSetOptionsRestProxy,
                cboDynaFormSetOptionsRestProxy,
                cboInputDocumentSetOptionsRestProxy,
                cboOutputDocumentSetOptionsRestProxy,
                processPermissionsSetFormByType,
                processPermissionsDeleteRestProxy,
                cboStatusCase,
                cboTargetTask,
                cboGroupOrUser,
                cboOriginTask,
                optionsType,
                cboType,
                cboDynaForm,
                cboInputDocument,
                cboOutputDocument,
                cboPermission,
                cboParticipationRequired,
                processPermissionsDataIni = {},
                notification,
                notificationText = "Fields marked with an asterisk (%%ASTERISK%%) are required.".translate()
                    .replace(/%%ASTERISK%%/g, '<span style="color: #e84c3d">*</span>');

            loadDataFromServerToFields = function () {
                var restClient = new PMRestClient({
                    typeRequest: 'post',
                    multipart: true,
                    data: {
                        calls: [
                            {
                                url: 'project/' + PMDesigner.project.id + '/',
                                method: 'GET'
                            }, {
                                url: 'project/' + PMDesigner.project.id + '/dynaforms',
                                method: 'GET'
                            }, {
                                url: 'project/' + PMDesigner.project.id + '/input-documents',
                                method: 'GET'
                            }, {
                                url: 'project/' + PMDesigner.project.id + '/output-documents',
                                method: 'GET'
                            }
                        ]
                    },
                    functionSuccess: function (xhr, response) {
                        var i;
                        data = response[0].response;
                        cboTargetTask.clearOptions();
                        cboOriginTask.clearOptions();
                        cboTargetTask.addOption({value: '', label: 'Select an option'.translate()});
                        cboOriginTask.addOption({value: '', label: 'All Tasks'.translate()});
                        for (i = 0; i <= data.diagrams[0].activities.length - 1; i += 1) {
                            cboTargetTask.addOption({
                                value: data.diagrams[0].activities[i].act_uid,
                                label: data.diagrams[0].activities[i].act_name
                            });
                            cboOriginTask.addOption({
                                value: data.diagrams[0].activities[i].act_uid,
                                label: data.diagrams[0].activities[i].act_name
                            });
                        }
                        //project/dynaforms
                        data = response[1].response;
                        cboDynaForm.clearOptions();
                        cboDynaForm.addOption({value: '', label: 'All'.translate()});
                        for (i = 0; i <= data.length - 1; i += 1) {
                            cboDynaForm.addOption({value: data[i].dyn_uid, label: data[i].dyn_title});
                        }
                        //project/input-documents
                        data = response[2].response;
                        cboInputDocument.clearOptions();
                        cboInputDocument.addOption({value: '', label: 'All'.translate()});
                        for (i = 0; i <= data.length - 1; i += 1) {
                            cboInputDocument.addOption({value: data[i].inp_doc_uid, label: data[i].inp_doc_title});
                        }
                        //project/output-documents
                        data = response[3].response;
                        cboOutputDocument.clearOptions();
                        cboOutputDocument.addOption({value: '', label: 'All'.translate()});
                        for (i = 0; i <= data.length - 1; i += 1) {
                            cboOutputDocument.addOption({value: data[i].out_doc_uid, label: data[i].out_doc_title});
                        }
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    }
                });
                restClient.setBaseEndPoint('');
                restClient.executeRestClient();
            };

            disableAllItems = function () {
                winGrdpnlProcessPermissions.getItems()[0].setVisible(false);
                winGrdpnlProcessPermissions.getItems()[1].setVisible(false);
                winGrdpnlProcessPermissions.hideFooter();
            };

            isDirty2 = function () {
                var user,
                    dynaForm,
                    inputDocument,
                    outputDocument,
                    flagInsert = (typeof(processPermissionsData.op_case_status) === "undefined") ? true : false;

                if (flagInsert) {
                    processPermissionsData = processPermissionsDataIni;
                }

                if (cboStatusCase.getValue() != processPermissionsData.op_case_status) {
                    return true;
                }

                if (cboTargetTask.getValue() != processPermissionsData.tas_uid) {
                    return true;
                }

                user = (cboGroupOrUser.get("value") !== null && cboGroupOrUser.get("value")) ? cboGroupOrUser.get("value") : "";

                if (user != processPermissionsData.usr_uid) {
                    return true;
                }

                if (cboOriginTask.getValue() != processPermissionsData.op_task_source) {
                    return true;
                }

                if (cboParticipationRequired.getValue() != processPermissionsData.op_participate) {
                    return true;
                }

                if (cboType.getValue() != processPermissionsData.op_obj_type) {
                    return true;
                }

                switch (cboType.getValue()) {
                    case "DYNAFORM":
                        dynaForm = (cboDynaForm.getValue() !== "") ? cboDynaForm.getValue() : "0";

                        if (dynaForm != processPermissionsData.op_obj_uid) {
                            return true;
                        }

                        if (cboPermission.getValue() != processPermissionsData.op_action) {
                            return true;
                        }
                        break;
                    case "ATTACHMENT":
                        if (cboPermission.getValue() !== processPermissionsData.op_action) {
                            return true;
                        }
                        break;
                    case "INPUT":
                        inputDocument = (cboInputDocument.getValue() !== "") ? cboInputDocument.getValue() : "0";

                        if (inputDocument != processPermissionsData.op_obj_uid) {
                            return true;
                        }

                        if (cboPermission.getValue() != processPermissionsData.op_action) {
                            return true;
                        }

                        break;
                    case "OUTPUT":
                        outputDocument = (cboOutputDocument.getValue() !== "") ? cboOutputDocument.getValue() : "0";

                        if (outputDocument != processPermissionsData.op_obj_uid) {
                            return true;
                        }
                        if (cboPermission.getValue() != processPermissionsData.op_action) {
                            return true;
                        }
                        break;
                    case "CASES_NOTES":
                    case "SUMMARY_FORM":
                        break;
                    case "ANY":
                    case "MSGS_HISTORY":
                        if (cboPermission.getValue() != processPermissionsData.op_action) {
                            return true;
                        }
                        break;
                }

                return false;
            };

            isDirtyFormProcessPermission = function () {
                $("input,select,textarea").blur();
                if (frmProcessPermissions.isVisible()) {
                    var result = frmProcessPermissions.isDirty(), message_window;

                    if (isDirty2()) {
                        message_window = new PMUI.ui.MessageWindow({
                            id: "cancelMessageTriggers",
                            windowMessageType: 'warning',
                            width: 490,
                            title: "Permissions".translate(),
                            message: 'Are you sure you want to discard your changes?'.translate(),
                            footerItems: [
                                {
                                    text: "No".translate(),
                                    handler: function () {
                                        message_window.close();
                                    },
                                    buttonType: "error"
                                },
                                {
                                    text: "Yes".translate(),
                                    handler: function () {
                                        message_window.close();
                                        PROCESS_PERMISSIONS_OPTION = "";
                                        PROCESS_PERMISSIONS_UID = "";
                                        cboGroupOrUser.html.find("input").val("");
                                        cboGroupOrUser.value = "";
                                        if (clickedClose) {
                                            winGrdpnlProcessPermissions.close();
                                        } else {
                                            refreshGridPanelInMainWindow(false);
                                        }

                                    },
                                    buttonType: "success"
                                }
                            ]
                        });
                        message_window.open();
                        message_window.showFooter();
                    } else {
                        if (cboGroupOrUser.html.find("input").val()) {
                            cboGroupOrUser.html.find("input").val("");
                        }
                        cboGroupOrUser.containerList.hide();
                        PROCESS_PERMISSIONS_OPTION = "";
                        PROCESS_PERMISSIONS_UID = "";
                        refreshGridPanelInMainWindow(false);
                        if (clickedClose) {
                            winGrdpnlProcessPermissions.close();
                        }
                    }
                } else {
                    winGrdpnlProcessPermissions.close();
                }
            };
            refreshGridPanelInMainWindow = function (load) {
                disableAllItems();
                PROCESS_PERMISSIONS_OPTION = "";
                PROCESS_PERMISSIONS_UID = "";
                winGrdpnlProcessPermissions.getItems()[0].setVisible(true);
                winGrdpnlProcessPermissions.setTitle("Permissions".translate());
                load = load != null ? load : true;
                if (load) {
                    processPermissionsGetRestProxy(grdpnlProcessPermissions);
                }
            };

            processPermissionsGetRestProxy = function (grdpnl) {
                var restProxy = new PMRestClient({
                    endpoint: "process-permissions",
                    typeRequest: "get",
                    functionSuccess: function (xhr, response) {
                        listProcessPermissions = response;
                        grdpnl.setDataItems(listProcessPermissions);
                        grdpnl.sort('group_user', 'asc');
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    }
                });

                restProxy.executeRestClient();
            };

            processPermissionsPostRestProxy = function (data) {
                var restProxy = new PMRestClient({
                    endpoint: "process-permission",
                    typeRequest: "post",
                    data: data,
                    functionSuccess: function (xhr, response) {
                        refreshGridPanelInMainWindow();
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    },
                    messageSuccess: 'Permission saved successfully'.translate(),
                    flashContainer: grdpnlProcessPermissions
                });
                restProxy.executeRestClient();
            };

            processPermissionGetRestProxy = function (processPermissionsUid) {
                var restProxy = new PMRestClient({
                    endpoint: "process-permission/" + processPermissionsUid,
                    typeRequest: "get",
                    functionSuccess: function (xhr, response) {
                        var data = response;
                        processPermissionsSetForm("PUT", data);
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    }
                });
                restProxy.executeRestClient();
            };

            processPermissionsPutRestProxy = function (processPermissionsUid, data) {
                var restProxy = new PMRestClient({
                    endpoint: "process-permission/" + processPermissionsUid,
                    typeRequest: "update",
                    data: data,
                    functionSuccess: function (xhr, response) {
                        refreshGridPanelInMainWindow();
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    },
                    messageSuccess: 'Permission edited successfully'.translate(),
                    flashContainer: grdpnlProcessPermissions
                });
                restProxy.executeRestClient();
            };

            cboTargetCboOriginTaskSetOptionsRestProxy = function (cboTargetTask, cboOriginTask) {
                cboTargetTask.clearOptions();
                cboOriginTask.clearOptions();
                var restProxy = new PMRestClient({
                    typeRequest: "get",
                    functionSuccess: function (xhr, response) {
                        var data = response, i;
                        cboTargetTask.addOption({value: "", label: "Select one option".translate()});
                        cboOriginTask.addOption({value: "", label: "All Tasks".translate()});
                        for (i = 0; i <= data.diagrams[0].activities.length - 1; i += 1) {
                            cboTargetTask.addOption({
                                value: data.diagrams[0].activities[i].act_uid,
                                label: data.diagrams[0].activities[i].act_name
                            });
                            cboOriginTask.addOption({
                                value: data.diagrams[0].activities[i].act_uid,
                                label: data.diagrams[0].activities[i].act_name
                            });
                        }
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    }
                });

                restProxy.executeRestClient();
            };

            cboDynaFormSetOptionsRestProxy = function (cboDynaForm) {
                cboDynaForm.clearOptions();
                var restProxy = new PMRestClient({
                    endpoint: "dynaforms",
                    typeRequest: "get",
                    functionSuccess: function (xhr, response) {
                        var data = response, i;
                        cboDynaForm.addOption({value: "", label: "All".translate()});
                        for (i = 0; i <= data.length - 1; i += 1) {
                            cboDynaForm.addOption({value: data[i].dyn_uid, label: data[i].dyn_title});
                        }
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    }
                });
                restProxy.executeRestClient();
            };

            cboInputDocumentSetOptionsRestProxy = function (cboInputDocument) {
                cboInputDocument.clearOptions();
                var restProxy = new PMRestClient({
                    endpoint: "input-documents",
                    typeRequest: "get",
                    functionSuccess: function (xhr, response) {
                        var data = response, i;
                        cboInputDocument.addOption({value: "", label: "All".translate()});
                        for (i = 0; i <= data.length - 1; i += 1) {
                            cboInputDocument.addOption({value: data[i].inp_doc_uid, label: data[i].inp_doc_title});
                        }
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    }
                });
                restProxy.executeRestClient();
            };

            cboOutputDocumentSetOptionsRestProxy = function (cboOutputDocument) {
                var restProxy;
                cboOutputDocument.clearOptions();
                restProxy = new PMRestClient({
                    endpoint: "output-documents",
                    typeRequest: "get",
                    functionSuccess: function (xhr, response) {
                        var data = response, i;
                        cboOutputDocument.addOption({value: "", label: "All".translate()});
                        for (i = 0; i <= data.length - 1; i += 1) {
                            cboOutputDocument.addOption({value: data[i].out_doc_uid, label: data[i].out_doc_title});
                        }
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    }
                });
                restProxy.executeRestClient();
            };

            processPermissionsSetFormByType = function (type) {
                cboPermission.removeOption("DELETE");
                cboPermission.removeOption("RESEND");

                cboPermission.reset();
                cboOriginTask.setVisible(true);
                cboParticipationRequired.setVisible(true);
                cboStatusCase.setVisible(true);
                cboDynaForm.setVisible(false);
                cboInputDocument.setVisible(false);
                cboOutputDocument.setVisible(false);
                cboPermission.setVisible(false);

                switch (type) {
                    case "DYNAFORM":
                        cboDynaForm.setVisible(true);
                        cboPermission.setVisible(true);
                        break;
                    case "ATTACHMENT":
                        cboPermission.setVisible(true);
                        break;
                    case "INPUT":
                        cboPermission.addOption({value: "DELETE", label: "Delete".translate()});

                        cboInputDocument.setVisible(true);
                        cboPermission.setVisible(true);
                        break;
                    case "OUTPUT":
                        cboPermission.addOption({value: "DELETE", label: "Delete".translate()});

                        cboOutputDocument.setVisible(true);
                        cboPermission.setVisible(true);
                        break;
                    case "CASES_NOTES":
                    case "SUMMARY_FORM":
                        break;
                    case "MSGS_HISTORY":
                        cboPermission.addOption({value: "RESEND", label: "Resend".translate()});

                        cboPermission.setVisible(true);
                        break;
                    case "ANY":
                        cboPermission.setVisible(true);
                        break;
                    case "REASSIGN_MY_CASES":
                        cboOriginTask.setVisible(false);
                        cboParticipationRequired.setVisible(false);
                        cboStatusCase.setVisible(false);
                        break;
                }
            };

            processPermissionsDeleteRestProxy = function (processPermissionsUid) {
                var restProxy = new PMRestClient({
                    endpoint: "process-permission/" + processPermissionsUid,
                    typeRequest: "remove",
                    functionSuccess: function (xhr, response) {
                        refreshGridPanelInMainWindow();
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    },
                    messageSuccess: 'Permission deleted successfully'.translate(),
                    flashContainer: grdpnlProcessPermissions
                });
                restProxy.executeRestClient();
            };

            processPermissionsSetForm = function (option, data) {
                cboGroupOrUser.hideMessageRequired();
                processPermissionsData = data;
                PROCESS_PERMISSIONS_OPTION = option;
                PROCESS_PERMISSIONS_UID = (typeof(processPermissionsData.op_uid) !== "undefined") ? processPermissionsData.op_uid : "";

                disableAllItems();
                winGrdpnlProcessPermissions.showFooter();
                winGrdpnlProcessPermissions.getItems()[1].setVisible(true);

                loadDataFromServerToFields();

                switch (option) {
                    case "POST":
                        winGrdpnlProcessPermissions.setTitle("Create permission".translate());
                        frmProcessPermissions.reset();
                        processPermissionsSetFormByType(cboType.getValue());

                        break;
                    case "PUT":
                        winGrdpnlProcessPermissions.setTitle("Edit permission".translate());
                        cboStatusCase.setValue(processPermissionsData.op_case_status);
                        cboTargetTask.setValue(processPermissionsData.tas_uid);

                        var endpoint;
                        if (processPermissionsData.op_user_relation == 1) {
                            endpoint = "users/" + processPermissionsData.usr_uid;
                        }
                        if (processPermissionsData.op_user_relation == 2) {
                            endpoint = "groups/" + processPermissionsData.usr_uid;
                        }

                        if (endpoint) {
                            var restClient = new PMRestClient({
                                typeRequest: 'get',
                                functionSuccess: function (xhr, response) {
                                    if (response.hasOwnProperty("usr_uid")) {
                                        cboGroupOrUser.set("value", response["usr_uid"]);
                                        cboGroupOrUser.data = response;
                                        cboGroupOrUser.html.find("input").val(response["usr_firstname"] + " " + response["usr_lastname"] + " " + "(" + response["usr_username"] + ")");
                                    }
                                    if (response.hasOwnProperty("grp_uid")) {
                                        cboGroupOrUser.set("value", response["grp_uid"]);
                                        cboGroupOrUser.data = response;
                                        cboGroupOrUser.html.find("input").val(response["grp_title"]);
                                    }
                                },
                                functionFailure: function (xhr, response) {
                                    PMDesigner.msgWinError(response.error.message);
                                },
                                messageError: 'There are problems saving the assigned user, please try again.'.translate()
                            });
                            restClient.setBaseEndPoint(endpoint);
                            restClient.executeRestClient();
                        }

                        cboParticipationRequired.setValue(processPermissionsData.op_participate);
                        cboType.setValue(processPermissionsData.op_obj_type);
                        cboOriginTask.setValue(processPermissionsData.op_task_source);
                        processPermissionsSetFormByType(processPermissionsData.op_obj_type);
                        switch (processPermissionsData.op_obj_type) {
                            case "ANY":
                                cboPermission.setValue(processPermissionsData.op_action);
                                break;
                            case "DYNAFORM":
                                cboDynaForm.setValue(processPermissionsData.op_obj_uid);
                                cboPermission.setValue(processPermissionsData.op_action);
                                break;
                            case "ATTACHMENT":
                                cboPermission.setValue(processPermissionsData.op_action);
                                break;
                            case "INPUT":
                                cboInputDocument.setValue(processPermissionsData.op_obj_uid);
                                cboPermission.setValue(processPermissionsData.op_action);
                                break;
                            case "OUTPUT":
                                cboOutputDocument.setValue(processPermissionsData.op_obj_uid);
                                cboPermission.setValue(processPermissionsData.op_action);
                                break;
                            case "CASES_NOTES":
                            case "SUMMARY_FORM":
                                break;
                            case "MSGS_HISTORY":
                                cboPermission.setValue(processPermissionsData.op_action);
                                break;
                        }
                        break;
                }
                frmProcessPermissions.setFocus();
            };

            cboStatusCase = new PMUI.field.DropDownListField({
                id: "cboStatusCase",
                name: "cboStatusCase",
                controlsWidth: "120px",
                label: "Case Status".translate(),
                value: "ALL",
                options: [
                    {
                        value: "ALL",
                        label: "All".translate()
                    },
                    {
                        value: "DRAFT",
                        label: "DRAFT".translate()
                    },
                    {
                        value: "TO_DO",
                        label: "TO DO".translate()
                    },
                    {
                        value: "PAUSED",
                        label: "PAUSED".translate()
                    },
                    {
                        value: "COMPLETED",
                        label: "COMPLETED".translate()
                    }
                ]
            });

            cboTargetTask = new PMUI.field.DropDownListField({
                id: "cboTargetTask",
                name: "cboTargetTask",
                controlsWidth: "300px",
                label: "Target Task".translate(),
                options: null
            });
            cboGroupOrUser = new SuggestField({
                label: "Group or User".translate(),
                id: "cboGroupOrUser",
                name: "cboGroupOrUser",
                placeholder: "suggest users and groups",
                width: 500,
                required: true,
                separatingText: ["Groups", "Users"],
                dynamicLoad: {
                    data: [
                        {
                            key: "grp_uid",
                            label: ["grp_title"]
                        },
                        {
                            key: "usr_uid",
                            label: ["usr_firstname", "usr_lastname", "(", "usr_username", ")"]
                        }
                    ],
                    keys: {
                        url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE,
                        accessToken: PMDesigner.project.tokens.access_token,
                        endpoints: [
                            {
                                method: "GET",
                                url: 'groups'
                            }, {
                                method: "GET",
                                url: 'users'
                            }
                        ]
                    }
                }
            });

            cboOriginTask = new PMUI.field.DropDownListField({
                id: "cboOriginTask",
                name: "cboOriginTask",
                controlsWidth: "300px",
                label: "Origin Task".translate(),
                options: null
            });

            cboParticipationRequired = new PMUI.field.DropDownListField({
                id: "cboParticipationRequired",
                name: "cboParticipationRequired",
                controlsWidth: "70px",
                label: "Participation required?".translate(),
                value: "0",
                options: [
                    {
                        value: "0",
                        label: "No".translate()
                    },
                    {
                        value: "1",
                        label: "Yes".translate()
                    }
                ]
            });
            notification = new PMUI.field.TextAnnotationField({
                id: "requiredMessage",
                name: "Message",
                textType: PMUI.field.TextAnnotationField.TEXT_TYPES.HTML,
                text: notificationText,
                text_Align: "center"
            });
            optionsType = [
                {
                    value: "ANY",
                    label: "All".translate()
                },
                {
                    value: "DYNAFORM",
                    label: "Dynaform".translate()
                },
                {
                    value: "ATTACHMENT",
                    label: "Attachment".translate()
                },
                {
                    value: "INPUT",
                    label: "Input Document".translate()
                },
                {
                    value: "OUTPUT",
                    label: "Output Document".translate()
                },
                {
                    value: "CASES_NOTES",
                    label: "Cases Notes".translate()
                },
                {
                    value: "MSGS_HISTORY",
                    label: "Messages History".translate()
                },
                {
                    value: "REASSIGN_MY_CASES",
                    label: "Reassign my cases".translate()
                }
            ];

            if (enterprise == "1") {
                optionsType.push({value: "SUMMARY_FORM", label: "Summary Form".translate()});
            }
            // sorting the optionsType
            optionsType.sort(function(a, b) {
                return (a.label > b.label) ? 1 : ((b.label > a.label) ? -1 : 0);
            });

            cboType = new PMUI.field.DropDownListField({
                id: "cboType",
                name: "cboType",
                controlsWidth: "180px",
                label: "Type".translate(),
                value: "ANY",
                options: optionsType,
                onChange: function (newValue, prevValue) {
                    processPermissionsSetFormByType(cboType.getValue());
                }
            });

            cboDynaForm = new PMUI.field.DropDownListField({
                id: "cboDynaForm",
                name: "cboDynaForm",
                controlsWidth: "300px",
                label: "DynaForm".translate(),
                options: [],
                visible: false
            });

            cboInputDocument = new PMUI.field.DropDownListField({
                id: "cboInputDocument",
                name: "cboInputDocument",
                controlsWidth: "300px",
                label: "Input Document".translate(),
                options: [],
                visible: false
            });

            cboOutputDocument = new PMUI.field.DropDownListField({
                id: "cboOutputDocument",
                name: "cboOutputDocument",
                controlsWidth: "300px",
                label: "Output Document".translate(),
                options: [],
                visible: false
            });

            cboPermission = new PMUI.field.DropDownListField({
                id: "cboPermission",
                name: "cboPermission",
                controlsWidth: "100px",
                label: "Permission".translate(),
                value: "VIEW",
                options: [
                    {
                        value: "VIEW",
                        label: "View".translate()
                    },
                    {
                        value: "BLOCK",
                        label: "Block".translate()
                    }
                ],
                visible: true
            });

            frmProcessPermissions = new PMUI.form.Form({
                id: "frmProcessPermissions",

                title: "",
                width: "890px",
                items: [
                    cboType,
                    cboStatusCase,
                    cboTargetTask,
                    cboOriginTask,
                    cboParticipationRequired,
                    cboDynaForm,
                    cboInputDocument,
                    cboOutputDocument,
                    cboPermission
                ]
            });

            btnCreate = new PMUI.ui.Button({
                id: "btnCreate",
                text: "Create".translate(),
                height: "36px",
                width: 100,
                style: {
                    cssClasses: [
                        "mafe-button-create"
                    ]
                },

                handler: function () {
                    frmProcessPermissions.reset();
                    processPermissionsDataIni = {};

                    processPermissionsDataIni.op_case_status = "ALL";
                    processPermissionsDataIni.tas_uid = "";
                    processPermissionsDataIni.usr_uid = "";
                    processPermissionsDataIni.op_task_source = "";
                    processPermissionsDataIni.op_participate = "0";
                    processPermissionsDataIni.op_obj_type = "ANY";
                    processPermissionsDataIni.op_obj_uid = "";
                    processPermissionsDataIni.op_action = "VIEW";

                    processPermissionsSetForm("POST", {});
                }
            });

            grdpnlProcessPermissions = new PMUI.grid.GridPanel({
                id: "grdpnlProcessPermissions",
                filterPlaceholder: "Search ...".translate(),
                emptyMessage: 'No records found'.translate(),
                nextLabel: 'Next'.translate(),
                previousLabel: 'Previous'.translate(),
                customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
                    return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
                },
                pageSize: 10,
                width: "96%",
                tableContainerHeight: 374,
                //height: DEFAULT_WINDOW_HEIGHT - 60,
                style: {
                    cssClasses: ["mafe-gridPanel"]
                },
                filterable: true,
                columns: [
                    {
                        columnData: "group_user",
                        title: "Group or User".translate(),
                        alignmentCell: 'left',
                        width: "190px",
                        sortable: true
                    },
                    {
                        columnData: "op_obj_type",
                        title: "Type".translate(),
                        alignmentCell: 'left',
                        width: "100px",
                        sortable: true
                    },
                    {
                        columnData: "participated",
                        title: "Participation".translate(),
                        alignmentCell: 'left',
                        width: "115px",
                        sortable: true
                    },
                    {
                        columnData: "object",
                        title: "Object".translate(),
                        alignmentCell: 'left',
                        width: "158px",
                        sortable: true
                    },
                    {
                        columnData: "op_action",
                        title: "Permission".translate(),
                        alignmentCell: 'left',
                        width: "100px",
                        sortable: true
                    },
                    {
                        columnData: "op_case_status",
                        title: "Status".translate(),
                        alignmentCell: 'left',
                        width: "70px",
                        sortable: true
                    },
                    {
                        id: 'grdpnlProcessPermissionsButtonEdit',
                        dataType: "button",
                        title: "",
                        buttonLabel: "Edit".translate(),
                        width: "70px",
                        buttonStyle: {
                            cssClasses: [
                                "mafe-button-edit"
                            ]
                        },

                        onButtonClick: function (row, grid) {
                            var data;
                            frmProcessPermissions.reset();
                            data = row.getData();
                            processPermissionGetRestProxy(data.op_uid);
                        }
                    },
                    {
                        id: 'grdpnlProcessPermissionsButtonDelete',
                        dataType: "button",
                        title: "",
                        buttonLabel: "Delete".translate(),
                        width: "80px",
                        buttonStyle: {
                            cssClasses: [
                                "mafe-button-delete"
                            ]
                        },

                        onButtonClick: function (row, grid) {
                            var data = row.getData(), msgWarning;
                            msgWarning = new PMUI.ui.MessageWindow({
                                id: "msgWarning",
                                windowMessageType: 'warning',
                                width: 490,
                                title: "Permissions".translate(),
                                message: "Do you want to delete this permission?".translate(),
                                footerItems: [
                                    {
                                        id: 'msgWarningButtonNo',
                                        text: "No".translate(),
                                        handler: function () {
                                            msgWarning.close();
                                        },
                                        buttonType: "error"
                                    },
                                    {
                                        id: 'msgWarningButtonYes',
                                        text: "Yes".translate(),
                                        handler: function () {
                                            processPermissionsDeleteRestProxy(data.op_uid);
                                            msgWarning.close();
                                        },
                                        buttonType: "success"
                                    }
                                ]
                            });

                            msgWarning.open();
                            msgWarning.dom.titleContainer.style.height = '17px';
                            msgWarning.showFooter();
                        }
                    }
                ],
                dataItems: null
            });

            winGrdpnlProcessPermissions = new PMUI.ui.Window({
                id: "winGrdpnlProcessPermissions",
                title: "Permissions".translate(),
                width: DEFAULT_WINDOW_WIDTH,
                height: DEFAULT_WINDOW_HEIGHT,
                buttonPanelPosition: "bottom",
                footerAlign: "right",
                onBeforeClose: function () {
                    clickedClose = true;
                    isDirtyFormProcessPermission();
                },
                footerItems: [
                    {
                        id: "btnCancel",
                        text: "Cancel".translate(),
                        buttonType: "error",
                        handler: function () {
                            clickedClose = false;
                            isDirtyFormProcessPermission();
                        }
                    },
                    {
                        id: "btnSave",
                        text: "Save".translate(),
                        buttonType: "success",
                        handler: function () {
                            var cboGroupOrUserValue = "", groupOrUser, data;

                            if (frmProcessPermissions.isValid() && cboGroupOrUser.isValid()) {
                                if (cboGroupOrUser.data) {
                                    if (cboGroupOrUser.data.hasOwnProperty("usr_uid")) {
                                        cboGroupOrUserValue = "1|" + cboGroupOrUser.get("value");
                                    }
                                    if (cboGroupOrUser.data.hasOwnProperty("grp_uid")) {
                                        cboGroupOrUserValue = "2|" + cboGroupOrUser.get("value");
                                    }
                                } else {
                                    cboGroupOrUserValue = "";
                                }
                                groupOrUser = cboGroupOrUserValue.split("|");

                                data = {
                                    op_case_status: cboStatusCase.getValue(),
                                    tas_uid: cboTargetTask.getValue() === '0' ? '' : cboTargetTask.getValue(),
                                    op_user_relation: groupOrUser[0],
                                    usr_uid: groupOrUser[1],
                                    op_task_source: cboOriginTask.getValue() === '0' ? '' : cboOriginTask.getValue(),
                                    op_participate: cboParticipationRequired.getValue()
                                };

                                switch (cboType.getValue()) {
                                    case "DYNAFORM":
                                        data["op_obj_type"] = cboType.getValue();
                                        data["dynaforms"] = cboDynaForm.getValue();
                                        data["op_action"] = cboPermission.getValue();
                                        break;
                                    case "INPUT":
                                        data["op_obj_type"] = cboType.getValue();
                                        data["inputs"] = cboInputDocument.getValue();
                                        data["op_action"] = cboPermission.getValue();
                                        break;
                                    case "OUTPUT":
                                        data["op_obj_type"] = cboType.getValue();
                                        data["outputs"] = cboOutputDocument.getValue();
                                        data["op_action"] = cboPermission.getValue();
                                        break;
                                    case "CASES_NOTES":
                                        data["op_obj_type"] = cboType.getValue();
                                        data["op_action"] = cboPermission.getValue();
                                        break;
                                    case "REASSIGN_MY_CASES":
                                        data = {};
                                        data["op_user_relation"] =  groupOrUser[0];
                                        data["usr_uid"] = groupOrUser[1];
                                        data["tas_uid"] = cboTargetTask.getValue() === '0' ? '' : cboTargetTask.getValue();
                                        data["op_obj_type"] = cboType.getValue();
                                        break;
                                    default:
                                        data["op_obj_type"] = cboType.getValue();
                                        data["op_action"] = cboPermission.getValue();
                                        break;
                                }
                                switch (PROCESS_PERMISSIONS_OPTION) {
                                    case "POST":
                                        processPermissionsPostRestProxy(data);
                                        break;
                                    case "PUT":
                                        processPermissionsPutRestProxy(PROCESS_PERMISSIONS_UID, data);
                                        break;
                                }
                            } else {
                                cboGroupOrUser.showMessageRequired();
                                frmProcessPermissions.addItem(notification)
                            }

                            cboGroupOrUser.html.find("input").val("");
                            cboGroupOrUser.value = "";
                        }
                    }
                ]
            });

            winGrdpnlProcessPermissions.addItem(grdpnlProcessPermissions);
            winGrdpnlProcessPermissions.addItem(frmProcessPermissions);

            refreshGridPanelInMainWindow();
            if (typeof listProcessPermissions !== "undefined") {
                winGrdpnlProcessPermissions.open();
                $(cboGroupOrUser.createHTML()).insertBefore(cboType.html);


                cboGroupOrUser.html.find("input").val("");
                jQuery(grdpnlProcessPermissions.html).css({
                    margin: "2px"
                });
                winGrdpnlProcessPermissions.body.style.height = "auto";
                $('#grdpnlProcessPermissions .pmui-textcontrol').css({'margin-top': '5px', width: '250px'});
                winGrdpnlProcessPermissions.defineEvents();
                applyStyleWindowForm(winGrdpnlProcessPermissions);

                grdpnlProcessPermissions.dom.toolbar.appendChild(btnCreate.getHTML());
                btnCreate.defineEvents();
                disableAllItems();
                refreshGridPanelInMainWindow();
            }
        };

        PMDesigner.processPermissions.create = function () {
            PMDesigner.processPermissions();

            frmProcessPermissions.reset();
            processPermissionsDataIni = {};

            processPermissionsDataIni.op_case_status = "ALL";
            processPermissionsDataIni.tas_uid = "";
            processPermissionsDataIni.usr_uid = "";
            processPermissionsDataIni.op_task_source = "";
            processPermissionsDataIni.op_participate = "0";
            processPermissionsDataIni.op_obj_type = "ANY";
            processPermissionsDataIni.op_obj_uid = "";
            processPermissionsDataIni.op_action = "VIEW";

            processPermissionsSetForm("POST", {});
        };
    }()
);

(function () {
    var winHtmlShow, loadValuesStartMessage;

    PMDesigner.startMessage = function (element) {
        var winGrdpnlStartMessage,
            btnNew,
            cboUser,
            cboDynaForm,
            btnGenerateStartMessagePage,
            frmDataStartMessage,
            frmHtml,
            winFrmDataStartMessage,
            restProxy,
            disableAllItems,
            winFrmStartMessageShow,
            updateStartMessage,
            loadDataDynaform,
            loadDataUsers,
            loadDataForm,
            listUsers = [],
            cboMethod,
            listDynaforms = [],
            dataForm = [],
            enableGenerateWebEntry,
            btnSaveStartMessagePage,
            btnStartMessageCancel,
            btnClose,
            startMessage = element;

        disableAllItems = function () {
            winGrdpnlStartMessage.getItems()[0].setVisible(false);
            winGrdpnlStartMessage.getItems()[1].setVisible(false);
            btnGenerateStartMessagePage.setVisible(false);
            btnStartMessageCancel.setVisible(false);
            btnClose.setVisible(false);
            winGrdpnlStartMessage.hideFooter();
        };

        winFrmStartMessageShow = function () {
            disableAllItems();
            frmDataStartMessage.reset();
            winGrdpnlStartMessage.showFooter();
            winGrdpnlStartMessage.getItems()[0].setVisible(true);
            btnGenerateStartMessagePage.setVisible(true);
            btnStartMessageCancel.setVisible(true);
            loadDataUsers();
            loadDataDynaform();
            loadDataForm();
            frmDataStartMessage.setFocus();
        };

        winHtmlShow = function (msgHtml) {
            if (typeof msgHtml == 'undefined') {
                msgHtml = dataForm.we_data;
                if (dataForm.we_method == "WS") {
                    window.open(msgHtml);
                    winGrdpnlStartMessage.close();
                    return false;
                }
            }
            disableAllItems();
            winGrdpnlStartMessage.showFooter();
            winGrdpnlStartMessage.footer.getItems()[1].setVisible(false);
            btnClose.setVisible(true);
            winGrdpnlStartMessage.getItems()[1].setVisible(true);
            winGrdpnlStartMessage.getItems()[1].setWidth(925);
            winGrdpnlStartMessage.getItems()[1].setHeight(440);
            winGrdpnlStartMessage.getItems()[1].hideHeader();
            frmHtml.getFields()[0].setHeight(396);
            frmHtml.getItems()[0].setValue(msgHtml);
            frmHtml.panel.style.addProperties({'box-sizing': 'initial'});
            frmHtml.style.addProperties({marginLeft: '16px'});
            frmHtml.setFocus();
            return true;
        };

        loadValuesStartMessage = function (flag) {
            var viewFlagForm,
                restClient,
                response;
            listUsers = [];
            listDynaforms = [];
            dataForm = [];
            restClient = new PMRestClient({
                typeRequest: 'post',
                multipart: true,
                data: {
                    calls: [
                        {
                            url: 'activity/' + startMessage.ports.get(0).connection.flo_element_dest + '/assignee/all',
                            method: 'GET'
                        },
                        {
                            url: "activity/" + startMessage.ports.get(0).connection.flo_element_dest + "/steps",
                            method: 'GET'
                        }, {
                            url: 'web-entry/' + startMessage.evn_uid,
                            method: 'GET'
                        }
                    ]
                },
                functionSuccess: function (xhr, response) {
                    listUsers = response[0].response;
                    listDynaforms = response[1].response;
                    dataForm = response[2].response;
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            });
            restClient.executeRestClient();
            if (flag) {
                viewFlagForm = false;
                if (typeof dataForm == 'object' && dataForm.we_data != null) {
                    viewFlagForm = true;
                }
                response = [listUsers.length, listDynaforms.length, viewFlagForm];
                return response;
            }
            return true;
        };

        updateStartMessage = function (data) {
            restClient = new PMRestClient({
                typeRequest: 'post',
                multipart: true,
                data: {
                    calls: [
                        {
                            url: "web-entry/" + startMessage.evn_uid,
                            method: 'PUT',
                            data: data
                        },
                        {
                            url: 'web-entry/' + startMessage.evn_uid,
                            method: 'GET'
                        }
                    ]
                },
                functionSuccess: function (xhr, response) {
                    dataForm = response[1].response;
                    if (data.we_method == "HTML") {
                        winHtmlShow(response.we_data);
                    }
                    btnGenerateStartMessagePage.setVisible(false);
                    btnStartMessageCancel.setVisible(false);
                    btnClose.setVisible(true);
                    winGrdpnlStartMessage.footer.getItems()[1].setVisible(false);
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                },
                messageError: ["There are problems updating the Web Entry, please try again.".translate()],
                messageSuccess: ['Web Entry updated successfully'.translate()],
                flashContainer: frmDataStartMessage
            });
            restClient.executeRestClient();
        };

        loadDataDynaform = function () {
            var i;
            cboDynaForm.clearOptions();
            for (i = 0; i <= listDynaforms.length - 1; i++) {
                if (listDynaforms[i].step_type_obj == "DYNAFORM") {
                    cboDynaForm.addOption({
                        value: listDynaforms[i].step_uid_obj,
                        label: listDynaforms[i].obj_title
                    });
                }
            }
        };

        loadDataUsers = function () {
            var i;
            cboUser.clearOptions();
            for (i = 0; i <= listUsers.length - 1; i++) {
                cboUser.addOption({
                    value: listUsers[i].aas_uid,
                    label: listUsers[i].aas_name + ' ' + listUsers[i].aas_lastname
                });
            }
        };

        loadDataForm = function () {
            cboUser.setValue(dataForm.usr_uid);
            cboDynaForm.setValue(dataForm.dyn_uid);
            cboMethod.setValue(dataForm.we_method);
        };

        enableGenerateWebEntry = function () {
            btnGenerateStartMessagePage.setVisible(true);
            btnStartMessageCancel.setVisible(true);
            btnClose.setVisible(false);
            winGrdpnlStartMessage.footer.getItems()[1].setVisible(true);
        };
        cboUser = new PMUI.field.DropDownListField({
            id: "cboUser",
            name: "cboUser",
            label: "User".translate(),
            required: true,
            options: null,
            onChange: function (newVal, oldVal) {
                enableGenerateWebEntry();
            },
            controlsWidth: 350
        });

        cboDynaForm = new PMUI.field.DropDownListField({
            id: "cboDynaForm",
            name: "cboDynaForm",
            label: "Initial Dynaform".translate(),
            options: null,
            required: true,
            onChange: function (newVal, oldVal) {
                enableGenerateWebEntry();
            },
            controlsWidth: 350
        });

        cboMethod = new PMUI.field.DropDownListField({
            id: "cboMethod",
            name: "cboMethod",
            label: "Method".translate(),
            value: "WS",
            options: [
                {
                    value: "WS",
                    label: "PHP pages with Web Services".translate()
                },
                {
                    value: "HTML",
                    label: "Single HTML".translate()
                }
            ],
            onChange: function (newVal, oldVal) {
                enableGenerateWebEntry();
            },
            controlsWidth: 350
        });

        btnGenerateStartMessagePage = new PMUI.ui.Button({
            id: "btnGenerateStartMessagePage",
            text: "Generate Web Entry Page".translate(),
            handler: function () {
                var flagGenerateStartMessage = 0, data;
                data = {
                    tas_uid: startMessage.ports.get(0).connection.flo_element_dest,
                    dyn_uid: cboDynaForm.getValue(),
                    usr_uid: cboUser.getValue(),
                    we_title: startMessage.evn_name,
                    we_description: '',
                    we_method: cboMethod.getValue(),
                    we_input_document_access: 1
                };

                if (frmDataStartMessage.isValid()) {
                    updateStartMessage(data);
                }
            },
            buttonType: 'success',
            height: 31,
            visible: true
        });
        btnSaveStartMessagePage = new PMUI.ui.Button({
            id: "btnGenerateStartMessagePage",
            text: "Save".translate(),
            handler: function () {
                var flagGenerateStartMessage = 0, data;
                data = {
                    tas_uid: startMessage.ports.get(0).connection.flo_element_dest,
                    dyn_uid: cboDynaForm.getValue(),
                    usr_uid: cboUser.getValue(),
                    we_title: startMessage.evn_name,
                    we_description: '',
                    we_method: cboMethod.getValue(),
                    we_input_document_access: 1
                };

                if (frmDataStartMessage.isValid()) {
                    updateStartMessage(data);
                }
            },
            buttonType: 'success',
            height: 31,
            visible: true
        });

        frmDataStartMessage = new PMUI.form.Form({
            id: "frmDataStartMessage",
            title: "",
            width: DEFAULT_WINDOW_WIDTH - 70,
            items: [
                cboUser,
                cboDynaForm,
                cboMethod
            ],
            visibleHeader: false
        });

        btnClose = new PMUI.ui.Button({
            id: "btnClose",
            text: "Close".translate(),
            handler: function () {
                winGrdpnlStartMessage.close();
            },
            buttonType: 'success',
            height: 31
        });

        btnStartMessageCancel = new PMUI.ui.Button({
            id: "btnStartMessageCancel",
            text: "Cancel".translate(),
            handler: function () {
                var message_window;
                if (frmDataStartMessage.isDirty()) {
                    message_window = new PMUI.ui.MessageWindow({
                        windowMessageType: 'warning',
                        id: "cancelMessageStartTimer",
                        title: "Start Message Event".translate(),
                        message: 'Are you sure you want to discard your changes?'.translate(),
                        bodyHeight: 'auto',
                        width: 490,
                        footerItems: [
                            {
                                text: 'No'.translate(),
                                handler: function () {
                                    message_window.close();
                                },
                                buttonType: "error"
                            },
                            {
                                text: 'Yes'.translate(),
                                handler: function () {
                                    message_window.close();
                                    winGrdpnlStartMessage.close();
                                },
                                buttonType: "success"
                            }
                        ]
                    });
                    message_window.open();
                    message_window.showFooter();
                } else {
                    frmDataStartMessage.reset();
                    winGrdpnlStartMessage.close();
                }
            }
        });

        frmHtml = new PMUI.form.Form({
            id: "frmHtml",
            title: "",
            width: DEFAULT_WINDOW_WIDTH - 43,
            items: [
                {
                    id: "txtHtml",
                    name: "txtHtml",
                    pmType: "textarea",
                    valueType: "string",
                    rows: 400,
                    value: '',
                    controlsWidth: DEFAULT_WINDOW_WIDTH - 50,
                    labelVisible: false,
                    style: {cssClasses: ['mafe-textarea-resize']}
                }
            ]
        });

        winGrdpnlStartMessage = new PMUI.ui.Window({
            id: "winGrdpnlStartMessage",
            title: "Start Message Event".translate(),
            height: DEFAULT_WINDOW_HEIGHT,
            width: DEFAULT_WINDOW_WIDTH,
            buttonPanelPosition: "top",
            buttons: [btnSaveStartMessagePage, {pmType: 'label', text: 'or'}, btnStartMessageCancel, btnClose]
        });

        winGrdpnlStartMessage.addItem(frmDataStartMessage);

        openForm = function () {
            winGrdpnlStartMessage.open();
            winGrdpnlStartMessage.defineEvents();
            applyStyleWindowForm(winGrdpnlStartMessage);
            winGrdpnlStartMessage.footer.html.style.textAlign = 'right';
            winFrmStartMessageShow();
        }
    };

    PMDesigner.startMessage.openForm = function (element) {
        openForm();
    };

    PMDesigner.startMessage.viewForm = function (element) {
        openForm();
    };

    PMDesigner.startMessage.validate = function (starMessageEvent) {
        if (starMessageEvent.ports.isEmpty()) {
            PMDesigner.msgFlash('Must connect to a Task'.translate(), document.body, 'error', 4000, 5);
            return [false, false];
        }
        PMDesigner.startMessage(starMessageEvent);
        response = loadValuesStartMessage(true);
        if (response[0] == 0) {
            PMDesigner.msgFlash('The task doesn\'t have assigned users'.translate(), document.body, 'info', 4000);
            return [false, false];
        }
        if (response[1] == 0) {
            PMDesigner.msgFlash('The task doesn\'t have assigned Dynaforms'.translate(), document.body, 'info', 4000);
            return [false, false];
        }
        return [true, response[2]];
    }
}());

(function () {
    var windowPropSub;

    PMDesigner.propertiesSubProcess = function (activity) {
        var typeVariables = ['@@', '@#', '@=', '@&'],
            restClient,
            isDirtyFormSubProcess,
            getSubProcess,
            formVarTexOriginOut,
            updateSubProcess,
            formVarTexOriginIn,
            loadDropProcess,
            formVarTexTargetOut,
            formVarTexTargetIn,
            windowPropertiesSub,
            formProperties,
            sepInputs,
            gridVariablesOut,
            gridVariablesIn,
            formVariablesPanelOut,
            formVariablesPanelIn,
            validateVariable,
            formVarButtonAddOut,
            formVarButtonAddIn,
            labelVariablesOut,
            labelVariablesIn,
            loadDataServer,
            loadActivity,
            isDirty = false;

        restClient = new PMRestClient({
            endpoint: 'projects',
            typeRequest: 'get',
            functionSuccess: function (xhr, response) {
                for (var i = 0; i < response.length; i += 1) {
                    if (response[i].prj_uid != PMDesigner.project.projectId) {
                        formProperties.getField('out_doc_process').addOption({
                            label: response[i].prj_name,
                            value: response[i].prj_uid
                        });
                    }
                }
            },
            functionFailure: function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            },
            messageError: "There are problems getting the output documents, please try again.".translate()
        });
        isDirtyFormSubProcess = function () {
            var message_window;
            if (formProperties.isDirty() || isDirty) {
                message_window = new PMUI.ui.MessageWindow({
                    windowMessageType: 'warning',
                    width: 490,
                    bodyHeight: 'auto',
                    id: "cancelSaveSubprocPropertiesWin",
                    title: "Sub-process Properties".translate(),
                    message: 'Are you sure you want to discard your changes?'.translate(),
                    footerItems: [
                        {
                            text: "No".translate(),
                            handler: function () {
                                message_window.close();
                            },
                            buttonType: "error"
                        },
                        {
                            text: "Yes".translate(),
                            handler: function () {
                                message_window.close();
                                formProperties.reset();
                                gridVariablesOut.setDataItems("");
                                gridVariablesIn.setDataItems("");
                                formProperties.setDirty(false);
                                windowPropertiesSub.close();
                            },
                            buttonType: "success"
                        }
                    ]
                });
                message_window.open();
                message_window.showFooter();
            } else {
                windowPropertiesSub.close();
            }
        };
        getSubProcess = function () {
            var restProxy = new PMRestClient({
                endpoint: "subprocess/" + activity.act_uid,
                typeRequest: "get",
                functionSuccess: function (xhr, response) {
                    var yout,
                        oout,
                        jout,
                        itemsOut,
                        i,
                        xin,
                        yin,
                        oin,
                        jin,
                        dout,
                        din,
                        itemsIn,
                        xout;
                    formProperties.getField('out_doc_title').setValue(response.spr_name);
                    formProperties.getField('out_doc_process').setValue(response.spr_pro);
                    formProperties.getField('out_doc_type').setValue(response.spr_synchronous);

                    if (response.spr_variables_out != false) {
                        xout = JSON.stringify(response.spr_variables_out);
                        yout = xout.substring(1, xout.length - 1);
                        oout = yout.replace(/"/g, '');
                        jout = oout.split(',');

                        itemsOut = [];
                        for (i = 0; i < jout.length; i += 1) {
                            dout = jout[i].split(':');
                            itemsOut.push({
                                origin: dout[0],
                                target: dout[1]
                            });
                        }
                        gridVariablesOut.setDataItems(itemsOut);

                        gridVariablesIn.setVisible(false);
                        formVariablesPanelIn.setVisible(false);
                    }

                    if (response.spr_variables_in != false && response.spr_variables_in != undefined) {
                        xin = JSON.stringify(response.spr_variables_in);
                        yin = xin.substring(1, xin.length - 1);
                        oin = yin.replace(/"/g, '');
                        jin = oin.split(',');

                        itemsIn = [];
                        for (i = 0; i < jin.length; i += 1) {
                            din = jin[i].split(':');
                            itemsIn.push({
                                origin: din[0],
                                target: din[1]
                            });
                        }
                        gridVariablesIn.setDataItems(itemsIn);
                    }

                    if (response.spr_synchronous == "1") {
                        gridVariablesIn.setVisible(true);
                        formVariablesPanelIn.setVisible(true);
                    }
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            });
            restProxy.executeRestClient();
        };

        updateSubProcess = function (data) {
            var restProxy = new PMRestClient({
                endpoint: "subprocess/" + activity.act_uid,
                typeRequest: "update",
                data: data,
                functionSuccess: function (xhr, response) {
                    var name = formProperties.getData().out_doc_title;
                    activity.setName(name);
                    activity.setActName(name);
                    PMDesigner.project.dirty = true;

                    formProperties.reset();
                    gridVariablesOut.setDataItems("");
                    gridVariablesIn.setDataItems("");
                    formProperties.setDirty(false);
                    windowPropertiesSub.close();
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            });
            restProxy.executeRestClient();
        };

        formVarTexOriginOut = new CriteriaField({
            id: 'idformVarTexOriginOut',
            pmType: "text",
            name: 'nmformVarTexOriginOut',
            placeholder: 'Origin'.translate(),
            labelVisible: false,
            controlsWidth: 150
        });

        formVarTexOriginIn = new CriteriaField({
            id: 'idformVarTexOriginIn',
            pmType: "text",
            name: 'nmformVarTexOriginIn',
            placeholder: 'Select Origin Process'.translate(),
            labelVisible: false,
            controlsWidth: 150
        });

        formVarTexTargetOut = new CriteriaField({
            id: 'idformVarTexTargetOut',
            pmType: "text",
            name: 'nmformVarTexTargetOut',
            placeholder: 'Select Target Process'.translate(),
            labelVisible: false,
            controlsWidth: 150,
            required: true
        });

        formVarTexTargetIn = new CriteriaField({
            id: 'idformVarTexTargetIn',
            pmType: "text",
            name: 'nmformVarTexTargetIn',
            placeholder: 'Target'.translate(),
            labelVisible: false,
            controlsWidth: 150
        });

        windowPropertiesSub = new PMUI.ui.Window({
            id: 'propSubWindow',
            title: "Sub-process Properties".translate(),
            height: DEFAULT_WINDOW_HEIGHT,
            width: DEFAULT_WINDOW_WIDTH,
            bodyHeight: '465px',
            bodyWidth: '900px',
            onBeforeClose: function () {
                isDirtyFormSubProcess();
            },
            buttons: [
                {
                    id: 'propSubButtonClose',
                    text: "Close".translate(),
                    buttonType: 'error',
                    handler: function () {
                        isDirtyFormSubProcess();
                    }
                },
                {
                    id: 'propSubButtonSave',
                    text: "Save".translate(),
                    buttonType: 'success',
                    handler: function () {
                        if (!formProperties.isValid()) {
                            return;
                        }
                        var propertiesData = formProperties.getData();
                        var variablesOutData = gridVariablesOut.getData();
                        var variablesOut = {};
                        if (variablesOutData.length > "0") {
                            for (i = 0; i < variablesOutData.length; i += 1) {
                                variablesOut[variablesOutData[i].origin] = variablesOutData[i].target;
                            }
                        }
                        var variablesIn = {};
                        if (propertiesData.out_doc_type == "1") {
                            var variablesInData = gridVariablesIn.getData();
                            if (variablesInData.length > "0") {
                                for (j = 0; j < variablesInData.length; j += 1) {
                                    variablesIn[variablesInData[j].origin] = variablesInData[j].target;
                                }
                            }
                        }

                        var dataToSend = {
                            spr_pro: propertiesData.out_doc_process,
                            spr_tas: propertiesData.spr_tas,
                            spr_name: propertiesData.out_doc_title,
                            spr_synchronous: propertiesData.out_doc_type,
                            spr_variables_out: variablesOut,
                            spr_variables_in: variablesIn
                        };

                        updateSubProcess(dataToSend);
                    }
                }
            ],
            buttonPanelPosition: 'bottom',
            buttonsPosition: 'right'
        });

        formProperties = new PMUI.form.Form({
            id: 'propSubForm',
            title: "",
            fieldset: true,
            visibleHeader: false,
            width: 926,
            height: 'auto',
            items: [
                {
                    id: 'propSubFormTitle',
                    pmType: "text",
                    name: 'out_doc_title',
                    label: "Sub-Process name".translate(),
                    required: true,
                    controlsWidth: 300
                },
                {
                    id: 'propSubFormProcess',
                    pmType: "dropdown",
                    name: 'out_doc_process',
                    label: "Process".translate(),
                    controlsWidth: 300,
                    required: true,
                    options: [
                        {
                            label: "- Select a process -".translate(),
                            value: "",
                            disabled: true,
                            selected: true
                        }
                    ],
                    value: "",
                    onChange: function (a, b) {
                        if (a.trim().length !== 0) {
                            formVarTexTargetOut.buttonHTML.enable();
                            formVarTexOriginIn.buttonHTML.enable();
                            formVarTexTargetOut.controls[0].setPlaceholder("Target");
                            formVarTexOriginIn.controls[0].setPlaceholder("Origin");
                        } else {
                            formVarTexTargetOut.buttonHTML.disable();
                            formVarTexOriginIn.buttonHTML.disable();
                        }
                        formVarTexTargetOut.setProcess(a);
                        formVarTexOriginIn.setProcess(a);
                        loadActivity(a);
                    }
                },
                {
                    id: 'propSubFormActivity',
                    pmType: "dropdown",
                    name: 'spr_tas',
                    label: "Starting activity".translate(),
                    controlsWidth: 300,
                    required: true,
                    options: [
                        {
                            label: "- Select starting activity -".translate(),
                            value: "",
                            disabled: true,
                            selected: true
                        }
                    ],
                    value: "",
                    onChange: function (a, b) {
                    }
                },
                {
                    id: 'propSubFormType',
                    pmType: "dropdown",
                    name: 'out_doc_type',
                    label: "Type".translate(),
                    controlsWidth: 150,
                    required: true,
                    options: [
                        {
                            label: "Asynchronous".translate(),
                            value: "0"
                        },
                        {
                            label: "Synchronous".translate(),
                            value: "1"
                        }
                    ],
                    value: "0",
                    onChange: function (a, b) {
                        isDirty = true;
                    }
                }
            ],
            onChange: function (newValue, prevValue) {
                switch (newValue.value) {
                    case "1" :
                        gridVariablesIn.setVisible(true);
                        gridVariablesIn.style.addProperties({marginLeft: '45px'});
                        formVariablesPanelIn.setVisible(true);
                        formVariablesPanelIn.getItems()[2].setWidth(80);
                        $(formVariablesPanelIn.html).css({width: '850px', marginLeft: '45px'});
                        windowPropertiesSub.getItems()[4].setVisible(true);
                        break;
                    case "0":
                        gridVariablesIn.setVisible(false);
                        formVariablesPanelIn.setVisible(false);
                        windowPropertiesSub.getItems()[4].setVisible(false);
                        break;
                    default :
                        break;
                }
            }
        });

        sepInputs = new PMSeparatorLineField({
            controlHeight: '1px',
            controlColor: "#CDCDCD",
            controlsWidth: "890px",
            marginLeft: '0%'
        });

        formProperties.addItem(sepInputs);

        gridVariablesOut = new PMUI.grid.GridPanel({
            id: 'propSubGridVariablesOut',
            emptyMessage: 'No records found'.translate(),
            nextLabel: 'Next'.translate(),
            previousLabel: 'Previous'.translate(),
            customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
                return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
            },
            pageSize: 10,
            style: {
                cssClasses: ["mafe-gridPanel"]
            },
            columns: [
                {
                    title: 'Origin'.translate(),
                    dataType: 'string',
                    width: '40%',
                    alignmentCell: 'left',
                    columnData: "origin"
                },
                {
                    title: 'Target'.translate(),
                    dataType: 'string',
                    width: '40%',
                    alignmentCell: 'left',
                    columnData: "target"
                },
                {
                    id: 'propSubGridVariablesOutButtonDelete',
                    title: '',
                    dataType: 'button',
                    width: '20%',
                    buttonLabel: 'Delete'.translate(),
                    onButtonClick: function (row, grid) {
                        grid.removeItem(row);
                        isDirty = true;
                    },
                    buttonStyle: {
                        cssClasses: [
                            'mafe-button-delete'
                        ]
                    }

                }
            ]
        });

        gridVariablesIn = new PMUI.grid.GridPanel({
            id: 'propSubGridVariablesIn',
            emptyMessage: 'No records found'.translate(),
            nextLabel: 'Next'.translate(),
            previousLabel: 'Previous'.translate(),
            customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
                return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
            },
            pageSize: 10,
            columns: [
                {
                    title: 'Origin'.translate(),
                    dataType: 'string',
                    width: '40%',
                    alignmentCell: 'left',
                    columnData: "origin"
                },
                {
                    title: 'Target'.translate(),
                    dataType: 'string',
                    width: '40%',
                    alignmentCell: 'left',
                    columnData: "target"
                },
                {
                    id: 'propSubGridVariablesInButtonDelete',
                    title: '',
                    dataType: 'button',
                    width: '20%',
                    buttonLabel: 'Delete'.translate(),
                    onButtonClick: function (row, grid) {
                        grid.removeItem(row);
                        isDirty = true;
                    },
                    buttonStyle: {
                        cssClasses: [
                            'mafe-button-delete'
                        ]
                    }
                }
            ]
        });

        formVariablesPanelOut = new PMUI.core.Panel({
            id: "formVariablesPanelOut",
            layout: "hbox",
            fieldset: true,
            width: DEFAULT_WINDOW_WIDTH - 70
        });

        formVariablesPanelIn = new PMUI.core.Panel({
            id: "formVariablesPanelIn",
            layout: "hbox",
            fieldset: true,
            legend: "Variables In".translate(),
            width: DEFAULT_WINDOW_WIDTH - 70
        });

        validateVariable = function (field) {
            var value;
            field.controls[0].style.removeClasses(['error'])
            value = field.getValue();
            if (value == '') {
                PMDesigner.msgWinWarning('Please insert variable before adding to the list.'.translate());
                field.controls[0].style.addClasses(['error'])
                return false;
            }
            if (typeVariables.indexOf(String(value.substring(0, 2))) != -1) {
                return true;
            }
            PMDesigner.msgWinWarning("The value introduced doesn\'t have the correct format with a vallid prefix (@@, @#, @=, @&)".translate());
            field.controls[0].style.addClasses(['error'])
            return false;
        };

        formVarButtonAddOut = new PMUI.ui.Button({
            id: 'formVarButtonAddOut',
            text: 'Add'.translate(),
            width: 60,
            visible: false,
            buttonType: "success",
            buttonStyle: {
                cssClasses: [
                    'mafe-button-properties'
                ]
            },
            style: {
                cssProperties: {
                    padding: "5px"
                }
            },
            handler: function () {
                var gridOut, i;
                if (validateVariable(formVarTexOriginOut) && validateVariable(formVarTexTargetOut)) {
                    a = formVarTexOriginOut.getValue();
                    b = formVarTexTargetOut.getValue();
                    gridVariablesOut.addDataItem({origin: a, target: b});
                    formVarTexOriginOut.setValue("");
                    formVarTexTargetOut.setValue("");
                    isDirty = true;

                    gridOut = document.getElementById("propSubGridVariablesOut").getElementsByTagName("table")[0];
                    i = gridOut.rows.length - 1;
                    gridOut.getElementsByClassName("pmui pmui-button")[i].style.marginTop = "2px";
                }
            }
        });

        formVarButtonAddIn = new PMUI.ui.Button({
            id: 'formVarButtonAddIn',
            text: 'Add'.translate(),
            width: 60,
            buttonStyle: {
                cssClasses: [
                    'mafe-button-properties'
                ]
            },
            style: {
                cssProperties: {
                    padding: "5px",
                }
            },
            buttonType: 'success',
            handler: function () {
                var gridOut, i;
                if (validateVariable(formVarTexOriginIn) && validateVariable(formVarTexTargetIn)) {
                    a = formVarTexOriginIn.getValue();
                    b = formVarTexTargetIn.getValue();
                    gridVariablesIn.addDataItem({origin: a, target: b});
                    formVarTexOriginIn.setValue("");
                    formVarTexTargetIn.setValue("");
                    isDirty = true;

                    gridOut = document.getElementById("propSubGridVariablesIn").getElementsByTagName("table")[0];
                    i = gridOut.rows.length - 1;
                    gridOut.getElementsByClassName("pmui pmui-button")[i].style.marginTop = "2px";
                }
            }
        });

        labelVariablesOut = new PMUI.ui.TextLabel({
            textMode: 'plain',
            text: 'Variables Out'.translate()
        });

        labelVariablesIn = new PMUI.ui.TextLabel({
            textMode: 'plain',
            text: 'Variables In'.translate()
        });

        loadDropProcess = function () {
            formProperties.getField('out_doc_process').clearOptions();
            formProperties.getField('out_doc_process').addOption({
                label: "- Select a process -".translate(),
                value: "",
                disabled: true,
                selected: true
            });
        };
        loadDataServer = function () {
            var restClient = new PMRestClient({
                typeRequest: 'post',
                multipart: true,
                data: {
                    calls: [
                        {
                            url: 'projects',
                            method: 'GET'
                        },
                        {
                            url: 'project/' + PMDesigner.project.id + '/subprocess/' + activity.act_uid,
                            method: 'GET'
                        }
                    ]
                },
                functionSuccess: function (xhr, response) {
                    var dt,
                        itemsOut,
                        yout,
                        oout,
                        jout,
                        dout,
                        xin,
                        yin,
                        oin,
                        jin,
                        din,
                        itemsIn,
                        xout;

                    //projects
                    dt = response[0].response;
                    for (var i = 0; i < dt.length; i += 1) {
                        if (dt[i].prj_uid !== PMDesigner.project.projectId) {
                            formProperties.getField('out_doc_process').addOption({
                                label: dt[i].prj_name,
                                value: dt[i].prj_uid
                            });
                        }
                    }
                    //subprocess
                    dt = response[1].response;
                    formProperties.getField('out_doc_title').setValue(dt.spr_name);
                    formProperties.getField('out_doc_process').setValue(dt.spr_pro === '0' ? '' : dt.spr_pro);
                    formProperties.getField('out_doc_type').setValue(dt.spr_synchronous);

                    if (dt.spr_variables_out !== false) { //Asynchronous
                        xout = JSON.stringify(dt.spr_variables_out);
                        itemsOut = [];
                        if (xout != "[]") {
                            yout = xout.substring(1, xout.length - 1);
                            oout = yout.replace(/"/g, '');
                            jout = oout.split(',');
                            for (i = 0; i < jout.length; i += 1) {
                                dout = jout[i].split(':');
                                itemsOut.push({
                                    origin: dout[0],
                                    target: dout[1]
                                });
                            }
                        }
                        gridVariablesOut.setDataItems(itemsOut);
                        gridVariablesIn.setVisible(false);
                        formVariablesPanelIn.setVisible(false);
                    }

                    if (dt.spr_variables_in !== false && dt.spr_variables_in !== undefined) { //Synchronous
                        xin = JSON.stringify(dt.spr_variables_in);
                        itemsIn = [];
                        if (xin != "[]") {
                            yin = xin.substring(1, xin.length - 1);
                            oin = yin.replace(/"/g, '');
                            jin = oin.split(',');
                            for (i = 0; i < jin.length; i += 1) {
                                din = jin[i].split(':');
                                itemsIn.push({
                                    origin: din[0],
                                    target: din[1]
                                });
                            }
                        }
                        gridVariablesIn.setDataItems(itemsIn);
                    }

                    if (dt.spr_synchronous === "1") {
                        gridVariablesIn.setVisible(true);
                        formVariablesPanelIn.setVisible(true);
                    }
                    if (formProperties.getField('out_doc_process').getValue() !== '') {
                        loadActivity(formProperties.getField('out_doc_process').getValue());
                    }
                    formProperties.getField('spr_tas').setValue(dt.spr_tas === '0' ? '' : dt.spr_tas);
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            });
            restClient.setBaseEndPoint('');
            restClient.executeRestClient();
        };
        loadActivity = function (value) {
            var restClient = new PMRestClient({
                typeRequest: 'post',
                multipart: true,
                data: {
                    calls: [
                        {
                            url: 'project/' + value + '/starting-tasks',
                            method: 'GET'
                        }
                    ]
                },
                functionSuccess: function (xhr, response) {
                    var dropdown,
                        i,
                        dt = response[0].response;
                    dt = dt.sort(function (a, b) {
                        return a.act_name.toString().toLowerCase() > b.act_name.toString().toLowerCase();
                    });
                    dropdown = formProperties.getField('spr_tas');
                    dropdown.clearOptions();
                    dropdown.addOption({
                        value: '',
                        label: '- Select starting activity -'.translate()
                    });
                    for (i = 0; i < dt.length; i += 1) {
                        dropdown.addOption({
                            value: dt[i].act_uid,
                            label: dt[i].act_name
                        });
                    }
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            });
            restClient.setBaseEndPoint('');
            restClient.executeRestClient();
        };

        formVariablesPanelOut.addItem(formVarTexOriginOut);
        formVariablesPanelOut.addItem(formVarTexTargetOut);
        formVariablesPanelOut.addItem(formVarButtonAddOut);

        formVariablesPanelIn.addItem(formVarTexOriginIn);
        formVariablesPanelIn.addItem(formVarTexTargetIn);
        formVariablesPanelIn.addItem(formVarButtonAddIn);

        gridVariablesIn.setVisible(false);
        formVariablesPanelIn.setVisible(false);

        windowPropertiesSub.addItem(formProperties);
        windowPropertiesSub.addItem(new PMUI.ui.TextLabel({
            width: 890,
            text: 'Variables Out'.translate(),
            style: {
                cssProperties: {
                    background: '#aaaaaa',
                    margin: '-15px 10px 10px 15px',
                    color: 'white',
                    padding: '6px',
                    'font-weight': 'bold'
                }
            },
            display: 'block'
        }));
        windowPropertiesSub.addItem(formVariablesPanelOut);
        windowPropertiesSub.addItem(gridVariablesOut);

        windowPropertiesSub.addItem(new PMUI.ui.TextLabel({
            width: 890,
            text: 'Variables In'.translate(),
            style: {
                cssProperties: {
                    background: '#aaaaaa',
                    margin: '15px 10px 10px 15px',
                    color: 'white',
                    padding: '6px',
                    'font-weight': 'bold'
                }
            },
            display: 'block',
            visible: false
        }));

        windowPropertiesSub.addItem(formVariablesPanelIn);
        windowPropertiesSub.addItem(gridVariablesIn);

        loadDropProcess();
        gridVariablesOut.setDataItems("");
        gridVariablesIn.setDataItems("");
        loadDataServer();

        if (formProperties.getItems()[1].getValue() != "0") {
            formVarTexTargetOut.setProcess(formProperties.getItems()[1].getValue());
            formVarTexOriginIn.setProcess(formProperties.getItems()[1].getValue());
        }
        windowPropertiesSub.open();
        if (formProperties.getField('out_doc_process').getValue().trim().length === 0) {
            formVarTexTargetOut.buttonHTML.disable();
            formVarTexOriginIn.buttonHTML.disable();
        } else {
            formVarTexTargetOut.buttonHTML.enable();
            formVarTexOriginIn.buttonHTML.enable();
        }
        windowPropertiesSub.showFooter();
        applyStyleWindowForm(windowPropertiesSub);
        formVariablesPanelIn.getItems()[0].dom.labelTextContainer.style.display = "none";
        formVariablesPanelIn.getItems()[1].dom.labelTextContainer.style.display = "none";
        formVariablesPanelOut.getItems()[0].dom.labelTextContainer.style.display = "none";
        formVariablesPanelOut.getItems()[1].dom.labelTextContainer.style.display = "none";
        formVariablesPanelOut.style.addProperties({marginLeft: '45px'});
        gridVariablesOut.style.addProperties({marginLeft: '45px'});
        if (formVariablesPanelIn.visible == true) {
            gridVariablesIn.setVisible(true);
            gridVariablesIn.style.addProperties({marginLeft: '45px'});
            formVariablesPanelIn.setVisible(true);
            formVariablesPanelIn.getItems()[2].setWidth(80);
            $(formVariablesPanelIn.html).css({width: '850px', marginLeft: '45px'});
            windowPropertiesSub.getItems()[4].setVisible(true);
        }

        formVariablesPanelOut.style.addProperties({width: '870px'});
        $(gridVariablesIn.html).find(".pmui-gridpanel-footer").css("position", "static");
        $(gridVariablesIn.html).find(".pmui-gridpanel-footer").css("margin-top", "0px");
        $(gridVariablesOut.html).find(".pmui-gridpanel-footer").css("position", "static");
        $(gridVariablesOut.html).find(".pmui-gridpanel-footer").css("margin-top", "0px");
        windowPropertiesSub.defineEvents();
        gridVariablesOut.html.children[0].style.display = "none";
        gridVariablesIn.html.children[0].style.display = "none";
        formProperties.getField('out_doc_title').setFocus();
        formVarButtonAddOut.setWidth(80);
        formVarButtonAddOut.setVisible(true);

    };
}());
if (typeof(consolidated) == 'undefined') {
    consolidated = '0';
}
PMDesigner.activityProperties = function (activity) {
    switch (activity.getActivityType()) {
        case "SUB_PROCESS":
            PMDesigner.propertiesSubProcess(activity);
            break;
        case "TASK":
            if (activity.getTaskType() === "SCRIPTTASK") {
                PMDesigner.scriptTaskProperties(activity);
            } else {
                PMDesigner.taskProperties(activity);
            }
            break;
    }
};

PMDesigner.taskProperties = function (activity) {
    var formDefinitions,
        featuresForms = [],
        propertiesTabs,
        formCaseLabels,
        dataProperties,
        formTimingControl,
        configurationForms,
        customGrid,
        formNotifications,
        buttonFieldCancel,
        buttonFieldAdd,
        abeForm,
        MobilePanel,
        enableTaskMobile,
        routeCaseMobile,
        abeMainPanel,
        abeAddOption,
        abeTemplates,
        abeDynaforms,
        abeReceiverAccount,
        abeEmailAcount,
        abeFields,
        warningChanges,
        windowProperties,
        processDataProperties,
        i,
        editRow = null,
        arrayTrue = '["TRUE"]',
        arrayFalse = '["FALSE"]',
        stringTrue = "TRUE",
        stringFalse = "FALSE",
        defaultServerlabel = "Mail (PHP)".translate();

    configurationForms = [
        {
            id: 'windowPropertiesTabPanelDefinitions',
            icon: '',
            title: 'Definitions'.translate(),
            panel: formDefinitions = new PMUI.form.Form({
                id: 'formDefinitions',
                visibleHeader: false,
                width: DEFAULT_WINDOW_WIDTH - 250,
                items: [{
                    id: 'formDefinitionsUID',
                    pmType: 'text',
                    name: 'UID',
                    valueType: 'string',
                    label: 'UID'.translate(),
                    controlsWidth: 300
                }, {
                    id: 'formDefinitionsTitle',
                    pmType: 'text',
                    name: 'tas_title',
                    valueType: 'string',
                    label: 'Title'.translate(),
                    placeholder: 'Insert a title'.translate(),
                    required: true,
                    controlsWidth: 300
                }, {
                    id: 'formDefinitionsDescription',
                    pmType: 'textarea',
                    name: 'tas_description',
                    valueType: 'string',
                    label: 'Description'.translate(),
                    placeholder: 'Insert a description'.translate(),
                    rows: 100,
                    controlsWidth: 300,
                    style: {cssClasses: ['mafe-textarea-resize']}
                }, new CriteriaField({
                    id: 'formDefinitionsVariable',
                    pmType: 'text',
                    name: 'tas_priority_variable',
                    valueType: 'string',
                    label: 'Variable for Case priority'.translate(),
                    controlsWidth: 300
                }), {
                    id: 'formDefinitionsRouting',
                    pmType: 'dropdown',
                    name: 'tas_derivation_screen_tpl',
                    valueType: 'string',
                    label: 'Routing Screen Template'.translate(),
                    controlsWidth: 150,
                    style: {
                        cssProperties: {
                            float: "left"
                        }
                    }
                }
                ]
            })
        },
        {
            id: 'windowPropertiesTabPanelCaseLabels',
            icon: '',
            title: 'Case Labels'.translate(),
            panel: formCaseLabels = new PMUI.form.Form({
                id: 'formCaseLabels',
                visibleHeader: false,
                width: DEFAULT_WINDOW_WIDTH - 250,
                items: [new CriteriaField({
                    id: 'formCaseLabelsTitle',
                    pmType: 'text',
                    name: 'tas_def_title',
                    valueType: 'string',
                    label: 'Title'.translate(),
                    placeholder: 'Insert a title'.translate(),
                    controlsWidth: DEFAULT_WINDOW_WIDTH - 527
                }), new CriteriaField({
                    id: 'formCaseLabelsDescription',
                    pmType: 'textarea',
                    name: 'tas_def_description',
                    valueType: 'string',
                    label: 'Description'.translate(),
                    placeholder: 'Insert a description'.translate(),
                    rows: 100,
                    controlsWidth: DEFAULT_WINDOW_WIDTH - 527,
                    renderType: 'textarea'
                })
                ]
            })
        },
        {
            id: 'windowPropertiesTabPanelTimingControl',
            icon: '',
            title: 'Timing Control'.translate(),
            panel: formTimingControl = new PMUI.form.Form({
                id: 'formTimingControl',
                visibleHeader: false,
                width: DEFAULT_WINDOW_WIDTH - 250,
                items: [{
                    id: 'formTimingControlFly',
                    pmType: 'checkbox',
                    name: 'tas_transfer_fly',
                    labelVisible: false,
                    options: [
                        {
                            id: 'formTimingControlOption',
                            label: 'Allow users to change the task duration in runtime'.translate(),
                            value: '1'
                        }
                    ],
                    onChange: function (val) {
                        changeTimingControl(this.controls[0].selected);
                    }
                }, {
                    id: 'formTimingMainPanel',
                    pmType: 'panel',
                    items: [
                        {
                            id: 'formTimingControlTask',
                            pmType: 'text',
                            name: 'tas_duration',
                            valueType: 'string',
                            label: 'Task duration'.translate(),
                            required: true,
                            maxLength: 3,
                            controlsWidth: 200,
                            validators: [
                                {
                                    pmType: "regexp",
                                    criteria: /^\d*$/,
                                    errorMessage: "Please enter a positive integer value".translate()
                                }
                            ]
                        },
                        {
                            id: 'formTimingControlAverage',
                            pmType: 'text',
                            name: 'tas_average',
                            valueType: 'string',
                            label: 'Average'.translate(),
                            maxLength: 3,
                            readOnly: true,
                            controlsWidth: 200
                        },
                        {
                            id: 'formTimingControlSdv',
                            pmType: 'text',
                            name: 'tas_sdv',
                            valueType: 'string',
                            label: 'SDV'.translate(),
                            maxLength: 3,
                            readOnly: true,
                            controlsWidth: 200
                        },
                        {
                            id: 'formTimingControlTime',
                            pmType: 'dropdown',
                            name: 'tas_timeunit',
                            label: 'Time unit'.translate(),
                            controlsWidth: 100,
                            options: [{
                                id: 'formTimingControlTime1',
                                label: 'Hours'.translate(),
                                value: 'HOURS'
                            }, {
                                id: 'formTimingControlTime2',
                                label: 'Days'.translate(),
                                value: 'DAYS'
                            }, {
                                id: 'formTimingControlTime3',
                                label: 'Minutes'.translate(),
                                value: 'MINUTES'
                            }
                            ]
                        }, {
                            id: 'formTimingControlCount',
                            pmType: 'dropdown',
                            name: 'tas_type_day',
                            label: 'Count days by'.translate(),
                            controlsWidth: 150,
                            options: [{
                                id: 'formTimingControlCount1',
                                label: 'Work Days'.translate(),
                                value: 1
                            }, {
                                id: 'formTimingControlCount2',
                                label: 'Calendar Days'.translate(),
                                value: 2
                            }
                            ]
                        }, {
                            id: 'formTimingControlCalendar',
                            pmType: 'dropdown',
                            name: 'tas_calendar',
                            label: 'Calendar'.translate(),
                            options: [],
                            controlsWidth: 150
                        }
                    ]
                }
                ]
            })
        },
        {
            id: 'windowPropertiesTabPanelNotifications',
            icon: '',
            title: 'Notifications'.translate(),
            panel: formNotifications = new PMUI.form.Form({
                id: 'formNotifications',
                visibleHeader: false,
                width: DEFAULT_WINDOW_WIDTH - 250,
                items: [
                    {
                        id: 'formNotificationsTasSend',
                        pmType: 'checkbox',
                        name: 'tas_send_last_email',
                        labelVisible: false,
                        options: [{
                            id: 'formNotificationsTasSend1',
                            label: 'After routing notify the next assigned user(s)'.translate(),
                            value: '1'
                        }
                        ],
                        onChange: function (val) {
                            changeFormNotifications(this.controls[0].selected, 'text');
                        }
                    }, {
                        id: 'formNotificationsMainPanel',
                        pmType: 'panel',
                        items: [
                            {
                                id: 'tas_email_server_uid',
                                name: 'tas_email_server_uid',
                                pmType: 'dropdown',
                                label: 'Email account'.translate(),
                                controlsWidth: 300,
                                labelWidth: "27%",
                                options: [
                                    {
                                        label: 'Default email account'.translate(),
                                        value: ''
                                    }
                                ]
                            },
                            {
                                id: 'tas_not_email_from_format',
                                name: 'tas_not_email_from_format',
                                pmType: 'dropdown',
                                label: 'Sender Name'.translate(),
                                controlsWidth: 300,
                                labelWidth: "27%",
                                options: [
                                    {
                                        id: 'assignedUser',
                                        label: 'Previous User'.translate(),
                                        value: 0
                                    }, {
                                        id: 'emailAccountSettings',
                                        label: 'Email Sender Name'.translate(),
                                        value: 1
                                    }
                                ]
                            },
                            new CriteriaField({
                                id: 'formNotificationsSubject',
                                pmType: 'text',
                                name: 'tas_def_subject_message',
                                valueType: 'string',
                                label: 'Subject'.translate(),
                                placeholder: 'Insert a title'.translate(),
                                required: true,
                                controlsWidth: 300,
                                labelWidth: "27%"
                            }), {
                                id: 'formNotificationsContent',
                                pmType: 'dropdown',
                                name: 'tas_def_message_type',
                                label: 'Content Type'.translate(),
                                controlsWidth: 150,
                                labelWidth: "27%",
                                options: [{
                                    id: 'formNotificationsContent1',
                                    value: 'text',
                                    label: 'Plain Text'.translate()
                                }, {
                                    id: 'formNotificationsContent2',
                                    value: 'template',
                                    label: 'Html Template'.translate()
                                }
                                ],
                                onChange: function (value) {
                                    visibleContentType(value);
                                }
                            }, new CriteriaField({
                                id: 'formNotificationsMessage',
                                pmType: 'textarea',
                                name: 'tas_def_message',
                                valueType: 'string',
                                label: 'Message'.translate(),
                                placeholder: 'Insert a message'.translate(),
                                rows: 100,
                                width: 10,
                                required: true,
                                controlsWidth: 300,
                                renderType: 'textarea',
                                labelWidth: "27%"
                            }), {
                                id: 'formNotificationsTemplate',
                                pmType: 'dropdown',
                                name: 'tas_def_message_template',
                                label: 'Template'.translate(),
                                options: [{
                                    value: '',
                                    label: ''
                                }],
                                labelWidth: "27%"
                            }
                        ]
                    },
                    {
                        id: 'formNotificationsTasReceive',
                        pmType: 'checkbox',
                        name: 'tas_receive_last_email',
                        labelVisible: false,
                        options: [{
                            id: 'formNotificationsTasReceive',
                            label: 'Notify the assigned user to this task'.translate(),
                            value: '1'
                        }
                        ],
                        onChange: function (val) {
                            changeFormNotificationsReceive(this.controls[0].selected, 'text');
                        }
                    },
                    {
                        id: 'formNotificationsReceivePanel',
                        pmType: 'panel',
                        items: [
                            {
                                id: 'tas_receive_server_uid',
                                name: 'tas_receive_server_uid',
                                pmType: 'dropdown',
                                label: 'Email account'.translate(),
                                controlsWidth: 300,
                                labelWidth: "27%",
                                options: [
                                    {
                                        label: 'Default email account'.translate(),
                                        value: ''
                                    }
                                ]
                            },
                            {
                                id: 'tas_receive_email_from_format',
                                name: 'tas_receive_email_from_format',
                                pmType: 'dropdown',
                                label: 'Sender Name'.translate(),
                                controlsWidth: 300,
                                labelWidth: "27%",
                                options: [
                                    {
                                        id: 'assignedUser',
                                        label: 'Previous User'.translate(),
                                        value: 0
                                    }, {
                                        id: 'emailAccountSettings',
                                        label: 'Email Sender Name'.translate(),
                                        value: 1
                                    }
                                ]
                            },
                            new CriteriaField({
                                id: 'tas_receive_subject_message',
                                pmType: 'text',
                                name: 'tas_receive_subject_message',
                                valueType: 'string',
                                label: 'Subject'.translate(),
                                placeholder: 'Insert a title'.translate(),
                                required: true,
                                controlsWidth: 300,
                                labelWidth: "27%"
                            }),
                            {
                                id: 'tas_receive_message_type',
                                pmType: 'dropdown',
                                name: 'tas_receive_message_type',
                                label: 'Content Type'.translate(),
                                controlsWidth: 150,
                                labelWidth: "27%",
                                options: [{
                                    id: 'formNotificationsReceive1',
                                    value: 'text',
                                    label: 'Plain Text'.translate()
                                }, {
                                    id: 'formNotificationsReceive2',
                                    value: 'template',
                                    label: 'Html Template'.translate()
                                }
                                ],
                                onChange: function (value) {
                                    visibleContentTypeReceive(value);
                                }
                            },
                            new CriteriaField({
                                id: 'tas_receive_message',
                                pmType: 'textarea',
                                name: 'tas_receive_message',
                                valueType: 'string',
                                label: 'Message'.translate(),
                                placeholder: 'Insert a message'.translate(),
                                rows: 100,
                                width: 10,
                                required: true,
                                controlsWidth: 300,
                                renderType: 'textarea',
                                labelWidth: "27%"
                            }),
                            {
                                id: 'tas_receive_message_template',
                                pmType: 'dropdown',
                                name: 'tas_receive_message_template',
                                label: 'Template'.translate(),
                                options: [{
                                    value: '',
                                    label: ''
                                }],
                                labelWidth: "27%"
                            }

                        ]
                    }
                ]
            })
        }
    ];
    if (consolidated == '1') {
        configurationForms.push({
            id: 'windowPropertiesTabPanelConsolidated',
            icon: '',
            title: 'Consolidated Case '.translate(),
            panel: formConsolidated = new PMUI.form.Form({
                id: 'formConsolidated',
                visibleHeader: false,
                width: DEFAULT_WINDOW_WIDTH - 250,
                items: [
                    {
                        id: 'formConsolidatedEnable',
                        pmType: 'checkbox',
                        name: 'consolidated_enable',
                        labelVisible: false,
                        options: [{
                            label: 'Enable consolidate for this task.'.translate(),
                            value: '1'
                        }
                        ],
                        onChange: function (val) {
                            changeConsolidated(this.controls[0].selected);
                        }
                    }, {
                        id: 'formConsolidatedMainPanel',
                        pmType: 'panel',
                        items: [
                            {
                                id: 'formConsolidatedReportTable',
                                pmType: 'text',
                                name: 'consolidated_report_table',
                                visible: false,
                                valueType: 'string',
                                controlsWidth: 300
                            }, {
                                id: 'formConsolidatedDynaform',
                                pmType: 'dropdown',
                                name: 'consolidated_dynaform',
                                label: 'Dynaform Template'.translate(),
                                options: [],
                                required: true,
                                controlsWidth: 300
                            }, {
                                id: 'formConsolidatedTable',
                                pmType: 'text',
                                name: 'consolidated_table',
                                valueType: 'string',
                                label: 'Table Name'.translate(),
                                placeholder: 'Insert a table name'.translate(),
                                required: true,
                                controlsWidth: 300,
                                style: {
                                    cssProperties: {
                                        float: "left"
                                    }
                                },
                                validators: [{
                                    pmType: "regexp",
                                    criteria: /^[a-zA-Z0-9_]+$/,
                                    errorMessage: "The table name can not contain spaces".translate()
                                }]
                            }, {
                                id: 'formConsolidatedTitle',
                                pmType: 'text',
                                name: 'consolidated_title',
                                valueType: 'string',
                                label: 'Title'.translate(),
                                placeholder: 'Insert a title'.translate(),
                                required: true,
                                controlsWidth: 300,
                                style: {
                                    cssProperties: {
                                        float: "left"
                                    }
                                }
                            }
                        ]
                    }
                ]
            })
        });
    }

    /*----------------------------------********---------------------------------*/
    /**features-begins**/
    customGrid = new PMUI.grid.GridPanel({
        id: 'customGrid',
        pageSize: 10,
        width: DEFAULT_WINDOW_WIDTH - 400,
        style: {
            cssClasses: ['mafe-gridPanel'],
            cssProperties: {
                'margin-left': '0px',
                'padding-top': '5px'
            }
        },
        emptyMessage: 'No records found'.translate(),
        nextLabel: 'Next'.translate(),
        previousLabel: 'Previous'.translate(),
        behavior: 'dragdropsort',
        filterable: false,
        customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
            return '';
        },
        columns: [{
            id: 'abe_custom_value',
            title: 'Value'.translate(),
            dataType: 'string',
            columnData: 'abe_custom_value',
            alignmentCell: 'left',
            width: 95,
            sortable: true
        }, {
            id: 'abe_custom_label',
            title: 'Label'.translate(),
            dataType: 'string',
            alignmentCell: 'left',
            columnData: 'abe_custom_label',
            width: 135,
            sortable: true
        }, {
            id: 'abe_custom_format',
            title: 'Format (CSS)'.translate(),
            dataType: 'string',
            alignmentCell: 'left',
            columnData: 'abe_custom_format',
            width: 320,
            sortable: true
        }, {
            id: 'varEdit',
            dataType: 'button',
            title: '',
            buttonLabel: 'Edit'.translate(),
            width: '68px',
            buttonStyle: {
                cssClasses: [
                    'mafe-button-edit'
                ]
            },
            onButtonClick: function (row, grid) {
                customGridRowEdit(row);
            }
        }, {
            id: 'varDelete',
            dataType: 'button',
            title: '',
            buttonLabel: function (row, data) {
                return 'Delete'.translate();
            },
            width: '68px',
            buttonStyle: {
                cssClasses: [
                    'mafe-button-delete'
                ]
            },
            onButtonClick: function (row, grid) {
                customGrid.removeItem(row);
            }
        }
        ],
        dataItems: null
    });

    abeFields = {
        'type': {
            name: "ABE_TYPE",
            id: "ABE_TYPE",
            label: "Type",
            pmType: "dropdown",
            options: [
                {
                    "label": "- None -".translate(),
                    "value": "",
                    "type": "default"
                },
                {
                    "label": "Link to fill a form".translate(),
                    "value": "LINK"
                },
                {
                    "label": "Use a field to generate actions links".translate(),
                    "value": "FIELD"
                },
                {
                    "label": "Email response".translate(),
                    "value": "RESPONSE"
                },
                {
                    "label": "Custom actions".translate(),
                    "value": "CUSTOM"
                }
            ],
            onChange: function (val) {
                var addOptionForm, buttonAdd, buttonAddControl, buttonCancel, buttonCancelControl;
                abeFields.type.value = val;
                $(abeFields.email.getHTML()).find('.pmui-field-message span').css('display', 'none');
                $(abeFields.action.getHTML()).find('.pmui-field-message span').css('display', 'none');
                $customGrid = $("#customGrid");
                if (abeAddOption && abeAddOption.getHTML()) {
                    addOptionForm = $(abeAddOption.getHTML());
                    addOptionForm.hide();
                }
                switch (val) {
                    case '' :
                        abeForm.setItems([abeFields.type]);
                        break;
                    case 'LINK' :
                        $customGrid.hide().appendTo($("#windowProperties").find(".pmui-window-body:eq(0)"));
                        abeForm.setItems([
                            abeFields.type,
                            abeFields.template,
                            abeFields.dynaform,
                            abeFields.subject,
                            abeFields.email,
                            abeFields.emailAccount,
                            abeFields.emailFrom,
                            abeFields.note,
                            abeFields.forceLogin]);
                        abeForm.getField('ABE_CASE_NOTE_IN_RESPONSE').setValue(abeFields.note.value);
                        abeForm.getField("ABE_FORCE_LOGIN").setValue(abeFields.forceLogin.value);
                        break;
                    case 'FIELD' :
                        $customGrid.hide().appendTo($("#windowProperties").find(".pmui-window-body:eq(0)"));
                        abeForm.setItems([
                            abeFields.type,
                            abeFields.template,
                            abeFields.dynaform,
                            abeFields.subject,
                            abeFields.email,
                            abeFields.action,
                            abeFields.emailAccount,
                            abeFields.emailFrom,
                            abeFields.note,
                            abeFields.forceLogin]);
                        abeForm.getField('ABE_CASE_NOTE_IN_RESPONSE').setValue(abeFields.note.value);
                        abeForm.getField("ABE_FORCE_LOGIN").setValue(abeFields.forceLogin.value);
                        abeFields.action.setLabel('Variable sent in email'.translate());
                        abeFields.action.setPlaceholder('Insert a variable with options'.translate());
                        break;
                    case 'RESPONSE' :
                        abeForm.setItems([
                            abeFields.type,
                            abeFields.receiverAccount,
                            abeFields.emailAccount,
                            abeFields.emailFrom,
                            abeFields.email,
                            abeFields.subject,
                            abeFields.template,
                            abeFields.action,
                            abeFields.body,
                            abeFields.note]);
                        abeForm.getField("ABE_CASE_NOTE_IN_RESPONSE").setValue(abeFields.note.value);
                        if (!abeAddOption) {
                            abeMainPanel.addItem(abeAddOption = new PMUI.form.Form(abeFields.customGrid));
                        } else {
                            addOptionForm.show();
                        }
                        buttonAdd = abeAddOption.getField("buttonFieldAdd");
                        buttonAddControl = buttonAdd.getControl();
                        buttonAddControl.button.setButtonType("success");

                        buttonCancel = abeAddOption.getField("buttonFieldCancel");
                        buttonCancelControl = buttonCancel.getControl();
                        buttonCancelControl.button.setButtonType("error");
                        abeFields.action.setLabel('Store option in'.translate());
                        abeFields.action.setPlaceholder('Store result in variable @@myResult'.translate());
                        customDOM();
                        break;
                    case 'CUSTOM' :
                        abeForm.setItems([
                            abeFields.type,
                            abeFields.emailAccount,
                            abeFields.emailFrom,
                            abeFields.email,
                            abeFields.subject,
                            abeFields.template,
                            abeFields.action,
                            abeFields.note,
                            abeFields.forceLogin]);
                        abeForm.getField("ABE_CASE_NOTE_IN_RESPONSE").setValue(abeFields.note.value);
                        abeForm.getField("ABE_FORCE_LOGIN").setValue(abeFields.forceLogin.value);
                        if (!abeAddOption) {
                            abeMainPanel.addItem(abeAddOption = new PMUI.form.Form(abeFields.customGrid));
                        } else {
                            addOptionForm.show();
                        }
                        buttonAdd = abeAddOption.getField("buttonFieldAdd");
                        buttonAddControl = buttonAdd.getControl();
                        buttonAddControl.button.setButtonType("success");

                        buttonCancel = abeAddOption.getField("buttonFieldCancel");
                        buttonCancelControl = buttonCancel.getControl();
                        buttonCancelControl.button.setButtonType("error");
                        abeFields.action.setLabel('Store Result In'.translate());
                        abeFields.action.setPlaceholder('Store result in variable @@myResult'.translate());
                        customDOM();
                        break;
                }
            }
        },
        'template': abeTemplates = {
            name: "ABE_TEMPLATE",
            label: 'Email template'.translate(),
            pmType: "dropdown",
            controlsWidth: 300,
            required: true,
            options: [
                {
                    "value": "",
                    "label": "- Select a Template -".translate(),
                    "type": "default"
                }
            ],
            onChange: function (val) {
            }
        },
        'dynaform': abeDynaforms = {
            name: "DYN_UID",
            label: 'Dynaform'.translate(),
            pmType: "dropdown",
            controlsWidth: 300,
            options: [
                {
                    "value": "",
                    "label": "- Select a Dynaform -".translate(),
                    "type": "default"
                }
            ],
            onChange: function (val) {
            }
        },
        'subject': new CriteriaField({
            id: 'ABE_SUBJECT_FIELD',
            pmType: 'text',
            name: 'ABE_SUBJECT_FIELD',
            valueType: 'string',
            label: 'Subject by email'.translate(),
            placeholder: 'Insert a subject variable'.translate(),
            required: false,
            controlsWidth: 250
        }),
        'email': new CriteriaField({
            id: 'ABE_EMAIL_FIELD',
            pmType: 'text',
            name: 'ABE_EMAIL_FIELD',
            valueType: 'string',
            label: 'Email variable'.translate(),
            placeholder: 'Insert an email variable'.translate(),
            helper: "It leaving this field in blank, the next user's email will be used.".translate(),
            controlsWidth: 250
        }),
        'action': new CriteriaField({
            id: 'ABE_ACTION_FIELD',
            pmType: 'text',
            name: 'ABE_ACTION_FIELD',
            valueType: 'string',
            label: 'Variable sent in email'.translate(),
            placeholder: 'Insert a variable with options'.translate(),
            required: true,
            controlsWidth: 250
        }),
        'body': new CriteriaField({
            id: 'ABE_ACTION_BODY_FIELD',
            pmType: 'text',
            name: 'ABE_ACTION_BODY_FIELD',
            valueType: 'string',
            label: 'Store email body in'.translate(),
            placeholder: 'Store result in variable @@myResult'.translate(),
            required: true,
            controlsWidth: 250
        }),
        'receiverAccount': abeReceiverAccount =  {
            id: 'ABE_EMAIL_SERVER_RECEIVER_UID',
            pmType: 'dropdown',
            name: 'ABE_EMAIL_SERVER_RECEIVER_UID',
            label: 'Receiver account'.translate(),
            controlsWidth: 300,
            required: true,
            options: [
                {
                    id: 'selectIMAPEmailAccount',
                    label: 'Please select an IMAP Email Server Account'.translate(),
                    value: ''
                }
            ]
        },
        'emailAccount': abeEmailAcount =  {
            id: 'ABE_EMAIL_SERVER_UID',
            pmType: 'dropdown',
            name: 'ABE_EMAIL_SERVER_UID',
            label: 'Email account'.translate(),
            controlsWidth: 300,
            options: [
                {
                    id: 'defEmailAccount',
                    label: 'Default email account'.translate(),
                    value: ''
                }
            ]
        },
        'emailFrom': {
            id: 'ABE_MAILSERVER_OR_MAILCURRENT',
            pmType: 'dropdown',
            name: 'ABE_MAILSERVER_OR_MAILCURRENT',
            label: 'Sender Name'.translate(),
            controlsWidth: 300,
            options: [
                {
                    id: 'emailServerConfiguration',
                    label: 'Previous user'.translate(),
                    value: 0
                }, {
                    id: 'femailCurrentUser',
                    label: 'Email Sender Name'.translate(),
                    value: 1
                }
            ]
        },
        'note': {
            name: "ABE_CASE_NOTE_IN_RESPONSE",
            pmType: "checkbox",
            labelVisible: false,
            options: [
                {
                    "id": "formTimingControlOption",
                    "label": "Register a Case Note when the recipient submits the Response".translate(),
                    "value": "1"
                }
            ]
        },
        forceLogin: {
            name: "ABE_FORCE_LOGIN",
            pmType: "checkbox",
            labelVisible: false,
            options: [
                {
                    id: "ABE_FORCE_LOGIN",
                    label: "Force user login".translate(),
                    value: 1
                }
            ]
        },
        'customGrid': {
            id: "customGridPanel",
            pmType: "panel",
            visibleHeader: false,
            layout: 'vbox',
            fieldset: false,
            width: 735,
            legend: "Options".translate(),
            items: [
                {
                    pmType: 'panel',
                    legend: "Options".translate(),
                    fieldset: true,
                    layout: 'hbox',
                    items: [
                        {
                            pmType: "panel",
                            id: "customGridPanelControls",
                            layout: "hbox",
                            proportion: 1.7,
                            items: [
                                {
                                    pmType: "panel",
                                    layout: "vbox",
                                    id: "firstPanel",
                                    proportion: 0.8,
                                    items: [
                                        {
                                            pmType: "text",
                                            name: "abe_custom_value_add",
                                            id: "abe_custom_value_add",
                                            label: "Value".translate(),
                                            valueType: "string",
                                            labelWidth: "37%",
                                            controlsWidth: 120,
                                            maxLength: 255,
                                            required: true
                                        },
                                        {
                                            pmType: "text",
                                            name: "abe_custom_label_add",
                                            id: "abe_custom_label_add",
                                            label: "Label".translate(),
                                            valueType: "string",
                                            labelWidth: "37%",
                                            controlsWidth: 120,
                                            maxLength: 255,
                                            required: true
                                        }
                                    ]
                                },
                                {
                                    pmType: "panel",
                                    layout: "vbox",
                                    id: "secondPanel",
                                    proportion: 1.2,
                                    items: [
                                        {
                                            pmType: "textarea",
                                            name: "abe_custom_format_add",
                                            id: "abe_custom_format_add",
                                            label: "Format (CSS)".translate(),
                                            valueType: "string",
                                            required: false,
                                            controlsWidth: 235,
                                            placeholder: "padding:12px;\ntext-decoration:none;\nborder-radius:2px;\nbackground:#1fbc99;\nborder:1px solid #1ba385;",
                                            style: {
                                                cssClasses: [
                                                    'mafe-textarea-resize'
                                                ]
                                            },
                                            rows: 90,
                                            validators: [
                                                {
                                                    pmType: "regexp",
                                                    criteria: /^[a-zA-Z0-9\s\[\]\.\-_#%;,=:()']*$/,
                                                    errorMessage: "Please enter only CSS code".translate()
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ]
                        },
                        {
                            pmType: "panel",
                            id: "thirdPanel",
                            layout: "vbox",
                            proportion: 0.3,
                            items: [
                                {
                                    id: 'buttonFieldAdd',
                                    pmType: 'buttonField',
                                    value: 'Add'.translate(),
                                    labelVisible: false,
                                    buttonAlign: 'center',
                                    handler: function (field) {
                                        addAcceptedValue();
                                    },
                                    buttonType: "success",
                                    controlsWidth: 120,
                                    style: {
                                        cssProperties: {
                                            'width': 'auto'
                                        }
                                    }
                                },
                                {
                                    id: 'buttonFieldCancel',
                                    pmType: 'buttonField',
                                    value: 'Cancel'.translate(),
                                    labelVisible: false,
                                    visible: false,
                                    buttonAlign: 'center',
                                    handler: function (field) {
                                        clearAddOptionForm();
                                    },
                                    buttonType: "error",
                                    controlsWidth: 120,
                                    style: {
                                        cssProperties: {
                                            'width': 'auto'
                                        }
                                    },
                                    buttonStyle: {
                                        cssClasses: [
                                            'mafe-button-delete'
                                        ]
                                    }
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    };
    for (i in ENABLED_FEATURES) {
        if (ENABLED_FEATURES[i] === 'zLhSk5TeEQrNFI2RXFEVktyUGpnczV1WEJNWVp6cjYxbTU3R29mVXVZNWhZQT0=') {
            featuresForms.push({
                id: "ActionsByEmailConfiguration",
                title: "Actions by Email".translate(),
                icon: "",
                panel: abeMainPanel = new PMUI.core.Panel({
                    id: "ActionsByEmailPanel",
                    items: [
                        abeForm = new PMUI.form.Form({
                            id: "ActionsByEmail",
                            name: "ActionsByEmail",
                            visibleHeader: false,
                            width: DEFAULT_WINDOW_WIDTH - 245,
                            items: [abeFields.type]
                        })
                    ]
                })
            });
        }
    }
    featuresForms.push({
        id: 'OfflineMobileConfiguration',
        icon: '',
        title: 'Mobile'.translate(),
        panel: MobilePanel = new PMUI.core.Panel({
            id: "MobilePanel",
            items: [
                enableTaskMobile = new PMUI.field.CheckBoxGroupField({
                    id: 'enableTaskMobile',
                    name: 'tas_offline',
                    pmType: 'checkbox',
                    labelVisible: false,
                    options: [{
                        id: 'enableTaskOffLineUse',
                        label: 'Enable this task for offline use:'.translate(),
                        value: 'TRUE'
                    }],
                    onChange: function (value) {
                        var valueField = this.getControl(0).isSelected();
                        onChangeMobileOffline(valueField);
                    }
                }),
                routeCaseMobile = new PMUI.field.CheckBoxGroupField({
                    id: 'routeCaseMobile',
                    name: 'tas_auto_root',
                    pmType: 'checkbox',
                    labelVisible: false,
                    options: [{
                        id: 'routeCaseBackOnline',
                        label: 'Route case when back online'.translate(),
                        value: 'TRUE'
                    }]
                }),
                new PMUI.field.TextAnnotationField({
                    text: 'Triggers after and before a step are not supported when working offline'.translate(),
                    text_Align: 'center',
                    proportion: 1.4,
                    style: {
                        cssProperties: {
                            color: 'red'
                        }
                    }
                })
            ]
        })
    });
    /**features-ends**/
    /*----------------------------------********---------------------------------*/

    warningChanges = new PMUI.ui.MessageWindow({
        id: 'warningChanges',
        windowMessageType: 'warning',
        width: 490,
        bodyHeight: 'auto',
        title: 'Activity Properties'.translate(),
        message: 'Are you sure you want to discard your changes?'.translate(),
        footerItems: [
            {
                id: 'warningChangesButtonNo',
                text: "No".translate(),
                handler: function () {
                    warningChanges.close();
                },
                buttonType: "error"
            },
            {
                id: 'warningChangesButtonYes',
                text: "Yes".translate(),
                handler: function () {
                    windowProperties.close();
                    warningChanges.close();
                },
                buttonType: "success"
            }
        ]
    });
    windowProperties = new PMUI.ui.Window({
        id: 'windowProperties',
        title: 'Activity Properties'.translate(),
        height: DEFAULT_WINDOW_HEIGHT,
        width: DEFAULT_WINDOW_WIDTH,
        onOpen: function () {
            loadServerData();
        },
        items: [
            propertiesTabs = new PMUI.panel.TabPanel({
                id: 'windowPropertiesTabPanel',
                height: 435,
                width: DEFAULT_WINDOW_WIDTH - 50,
                style: {
                    cssProperties: {
                        'margin-left': '10px'
                    }
                },
                items: configurationForms.concat(featuresForms),
                itemsPosition: {
                    position: 'left'
                },
                onTabClick: function (tab) {
                    setFocusTab(tab);
                }
            }),
            customGrid
        ],
        onBeforeClose: function () {
            if (isDirty()) {
                warningChanges.open();
                warningChanges.showFooter();
            } else {
                windowProperties.close();
            }
        },
        buttonPanelPosition: 'bottom',
        buttonsPosition: 'right',
        buttons: [
            {
                id: 'windowPropertiesButtonCancel',
                text: 'Cancel'.translate(),
                handler: function () {
                    if (isDirty()) {
                        warningChanges.open();
                        warningChanges.showFooter();
                    } else {
                        windowProperties.close();
                    }
                },
                buttonType: "error"
            },
            {
                id: 'windowPropertiesButtonSave',
                text: 'Save'.translate(),
                handler: function () {
                    saveData();
                },
                buttonType: 'success'
            }
        ]
    });

    function changeConsolidated(value) {
        var panel = formConsolidated.getItem('formConsolidatedMainPanel');
        formConsolidated.getField('consolidated_enable').setValue(value ? '["1"]' : '[]');

        if (panel) {
            panel.setVisible(value);
            if (value) {
                panel.enable();
            } else {
                panel.disable();
            }
        }

        formConsolidated.getField('consolidated_report_table').setVisible(false);
    }

    function changeTimingControl(value) {
        var mainPanel = formTimingControl.getItem('formTimingMainPanel');
        formTimingControl.getField('tas_transfer_fly').setValue(value ? '["1"]' : '[]');
        if (mainPanel) {
            if (value) {
                mainPanel.disable();

            } else {
                mainPanel.enable();
            }
            mainPanel.setVisible(!value);
        }
        if (window.enterprise !== "1") {
            formTimingControl.getField('tas_average').setVisible(false);
            formTimingControl.getField('tas_sdv').setVisible(false);
        }
    }

    function changeFormNotifications(value, valueTwo) {
        var panel = formNotifications.getItem('formNotificationsMainPanel');

        if (panel) {
            panel.setVisible(value);
            if (value) {
                panel.enable();
            } else {
                panel.disable();
            }
        }

        if (value) {
            formNotifications.getField('tas_def_message_type').setValue(valueTwo);
            visibleContentType(valueTwo);
        }
        formNotifications.getField('tas_send_last_email').setValue(value ? '["1"]' : '[]');
        formNotifications.getField('tas_def_subject_message').setFocus();
    }

    /**
     * Notification receive change handler, according to user selection
     * @param value
     * @param valueTwo
     */
    function changeFormNotificationsReceive(value, valueTwo) {
        var panel = formNotifications.getItem('formNotificationsReceivePanel');

        if (panel) {
            panel.setVisible(value);
            if (value) {
                panel.enable();
            } else {
                panel.disable();
            }
        }

        if (value) {
            formNotifications.getField('tas_receive_message_type').setValue(valueTwo);
            visibleContentTypeReceive(valueTwo);
        }
        formNotifications.getField('tas_receive_last_email').setValue(value ? '["1"]' : '[]');
        formNotifications.getField('tas_receive_subject_message').setFocus();
    }

    /**
     * Get value parsed
     * @param valueCheckBox '["TRUE"]'/'["FALSE"]'
     * @returns {string} "TRUE"/"FALSE"
     */
    function getValueCheckBox(valueCheckBox) {
        var optionSelected = JSON.parse(valueCheckBox),
            valChecked;
        valChecked = (Array.isArray(optionSelected) && optionSelected.length === 1) ? optionSelected[0] : stringFalse;
        return valChecked;
    }

    function loadFormData(response) {
        dataProperties = response.properties;
        formDefinitions.getField('UID').setReadOnly(true);
        formDefinitions.getField('UID').setValue(activity.id);
        formDefinitions.getField('tas_title').setValue(dataProperties.tas_title);
        formDefinitions.getField('tas_title').setFocus();
        formDefinitions.getField('tas_description').setValue(dataProperties.tas_description);
        formDefinitions.getField('tas_priority_variable').setValue(dataProperties.tas_priority_variable);
        formDefinitions.getField('tas_derivation_screen_tpl').setValue(dataProperties.tas_derivation_screen_tpl);

        formCaseLabels.getField('tas_def_title').setValue(dataProperties.tas_def_title);
        formCaseLabels.getField('tas_def_description').setValue(dataProperties.tas_def_description);

        if (dataProperties.tas_transfer_fly === 'FALSE') {
            changeTimingControl(false);
        } else {
            changeTimingControl(true);
        }
        formTimingControl.getField('tas_duration').setValue(dataProperties.tas_duration);
        formTimingControl.getField('tas_timeunit').setValue(dataProperties.tas_timeunit);
        formTimingControl.getField('tas_type_day').setValue(dataProperties.tas_type_day);
        formTimingControl.getField('tas_calendar').setValue(dataProperties.tas_calendar);

        if (window.enterprise === "1") {
            formTimingControl.getField('tas_average').setValue(dataProperties.tas_average);
            formTimingControl.getField('tas_sdv').setValue(dataProperties.tas_sdv);
        }

        changeFormNotifications(dataProperties.tas_send_last_email === 'TRUE', dataProperties.tas_def_message_type);
        changeFormNotificationsReceive(dataProperties.tas_receive_last_email === 'TRUE', dataProperties.tas_receive_message_type);
        formNotifications.getField('tas_def_subject_message').setValue(dataProperties.tas_def_subject_message);
        formNotifications.getField('tas_def_message_type').setValue(dataProperties.tas_def_message_type);
        formNotifications.getField('tas_def_message').setValue(dataProperties.tas_def_message);
        formNotifications.getField('tas_email_server_uid').setValue(dataProperties.tas_email_server_uid);
        formNotifications.getField('tas_def_message_template').setValue(dataProperties.tas_def_message_template);
        formNotifications.getField('tas_not_email_from_format').setValue(dataProperties.tas_not_email_from_format);
        // receive
        formNotifications.getField('tas_receive_subject_message').setValue(dataProperties.tas_receive_subject_message);
        formNotifications.getField('tas_receive_message_type').setValue(dataProperties.tas_receive_message_type);
        formNotifications.getField('tas_receive_message').setValue(dataProperties.tas_receive_message);
        formNotifications.getField('tas_receive_server_uid').setValue(dataProperties.tas_receive_server_uid);
        formNotifications.getField('tas_receive_message_template').setValue(dataProperties.tas_receive_message_template);
        formNotifications.getField('tas_receive_email_from_format').setValue(dataProperties.tas_receive_email_from_format);
    }
    /*----------------------------------********---------------------------------*/

    /**
     * Set Visibility from second check(routeCaseMobile) and set value for false
     * @param value
     * @param valueTwo
     */
    function changeFormOfflineMobile(data) {
        // Offline setData parameters
        var dataProperties = data.properties || null,
            parseValue = dataProperties.tas_offline === stringTrue ? true : false;

        enableTaskMobile.setValue(dataProperties.tas_offline === stringTrue ? arrayTrue : arrayFalse);
        routeCaseMobile.setValue(dataProperties.tas_auto_root === stringTrue ? arrayTrue : arrayFalse);
        onChangeMobileOffline(parseValue);
    }

    function onChangeMobileOffline(parseValue) {
        if (typeof parseValue === "boolean" && !parseValue) {
            routeCaseMobile.setValue(arrayFalse);
        }
        routeCaseMobile.setVisible(parseValue);
    }

    /****feature-begin*****/
    function loadFeaturesConfiguration(response) {
        var form, type, i, j, customGridData;
        if (typeof response === 'undefined') {
            abeForm.setItems([abeFields.template, abeFields.subject, abeFields.email, abeFields.emailFrom, abeFields.action, abeFields.note, abeFields.forceLogin]);
            return;
        }
        for (i in response) {
            switch (response[i]['feature']) {
                case 'ActionsByEmail':
                    abeForm._form_data = response[i];
                    type = '';
                    if (typeof response[i]['ABE_TYPE'] !== 'undefined') {
                        type = response[i]['ABE_TYPE'];
                    }
                    abeFields.type.value = type;
                    for (j in abeFields) {
                        abeFields[j].value = (response[i][abeFields[j].name] || '');
                    }
                    switch (type) {
                        case '' :
                            abeForm.setItems([abeFields.type]);
                            break;
                        case 'LINK' :
                            abeForm.setItems([
                                abeFields.type,
                                abeFields.template,
                                abeFields.dynaform,
                                abeFields.subject,
                                abeFields.email,
                                abeFields.emailFrom,
                                abeFields.note,
                                abeFields.forceLogin]);
                            abeForm.getField('ABE_CASE_NOTE_IN_RESPONSE').setValue(abeFields.note.value);
                            abeForm.getField("ABE_FORCE_LOGIN").setValue(abeFields.forceLogin.value);
                            break;
                        case 'FIELD' :
                            abeForm.setItems([
                                abeFields.type,
                                abeFields.template,
                                abeFields.dynaform,
                                abeFields.subject,
                                abeFields.email,
                                abeFields.action,
                                abeFields.emailFrom,
                                abeFields.note,
                                abeFields.forceLogin]);
                            abeForm.getField('ABE_CASE_NOTE_IN_RESPONSE').setValue(abeFields.note.value);
                            abeForm.getField("ABE_FORCE_LOGIN").setValue(abeFields.forceLogin.value);
                            break;
                        case 'RESPONSE' :
                            abeForm.setItems([
                                abeFields.type,
                                abeFields.emailFrom,
                                abeFields.email,
                                abeFields.subject,
                                abeFields.template,
                                abeFields.action,
                                abeFields.body,
                                abeFields.receiverAccount,
                                abeFields.note,
                                abeFields.customGrid]);
                            abeForm.getField("ABE_CASE_NOTE_IN_RESPONSE").setValue(abeFields.note.value);

                            customGridData = response[i].ABE_CUSTOM_GRID;
                            customGrid.setDataItems(customGridData);

                            break;
                        case 'CUSTOM' :
                            abeForm.setItems([
                                abeFields.type,
                                abeFields.emailFrom,
                                abeFields.email,
                                abeFields.subject,
                                abeFields.template,
                                abeFields.action,
                                abeFields.note,
                                abeFields.forceLogin,
                                abeFields.customGrid]);
                            abeForm.getField("ABE_CASE_NOTE_IN_RESPONSE").setValue(abeFields.note.value);
                            abeForm.getField("ABE_FORCE_LOGIN").setValue(abeFields.forceLogin.value);

                            customGridData = response[i].ABE_CUSTOM_GRID;
                            customGrid.setDataItems(customGridData);

                            break;
                    }
                    break;
            }
        }
    }

    processDataProperties = function (properties) {
        var fields = properties.form.getFields(), i;
        properties.data['_features'] = (properties.data['_features'] || {});

        if (typeof properties.form._form_data === 'undefined') {
            properties.form._form_data = {};
            properties.form._form_data['ABE_UID'] = '';
            properties.form._form_data['PRO_UID'] = PMDesigner.project.id;
            properties.form._form_data['TAS_UID'] = activity.id;
        }

        properties.data['_features'][properties.feature] = {
            'type': properties.type,
            'fields': {
                'ABE_UID': properties.form._form_data['ABE_UID'],
                'ABE_TYPE': 'FIELD',
                'PRO_UID': properties.form._form_data['PRO_UID'],
                'TAS_UID': properties.form._form_data['TAS_UID']
            }
        };
        for (i in fields) {
            properties.data
                ['_features']
                [properties.feature]
                ['fields']
                [fields[i].name] = fields[i].getValue();
        }
        return properties.data;
    };

    function loadStepsConsolidated(response) {
        var field = formConsolidated.getField('consolidated_dynaform'), i;
        field.clearOptions();
        field.addOption({
            value: '',
            label: '- None -'.translate()
        });
        for (i = 0; i < response.length; i += 1) {
            if (response[i].step_type_obj == "DYNAFORM") {
                field.addOption({
                    value: response[i].step_uid_obj,
                    label: response[i].obj_title
                });
            }
        }
    }

    function loadFormConsolidate(response) {
        if (response.rep_tab_uid != '') {
            formConsolidated.getField('consolidated_report_table').setValue(response.rep_tab_uid);
            formConsolidated.getField('consolidated_dynaform').setValue(response.dyn_uid);
            changeConsolidated(true);
        } else {
            changeConsolidated(false);
        }
        formConsolidated.getField('consolidated_table').setValue(response.rep_tab_name);
        formConsolidated.getField('consolidated_title').setValue(response.con_value);
    }

    /****feature-end*****/
    /*----------------------------------********---------------------------------*/
    function loadCalendar(response) {
        var field = formTimingControl.getField('tas_calendar'), i;
        field.clearOptions();
        field.addOption({
            value: '',
            label: '- None -'.translate()
        });
        for (i = 0; i < response.length; i += 1) {
            field.addOption({
                value: response[i].cal_uid,
                label: response[i].cal_name
            });
        }
    }

    /**
     * Loads the template from rest service response
     * @param response
     * @param fieldName
     */
    function loadTemplate(response, fieldName) {
        var field = formNotifications.getField(fieldName), i, field2;
        field.clearOptions();
        field.addOption({
            value: 'alert_message.html',
            label: '-- Default --'.translate()
        });
        for (i = 0; i < response.length; i += 1) {
            field.addOption({
                value: response[i].prf_filename,
                label: response[i].prf_filename
            });
        }
        field2 = formDefinitions.getField('tas_derivation_screen_tpl');
        field2.clearOptions();
        field2.addOption({
            value: '',
            label: '-- Default --'.translate()
        });
        for (i = 0; i < response.length; i += 1) {
            if (response[i].prf_filename !== 'alert_message.html') {
                field2.addOption({
                    value: response[i].prf_filename,
                    label: response[i].prf_filename
                });
            }
        }
    };
    /**
     * Load Email account server dropdown options
     * @param response
     */
    function loadEmailAccount(response, fieldName) {
        var field = formNotifications.getField(fieldName) || null,
            i;
        if (response instanceof Array && field) {
            for (i = 0; i < response.length; i += 1) {
                field.addOption({
                    value: response[i].mess_uid,
                    label: response[i].mess_engine === "MAIL" ?
                        response[i].mess_from_name && response[i].mess_from_name !== "" ?
                        response[i].mess_from_name : defaultServerlabel : response[i].mess_from_name && response[i].mess_from_name !== "" ?
                        response[i].mess_from_name + ' <' + response[i].mess_account + '>' : ' <' + response[i].mess_account + '>'
                });
            }
        }
    };

    function loadABETemplateField(templates) {
        var templateField = abeForm.getField('ABE_TEMPLATE'), i;
        for (i in templates) {
            if (templateField !== null) {
                templateField.addOption({
                    value: templates[i].FIELD,
                    label: templates[i].NAME
                });
            }
            abeTemplates.options.push({
                value: templates[i].FIELD,
                label: templates[i].NAME
            });
        }
    };
    /**
     * Loads the email accounts settings
     * @param response
     */
    function loadABEmailAccount(response) {
        var accountField = abeForm.getField('ABE_EMAIL_SERVER_UID') || null,
            i;

        if (response instanceof Array) {
            for (i = 0; i < response.length; i += 1) {
                if (accountField !== null) {
                    accountField.addOption({
                        value: response[i].mess_uid,
                        label: response[i].mess_engine === "MAIL" ?
                            response[i].mess_from_name && response[i].mess_from_name !== "" ?
                            response[i].mess_from_name : defaultServerlabel : response[i].mess_from_name && response[i].mess_from_name !== "" ?
                            response[i].mess_from_name + ' <' + response[i].mess_account + '>' : ' <' + response[i].mess_account + '>'
                    });
                }
                abeEmailAcount.options.push({
                    value: response[i].mess_uid,
                    label: response[i].mess_engine === "MAIL" ?
                        response[i].mess_from_name && response[i].mess_from_name !== "" ?
                        response[i].mess_from_name : defaultServerlabel : response[i].mess_from_name && response[i].mess_from_name !== "" ?
                        response[i].mess_from_name + ' <' + response[i].mess_account + '>' : ' <' + response[i].mess_account + '>'
                });
            }
        }

    };

    /**
     * Loads the IMAP email accounts settings
     * @param response
     */
    function loadABImapEmailAccount(response) {
        var accountField = abeForm.getField('ABE_RECEIVER_EMAIL_SERVER_UID') || null,
            i;

        if (response instanceof Array) {
            for (i = 0; i < response.length; i += 1) {
                if (response[i].mess_engine === "IMAP" || response[i].mess_engine === "GMAILAPI" || response[i].mess_engine === "OFFICE365API") {
                    if (accountField !== null) {
                        accountField.addOption({
                            value: response[i].mess_uid,
                            label: response[i].mess_from_name && response[i].mess_from_name !== "" ?
                                    response[i].mess_from_name + ' <' + response[i].mess_account + '>' : ' <' + response[i].mess_account + '>'
                        });
                    }
                    abeReceiverAccount.options.push({
                        value: response[i].mess_uid,
                        label: response[i].mess_from_name && response[i].mess_from_name !== "" ?
                                response[i].mess_from_name + ' <' + response[i].mess_account + '>' : ' <' + response[i].mess_account + '>'
                    });
                }
            }
        }

    };

    function loadABEDynaformField(dynaforms) {
        var dynaformField = abeForm.getField('DYN_UID'), i;
        for (i in dynaforms) {
            if (dynaformField !== null) {
                dynaformField.addOption({
                    value: dynaforms[i].DYN_UID,
                    label: dynaforms[i].DYN_NAME
                });
            }
            abeDynaforms.options.push({
                value: dynaforms[i].DYN_UID,
                label: dynaforms[i].DYN_NAME
            });
        }
    }

    function loadServerData() {
        var restClient, i;
        restClient = new PMRestClient({
            typeRequest: 'post',
            multipart: true,
            data: {
                calls: {
                    "formconfig": {
                        "url": 'project/' + PMDesigner.project.id + '/activity/' + activity.id,
                        "method": 'GET'
                    },
                    "calendar": {
                        "url": 'calendar',
                        "method": 'GET'
                    },
                    "templates": {
                        "url": 'project/' + PMDesigner.project.id + '/file-manager?path=templates',
                        "method": 'GET'
                    },
                    "emailserver": {
                        "url": 'project/' + PMDesigner.project.id + '/email-event/accounts/emailServer',
                        "method": 'GET'
                    }
                }
            },
            functionSuccess: function (xhr, response) {
                loadTemplate(response["templates"].response, 'tas_def_message_template');
                loadTemplate(response["templates"].response, 'tas_receive_message_template');
                loadCalendar(response["calendar"].response);
                loadFormData(response["formconfig"].response);
                loadEmailAccount(response["emailserver"].response, 'tas_email_server_uid');
                loadEmailAccount(response["emailserver"].response, 'tas_receive_server_uid');

                /*----------------------------------********---------------------------------*/
                /****features-begin****/
                if (consolidated == '1') {
                    if(response["steps"]) {
                        loadStepsConsolidated(response["steps"].response);
                    }
                    if (response["consolidate"]){
                        loadFormConsolidate(response["consolidate"].response);
                    }
                }

                for (i in ENABLED_FEATURES) {
                    if (ENABLED_FEATURES[i] == 'zLhSk5TeEQrNFI2RXFEVktyUGpnczV1WEJNWVp6cjYxbTU3R29mVXVZNWhZQT0=') {
                        changeFormOfflineMobile(response["formconfig"].response);
                        loadABEmailAccount(response["emailserver"].response);
                        loadABImapEmailAccount(response["emailserver"].response);
                        if (response["featureconfig"]) {
                            loadFeaturesConfiguration(response["featureconfig"].response);
                        }
                        if (response["abetemplates"]) {
                            loadABETemplateField(response["abetemplates"].response);
                        }
                        if (response['abedynaforms']) {
                            loadABEDynaformField(response['abedynaforms'].response);
                        }
                    }
                }
                /****features-end****/
                /*----------------------------------********---------------------------------*/
            },
            functionFailure: function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            }
        });
        if (consolidated == '1') {
            $.extend(restClient.data.calls, {
                "steps": {
                    'url': 'project/' + PMDesigner.project.id + '/activity/' + activity.id + '/steps',
                    'method': 'GET'
                },
                "consolidate": {
                    "url": 'consolidated/' + activity.id,
                    "method": 'GET'
                }
            });
        }
        /*----------------------------------********---------------------------------*/
        /****features-begin****/
        for (i in ENABLED_FEATURES) {
            if (ENABLED_FEATURES[i] == 'zLhSk5TeEQrNFI2RXFEVktyUGpnczV1WEJNWVp6cjYxbTU3R29mVXVZNWhZQT0=') {
                $.extend(restClient.data.calls, {
                    "featureconfig": {
                        url: 'project/' + PMDesigner.project.id + '/activity/' + activity.id + '/feature-configuration/',
                        method: 'GET'
                    },
                    "abetemplates": {
                        "url": 'ActionsByEmail/Templates/' + PMDesigner.project.id,
                        "method": 'GET'
                    },
                    "abedynaforms": {
                        url: 'ActionsByEmail/Dynaforms/' + PMDesigner.project.id,
                        method: 'GET'
                    }
                });
            }
        }
        /****features-end****/
        /*----------------------------------********---------------------------------*/
        restClient.setBaseEndPoint('');
        restClient.executeRestClient();
    }

    function visibleContentType(value) {
        formNotifications.getField('tas_def_message_template').disable();
        formNotifications.getField('tas_def_message').disable();
        if (value === 'text') {
            formNotifications.getField('tas_def_message').enable();
            formNotifications.getField('tas_def_message').setVisible(true);
            formNotifications.getField('tas_def_message_template').setVisible(false);
        }
        if (value === 'template') {
            formNotifications.getField('tas_def_message_template').enable();
            formNotifications.getField('tas_def_message').setVisible(false);
            formNotifications.getField('tas_def_message_template').setVisible(true);
        }
    };
    /**
     * content type handler, enable or disable templates or message field
     * @param value
     */
    function visibleContentTypeReceive(value) {
        formNotifications.getField('tas_receive_message_template').disable();
        formNotifications.getField('tas_receive_message').disable();
        if (value === 'text') {
            formNotifications.getField('tas_receive_message').enable();
            formNotifications.getField('tas_receive_message').setVisible(true);
            formNotifications.getField('tas_receive_message_template').setVisible(false);
        }
        if (value === 'template') {
            formNotifications.getField('tas_receive_message_template').enable();
            formNotifications.getField('tas_receive_message').setVisible(false);
            formNotifications.getField('tas_receive_message_template').setVisible(true);
        }
    };
    function saveData() {
        var tabPanel = windowProperties.getItem('windowPropertiesTabPanel'),
            tabItems = tabPanel.getItems(),
            valid = true,
            panel,
            tas_transfer_fly,
            tas_send_last_email,
            tas_receive_last_email,
            dataCaseLabels,
            dataDefinitions,
            dataTimingControl,
            dataNotification,
            consolidated_enable,
            message,
            i;

        /**Beging validations**/
        //Validate data inside forms.
        for (i = 0; i < tabItems.length; i += 1) {
            panel = tabItems[i].getPanel();
            if (panel instanceof PMUI.form.Form) {
                valid = valid && panel.isValid();
            } else {
                if (panel.getID() === "ActionsByEmailPanel") {
                    if (!abeForm.isValid()) {
                        valid = false;
                    }
                }
            }
            if (!valid) {
                tabItems[i].select();
                return;
            }
        }
        /**End validations**/

        /*----------------------------------********---------------------------------*/
        /****features-begin****/
        if (abeFields && abeFields.type.value === 'FIELD' && abeFields.action.value === '') {
            $(abeFields.action.getHTML()).find('.pmui-field-message span').css('display', 'block');
            return;
        }
        /****features-end****/
        /*----------------------------------********---------------------------------*/

        tas_transfer_fly = formTimingControl.getField('tas_transfer_fly').getValue() === '["1"]';
        tas_send_last_email = formNotifications.getField('tas_send_last_email').getValue() === '["1"]';
        tas_receive_last_email = formNotifications.getField('tas_receive_last_email').getValue() === '["1"]';

        if (tas_transfer_fly !== false) {
            var a = formTimingControl.getField('tas_transfer_fly').getValue();
            formTimingControl.reset();
            formTimingControl.getField('tas_transfer_fly').setValue(a);
            formTimingControl.getField('tas_duration').setValue('1');
            formTimingControl.getField('tas_timeunit').setValue('DAYS');
            formTimingControl.getField('tas_type_day').setValue('');
            formTimingControl.getField('tas_calendar').setValue('');
        }

        if ((navigator.userAgent.indexOf("MSIE") != -1) || (navigator.userAgent.indexOf("Trident") != -1)) {
            dataDefinitions = getData2PMUI(formDefinitions.html);
            dataCaseLabels = getData2PMUI(formCaseLabels.html);
            dataTimingControl = getData2PMUI(formTimingControl.html);
            dataNotification = getData2PMUI(formNotifications.html);
            if (!dataCaseLabels) {
                dataCaseLabels = {};
            }
            if (!dataTimingControl) {
                dataTimingControl = {};
            }
            if (!dataNotification) {
                dataNotification = {};
            }
        } else {
            dataDefinitions = formDefinitions.getData();
            dataCaseLabels = formCaseLabels.getData();
            dataTimingControl = formTimingControl.getData();
            dataNotification = formNotifications.getData();
        }

        if (dataDefinitions['tas_title']) {
            dataProperties.tas_title = dataDefinitions['tas_title'];
        }
        if (dataDefinitions['tas_description']) {
            dataProperties.tas_description = dataDefinitions['tas_description'];
        }
        if (dataDefinitions['tas_priority_variable'] || typeof dataDefinitions['tas_priority_variable'] == "string") {
            dataProperties.tas_priority_variable = dataDefinitions['tas_priority_variable'];
        }
        if (dataDefinitions['tas_derivation_screen_tpl'] || dataDefinitions['tas_derivation_screen_tpl'] == "") {
            dataProperties.tas_derivation_screen_tpl = dataDefinitions['tas_derivation_screen_tpl'];
        }
        dataProperties.tas_def_title = dataCaseLabels['tas_def_title'];
        dataProperties.tas_def_description = dataCaseLabels['tas_def_description'];
        dataProperties.tas_transfer_fly = tas_transfer_fly ? 'TRUE' : 'FALSE';
        if (dataTimingControl['tas_duration']) {
            dataProperties.tas_duration = dataTimingControl['tas_duration'];
        }
        if (dataTimingControl['tas_timeunit']) {
            dataProperties.tas_timeunit = dataTimingControl['tas_timeunit'];
        }
        if (dataTimingControl['tas_type_day']) {
            dataProperties.tas_type_day = dataTimingControl['tas_type_day'];
        }
        if (dataTimingControl['tas_calendar']) {
            dataProperties.tas_calendar = dataTimingControl['tas_calendar'];
        }
        dataProperties.tas_send_last_email = tas_send_last_email ? 'TRUE' : 'FALSE';
        if (dataNotification['tas_not_email_from_format']) {
            dataProperties.tas_not_email_from_format = dataNotification['tas_not_email_from_format'];
        }
        if (dataNotification['tas_def_subject_message']) {
            dataProperties.tas_def_subject_message = dataNotification['tas_def_subject_message'];
        }
        if (dataNotification['tas_def_message_type']) {
            dataProperties.tas_def_message_type = dataNotification['tas_def_message_type'];
        }
        if (dataNotification['tas_def_message']) {
            dataProperties.tas_def_message = dataNotification['tas_def_message'];
        }
        if (dataNotification['tas_def_message_template']) {
            dataProperties.tas_def_message_template = dataNotification['tas_def_message_template'];
        }
        if (dataNotification['tas_email_server_uid'] !== 'undefined' && dataNotification['tas_email_server_uid'] !== null) {
            dataProperties.tas_email_server_uid = dataNotification['tas_email_server_uid'];
        }

        dataProperties.tas_receive_last_email = tas_receive_last_email ? 'TRUE' : 'FALSE';
        if (dataNotification['tas_receive_email_from_format']) {
            dataProperties.tas_receive_email_from_format = dataNotification['tas_receive_email_from_format'];
        }
        if (dataNotification['tas_receive_server_uid'] !== 'undefined' && dataNotification['tas_receive_server_uid'] !== null) {
            dataProperties.tas_receive_server_uid = dataNotification['tas_receive_server_uid'];
        }
        if (dataNotification['tas_receive_subject_message']) {
            dataProperties.tas_receive_subject_message = dataNotification['tas_receive_subject_message'];
        }
        if (dataNotification['tas_receive_message_type']) {
            dataProperties.tas_receive_message_type = dataNotification['tas_receive_message_type'];
        }
        if (dataNotification['tas_receive_message']) {
            dataProperties.tas_receive_message = dataNotification['tas_receive_message'];
        }
        if (dataNotification['tas_receive_message_template']) {
            dataProperties.tas_receive_message_template = dataNotification['tas_receive_message_template'];
        }
        /*----------------------------------********---------------------------------*/
        /** features-start */
        for (i in ENABLED_FEATURES) {
            if (ENABLED_FEATURES[i] == 'zLhSk5TeEQrNFI2RXFEVktyUGpnczV1WEJNWVp6cjYxbTU3R29mVXVZNWhZQT0=') {
                dataProperties = processDataProperties({
                    'type': 'configuration',
                    'feature': abeForm.id,
                    'data': dataProperties,
                    'form': abeForm
                });
                if (abeForm.getField("ABE_TYPE").getValue() === "CUSTOM" || abeForm.getField("ABE_TYPE").getValue() === "RESPONSE") {
                    if (customGrid.getData().length == 0) {
                        message = new PMUI.ui.FlashMessage({
                            message: "At least one option must be filled.".translate(),
                            duration: 3000,
                            severity: 'error',
                            appendTo: windowProperties.footer
                        });
                        message.show();
                        return;
                    } else {
                        dataProperties._features.ActionsByEmail.fields.ABE_CUSTOM_GRID = customGrid.getData();
                    }
                }
            }
        }
        // OffLine saveData parameters
        dataProperties[enableTaskMobile.name] = getValueCheckBox(enableTaskMobile.getValue());
        dataProperties[routeCaseMobile.name] = getValueCheckBox(routeCaseMobile.getValue());


        /** features-end */
        /*----------------------------------********---------------------------------*/

        if (consolidated == '1') {
            consolidated_enable = false;
            if (formConsolidated.getField('consolidated_enable').getValue() == '["1"]') {
                consolidated_enable = true;
                if (!formConsolidated.isValid()) {
                    return;
                }
            }
            dataProperties.consolidate_data = {
                "consolidated_enable": consolidated_enable,
                "consolidated_dynaform": formConsolidated.getField('consolidated_dynaform').getValue(),
                "consolidated_table": formConsolidated.getField('consolidated_table').getValue(),
                "consolidated_title": formConsolidated.getField('consolidated_title').getValue(),
                "consolidated_report_table": formConsolidated.getField('consolidated_report_table').getValue()
            };
        }

        (new PMRestClient({
            endpoint: 'activity/' + activity.id,
            typeRequest: 'update',
            messageError: '',
            data: {
                definition: {},
                properties: dataProperties
            },
            functionSuccess: function (xhr, response) {
                setNameActivity(dataProperties.tas_title);
                windowProperties.close();
            },
            functionFailure: function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            },
            messageSuccess: 'Task properties saved successfully'.translate(),
            flashContainer: document.body
        })).executeRestClient();
    }

    function setFocusTab(tab) {
        var style;
        $customGrid = $("#customGrid");
        $customGrid.hide().appendTo($("#windowProperties").find(".pmui-window-body:eq(0)"));

        if (tab.getTitle() === 'Definitions'.translate()) {
            formDefinitions.getField('tas_title').setFocus();
        }
        if (tab.getTitle() === 'Case Labels'.translate()) {
            formCaseLabels.getField('tas_def_title').setFocus();
            style = $('#formCaseLabelsDescription .pmui-field-label').attr("style");
            style = style + ' float: left;';
            $('#formCaseLabelsDescription .pmui-field-label').attr("style", style);
        }
        if (tab.getTitle() === 'Timing Control'.translate()) {
            formTimingControl.getField('tas_duration').setFocus();
        }
        if (tab.getTitle() === 'Notifications'.translate()) {
            formNotifications.getField('tas_def_subject_message').setFocus();
            style = $('#formNotificationsMessage .pmui-field-label').attr("style");
            style = style + ' float: left;';
            $('#formNotificationsMessage .pmui-field-label').attr("style", style);

            style = $('#tas_receive_message .pmui-field-label').attr("style");
            style = style + ' float: left;';
            $('#tas_receive_message .pmui-field-label').attr("style", style);
        }
        if (tab.getTitle() === 'Actions by Email'.translate()) {
            abeForm.getField("ABE_TYPE").onChange(abeForm.getField("ABE_TYPE").getValue());
        }
    }

    function isDirty() {
        return formDefinitions.isDirty() ||
            formCaseLabels.isDirty() ||
            formTimingControl.isDirty() ||
            formNotifications.isDirty();
    }

    function setNameActivity(name) {
        activity.setName(name);
        activity.setActName(name);
        PMDesigner.project.dirty = true;
    }

    windowProperties.open();
    windowProperties.showFooter();

    applyStyleWindowForm(windowProperties);
    if (consolidated == '1') {
        formConsolidated.getField('consolidated_report_table').setVisible(false);
    }
    /*----------------------------------********---------------------------------*/
    /** features-start */
    // validates if there is customGrid Action by Email grid
    if (windowProperties.getItem('customGrid')) {
        windowProperties.getItem('customGrid').setVisible(false);
    }
    /** features-end */
    /*----------------------------------********---------------------------------*/

    /**Builds the forms**/
    //This is necessary to do because many elements are built dynamically.
    var tabItems = propertiesTabs.getItems(), i;
    for (i = 0; i < tabItems.length; i += 1) {
        tabItems[i].select();
        setFocusTab(tabItems[i]);
    }
    tabItems[0].select();
    setFocusTab(tabItems[0]);
    /**End builds the forms**/

    function customDOM() {
        $customGrid = $("#customGrid");
        $customGrid.show().appendTo($("#customGridPanel").find("fieldset:eq(0)"));
        document.getElementById("customGridPanel").style.width = "720px";
        document.getElementById("customGridPanel").style.padding = "";
        document.getElementById("customGridPanel").getElementsByTagName("fieldset")[0].style.width = "100%";
        $(".pmui-gridpanel-footer").css({"text-align": "center", "margin-top": "10px", "width": "120%"});
        $(".pmui-gridpanel-footer").removeClass("pmui-gridpanel-footer");
    }

    function clearAddOptionForm() {
        abeAddOption.getField("abe_custom_value_add").setValue("");
        abeAddOption.getField("abe_custom_label_add").setValue("");
        abeAddOption.getField("abe_custom_format_add").setValue("");
        abeAddOption.getField("buttonFieldCancel").setVisible(false);
        abeAddOption.getField("buttonFieldAdd").setValue("Add".translate());
        editRow = null;
    };
    function addAcceptedValue() {
        var abeValue = abeAddOption.getField("abe_custom_value_add").getValue(),
            abeLabel = abeAddOption.getField("abe_custom_label_add").getValue(),
            abeFormat = abeAddOption.getField("abe_custom_format_add").getValue();

        if (abeAddOption && !abeAddOption.isValid()) {
            return;
        }

        if (!evaluateTags(abeFormat, 'validate')) {
            return;
        }

        if (editRow === null) {
            customGrid.addItem(new PMUI.grid.GridPanelRow({
                data: {
                    abe_custom_value: abeValue,
                    abe_custom_label: abeLabel,
                    abe_custom_format: abeFormat
                }
            }));
        } else {
            editRow.setData({
                abe_custom_value: abeValue,
                abe_custom_label: abeLabel,
                abe_custom_format: abeFormat
            });
            editRow = null;
            abeAddOption.getField("buttonFieldCancel").setVisible(false);
            abeAddOption.getField("buttonFieldAdd").setValue("Add".translate());
        }
        abeAddOption.getField("abe_custom_value_add").setValue("");
        abeAddOption.getField("abe_custom_label_add").setValue("");
        abeAddOption.getField("abe_custom_format_add").setValue("");
    }

    function customGridRowEdit(row) {
        editRow = row;
        row = row.getData();
        if (abeAddOption) {
            abeAddOption.getField("abe_custom_value_add").setValue(row.abe_custom_value);
            abeAddOption.getField("abe_custom_label_add").setValue(row.abe_custom_label);
            abeAddOption.getField("abe_custom_format_add").setValue(row.abe_custom_format);
            abeAddOption.getField("buttonFieldCancel").setVisible(true);
            abeAddOption.getField("buttonFieldAdd").setValue("Save".translate());
        }
    }

    function evaluateTags(html, action) {
        var oldHtml;
        var tagBody = '(?:[^"\'>]|"[^"]*"|\'[^\']*\')*';
        var pattern = /^[a-zA-Z0-9\s\[\]\.\-_#%;,=:()']*$/;
        var tagOrComment = new RegExp(
            '<(?:'
                // Comment body.
            + '!--(?:(?:-*[^->])*--+|-?)'
                // Special "raw text" elements whose content should be elided.
            + '|script\\b' + tagBody + '>[\\s\\S]*?</script\\s*'
            + '|style\\b' + tagBody + '>[\\s\\S]*?</style\\s*'
                // Regular name
            + '|/?[a-z]'
            + tagBody
            + ')>',
            'gi');

        var action = (typeof action === 'undefined') ? 'delete' : 'validate';
        if (action == "validate") {
            return pattern.test(html);
        } else {
            do {
                oldHtml = html;
                html = html.replace(tagOrComment, '');
            } while (html !== oldHtml);
            return html.replace(/</g, '&lt;');
        }
    }
};

var openCreateTemplates = false;

PMDesigner.ProcessFilesManager = function (processFileManagerOptionPath, optionCreation) {
    var rowselected = {};
    var rowselectedFile = {};
    var typeSave = '';
    var gridFilesManager;
    var gridTemplate;
    var gridPublic;
    var formEdit;
    var windowEdit;
    var windowCode;
    var editorHTML;
    var formUpload;
    var formUploadField;
    var initTinyMCE = null;
    var flagGridTemplate = true;
    var flagGridPublic = true;
    var presviusValueTiny = "";
    var buttonEditClass = 'mafe-button-edit';
    var buttonDeleteClass = 'mafe-button-delete';
    var buttonPropertiesClass = 'mafe-button-properties';

    var warningTemplate = new PMUI.ui.MessageWindow({
        id: 'warningTemplate',
        windowMessageType: 'warning',
        width: 490,
        title: 'Process Files Manager'.translate(),
        message: 'Do you want to delete this file?'.translate(),
        footerItems: [{
            id: 'warningTemplateButtonNo',
            text: 'No'.translate(),
            handler: function () {
                warningTemplate.close();
            },
            buttonType: "error"
        }, {
            id: 'warningTemplateButtonYes',
            text: 'Yes'.translate(),
            handler: function () {
                (new PMRestClient({
                    endpoint: 'file-manager/' + rowselectedFile.getData().prf_uid,
                    typeRequest: 'remove',
                    messageError: '',
                    functionSuccess: function (xhr, response) {
                        PMDesigner.msgFlash('File deleted successfully'.translate(), gridTemplate);
                        loadTemplate();
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    },
                    messageSuccess: 'File deleted successfully'.translate(),
                    flashContainer: gridTemplate
                })).executeRestClient();
                warningTemplate.close();
            },
            buttonType: "success"
        }
        ]
    });
    var isDirtyUpload = function () {
        if (formUpload.isDirty()) {
            var message_window = new PMUI.ui.MessageWindow({
                id: "cancelMessageTriggers",
                windowMessageType: 'warning',
                width: 490,
                title: "Upload File".translate(),
                message: 'Are you sure you want to discard your changes?'.translate(),
                footerItems: [
                    {
                        text: "No".translate(),
                        handler: function () {
                            message_window.close();
                        },
                        buttonType: "error"
                    },
                    {
                        text: "Yes".translate(),
                        handler: function () {
                            formUploadField.reset();
                            message_window.close();
                            windowUpload.close();
                        },
                        buttonType: "success"
                    }
                ]
            });
            message_window.open();
            message_window.showFooter();
        } else {
            windowUpload.close();
        }
    };

    var isDirtyFormEdit = function () {
        var fileContent = $(tinyMCE.activeEditor.getContent()).text().trim().length ? tinyMCE.activeEditor.getContent() : formEdit.getField("filecontent").getValue();
        if (formEdit.isDirty() || fileContent !== presviusValueTiny) {
            $(".mceSplitButtonMenu").each(function () {
                if ($(this).is(":visible")) {
                    $(this).addClass("mrdk").hide();
                }
            });
            var message_window = new PMUI.ui.MessageWindow({
                id: "cancelMessageTriggers",
                width: 490,
                windowMessageType: 'warning',
                title: 'Process Files Manager'.translate(),
                message: 'Are you sure you want to discard your changes?'.translate(),
                footerItems: [
                    {
                        text: "No".translate(),
                        handler: function () {
                            $(".mceSplitButtonMenu.mrdk").each(function () {
                                if ($(this).is(":hidden")) {
                                    $(this).removeClass("mrdk").show();
                                }
                            });
                            message_window.close();
                            windowFilesManager.close();
                        },
                        buttonType: "error"
                    }, {
                        text: "Yes".translate(),
                        handler: function () {
                            message_window.close();

                            if (clickedClose) {
                                windowEdit.close();
                            } else {
                                windowEdit.close();
                                windowFilesManager.open();
                            }
                        },
                        buttonType: "success"
                    }
                ]
            });
            message_window.open();
            message_window.showFooter();
        } else {
            if (clickedClose) {
                windowEdit.close();
            } else {
                windowEdit.close();
                windowFilesManager.open();
            }
        }
    };

    var warningPublic = new PMUI.ui.MessageWindow({
        id: 'warningPublic',
        title: 'Process Files Manager'.translate(),
        windowMessageType: 'warning',
        width: 490,
        message: 'Do you want to delete this file?'.translate(),
        footerItems: [{
            id: 'warningPublicButtonNo',
            text: 'No'.translate(),
            handler: function () {
                warningPublic.close();
            },
            buttonType: "error"
        }, {
            id: 'warningPublicButtonYes',
            text: 'Yes'.translate(),
            handler: function () {
                (new PMRestClient({
                    endpoint: 'file-manager/' + rowselectedFile.getData().prf_uid,
                    typeRequest: 'remove',
                    messageError: '',
                    functionSuccess: function (xhr, response) {
                        loadPublic();
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    },
                    messageSuccess: 'File deleted successfully'.translate(),
                    flashContainer: gridPublic
                })).executeRestClient();
                warningPublic.close();
            },
            buttonType: "success"
        }
        ]
    });

    var windowFilesManager = new PMUI.ui.Window({
        id: 'windowFilesManager',
        title: 'Process Files Manager'.translate(),
        height: DEFAULT_WINDOW_HEIGHT,
        width: DEFAULT_WINDOW_WIDTH,
        items: [
            gridFilesManager = new PMUI.grid.GridPanel({
                id: 'gridFilesManager',
                pageSize: 10,
                width: "96%",
                style: {
                    cssClasses: ["mafe-gridPanel"]
                },
                emptyMessage: 'No records found'.translate(),
                nextLabel: 'Next'.translate(),
                previousLabel: 'Previous'.translate(),
                customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
                    return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
                },
                dataItems: {},
                columns: [
                    {
                        title: 'Main Folders'.translate(),
                        dataType: 'string',
                        columnData: 'prf_path',
                        width: DEFAULT_WINDOW_WIDTH - 200,
                        alignmentCell: 'left',
                        sortable: true
                    },
                    {
                        id: 'filesGridViewCol',
                        title: '',
                        dataType: 'button',
                        buttonLabel: 'View'.translate(),
                        buttonStyle: {cssClasses: ['mafe-button-show']},
                        onButtonClick: function (row, grid) {
                            rowselected = row;
                            openFolder();
                        }
                    }
                ],
                onRowClick: function (row, grid) {
                    rowselected = row;
                    openFolder();
                }
            })
        ]
    });
    var buttonNew = new PMUI.ui.Button({
        id: 'createBtn',
        text: 'Create'.translate(),
        height: "38px",
        width: 100,
        style: {cssClasses: ['mafe-button-create']},
        handler: function (event) {
            newfile();
        }
    });
    var gridTemplate = new PMUI.grid.GridPanel({
        id: 'gridTemplate',
        filterPlaceholder: 'Search ...'.translate(),
        pageSize: 10,
        width: '96%',
        style: {
            cssClasses: ["mafe-gridPanel"]
        },
        tableContainerHeight: 374,
        emptyMessage: 'No records found'.translate(),
        nextLabel: 'Next'.translate(),
        previousLabel: 'Previous'.translate(),
        customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
            return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
        },
        dataItems: {},
        columns: [{
            title: "TEMPLATES".translate(),
            dataType: 'string',
            columnData: 'prf_filename',
            width: '500px',
            alignmentCell: 'left',
            sortable: true
        }, {
            dataType: 'string',
            columnData: 'prf_uid',
            alignmentCell: 'left',
            visible: false
        }, {
            dataType: 'string',
            columnData: 'prf_content',
            alignmentCell: 'left',
            visible: false
        }, {
            id: 'gridTemplateButtonHtmlEditor',
            title: '',
            dataType: 'button',
            buttonLabel: 'Html Editor'.translate(),
            width: '106px',
            onButtonClick: function (row, grid) {
                openHtmlEditor(row);
            },
            buttonStyle: {cssClasses: [buttonEditClass]}
        }, {
            id: 'gridTemplateButtonRichTextEditor',
            title: '',
            dataType: 'button',
            buttonLabel: 'Rich Text'.translate(),
            width: '100px',
            onButtonClick: function (row, grid) {
                presviusValueTiny = row.getData().prf_content ? row.getData().prf_content : "";
                rowselectedFile = row;
                editfile();
            },
            buttonStyle: {cssClasses: [buttonEditClass]}
        }, {
            id: 'gridTemplateButtonDownload',
            title: '',
            dataType: 'button',
            buttonLabel: 'Download'.translate(),
            width: '100px',
            onButtonClick: function (row, grid) {
                rowselectedFile = row;
                download();
            },
            buttonStyle: {cssClasses: [buttonPropertiesClass]}
        }, {
            id: 'gridTemplateButtonDelete',
            title: '',
            dataType: 'button',
            buttonLabel: 'Delete'.translate(),
            width: '82px',
            onButtonClick: function (row, grid) {
                rowselectedFile = row;
                warningTemplate.open();
                warningTemplate.showFooter();
            },
            buttonStyle: {cssClasses: [buttonDeleteClass]}
        }
        ]
    });
    var buttonPublicCreate = new PMUI.ui.Button({
        id: 'buttonPublicCreate',
        text: 'Create'.translate(),
        height: "38px",
        width: 100,
        style: {cssClasses: ['mafe-button-create']},
        handler: function (event) {
            newfile();
        }

    });
    var buttonUpload = new PMUI.ui.Button({
        id: 'uploadBtn',
        text: 'Upload'.translate(),
        style: {cssClasses: ['mafe-button-upload'], cssProperties: {'margin-right': '5px', 'float': 'none'}},
        handler: function (event) {
            windowUpload.open();
            applyStyleWindowForm(windowUpload);

        }
    });
    var gridPublic = new PMUI.grid.GridPanel({
        id: 'gridPublic',
        pageSize: 10,
        width: '96%',
        style: {
            cssClasses: ["mafe-gridPanel"]
        },
        emptyMessage: 'No records found'.translate(),
        filterPlaceholder: 'Search ...'.translate(),
        nextLabel: 'Next'.translate(),
        previousLabel: 'Previous'.translate(),
        tableContainerHeight: 374,
        customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
            return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
        },
        dataItems: {},
        columns: [{
            title: "PUBLIC".translate(),
            dataType: 'string',
            columnData: 'prf_filename',
            width: '704px',
            alignmentCell: 'left',
            sortable: true
        }, {
            dataType: 'string',
            columnData: 'prf_uid',
            alignmentCell: 'left',
            visible: false
        }, {
            id: 'gridPublicButtonDownload',
            title: '',
            dataType: 'button',
            buttonLabel: 'Download'.translate(),
            width: '111px',
            onButtonClick: function (row, grid) {
                rowselectedFile = row;
                download();
            },
            buttonStyle: {cssClasses: ['mafe-button-properties']}
        }, {
            id: 'gridPublicButtonDelete',
            title: '',
            dataType: 'button',
            buttonLabel: 'Delete'.translate(),
            width: '82px',
            onButtonClick: function (row, grid) {
                rowselectedFile = row;
                warningPublic.open();
                warningPublic.showFooter();
            },
            buttonStyle: {cssClasses: ['mafe-button-delete']}
        }
        ]
    });
    var windowUpload = new PMUI.ui.Window({
        id: 'windowUpload',
        title: 'Upload File'.translate(),
        height: 200,
        width: DEFAULT_WINDOW_WIDTH,
        onBeforeClose: function () {
            isDirtyUpload();
        },
        items: [
            formUpload = new PMUI.form.Form({
                id: 'formUpload',
                visibleHeader: false,
                items: [
                    formUploadField = new PMUI.field.UploadField({
                        id: 'formUploadField',
                        label: 'File'.translate(),
                        name: 'prf_file',
                        multiple: false,
                        labelWidth: '25%',
                        accept: 'text/html',
                        controlsWidth: 300
                    })
                ]
            })
        ],
        visibleFooter: true,
        buttonPanelPosition: 'bottom',
        buttonsPosition: 'right',
        buttons: [{
            id: 'windowUploadButtonCancel',
            text: 'Cancel'.translate(),
            handler: function () {
                isDirtyUpload();
            },
            buttonType: "error"
        }, {
            id: 'windowUploadButtonUpload',
            text: 'Upload'.translate(),
            handler: function () {
                uploadFile();
            },
            buttonType: "success"
        }
        ]
    });
    /**
     * Create window TinyMCE
     */
    function createWindowTinyMCE() {
        windowEdit = new PMUI.ui.Window({
            id: 'windowEdit',
            title: '',
            height: DEFAULT_WINDOW_HEIGHT - 80,
            width: DEFAULT_WINDOW_WIDTH,
            onBeforeClose: function () {
                PMDesigner.hideAllTinyEditorControls();
                clickedClose = true;
                isDirtyFormEdit();
            },
            items: [
                formEdit = new PMUI.form.Form({
                    id: 'formEdit',
                    visibleHeader: false,
                    width: 900,
                    items: [
                        new PMUI.field.TextField({
                            id: 'filename',
                            name: 'filename',
                            label: 'Filename'.translate(),
                            placeholder: 'Insert file name'.translate(),
                            required: true,
                            valueType: 'string',
                            labelWidth: "15%",
                            controlsWidth: 300,
                            validators: [{
                                pmType: "regexp",
                                criteria: /^[a-zA-Z0-9-_ ]*$/,
                                errorMessage: "File name is invalid".translate()
                            }]
                        }),
                        new PMUI.field.TextAreaField({
                            id: 'filecontent',
                            name: 'filecontent',
                            label: 'Content'.translate(),
                            value: '',
                            rows: 210,
                            labelWidth: "15%",
                            controlsWidth: 720,
                            onChange: function (currentValue, previousValue) {
                            },
                            style: {cssClasses: ['mafe-textarea-resize']}
                        })
                    ]
                })
            ],
            buttonPanelPosition: 'bottom',
            buttonsPosition: 'right',
            buttons: [{
                id: 'windowEditButtonCancel',
                text: 'Cancel'.translate(),
                buttonType: 'error',
                handler: function () {
                    PMDesigner.hideAllTinyEditorControls();
                    clickedClose = false;
                    isDirtyFormEdit();
                }
            }, {
                id: 'windowEditButtonSave',
                text: "Save".translate(),
                buttonType: 'success',
                handler: function () {
                    if (formEdit.isValid()) {
                        PMDesigner.hideAllTinyEditorControls();
                        $(".mceSplitButtonMenu").hide();
                        save();
                    }
                }
            }
            ]
        });
        formEdit.getField('filename').enable();
        formEdit.getField('filecontent').getControls()[0].getHTML().className = 'tmceEditor';
        windowEdit.open();
        windowEdit.showFooter();
        applyStyleWindowForm(windowEdit);
        tinyMCE.init({
            editor_selector: 'tmceEditor',
            mode: 'specific_textareas',
            directionality: 'ltr',
            verify_html: false,
            skin: 'o2k7',
            theme: 'advanced',
            skin_variant: 'silver',
            relative_urls : false,
            remove_script_host : false,
            theme_advanced_source_editor_width: DEFAULT_WINDOW_WIDTH - 50,
            theme_advanced_source_editor_height: DEFAULT_WINDOW_HEIGHT - 100,
            plugins: "advhr,advimage,advlink,advlist,autolink,autoresize,contextmenu,directionality,emotions,example,example_dependency,fullpage,fullscreen,iespell,inlinepopups,insertdatetime,layer,legacyoutput,lists,media,nonbreaking,noneditable,pagebreak,paste,preview,print,save,searchreplace,style,tabfocus,table,template,visualblocks,visualchars,wordcount,xhtmlxtras,pmSimpleUploader,pmVariablePicker,style",
            theme_advanced_buttons1: 'pmSimpleUploader,|,pmVariablePicker,|,bold,italic,underline,|,justifyleft,justifycenter,justifyright,justifyfull,|,fontselect,fontsizeselect,|,cut,copy,paste',
            theme_advanced_buttons2: 'bullist,numlist,|,outdent,indent,blockquote,|,tablecontrols,|,undo,redo,|,link,unlink,image,|,forecolor,backcolor,styleprops',
            theme_advanced_buttons3: 'hr,removeformat,visualaid,|,sub,sup,|,ltr,rtl,|,code',
            popup_css: "/js/tinymce/jscripts/tiny_mce/themes/advanced/skins/default/dialogTinyBpmn.css",
            convert_urls: false,
            onchange_callback: function (inst) {
                formEdit.getField('filecontent').setValue(tinyMCE.activeEditor.getContent({format: 'raw'}));
            },
            handle_event_callback: function (e) {
            },
            setup: function (ed) {
                ed.onSetContent.add(function (ed, l) {
                    formEdit.getField('filecontent').setValue(tinyMCE.activeEditor.getContent({format: 'raw'}));
                });
            },
            oninit: function () {
                tinyMCE.activeEditor.processID = PMDesigner.project.id;
            }
        });
        validateKeysField(formEdit.getField('filename').getControls()[0].getHTML(), ['isbackspace', 'isnumber', 'isletter', 'isunderscore', 'ishyphen', 'isparenthesisopening', 'isparenthesisclosing']);
        windowEdit.footer.getItems()[0].setHeight(38);
        windowEdit.footer.getItems()[1].setHeight(38);
        document.getElementById(windowEdit.footer.getItems()[0].id).style.lineHeight = '18px';
        document.getElementById(windowEdit.footer.getItems()[1].id).style.lineHeight = '18px';
    }
    /**
     * Create and open HTML Editor
     */
    function openHtmlEditor(rowSelected) {
        var buttonCancel,
            buttonSave,
            contentHTML,
            rowData = rowSelected.getData(),
            factorWidth = 0.9,
            factorHeight = 0.7;

        //CodeMirror
        editorHTML = new PMCodeMirror({
            id: "editCodeHTML"
        });

        //Footer´s buttons
        buttonSave = new PMUI.ui.Button({
            id: 'saveHtmlEditor',
            text: "Apply".translate(),
            buttonType: 'success',
            handler: function (event) {
                saveHtmlEditor();
            }
        });
        buttonCancel = new PMUI.ui.Button({
            id: 'cancelHtmlEditor',
            text: "Cancel".translate(),
            buttonType: 'error',
            handler: function (event) {
                windowCode.close();
            }
        });
        //Create Window with Code Mirror
        windowCode = new PMUI.ui.Window({
            id: 'windowCode',
            title: 'HTML Editor'.translate(),
            height: DEFAULT_WINDOW_HEIGHT * 0.9,
            width: DEFAULT_WINDOW_WIDTH,
            footerItems: [
                buttonCancel,
                buttonSave
            ],
            buttonPanelPosition: "bottom",
            footerAlign: "right"
        });

        windowCode.open();
        windowCode.showFooter();
        windowCode.addItem(editorHTML);
        contentHTML = $.isPlainObject(rowData) && !$.isEmptyObject(rowData) ? rowData.prf_content : "";
        editorHTML.cm.setSize(windowCode.getWidth() * factorWidth, windowCode.getHeight() * factorHeight);
        editorHTML.cm.setValue(contentHTML);
        editorHTML.related_row = rowData.prf_uid;

        // Apply styles
        $(".CodeMirror.cm-s-default.CodeMirror-wrap").css({
            "margin": "10px 0 0 20px",
            "border": "1px solid #c0c0c0"
        });
        $(".CodeMirror.cm-s-default.CodeMirror-wrap").after($ctrlSpaceMessage.css({
            "margin": "5px 5px 5px 20px"
        }));
        $(".pmui-window-body").css("overflow", "hidden");
        editorHTML.cm.refresh();
    }

    function newfile() {
        windowFilesManager.close();

        initTinyMCE = function () {
            tinyMCE.activeEditor.domainURL = "/sys" + WORKSPACE + "/" + LANG + "/" + SKIN + "/";
            tinyMCE.activeEditor.processID = PMDesigner.project.id;
        };
        createWindowTinyMCE();
        typeSave = 'new';
        var title = (processFileManagerOptionPath == 'templates') ? "Create ".translate() + " " + processFileManagerOptionPath.substring(0, processFileManagerOptionPath.length - 1).translate() : "Create ".translate() + " " + processFileManagerOptionPath.translate() + " " + "file".translate();
        windowEdit.setTitle(title.translate());
        var closeElement = windowEdit.header.childNodes[1];
        if (closeElement.addEventListener) {
            closeElement.addEventListener("click", function () {
                $(".mceSplitButtonMenu").hide();
            }, false);
        } else {
            closeElement.attachEvent("click", function () {
                $(".mceSplitButtonMenu").hide();
            });
        }
        formEdit.setFocus();
    }

    function editfile() {
        var title,
            closeElement;

        windowFilesManager.close();
        initTinyMCE = function () {
            tinyMCE.activeEditor.domainURL = "/sys" + WORKSPACE + "/" + LANG + "/" + SKIN + "/";
            tinyMCE.activeEditor.processID = PMDesigner.project.id;
        };
        createWindowTinyMCE();
        typeSave = 'update';
        title = "Edit ".translate() + " " + processFileManagerOptionPath.substring(0, processFileManagerOptionPath.length - 1).translate();
        if (windowEdit && formEdit) {
            windowEdit.setTitle(title.translate());
            closeElement = windowEdit.header.childNodes[1];
            if (closeElement.addEventListener) {
                closeElement.addEventListener("click", function () {
                    $(".mceSplitButtonMenu").hide();
                }, false);
            } else {
                closeElement.attachEvent("click", function () {
                    $(".mceSplitButtonMenu").hide();
                });
            }
            formEdit.getField('filename').getControl().setStyle({cssProperties: {"background": "#EEEEEE"}});
            formEdit.getField('filename').disable();
            formEdit.getField('filename').setValue(rowselectedFile.getData().prf_filename);
            var a = rowselectedFile.getData().prf_content;
            formEdit.getField('filecontent').setValue(a);
            if (tinyMCE.activeEditor) {
                tinyMCE.activeEditor.setContent(a);
            }
        }
    }

    function save() {
        var flagAux;

        if (!formEdit.isValid()) {
            flagAux = formEdit.visible;
        } else {
            flagAux = formEdit.isValid();
        }

        if (flagAux) {
            if (getData2PMUI(formEdit.html).filename == "") {
                return false;
            }
        }

        if ((navigator.userAgent.indexOf("MSIE") != -1) || (navigator.userAgent.indexOf("Trident") != -1)) {
            var data = getData2PMUI(formEdit.html);
        } else {
            var data = formEdit.getData();
        }

        if (typeSave === 'new') {
            (new PMRestClient({
                endpoint: 'file-manager',
                typeRequest: 'post',
                messageError: '',
                data: {
                    prf_filename: data.filename + ".html",
                    prf_path: processFileManagerOptionPath,
                    prf_content: data.filecontent
                },
                functionSuccess: function (xhr, response) {
                    windowEdit.close();
                    if (processFileManagerOptionPath == "templates") {
                        PMDesigner.msgFlash('File saved successfully'.translate(), gridTemplate);
                        windowFilesManager.open();
                        loadTemplate();
                    }
                    if (processFileManagerOptionPath == "public") {
                        PMDesigner.msgFlash('File saved successfully'.translate(), gridPublic);
                        windowFilesManager.open();
                        loadPublic();
                    }
                    formEdit.getField('filename').setValue('');
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            })).executeRestClient();
        }
        if (typeSave === 'update') {
            (new PMRestClient({
                endpoint: 'file-manager/' + rowselectedFile.getData().prf_uid,
                typeRequest: 'update',
                messageError: '',
                data: {
                    prf_content: tinyMCE.activeEditor.getContent()
                },
                functionSuccess: function (xhr, response) {
                    windowEdit.close();
                    if (processFileManagerOptionPath == "templates") {
                        PMDesigner.msgFlash('File updated successfully'.translate(), gridTemplate);
                        windowFilesManager.open();
                        loadTemplate();
                    }
                    if (processFileManagerOptionPath == "public") {
                        PMDesigner.msgFlash('File updated successfully'.translate(), gridPublic);
                        windowFilesManager.open();
                        loadPublic();
                    }
                    formEdit.getField('filename').setValue('');
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            })).executeRestClient();
        }
    }
    /**
     * Save Content of the Html Editor
     */
    function saveHtmlEditor() {
        var index = 0,
            codeMirror = windowCode.getItems()[index],
            contentCodeMirror = codeMirror.getValue(),
            rowRelatedUID = codeMirror.related_row,
            request;


        request = new PMRestClient({
            endpoint: 'file-manager/' + rowRelatedUID,
            typeRequest: 'update',
            messageError: 'Error Update File'.translate(),
            data: {
                prf_content: contentCodeMirror
            },
            functionSuccess: function (xhr, response) {
                windowCode.close();
                if (processFileManagerOptionPath == "templates") {
                    PMDesigner.msgFlash('File updated successfully'.translate(), gridTemplate);
                    windowFilesManager.open();
                    loadTemplate();
                }
                if (processFileManagerOptionPath == "public") {
                    PMDesigner.msgFlash('File updated successfully'.translate(), gridPublic);
                    windowFilesManager.open();
                    loadPublic();
                }
            },
            functionFailure: function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            }
        });
        request.executeRestClient();
    }

    function loadFileManager() {
        (new PMRestClient({
            endpoint: 'file-manager',
            typeRequest: 'get',
            messageError: '',
            functionSuccess: function (xhr, response) {
                gridFilesManager.clearItems();
                for (var i = 0; i < response.length; i++) {
                    gridFilesManager.addDataItem({prf_path: response[i].name});
                }
                if (openCreateTemplates) {
                    gridFilesManager.onRowClick(gridFilesManager.getItems()[0]);
                    newfile();
                }
            },
            functionFailure: function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            }
        })).executeRestClient();
    }

    function loadTemplate() {
        (new PMRestClient({
            endpoint: 'file-manager',
            typeRequest: 'get',
            messageError: '',
            data: {
                path: processFileManagerOptionPath,
                get_content: true
            },
            functionSuccess: function (xhr, response) {
                gridTemplate.clearItems();
                gridTemplate.clearItemsColumns();
                for (var i = 0; i < response.length; i++) {
                    gridTemplate.addDataItem({
                        prf_filename: response[i].prf_filename,
                        prf_content: response[i].prf_content,
                        prf_uid: response[i].prf_uid,
                        prf_assigned_routing_screen: response[i].prf_derivation_screen
                    });
                }
                gridTemplate.sort('prf_filename', 'asc');
            },
            functionFailure: function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            }
        })).executeRestClient();
        checkDerivationScreen();
    }

    function loadPublic() {
        (new PMRestClient({
            endpoint: 'file-manager',
            typeRequest: 'get',
            messageError: '',
            data: {
                path: processFileManagerOptionPath,
                get_content: false
            },
            functionSuccess: function (xhr, response) {
                gridPublic.clearItems();
                for (var i = 0; i < response.length; i++) {
                    gridPublic.addDataItem({
                        prf_filename: response[i].prf_filename,
                        prf_uid: response[i].prf_uid
                    });
                }
                gridPublic.sort('prf_filename', 'asc');
            },
            functionFailure: function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            }
        })).executeRestClient();
    }

    function download() {
        var xhr,
            win = window,
            value = 'blob',
            url = HTTP_SERVER_HOSTNAME + '/api/1.0/' + WORKSPACE + '/project/' + PMDesigner.project.id + '/file-manager/' + rowselectedFile.getData().prf_uid + '/download';

        if (win.XMLHttpRequest) {
            xhr = new XMLHttpRequest();
        } else if (win.ActiveXObject) {
            xhr = new ActiveXObject('Microsoft.XMLHTTP');
        }
        win.URL = win.URL || win.webkitURL;

        xhr.open('GET', url, true);
        xhr.responseType = value;
        xhr.setRequestHeader('Authorization', 'Bearer ' + PMDesigner.project.keys.access_token);
        xhr.setRequestHeader ('Cache-Control', 'no-cache');
        xhr.onload = function () {
            if (this.status === 200) {
                if (processFileManagerOptionPath == "templates") {
                    PMDesigner.msgFlash('Start file download successfully'.translate(), gridTemplate);
                }
                if (processFileManagerOptionPath == "public") {
                    PMDesigner.msgFlash('Start file download successfully'.translate(), gridPublic);
                }
                var doc = win.document, a = doc.createElementNS('http://www.w3.org/1999/xhtml', 'a'), event = doc.createEvent('MouseEvents');
                event.initMouseEvent('click', true, false, win, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
                var blob = xhr.response;
                if ((navigator.userAgent.indexOf("MSIE") != -1) || (navigator.userAgent.indexOf("Trident") != -1) || (navigator.userAgent.indexOf("Edge") != -1)) {
                    window.navigator.msSaveBlob(blob, rowselectedFile.getData().prf_filename);
                }
                else {
                    a.href = win.URL.createObjectURL(blob);
                    a.download = rowselectedFile.getData().prf_filename;
                    a.dispatchEvent(event);
                }
            }
        };
        xhr.send()


    }

    /**
     * Upload file to Public Files or Templates
     */
    function uploadFile() {
        var fileSelector = formUploadField.getHTML().getElementsByTagName('input')[0],
            formData = new FormData(),
            xhr;
        if (fileSelector.files.length === 0) {
            PMDesigner.msgFlash('Please select a file to upload'.translate(), windowUpload.footer, "info");
            return;
        }
        formData.append('form', fileSelector.files[0]);
        xhr = new XMLHttpRequest();
        xhr.open('POST', '/api/1.0/' + WORKSPACE + '/project/' + PMDesigner.project.id + '/process-files-manager/' + processFileManagerOptionPath, true);
        xhr.setRequestHeader('Authorization', 'Bearer ' + PMDesigner.project.keys.access_token);
        xhr.onload = function () {
            var resp;
            switch (xhr.status) {
                case 200:
                    formUploadField.reset();
                    windowUpload.close();
                    if (processFileManagerOptionPath === "templates") {
                        PMDesigner.msgFlash('File uploaded successfully'.translate(), gridTemplate);
                        loadTemplate();
                    }
                    if (processFileManagerOptionPath === "public") {
                        PMDesigner.msgFlash('File uploaded successfully'.translate(), gridPublic);
                        loadPublic();
                    }
                    break;
                default:
                    if (this.response) {
                        resp = JSON.parse(this.response);
                        PMDesigner.msgWinError(resp.message ? resp.message : resp.error.message);
                    }
                    break;
            }
        };
        xhr.send(formData);
    }

    function styleApp() {
        try {
            gridFilesManager.dom.toolbar.style.display = 'none';
            gridTemplate.dom.toolbar.getElementsByTagName('input')[0].style.visibility = 'hidden';
            gridPublic.dom.toolbar.getElementsByTagName('input')[0].style.visibility = 'hidden';

        } catch (e) {
        }
    }

    function disableAllFields() {
        windowFilesManager.getItems()[0].setVisible(false);

    }

    function openFolder() {
        disableAllFields();
        styleApp();

        if (processFileManagerOptionPath == "templates") {
            windowFilesManager.setTitle("Templates".translate());
            loadTemplate();
            if (flagGridTemplate) {
                windowFilesManager.addItem(gridTemplate);
                flagGridTemplate = false;
            }

            var buttonsTemplate = document.createElement("div");
            buttonsTemplate.id = "buttonsTemplate";
            buttonsTemplate.style.display = "inlineBlock";
            buttonsTemplate.style.float = "right";

            buttonsTemplate.appendChild(buttonUpload.getHTML());
            buttonsTemplate.appendChild(buttonNew.getHTML());
            //buttonsTemplate.appendChild(buttonBack.getHTML());

            buttonUpload.defineEvents();
            buttonNew.defineEvents();
            //buttonBack.defineEvents();

            gridTemplate.dom.toolbar.appendChild(buttonsTemplate);
            gridTemplate.setVisible(true);
            gridTemplate.dom.toolbar.getElementsByTagName('input')[0].style.visibility = 'visible';
            buttonNew.getHTML().style.top = '-3px';
        }
        if (processFileManagerOptionPath == "public") {
            windowFilesManager.setTitle("Public Files".translate());
            loadPublic();
            if (flagGridPublic) {
                windowFilesManager.addItem(gridPublic);
                flagGridPublic = false;
            }
            var buttonsPublic = document.createElement("div");
            buttonsPublic.id = "buttonsPublic";
            buttonsPublic.style.display = "inlineBlock";
            buttonsPublic.style.float = "right";

            buttonsPublic.appendChild(buttonUpload.getHTML());
            buttonsPublic.appendChild(buttonPublicCreate.getHTML());
            //buttonsPublic.appendChild(buttonBack.getHTML());

            buttonUpload.defineEvents();
            buttonPublicCreate.defineEvents();
            //buttonBack.defineEvents();

            gridPublic.dom.toolbar.appendChild(buttonsPublic);
            gridPublic.dom.toolbar.getElementsByTagName('input')[0].style.visibility = 'visible';
            gridPublic.setVisible(true);
            buttonPublicCreate.getHTML().style.top = '-3px';
        }
    }
    /**
     * Check Templates Assigned Derivation Screen
     * @returns {checkDerivationScreen}
     */
    function checkDerivationScreen () {
        var gridTemplateAux = gridTemplate,
            idRichText = "gridTemplateButtonRichTextEditor",
            rowsGrid,
            columnsGrid,
            dataRowGrid,
            columnRichText,
            index = 0,
            cell = {},
            message = "Editor unavailable",
            tooltipMafeClass = "mafe-action-tooltip-black",
            i,
            max;

        rowsGrid = gridTemplateAux.getItems();
        columnsGrid = gridTemplateAux.getColumns();
        columnRichText = $.grep(columnsGrid, function (item, index) {
            return item.id === idRichText;
        });
        for (i = 0, max = rowsGrid.length; i < max; i += 1) {
            dataRowGrid = rowsGrid[i].data.customKeys;
            if (dataRowGrid.prf_assigned_routing_screen && columnRichText.length) {
                columnRichText[index].disableCell(i);
                cell = columnRichText[index].getCells().length ? columnRichText[index].getCells()[i] : cell;
                if (!$.isEmptyObject(cell)) {
                    $(cell.getHTML()).attr('title', message).tooltip({
                        tooltipClass: tooltipMafeClass,
                        position: {
                            my: 'center top',
                            at: 'center bottom+4'
                        }
                    });
                }
            }
        }
        return this;
    }

    if (optionCreation == "CREATION_NORMAL") {
        windowFilesManager.open();
        openFolder();
    } else {
        windowFilesManager.open();
        openFolder();
        newfile();
    }
    checkDerivationScreen();
};

PMDesigner.ProcessFilesManager.createFirst = function (processFileManagerOptionPath, optionCreation) {
    PMDesigner.ProcessFilesManager(processFileManagerOptionPath, optionCreation);
};

(function () {
    PMDesigner.assigmentRules = function (event) {
        var formAssignmentRules,
            i,
            tabPanelAssignmentRules,
            windowAssignmentRules,
            dataProperties,
            activity = event,
            pageSizeAssignment = 9,
            pageSizeAssignmentAD = 9,
            quickMessageWindow = new QuickMessageWindow(),
            arrayObjectUserList = [],
            arrayObjectUserList2 = [],
            arrayObjectUsers = [],
            arrayObjectUsers2 = [],
            arrayObjectAdhocUser = [],
            arrayObjectAdhocUser2 = [],
            arrayObjectAdhocUserList = [],
            arrayObjectAdhocUserList2 = [],
            warningMessageWindowDirty,
            restClient,
            assigmentPanelGlobal,
            assigmentPanelUser,
            panelTitleUser,
            titleUser,
            panelGridUser,
            buttonsUsers,
            gridUsers,
            assigmentPanelUserList,
            panelTitleUserList,
            titleUserList,
            panelGridUserList,
            buttonsUserList,
            gridUserList,
            assigmentPanelGlobalAD,
            assigmentPanelUserAD,
            panelTitleUserAD,
            titleUserAD,
            panelSearchUserAD,
            searchGridUserAD,
            panelGridUserAD,
            buttonsUsersAD,
            gridUsersAD,
            assigmentPanelUserListAD,
            panelTitleUseListAD,
            titleUserListAD,
            panelGridUserListAD,
            buttonsUserListAD,
            gridUserListAD,
            panelContainerRules,
            panelContainerUsers,
            panelContainerUsersAdHoc,
            loadTrigger,
            loadFormData,
            loadServerData,
            loadFreeUsers,
            loadAssignmentUsers,
            loadAdHocFreeUsers,
            loadAdHocAssignmentUsers,
            updateRules,
            allHidden,
            changeRadioButtons,
            visibleService,
            hiddenTab,
            saveData,
            saveOrUpdateUserAndAdHocUsers,
            applyStyles,
            applyStylesAD,
            assignee,
            remove,
            assigneeAD,
            removeAD,
            groupRows,
            flashMessage = new PMUI.ui.FlashMessage({
                message: '',
                appendTo: document.body,
                duration: 1000,
                severity: "success"
            });

        warningMessageWindowDirty = new PMUI.ui.MessageWindow({
            id: 'warningMessageWindowDirty',
            windowMessageType: 'warning',
            width: 490,
            bodyHeight: 'auto',
            title: 'Routing Rule'.translate(),
            message: 'Are you sure you want to discard your changes?'.translate(),
            footerItems: [{
                id: 'warningMessageWindowDirtyButtonNo',
                text: 'No'.translate(),
                visible: true,
                handler: function () {
                    warningMessageWindowDirty.close();
                },
                buttonType: "error"
            }, {
                id: 'warningMessageWindowDirtyButtonYes',
                text: 'Yes'.translate(),
                visible: true,
                handler: function () {
                    warningMessageWindowDirty.close();
                    windowAssignmentRules.close();
                },
                buttonType: "success"
            }]
        });
        formAssignmentRules = new PMUI.form.Form({
            id: 'formAssignmentRules',
            visibleHeader: false,
            buttonPanelPosition: 'top',
            width: 910,
            items: [
                new PMUI.field.RadioButtonGroupField({
                    id: 'formTasAssignType',
                    name: 'tas_assign_type',
                    label: 'Case assignment method'.translate(),
                    required: false,
                    controlPositioning: 'horizontal',
                    maxDirectionOptions: 3,
                    options: [{
                        id: 'formTasAssignTypeCyclical',
                        label: 'Cyclical Assignment'.translate(),
                        value: 'BALANCED',
                        selected: true
                    }, {
                        id: 'formTasAssignTypeManual',
                        label: 'Manual Assignment'.translate(),
                        value: 'MANUAL'
                    }, {
                        id: 'formTasAssignTypeValue',
                        label: 'Value Based Assignment'.translate(),
                        value: 'EVALUATE'
                    }, {
                        id: 'formTasAssignTypeReports',
                        label: 'Reports to'.translate(),
                        value: 'REPORT_TO'
                    }, {
                        id: 'formTasAssignTypeSelf',
                        label: 'Self Service'.translate(),
                        value: 'SELF_SERVICE'
                    }, {
                        id: 'formTasAssignTypeSelfValue',
                        label: 'Self Service Value Based Assignment'.translate(),
                        value: 'SELF_SERVICE_EVALUATE'
                    }, {
                        id: 'formTasAssignTypeParallel',
                        label: 'Parallel Assignment'.translate(),
                        value: 'MULTIPLE_INSTANCE'
                    }, {
                        id: 'formTasAssignTypeParallel',
                        label: 'Value Based Assignment'.translate(),
                        value: 'MULTIPLE_INSTANCE_VALUE_BASED'
                    }
                    ],
                    onChange: function (newVal, oldVal) {
                        changeRadioButtons(newVal);
                    },
                }), new CriteriaField({
                    id: 'formAssignmentRulesVariable',
                    pmType: 'text',
                    name: 'tas_assign_variable',
                    valueType: 'string',
                    label: 'Variable for Value Based Assignment'.translate(),
                    controlsWidth: DEFAULT_WINDOW_WIDTH - 527
                }), new CriteriaField({
                    id: 'formAssignmentRulesVariableSelf',
                    pmType: 'text',
                    name: 'tas_group_variable',
                    valueType: 'string',
                    label: 'Variable for Self Service Value Based Assignment'.translate(),
                    controlsWidth: DEFAULT_WINDOW_WIDTH - 527
                }), {
                    id: 'formAssignmentRulesSetTimeout',
                    pmType: 'checkbox',
                    name: 'tas_selfservice_timeout',
                    label: 'Set a timeout'.translate(),
                    options: [{
                        id: 'formAssignmentRulesSetTimeoutOption',
                        label: '',
                        value: '1'
                    }
                    ],
                    onChange: function (val) {
                        visibleService(this.controls[0].selected);
                    }
                }, {
                    id: 'formAssignmentRulesTime',
                    pmType: 'text',
                    name: 'tas_selfservice_time',
                    valueType: 'string',
                    label: 'Time'.translate(),
                    required: true,
                    validators: [{
                        pmType: "regexp",
                        criteria: /^[0-9]*$/,
                        errorMessage: "Please enter a numeric value".translate()
                    }]
                }, {
                    id: 'formAssignmentRulesTimeUnit',
                    pmType: 'dropdown',
                    name: 'tas_selfservice_time_unit',
                    label: 'Time unit'.translate(),
                    options: [{
                        id: 'formAssignmentRulesTimeUnitOption1',
                        label: 'Hours'.translate(),
                        value: 'HOURS'
                    }, {
                        id: 'formAssignmentRulesTimeUnitOption2',
                        label: 'Days'.translate(),
                        value: 'DAYS'
                    }, {
                        id: 'formAssignmentRulesTimeUnitOption3',
                        label: 'Minutes'.translate(),
                        value: 'MINUTES'
                    }]
                }, {
                    id: 'formAssignmentRulesTrigger',
                    pmType: 'dropdown',
                    name: 'tas_selfservice_trigger_uid',
                    label: 'Trigger to execute'.translate(),
                    required: true,
                    options: [{
                        id: 'formAssignmentRulesTriggerOption1',
                        value: '',
                        label: ''
                    }]
                }, {
                    id: 'formAssignmentRulesTriggerExecute',
                    pmType: 'dropdown',
                    name: 'tas_selfservice_execution',
                    label: 'Execute Trigger'.translate(),
                    options: [{
                        id: 'formAssignmentRulesTriggerExecute1',
                        label: 'Every time scheduled by cron'.translate(),
                        value: 'EVERY_TIME'
                    }, {
                        id: 'formAssignmentRulesTriggerExecute2',
                        label: 'Once'.translate(),
                        value: 'ONCE'
                    }]
                }
            ]
        });
        restClient = new PMRestClient({
            endpoint: 'projects',
            typeRequest: 'get',
            messageError: "There are problems, please try again.".translate(),
            functionFailure: function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            }
        });

        assigmentPanelGlobal = new PMUI.core.Panel({
            id: "assigmentPanelGlobal",
            layout: "hbox",
            width: DEFAULT_WINDOW_WIDTH
        });
        assigmentPanelUser = new PMUI.core.Panel({
            id: "assigmentPanelUser",
            layout: "vbox",
            width: "60%",
            style: {
                cssClasses: [
                    'mafe-panel-assignment-white'
                ]
            }
        });
        panelTitleUser = new PMUI.core.Panel({
            id: "panelTitleUser",
            layout: "hbox"
        });
        titleUser = new PMUI.ui.TextLabel({
            id: "titleUser",
            label: " ",
            textMode: 'plain',
            text: 'Available users list'.translate(),
            style: {
                cssClasses: [
                    'mafe-designer-assigment-title'
                ]
            }
        });
        panelGridUser = new PMUI.core.Panel({
            id: "panelGridUser",
            layout: "hbox",
            style: {
                cssClasses: ["mafe-panel"]
            }
        });
        buttonsUsers = new PMUI.field.RadioButtonGroupField({
            id: "buttonsUsers",
            controlPositioning: 'horizontal',
            maxDirectionOptions: 3,
            options: [{
                id: 'buttonAllAv',
                label: 'View all'.translate(),
                value: 'all',
                selected: true
            }, {
                id: 'buttonUsersAv',
                label: 'View users'.translate(),
                value: 'user'
            }, {
                id: 'buttonGroupsAv',
                label: 'View groups'.translate(),
                value: 'group'
            }],
            onChange: function (newVal, oldVal) {
                switch (newVal) {
                    case "user" :
                        gridUsers.typeList = "user";
                        break;
                    case "group":
                        gridUsers.typeList = "group";
                        break;
                    default:
                        gridUsers.typeList = "";
                        break;
                }
                gridUsers.goToPage(0);
            }
        });
        gridUsers = new PMUI.grid.GridPanel({
            id: "gridUsers",
            pageSize: pageSizeAssignment - 1,
            edges: 2,
            behavior: 'dragdropsort',
            displayedPages: 2,
            filterable: true,
            filterPlaceholder: 'Search ...'.translate(),
            emptyMessage: 'No records found'.translate(),
            nextLabel: 'Next'.translate(),
            previousLabel: 'Previous'.translate(),
            tableContainerHeight: 242,
            customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
                return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
            },
            customDataRest: function (data) {
                var i;
                for (i = 0; i < data.length; i += 1) {
                    if (data[i].aas_type == "group") {
                        data[i]["available"] = data[i].aas_name;
                    } else {
                        data[i]["available"] = data[i].aas_name + " " + data[i].aas_lastname + " (" + data[i].aas_username + ")";
                    }
                }
                return data;
            },
            dynamicLoad: {
                keys: {
                    server: HTTP_SERVER_HOSTNAME,
                    projectID: PMDesigner.project.projectId,
                    workspace: WORKSPACE,
                    accessToken: PMDesigner.project.tokens.access_token,
                    endPoint: 'project/' + PMDesigner.project.id + '/activity/' + activity.id + '/available-assignee/paged'
                }
            },
            columns: [{
                id: 'gridUsersButtonLabel',
                title: '',
                dataType: 'button',
                width: "10%",
                buttonLabel: function (row, data) {
                    return data.fullName;
                },
                buttonStyle: {
                    cssClasses: [
                        'mafe-grid-button'
                    ]
                },
                onButtonClick: function (row, grid) {
                    var option, select;
                    select = document.createElement("span");
                    select.id = "list-usersIngroup";
                    option = document.createElement("span");
                    option.id = "list-usersIngroup-iem"

                    if (row.getData()["aas_type"] === 'group') {
                        var restClient = new PMRestClient({
                            typeRequest: 'get',
                            functionSuccess: function (xhr, response) {
                                var optionClone, i;
                                for (i = 0; i < response.length; i += 1) {
                                    if (i == 10) {
                                        optionClone = option.cloneNode(false);
                                        optionClone.innerHTML = "<b style='float: right'>. . .<b>";
                                        select.appendChild(optionClone);
                                    } else {
                                        optionClone = option.cloneNode(false);
                                        optionClone.textContent = "- " + response[i].usr_firstname + ' ' + response[i].usr_lastname;
                                        optionClone.title = response[i].usr_firstname + ' ' + response[i].usr_lastname;
                                        select.appendChild(optionClone);
                                    }
                                }
                                if (!optionClone) {
                                    optionClone = option.cloneNode(false);
                                    optionClone.textContent = "No users".translate();
                                    select.appendChild(optionClone);
                                }

                                quickMessageWindow.show($(row.html).find('a')[0], select);
                            },
                            functionFailure: function (xhr, response) {
                                PMDesigner.msgWinError(response.error.message);
                            },
                            messageError: 'There are problems saving the assigned user, please try again.'.translate()
                        });
                        restClient.setBaseEndPoint('group/' + row.getData()["aas_uid"] + '/users?start=0&limit=11');
                        restClient.executeRestClient();
                    }
                }
            },
                {
                    title: "",
                    dataType: 'string',
                    columnData: "available",
                    alignmentCell: "left",
                    width: "330px"
                },
                {
                    id: 'gridUsersButtonAssign',
                    title: '',
                    dataType: 'button',
                    width: "10%",
                    buttonStyle: {
                        cssClasses: [
                            'mafe-button-edit-assign'
                        ]
                    },
                    buttonLabel: function (row, data) {
                        var sw = row.getData()["aas_type"] === 'user';
                        row.getCells()[0].content.style.addClasses([sw ? 'button-icon-user' : 'button-icon-group']);
                        return '';
                    },
                    onButtonClick: function (row, grid) {
                        var dataRow = {};
                        grid = (grid != null) ? grid : gridUsers;
                        assignee(row);
                        gridUserList.goToPage(gridUserList.currentPage);
                        grid.goToPage(0);
                        gridUserList.goToPage(0);
                        flashMessage.setMessage("Assignee saved successfully".translate());
                        flashMessage.setAppendTo(windowAssignmentRules.getHTML());
                        flashMessage.show();
                    }
                }],
            onDropOut: function (item, origin, destiny) {
            },
            onDrop: function (a, row) {
                grid = this;
                remove(row);
                gridUserList.goToPage(gridUserList.currentPage);
                grid.goToPage(grid.currentPage);
                flashMessage.setMessage("The user/group was successfully removed".translate());
                flashMessage.setAppendTo(windowAssignmentRules.getHTML());
                flashMessage.show();
                return false;
            },
            style: {
                cssClasses: [
                    'mafe-designer-assigment-grid'
                ]
            }
        });
        assigmentPanelUserList = new PMUI.core.Panel({
            id: "assigmentPanelUserList",
            layout: "vbox",
            width: "60%",
            style: {
                cssClasses: [
                    'mafe-panel-assignment-white'
                ]
            }
        });
        panelTitleUserList = new PMUI.core.Panel({
            id: "panelTitleUserList",
            layout: "hbox"
        });
        titleUserList = new PMUI.ui.TextLabel({
            id: "titleUserList",
            textMode: 'plain',
            text: 'Assigned users list'.translate(),
            style: {
                cssClasses: [
                    'mafe-designer-assigment-title'
                ]
            }
        });
        panelGridUserList = new PMUI.core.Panel({
            id: "panelGridUserList",
            layout: "hbox",
            style: {
                cssClasses: ["mafe-panel"]
            }
        });
        buttonsUserList = new PMUI.field.RadioButtonGroupField({
            id: "buttonsUserList",
            controlPositioning: 'horizontal',
            maxDirectionOptions: 3,
            options: [{
                id: 'buttonAllAs',
                label: 'View all'.translate(),
                value: 'all',
                selected: true
            }, {
                id: 'buttonUsersAs',
                label: 'View users'.translate(),
                value: 'user'
            }, {
                id: 'buttonGroupsAs',
                label: 'View groups'.translate(),
                value: 'group'
            }],
            onChange: function (newVal, oldVal) {
                switch (newVal) {
                    case "user" :
                        gridUserList.typeList = "user";
                        break;
                    case "group":
                        gridUserList.typeList = "group";
                        break;
                    default:
                        gridUserList.typeList = "";
                        break;
                }
                gridUserList.goToPage(0);
            }
        });
        gridUserList = new PMUI.grid.GridPanel({
            id: "gridUserList",
            pageSize: pageSizeAssignment - 1,
            edges: 2,
            displayedPages: 2,
            behavior: 'dragdropsort',
            filterable: true,
            nextLabel: 'Next'.translate(),
            filterPlaceholder: 'Search ...'.translate(),
            previousLabel: 'Previous'.translate(),
            tableContainerHeight: 242,
            customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
                return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
            },
            emptyMessage: function () {
                var div = document.createElement('div'),
                    span = document.createElement('span');
                div.appendChild(span);
                div.className = "mafe-grid-panel-empty";
                div.style.height = gridUserList.dom.tableContainer.style.height;
                div.style.width = gridUserList.dom.tableContainer.style.width;
                span.innerHTML = 'Drag & Drop a User or a Group here'.translate();
                return div;
            },
            onEmpty: function () {
                gridUserList.dom.tableContainer.style.overflow = "hidden";
            },
            dynamicLoad: {
                keys: {
                    server: HTTP_SERVER_HOSTNAME,
                    projectID: PMDesigner.project.projectId,
                    workspace: WORKSPACE,
                    accessToken: PMDesigner.project.tokens.access_token,
                    endPoint: 'project/' + PMDesigner.project.id + '/activity/' + activity.id + '/assignee/paged'
                }
            },
            customDataRest: function (data) {
                var i;
                for (i = 0; i < data.length; i += 1) {
                    if (data[i].aas_type == "group") {
                        data[i]["assignment"] = data[i].aas_name;
                    } else {
                        data[i]["assignment"] = data[i].aas_name + " " + data[i].aas_lastname + " (" + data[i].aas_username + ")";
                    }
                }
                return data;
            },
            columns: [
                {
                    id: 'gridUserListButtonLabel',
                    title: "",
                    width: "10%",
                    dataType: 'button',
                    buttonLabel: function (row, data) {
                        return data.lastName;
                    },
                    buttonStyle: {
                        cssClasses: [
                            'mafe-grid-button'
                        ]
                    },
                    onButtonClick: function (row, grid) {
                        var option, select;
                        select = document.createElement("span");
                        select.id = "list-usersIngroup";
                        option = document.createElement("span");
                        option.id = "list-usersIngroup-iem";

                        if (row.getData()["aas_type"] === "group") {
                            var restClient = new PMRestClient({
                                typeRequest: 'get',
                                functionSuccess: function (xhr, response) {
                                    var optionClone, i;
                                    for (i = 0; i < response.length; i += 1) {
                                        if (i == 10) {
                                            optionClone = option.cloneNode(false);
                                            optionClone.innerHTML = "<b style='float: right'>. . .<b>";
                                            select.appendChild(optionClone);
                                        } else {
                                            optionClone = option.cloneNode(false);
                                            optionClone.textContent = "- " + response[i].usr_firstname + ' ' + response[i].usr_lastname;
                                            optionClone.title = response[i].usr_firstname + ' ' + response[i].usr_lastname;
                                            select.appendChild(optionClone);
                                        }
                                    }
                                    if (!optionClone) {
                                        optionClone = option.cloneNode(false);
                                        optionClone.textContent = "No users".translate();
                                        select.appendChild(optionClone);
                                    }

                                    quickMessageWindow.show($(row.html).find('a')[0], select);
                                },
                                functionFailure: function (xhr, response) {
                                    PMDesigner.msgWinError(response.error.message);
                                },
                                messageError: 'There are problems saving the assigned user, please try again.'.translate()
                            });
                            restClient.setBaseEndPoint("group/" + row.getData()["aas_uid"] + "/users?start=0&limit=11");
                            restClient.executeRestClient();
                        }
                    }
                },
                {
                    title: '',
                    dataType: 'string',
                    columnData: "assignment",
                    alignmentCell: "left",
                    width: "330px"
                },
                {
                    id: 'gridUserListButtonDelete',
                    title: '',
                    dataType: 'button',
                    width: "10%",
                    buttonStyle: {
                        cssClasses: [
                            'mafe-button-delete-assign'
                        ]
                    },
                    buttonLabel: function (row, data) {
                        var sw = row.getData()["aas_type"] === 'user';
                        row.getCells()[0].content.style.addClasses([sw ? 'button-icon-user' : 'button-icon-group']);
                        return '';
                    },
                    onButtonClick: function (row, grid) {
                        var dataRow = {};
                        grid = (grid != null) ? grid : gridUserList;
                        remove(row);
                        gridUserList.goToPage(gridUserList.currentPage);
                        grid.goToPage(0);
                        gridUsers.goToPage(0);
                        flashMessage.setMessage("The user/group was successfully removed".translate());
                        flashMessage.setAppendTo(windowAssignmentRules.getHTML());
                        flashMessage.show();
                    }
                }
            ],
            onDropOut: function (item, origin, destiny) {
            },
            onDrop: function (grid, row) {
                var dataRow = {};
                grid = this;
                assignee(row);
                gridUsers.goToPage(gridUsers.currentPage);
                grid.goToPage(grid.currentPage);
                flashMessage.setMessage("Assignee saved successfully".translate());
                flashMessage.setAppendTo(windowAssignmentRules.getHTML());
                flashMessage.show();
                return false;
            },
            style: {
                cssClasses: [
                    'mafe-designer-assigment-grid'
                ]
            }
        });

        assigmentPanelGlobalAD = new PMUI.core.Panel({
            id: "assigmentPanelGlobalAD",
            layout: "hbox",
            width: DEFAULT_WINDOW_WIDTH

        });
        assigmentPanelUserAD = new PMUI.core.Panel({
            id: "assigmentPanelUserAD",
            layout: "vbox",
            width: "60%",
            style: {
                cssClasses: [
                    'mafe-panel-assignment-white'
                ]
            }
        });
        panelTitleUserAD = new PMUI.core.Panel({
            id: "panelTitleUserAD",
            layout: "hbox"
        });
        titleUserAD = new PMUI.ui.TextLabel({
            id: "titleUserAD",
            label: " ",
            textMode: 'plain',
            text: 'Available users list'.translate(),
            style: {
                cssClasses: [
                    'mafe-designer-assigment-title'
                ]
            }
        });
        panelSearchUserAD = new PMUI.core.Panel({
            id: "panelSearchUserAD",
            layout: "hbox"
        });
        searchGridUserAD = new PMUI.field.TextField({
            id: "searchGridUserAD",
            label: " ",
            placeholder: 'Search ...'.translate(),
            style: {
                cssClasses: [
                    'mafe-assigment-search'
                ]
            }
        });
        panelGridUserAD = new PMUI.core.Panel({
            id: "panelGridUserAD",
            layout: "hbox",
            style: {
                cssClasses: ["mafe-panel"]
            }
        });
        buttonsUsersAD = new PMUI.field.RadioButtonGroupField({
            id: "buttonsUsersAD",
            controlPositioning: 'horizontal',
            maxDirectionOptions: 3,
            options: [{
                id: 'buttonAllAv',
                label: 'View all'.translate(),
                selected: true,
                value: 'all'
            }, {
                id: 'buttonUsersAv',
                label: 'View users'.translate(),
                value: 'user'
            }, {
                id: 'buttonGroupsAv',
                label: 'View groups'.translate(),
                value: 'group'
            }],
            onChange: function (newVal, oldVal) {
                switch (newVal) {
                    case "user" :
                        gridUsersAD.typeList = "user";
                        break;
                    case "group":
                        gridUsersAD.typeList = "group";
                        break;
                    default:
                        gridUsersAD.typeList = "";
                        break;
                }
                gridUsersAD.goToPage(0);
            }
        });
        gridUsersAD = new PMUI.grid.GridPanel({
            id: "gridUsersAD",
            pageSize: pageSizeAssignmentAD - 1,
            filterable: true,
            behavior: 'dragdropsort',
            filterPlaceholder: 'Search ...'.translate(),
            emptyMessage: 'No records found'.translate(),
            nextLabel: 'Next'.translate(),
            previousLabel: 'Previous'.translate(),
            tableContainerHeight: 242,
            customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
                return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
            },
            dynamicLoad: {
                keys: {
                    server: HTTP_SERVER_HOSTNAME,
                    projectID: PMDesigner.project.projectId,
                    workspace: WORKSPACE,
                    accessToken: PMDesigner.project.tokens.access_token,
                    endPoint: 'project/' + PMDesigner.project.id + '/activity/' + activity.id + '/adhoc-available-assignee/paged'
                }
            },
            customDataRest: function (data) {
                var i;
                for (i = 0; i < data.length; i += 1) {
                    if (data[i].ada_type == "group") {
                        data[i]["available"] = data[i].ada_name;
                    } else {
                        data[i]["available"] = data[i].ada_name + " " + data[i].ada_lastname + " (" + data[i].ada_username + ")";
                    }
                }
                return data;
            },
            columns: [
                {
                    id: 'gridUsersADButtonLabel',
                    title: '',
                    dataType: 'button',
                    width: "10%",
                    buttonLabel: function (row, data) {
                        return data.lastName;
                    },
                    buttonStyle: {
                        cssClasses: [
                            'mafe-grid-button'
                        ]
                    },
                    onButtonClick: function (row, grid) {
                        var option, select;
                        select = document.createElement("span");
                        select.id = "list-usersIngroup";
                        option = document.createElement("span");
                        option.id = "list-usersIngroup-iem";
                        if (row.getData()["ada_type"] === "group") {
                            var restClient = new PMRestClient({
                                typeRequest: 'get',
                                functionSuccess: function (xhr, response) {
                                    var optionClone, i;
                                    for (i = 0; i < response.length; i += 1) {
                                        if (i == 10) {
                                            optionClone = option.cloneNode(false);
                                            optionClone.innerHTML = "<b style='float: right'>. . .<b>";
                                            select.appendChild(optionClone);
                                        } else {
                                            optionClone = option.cloneNode(false);
                                            optionClone.textContent = "- " + response[i].usr_firstname + ' ' + response[i].usr_lastname;
                                            optionClone.title = response[i].usr_firstname + ' ' + response[i].usr_lastname;
                                            select.appendChild(optionClone);
                                        }
                                    }
                                    if (!optionClone) {
                                        optionClone = option.cloneNode(false);
                                        optionClone.textContent = "No users".translate();
                                        select.appendChild(optionClone);
                                    }

                                    quickMessageWindow.show($(row.html).find('a')[0], select);
                                },
                                functionFailure: function (xhr, response) {
                                    PMDesigner.msgWinError(response.error.message);
                                },
                                messageError: 'There are problems saving the assigned user, please try again.'.translate()
                            });
                            restClient.setBaseEndPoint("group/" + row.getData()["ada_uid"] + "/users?start=0&limit=11");
                            restClient.executeRestClient();
                        }
                    }
                },
                {
                    title: "",
                    dataType: 'string',
                    columnData: "available",
                    alignmentCell: "left",
                    width: "330px"
                },
                {
                    id: 'gridUsersADButtonAssign',
                    title: '',
                    dataType: 'button',
                    width: "10%",
                    buttonStyle: {
                        cssClasses: [
                            'mafe-button-edit-assign'
                        ]
                    },
                    buttonLabel: function (row, data) {
                        var sw = row.getData()["ada_type"] === 'user';
                        row.getCells()[0].content.style.addClasses([sw ? 'button-icon-user' : 'button-icon-group']);
                        return '';
                    },
                    onButtonClick: function (row, grid) {
                        var dataRow = {};
                        grid = (grid != null) ? grid : gridUsersAD;
                        assigneeAD(row);
                        grid.goToPage(gridUsersAD.currentPage);
                        gridUserListAD.goToPage(0);
                        flashMessage.setMessage("Assignee saved successfully".translate());
                        flashMessage.setAppendTo(windowAssignmentRules.getHTML());
                        flashMessage.show();
                    }
                }
            ],
            onDropOut: function (item, origin, destiny) {
                formAssignmentRules.dirty = true;
                grid = gridUsersAD;
                grid.sort('fullName', 'asc');
                buttonsUserListAD.setValue(buttonsUsersAD.getValue());
                groupRows(gridUsersAD, 'all');
                groupRows(gridUserListAD, 'all');
                arrayObjectAdhocUserList.push(item);
                index = arrayObjectAdhocUser.indexOf(item);
                if (index > -1) {
                    arrayObjectAdhocUser.splice(index, 1);
                }
            },
            onDrop: function (a, row) {
                grid = this;
                removeAD(row);
                gridUserListAD.goToPage(gridUserListAD.currentPage);
                grid.goToPage(grid.currentPage);
                flashMessage.setMessage("The user/group was successfully removed".translate());
                flashMessage.setAppendTo(windowAssignmentRules.getHTML());
                flashMessage.show();
                return false;
            },
            style: {
                cssClasses: [
                    'mafe-designer-assigment-grid'
                ]
            }
        });
        assigmentPanelUserListAD = new PMUI.core.Panel({
            id: "assigmentPanelUserListAD",
            layout: "vbox",
            style: {
                cssClasses: [
                    'mafe-panel-assignment-white'
                ]
            },
            width: "60%"
        });
        panelTitleUseListAD = new PMUI.core.Panel({
            id: "panelTitleUseListAD",
            layout: "hbox"
        });
        titleUserListAD = new PMUI.ui.TextLabel({
            id: "titleUserListAD",
            textMode: 'plain',
            text: 'Assigned users list'.translate(),
            style: {
                cssClasses: [
                    'mafe-designer-assigment-title'
                ]
            }
        });
        panelGridUserListAD = new PMUI.core.Panel({
            id: "panelGridUserListAD",
            layout: "hbox",
            style: {
                cssClasses: ["mafe-panel"]
            }
        });
        buttonsUserListAD = new PMUI.field.RadioButtonGroupField({
            id: "buttonsUserListAD",
            controlPositioning: 'horizontal',
            maxDirectionOptions: 3,
            options: [{
                id: 'buttonAllAs',
                label: 'View all'.translate(),
                value: 'all',
                selected: true
            }, {
                id: 'buttonUsersAs',
                label: 'View users'.translate(),
                value: 'user'
            }, {
                id: 'buttonGroupsAs',
                label: 'View groups'.translate(),
                value: 'group'
            }],
            onChange: function (newVal, oldVal) {
                switch (newVal) {
                    case "user" :
                        gridUserListAD.typeList = "user";
                        break;
                    case "group":
                        gridUserListAD.typeList = "group";
                        break;
                    default:
                        gridUserListAD.typeList = "";
                        break;
                }
                gridUserListAD.goToPage(0);
            }
        });
        gridUserListAD = new PMUI.grid.GridPanel({
            id: "gridUserListAD",
            pageSize: pageSizeAssignmentAD - 1,
            behavior: 'dragdropsort',
            filterable: true,
            filterPlaceholder: 'Search ...'.translate(),
            nextLabel: 'Next'.translate(),
            previousLabel: 'Previous'.translate(),
            tableContainerHeight: 242,
            customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
                return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
            },
            emptyMessage: function () {
                var div = document.createElement('div'),
                    span = document.createElement('span');
                div.appendChild(span);
                div.className = "mafe-grid-panel-empty";
                div.style.height = gridUserListAD.dom.tableContainer.style.height;
                div.style.width = gridUserListAD.dom.tableContainer.style.width;
                span.innerHTML = 'Drag & Drop a User or a Group here'.translate();
                return div;
            },
            onEmpty: function (grid, cell) {
                gridUserListAD.dom.tableContainer.style.overflow = "hidden";
            },
            dynamicLoad: {
                keys: {
                    server: HTTP_SERVER_HOSTNAME,
                    projectID: PMDesigner.project.projectId,
                    workspace: WORKSPACE,
                    accessToken: PMDesigner.project.tokens.access_token,
                    endPoint: 'project/' + PMDesigner.project.id + '/activity/' + activity.id + '/adhoc-assignee/paged'
                }
            },
            customDataRest: function (data) {
                var i;
                for (i = 0; i < data.length; i += 1) {
                    if (data[i].ada_type == "group") {
                        data[i]["assignee"] = data[i].ada_name;
                    } else {
                        data[i]["assignee"] = data[i].ada_name + " " + data[i].ada_lastname + " (" + data[i].ada_username + ")";
                    }
                }
                return data;
            },
            columns: [
                {
                    id: 'gridUserListADButtonLabel',
                    title: "",
                    width: "13%",
                    dataType: 'button',
                    buttonLabel: function (row, data) {
                        return data.lastName;
                    },
                    buttonStyle: {
                        cssClasses: [
                            'mafe-grid-button'
                        ]
                    },
                    onButtonClick: function (row, grid) {
                        var option, select;
                        select = document.createElement("span");
                        select.id = "list-usersIngroup";
                        option = document.createElement("span");
                        option.id = "list-usersIngroup-iem";
                        if (row.getData()["ada_type"] === "group") {
                            var i,
                                restClient = new PMRestClient({
                                    typeRequest: 'get',
                                    functionSuccess: function (xhr, response) {
                                        var optionClone, i;
                                        for (i = 0; i < response.length; i += 1) {
                                            if (i == 10) {
                                                optionClone = option.cloneNode(false);
                                                optionClone.innerHTML = "<b style='float: right'>. . .<b>";
                                                select.appendChild(optionClone);
                                            } else {
                                                optionClone = option.cloneNode(false);
                                                optionClone.textContent = "- " + response[i].usr_firstname + ' ' + response[i].usr_lastname;
                                                optionClone.title = response[i].usr_firstname + ' ' + response[i].usr_lastname;
                                                select.appendChild(optionClone);
                                            }
                                        }
                                        if (!optionClone) {
                                            optionClone = option.cloneNode(false);
                                            optionClone.textContent = "No users".translate();
                                            select.appendChild(optionClone);
                                        }

                                        quickMessageWindow.show($(row.html).find('a')[0], select);
                                    },
                                    functionFailure: function (xhr, response) {
                                        PMDesigner.msgWinError(response.error.message);
                                    },
                                    messageError: 'There are problems saving the assigned user, please try again.'.translate()
                                });
                            restClient.setBaseEndPoint("group/" + row.getData()["ada_uid"] + "/users?start=0&limit=11");
                            restClient.executeRestClient();
                        }
                    }
                },
                {
                    title: '',
                    dataType: 'string',
                    columnData: "assignee",
                    alignmentCell: "left",
                    width: "330px"
                },
                {
                    id: 'gridUserListADButtonDelete',
                    title: '',
                    dataType: 'button',
                    width: "10%",
                    buttonStyle: {
                        cssClasses: [
                            'mafe-button-delete-assign'
                        ]
                    },
                    buttonLabel: function (row, data) {
                        var sw = row.getData()["ada_type"] === 'user';
                        row.getCells()[0].content.style.addClasses([sw ? 'button-icon-user' : 'button-icon-group']);
                        return '';
                    },
                    onButtonClick: function (row, grid) {
                        var dataRow = {};
                        grid = (grid != null) ? grid : gridUserListAD;
                        removeAD(row);
                        grid.goToPage(grid.currentPage);
                        gridUsersAD.goToPage(gridUsersAD.currentPage);
                        flashMessage.setMessage("The user/group was successfully removed".translate());
                        flashMessage.setAppendTo(windowAssignmentRules.getHTML());
                        flashMessage.show();
                    }
                }
            ],
            onDropOut: function (item, origin, destiny) {
            },
            onDrop: function (a, row) {
                grid = this;
                assigneeAD(row);
                gridUsersAD.goToPage(gridUsersAD.currentPage);
                grid.goToPage(grid.currentPage);
                flashMessage.setMessage("Assignee saved successfully".translate());
                flashMessage.setAppendTo(windowAssignmentRules.getHTML());
                flashMessage.show();
                return false;
            },
            style: {
                cssClasses: [
                    'mafe-designer-assigment-grid'
                ]
            }
        });

        panelContainerRules = new PMUI.core.Panel({
            width: DEFAULT_WINDOW_WIDTH - 55,
            height: "auto",
            fieldset: true,
            items: [
                formAssignmentRules
            ]
        });
        panelContainerUsers = new PMUI.core.Panel({
            width: DEFAULT_WINDOW_WIDTH,
            height: "auto",
            fieldset: true,
            items: [
                assigmentPanelGlobal
            ]
        });
        panelContainerUsersAdHoc = new PMUI.core.Panel({
            width: DEFAULT_WINDOW_WIDTH,
            height: "auto",
            fieldset: true,
            items: [
                assigmentPanelGlobalAD
            ]
        });

        tabPanelAssignmentRules = new PMUI.panel.TabPanel({
            id: 'tabPanelAssignmentRules',
            width: "100%",
            height: "auto",
            items: [
                {
                    id: 'tabUsers',
                    title: 'Users'.translate(),
                    panel: panelContainerUsers
                },
                {
                    id: 'tabUsersAdHoc',
                    title: 'Ad Hoc Users'.translate(),
                    panel: panelContainerUsersAdHoc
                }
            ],
            onTabClick: function (item) {
                quickMessageWindow.close();
                switch (item.id) {
                    case 'tabRules':
                        break;
                    case 'tabUsers':
                        applyStyles();
                        gridUsers.goToPage(0);
                        gridUserList.goToPage(0);
                        break;
                    case 'tabUsersAdHoc':
                        applyStylesAD();
                        gridUsersAD.goToPage(0);
                        gridUserListAD.goToPage(0);
                        break;
                }
            }
        });
        windowAssignmentRules = new PMUI.ui.Window({
            id: 'windowAssignmentRules',
            title: 'Assignment Rules'.translate() + ': ' + activity.act_name,
            height: DEFAULT_WINDOW_HEIGHT,
            width: DEFAULT_WINDOW_WIDTH,
            footerItems: [
                {
                    id: 'windowConnectionsButtonCancel',
                    text: 'Close'.translate(),
                    handler: function () {
                        if (formAssignmentRules.isDirty()) {
                            warningMessageWindowDirty.open();
                            warningMessageWindowDirty.showFooter();
                        } else {
                            windowAssignmentRules.close();
                        }
                    },
                    buttonType: 'error'
                },
                {
                    buttonType: 'success',
                    id: 'windowPropertiesButtonSave',
                    text: "Save".translate(),
                    handler: function () {
                        saveData();
                    }
                }
            ],
            visibleFooter: true,
            buttonPanelPosition: 'bottom',
            footerAlign: "right",
            onBeforeClose: function () {
                if (formAssignmentRules.isDirty()) {
                    warningMessageWindowDirty.open();
                    warningMessageWindowDirty.showFooter();
                } else {
                    windowAssignmentRules.close();
                }
            }
        });

        loadTrigger = function (response) {
            var field = formAssignmentRules.getField('tas_selfservice_trigger_uid'), i;
            field.clearOptions();
            field.addOption({
                value: '',
                label: '- Select Trigger -'.translate()
            });
            for (i = 0; i < response.length; i += 1) {
                field.addOption({
                    value: response[i].tri_uid,
                    label: response[i].tri_title
                });
            }
        };
        loadFormData = function (response) {
            dataProperties = response.properties;
            formAssignmentRules.getField('tas_assign_type').setValue(dataProperties.tas_assign_type);
            formAssignmentRules.getField('tas_assign_variable').setValue(dataProperties.tas_assign_variable);
            formAssignmentRules.getField('tas_group_variable').setValue(dataProperties.tas_group_variable);
            changeRadioButtons(formAssignmentRules.getField('tas_assign_type').getValue());
            formAssignmentRules.getField('tas_selfservice_timeout').controls[0].deselect();
            if (dataProperties.tas_selfservice_timeout === 1) {
                formAssignmentRules.getField('tas_selfservice_timeout').controls[0].select();
            }
            formAssignmentRules.getField('tas_selfservice_time').setValue(dataProperties.tas_selfservice_time);
            formAssignmentRules.getField('tas_selfservice_time_unit').setValue(dataProperties.tas_selfservice_time_unit);
            formAssignmentRules.getField('tas_selfservice_trigger_uid').setValue(dataProperties.tas_selfservice_trigger_uid);
            formAssignmentRules.getField('tas_selfservice_execution').setValue(dataProperties.tas_selfservice_execution);
            visibleService(dataProperties.tas_selfservice_timeout === 1);
        };
        loadServerData = function () {
            var restClient = new PMRestClient({
                typeRequest: 'post',
                multipart: true,
                data: {
                    calls: [
                        {
                            url: 'project/' + PMDesigner.project.id + '/activity/' + activity.id,
                            method: 'GET'
                        }, {
                            url: 'project/' + PMDesigner.project.id + '/triggers',
                            method: 'GET'
                        }
                    ]
                },
                functionSuccess: function (xhr, response) {
                    loadTrigger(response[1].response);
                    loadFormData(response[0].response);
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            });
            restClient.setBaseEndPoint('');
            restClient.executeRestClient();
        };
        loadFreeUsers = function (response) {
            var i;
            gridUsers.clearItems();
            for (i = 0; i < response.length; i += 1) {
                gridUsers.addDataItem({
                    fullName: response[i].aas_name + " " + response[i].aas_lastname,
                    ass_uid: response[i].aas_uid,
                    ass_type: response[i].aas_type
                });
            }
        };
        loadAssignmentUsers = function (response) {
            var i;
            gridUserList.clearItems();
            for (i = 0; i < response.length; i += 1) {
                gridUserList.addDataItem({
                    fullName: response[i].aas_name + " " + response[i].aas_lastname,
                    ass_uid: response[i].aas_uid,
                    ass_type: response[i].aas_type
                });
            }
        };
        loadAdHocFreeUsers = function (response) {
            var i;
            gridUsersAD.clearItems();
            for (i = 0; i < response.length; i += 1) {
                gridUsersAD.addDataItem({
                    fullName: response[i].ada_name + " " + response[i].ada_lastname,
                    ada_uid: response[i].ada_uid,
                    ass_type: response[i].ada_type
                });
            }
        };
        loadAdHocAssignmentUsers = function (response) {
            var i;
            gridUserListAD.clearItems();
            for (i = 0; i < response.length; i += 1) {
                gridUserListAD.addDataItem({
                    fullName: response[i].ada_name + " " + response[i].ada_lastname,
                    ada_uid: response[i].ada_uid,
                    ass_type: response[i].ada_type
                });
            }
        };
        updateRules = function () {
            (new PMRestClient({
                endpoint: 'activity/' + activity.id,
                typeRequest: 'update',
                messageError: ' ',
                data: {
                    definition: {},
                    properties: dataProperties
                },
                messageSuccess: 'Assignment Rules saved successfully'.translate(),
                flashContainer: document.body,
                functionSuccess: function () {
                    formAssignmentRules.dirty = false;
                    windowAssignmentRules.close();
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            })).executeRestClient();
        };
        allHidden = function () {
            formAssignmentRules.getField('tas_assign_variable').setVisible(false);
            formAssignmentRules.getField('tas_group_variable').setVisible(false);
            formAssignmentRules.getField('tas_selfservice_timeout').setVisible(false);
            formAssignmentRules.getField('tas_selfservice_timeout').controls[0].deselect();
            formAssignmentRules.getField('tas_selfservice_time').setVisible(false);
            formAssignmentRules.getField('tas_selfservice_time_unit').setVisible(false);
            formAssignmentRules.getField('tas_selfservice_trigger_uid').setVisible(false);
            formAssignmentRules.getField('tas_selfservice_execution').setVisible(false);
        };
        changeRadioButtons = function (newVal) {
            allHidden();
            tabPanelAssignmentRules.setVisible(true);
            switch (newVal) {
                case 'EVALUATE':
                    formAssignmentRules.getField('tas_assign_variable').setVisible(true);
                    if (dataProperties.tas_assign_variable === "" || dataProperties.tas_assign_variable === null) {
                        formAssignmentRules.getField('tas_assign_variable').setValue('@@SYS_NEXT_USER_TO_BE_ASSIGNED');
                    } else {
                        formAssignmentRules.getField('tas_assign_variable').setValue(dataProperties.tas_assign_variable);
                    }
                    break;
                case 'SELF_SERVICE':
                    formAssignmentRules.getField('tas_selfservice_timeout').setVisible(true);
                    break;
                case 'SELF_SERVICE_EVALUATE':
                    formAssignmentRules.getField('tas_group_variable').setVisible(true);
                    if (dataProperties.tas_group_variable === "@@SYS_GROUP_TO_BE_ASSIGNED" || dataProperties.tas_group_variable === "" || dataProperties.tas_group_variable === null) {
                        formAssignmentRules.getField('tas_group_variable').setPlaceholder('@@ARRAY_OF_USERS or @@GROUP_UID');
                    } else {
                        formAssignmentRules.getField('tas_group_variable').setValue(dataProperties.tas_group_variable);
                    }
                    formAssignmentRules.getField('tas_selfservice_timeout').setVisible(true);
                    break;
                case 'REPORT_TO':
                    break;
                case 'MULTIPLE_INSTANCE_VALUE_BASED':
                    formAssignmentRules.getField('tas_assign_variable').setLabel("Array of users".translate());
                    if (formAssignmentRules.getField('tas_assign_variable').getValue() == "@@SYS_NEXT_USER_TO_BE_ASSIGNED" || formAssignmentRules.getField('tas_assign_variable').getValue() == "") {
                        formAssignmentRules.getField('tas_assign_variable').setValue('');
                        formAssignmentRules.getField('tas_assign_variable').setPlaceholder('@@ARRAY_OF_USERS');
                    }
                    formAssignmentRules.getField('tas_assign_variable').setVisible(true);
                    if (activity.act_loop_type == "PARALLEL") {
                        tabPanelAssignmentRules.setVisible(false);
                    }
                    break;
                default:
                    break;
            }
        };
        visibleService = function (value) {
            var a = formAssignmentRules.getField('tas_assign_type').getValue();
            if (a === 'SELF_SERVICE' || a === 'SELF_SERVICE_EVALUATE') {
                formAssignmentRules.getField('tas_selfservice_time').setVisible(value);
                formAssignmentRules.getField('tas_selfservice_time_unit').setVisible(value);
                formAssignmentRules.getField('tas_selfservice_trigger_uid').setVisible(value);
                formAssignmentRules.getField('tas_selfservice_execution').setVisible(value);
            }
        };
        hiddenTab = function (value) {
            tabPanelAssignmentRules.showTab(2);
        };
        saveData = function () {
            var a, b, c, d, tas_selfservice_timeout, data;
            tas_selfservice_timeout = formAssignmentRules.getField('tas_selfservice_timeout').getValue() === '["1"]';
            if (!tas_selfservice_timeout) {
                //validation because getData method do not work in IE
                if (navigator.userAgent.indexOf("MSIE") !== -1 || navigator.userAgent.indexOf("Trident") !== -1) {
                    data = getData2PMUI(formAssignmentRules.html);
                    b = data["tas_assign_variable"];
                } else {
                    b = formAssignmentRules.getField('tas_assign_variable').getValue();
                }
                a = formAssignmentRules.getField('tas_assign_type').getValue();
                c = formAssignmentRules.getField('tas_group_variable').getValue();
                d = formAssignmentRules.getField('tas_group_variable').getValue();
                formAssignmentRules.getField('tas_assign_type').setValue(a);
                formAssignmentRules.getField('tas_assign_variable').setValue(b);
                formAssignmentRules.getField('tas_group_variable').setValue(c);
                formAssignmentRules.getField('tas_selfservice_timeout').setValue(d);
                formAssignmentRules.getField('tas_selfservice_time').setValue('');
                formAssignmentRules.getField('tas_selfservice_time_unit').setValue('');
                formAssignmentRules.getField('tas_selfservice_trigger_uid').setValue('');
                formAssignmentRules.getField('tas_selfservice_execution').setValue('');
            } else {
                if (!formAssignmentRules.isValid()) {
                    return;
                }
            }
            dataProperties.tas_assign_type = formAssignmentRules.getField('tas_assign_type').getValue();
            dataProperties.tas_assign_variable = formAssignmentRules.getField('tas_assign_variable').getValue() === '' ? '@@SYS_NEXT_USER_TO_BE_ASSIGNED' : formAssignmentRules.getField('tas_assign_variable').getValue();
            dataProperties.tas_group_variable = formAssignmentRules.getField('tas_group_variable').getValue() === '' ? '@@SYS_GROUP_TO_BE_ASSIGNED' : formAssignmentRules.getField('tas_group_variable').getValue();
            dataProperties.tas_selfservice_timeout = tas_selfservice_timeout ? 1 : 0;
            dataProperties.tas_selfservice_time = formAssignmentRules.getField('tas_selfservice_time').getValue() !== "" ? parseInt(formAssignmentRules.getField('tas_selfservice_time').getValue(), 10) : 0;
            dataProperties.tas_selfservice_time_unit = formAssignmentRules.getField('tas_selfservice_time_unit').getValue();
            dataProperties.tas_selfservice_trigger_uid = formAssignmentRules.getField('tas_selfservice_trigger_uid').getValue();
            dataProperties.tas_selfservice_execution = formAssignmentRules.getField('tas_selfservice_execution').getValue();
            updateRules();
        };
        saveOrUpdateUserAndAdHocUsers = function () {
            //Assigne and Remove (users)
            var i, b;
            if (gridUserList.getItems().length > 0) {
                grid = gridUserList;
                for (i = 0; i < arrayObjectUserList.length; i += 1) {
                    b = arrayObjectUserList[i];
                    if (arrayObjectUserList2.indexOf(b) == -1) {
                        assignee(b);
                    }
                }
            }
            if (gridUsers.getItems().length > 0) {
                for (i = 0; i < arrayObjectUsers.length; i += 1) {
                    b = arrayObjectUsers[i];
                    if (arrayObjectUsers2.indexOf(b) == -1) {
                        remove(arrayObjectUsers[i]);
                    }
                }
            }
            //Assigne and Remove (AdHocUsers)
            if (gridUserListAD.getItems().length > 0) {
                grid = gridUserListAD;
                for (i = 0; i < arrayObjectAdhocUserList.length; i += 1) {
                    b = arrayObjectAdhocUserList[i];
                    if (arrayObjectAdhocUserList2.indexOf(b) == -1) {
                        assigneeAD(b);
                    }
                }
            }
            if (gridUsersAD.getItems().length > 0) {
                for (i = 0; i < arrayObjectAdhocUser.length; i += 1) {
                    b = arrayObjectAdhocUser[i];
                    if (arrayObjectAdhocUser2.indexOf(b) == -1) {
                        removeAD(arrayObjectAdhocUser[i]);
                    }
                }
            }
        };
        applyStyles = function () {
            gridUsers.dom.toolbar.appendChild(buttonsUsers.getHTML());
            buttonsUsers.defineEvents();
            gridUsers.dom.toolbar.style.height = "76px";
            gridUserList.dom.toolbar.appendChild(buttonsUserList.getHTML());
            buttonsUserList.defineEvents();
            gridUserList.dom.toolbar.style.height = "76px";
            buttonsUsers.dom.labelTextContainer.style.display = "none";
            buttonsUserList.dom.labelTextContainer.style.display = "none";
            gridUsers.hideHeaders();
            gridUserList.hideHeaders();
            assigmentPanelUserList.setHeight('100%');
            gridUsers.filterControl.html.style.width = "300px";
            gridUserList.filterControl.html.style.width = "300px";
        };
        applyStylesAD = function () {
            gridUsersAD.dom.toolbar.appendChild(buttonsUsersAD.getHTML());
            buttonsUsersAD.defineEvents();
            gridUsersAD.dom.toolbar.style.height = "76px";

            gridUserListAD.dom.toolbar.appendChild(buttonsUserListAD.getHTML());
            buttonsUserListAD.defineEvents();
            gridUserListAD.dom.toolbar.style.height = "76px";
            buttonsUsersAD.dom.labelTextContainer.style.display = "none";
            buttonsUserListAD.dom.labelTextContainer.style.display = "none";
            gridUsersAD.hideHeaders();
            gridUserListAD.hideHeaders();
            assigmentPanelUserListAD.setHeight('100%');
            gridUsersAD.filterControl.html.style.width = "300px";
            gridUserListAD.filterControl.html.style.width = "300px";
        };
        assignee = function (row) {
            restClient.setTypeRequest("post");
            restClient.setEndpoint("activity/" + activity.id + "/assignee");
            restClient.setData({aas_uid: row.getData()["aas_uid"], aas_type: row.getData()["aas_type"]});
            restClient.functionSuccess = function (xhr, response) {
            };
            restClient.executeRestClient();
        };
        remove = function (row) {
            restClient.setTypeRequest("remove");
            restClient.setEndpoint("activity/" + activity.id + "/assignee/" + row.getData().aas_uid);
            restClient.functionSuccess = function (xhr, response) {
            };
            restClient.executeRestClient();
        };
        assigneeAD = function (row) {
            restClient.setTypeRequest("post");
            restClient.setEndpoint("activity/" + activity.id + "/adhoc-assignee");
            restClient.setData({ada_uid: row.getData()["ada_uid"], ada_type: row.getData()["ada_type"]});
            restClient.functionSuccess = function (xhr, response) {
            };
            restClient.executeRestClient();
        };
        removeAD = function (row) {
            restClient.setTypeRequest("remove");
            restClient.setEndpoint("activity/" + activity.id + "/adhoc-assignee/" + row.getData()["ada_uid"]);
            restClient.functionSuccess = function (xhr, response) {
            };
            restClient.executeRestClient();
        };
        groupRows = function (grid, value) {
            var i, items;
            if (grid.memorystack === undefined) {
                grid.memorystack = [];
            }
            items = grid.getItems();
            while (grid.memorystack.length > 0) {
                grid.addItem(grid.memorystack.pop());
            }
            if (value !== 'all') {
                for (i = 0; i < items.length; i += 1) {
                    if (items[i].getData().ass_type !== value) {
                        grid.memorystack.push(items[i]);
                        grid.removeItem(items[i]);
                    }
                }
            }
            grid.sort('fullName', 'asc');
        };

        function onchangeRadio(grid, fieldName) {
            var radioButTrat = document.getElementsByName(fieldName), i;
            for (i = 0; i < radioButTrat.length; i += 1) {
                if (radioButTrat[i].checked == true) {
                    quickMessageWindow.close();
                    groupRows(grid, radioButTrat[i].value);
                }
            }
        }

        function domSettings() {
            if (activity.act_loop_type == "PARALLEL") {
                $(formAssignmentRules.getField("tas_assign_type").controls[0].html).parent().hide();
                $(formAssignmentRules.getField("tas_assign_type").controls[1].html).parent().hide();
                $(formAssignmentRules.getField("tas_assign_type").controls[2].html).parent().hide();
                $(formAssignmentRules.getField("tas_assign_type").controls[3].html).parent().hide();
                $(formAssignmentRules.getField("tas_assign_type").controls[4].html).parent().hide();
                $(formAssignmentRules.getField("tas_assign_type").controls[5].html).parent().hide();
                $(formAssignmentRules.getField("tas_assign_type").controls[6].html).parent().show();
                $(formAssignmentRules.getField("tas_assign_type").controls[7].html).parent().show();
                if (formAssignmentRules.getField('tas_assign_type').getValue() == "MULTIPLE_INSTANCE_VALUE_BASED") {
                    formAssignmentRules.getField('tas_assign_type').setValue("MULTIPLE_INSTANCE_VALUE_BASED");
                } else {
                    formAssignmentRules.getField('tas_assign_type').setValue("MULTIPLE_INSTANCE");
                }
            } else {
                $(formAssignmentRules.getField("tas_assign_type").controls[0].html).parent().show();
                $(formAssignmentRules.getField("tas_assign_type").controls[1].html).parent().show();
                $(formAssignmentRules.getField("tas_assign_type").controls[2].html).parent().show();
                $(formAssignmentRules.getField("tas_assign_type").controls[3].html).parent().show();
                $(formAssignmentRules.getField("tas_assign_type").controls[4].html).parent().show();
                $(formAssignmentRules.getField("tas_assign_type").controls[5].html).parent().show();
                $(formAssignmentRules.getField("tas_assign_type").controls[6].html).parent().hide();
                $(formAssignmentRules.getField("tas_assign_type").controls[7].html).parent().hide();
                if (formAssignmentRules.getField('tas_assign_type').getValue() == "MULTIPLE_INSTANCE_VALUE_BASED") {
                    formAssignmentRules.getField('tas_assign_type').setValue("BALANCED");
                    formAssignmentRules.getField('tas_assign_variable').setVisible(false);
                }
            }
        };

        panelTitleUser.addItem(titleUser);
        panelGridUser.addItem(gridUsers);
        assigmentPanelUser.addItem(panelTitleUser);
        assigmentPanelUser.addItem(panelGridUser);
        assigmentPanelGlobal.addItem(assigmentPanelUser);
        panelTitleUserList.addItem(titleUserList);
        panelGridUserList.addItem(gridUserList);
        assigmentPanelUserList.addItem(panelTitleUserList);
        assigmentPanelUserList.addItem(panelGridUserList);
        assigmentPanelGlobal.addItem(assigmentPanelUserList);

        panelTitleUserAD.addItem(titleUserAD);
        panelSearchUserAD.addItem(searchGridUserAD);
        panelGridUserAD.addItem(gridUsersAD);
        assigmentPanelUserAD.addItem(panelTitleUserAD);
        assigmentPanelUserAD.addItem(panelGridUserAD);
        assigmentPanelGlobalAD.addItem(assigmentPanelUserAD);
        panelTitleUseListAD.addItem(titleUserListAD);
        panelGridUserListAD.addItem(gridUserListAD);
        assigmentPanelUserListAD.addItem(panelTitleUseListAD);
        assigmentPanelUserListAD.addItem(panelGridUserListAD);
        assigmentPanelGlobalAD.addItem(assigmentPanelUserListAD);

        if (formAssignmentRules.dirty == null) {
            formAssignmentRules.dirty = false;
        }
        windowAssignmentRules.addItem(formAssignmentRules);
        windowAssignmentRules.addItem(tabPanelAssignmentRules);
        windowAssignmentRules.open();
        applyStyleWindowForm(windowAssignmentRules);
        windowAssignmentRules.body.style.overflowY = 'auto';
        windowAssignmentRules.body.style.overflowX = 'hidden';
        windowAssignmentRules.defineEvents();
        buttonsUsers.defineEvents();
        tabPanelAssignmentRules.itemClick(0);
        loadServerData();
        domSettings();
        //array for Users
        usersgrid = gridUsers;
        for (i = 0; i < usersgrid.getItems().length; i += 1) {
            arrayObjectUsers2[i] = usersgrid.getItems()[i];
        }
        userslist = gridUserList;
        for (i = 0; i < userslist.getItems().length; i += 1) {
            arrayObjectUserList2[i] = userslist.getItems()[i];
        }
        //array for AdHocUsers
        usersgrid = gridUsersAD;
        for (i = 0; i < usersgrid.getItems().length; i += 1) {
            arrayObjectAdhocUser2[i] = usersgrid.getItems()[i];
        }
        userslist = gridUserListAD;
        for (i = 0; i < userslist.getItems().length; i += 1) {
            arrayObjectAdhocUserList2[i] = userslist.getItems()[i];
        }
        formAssignmentRules.html.style.marginLeft = '30px';
        formAssignmentRulesSetTimeoutOption;
        document.getElementById("formAssignmentRulesSetTimeoutOption").childNodes[0].onchange = function () {
            visibleService(this.checked);
        };
        $(".pmui-field-control-table").css("border", "0px");
        $(".pmui-field-label").css("padding", "0px");
        $(".mafe-designer-assigment-title").css("margin-top", "25px");
        $("#formTasAssignType").append("<hr style=' border: 0; border-top: 1px solid #eee;'>");
        $(".pmui-gridpanel-footer").addClass("pmui-gridpanel-footer-dinamic");
    };
}());
(function () {
    PMDesigner.assigmentSupervisors = function (event) {
        var assigmentWindow = null,
            pageSizeAssignment = 8,
            restClient,
            assigmentPanelUser,
            assigmentPanelUserList,
            assigmentPanelGlobal,
            panelTitleUser,
            titleUser,
            panelSearchUser,
            searchGridUser,
            panelGridUser,
            buttonsUsers,
            gridUsers,
            panelTitleUserList,
            titleUserList,
            panelSearchUserList,
            searchGridUserList,
            panelGridUserList,
            buttonsUserList,
            gridUserList,
            applyStyles,
            loadServerData,
            loadUsers,
            loadUsersList,
            groupRows,
            assignee,
            remove,
            quickMessageWindow = new QuickMessageWindow();

        assigmentWindow = new PMUI.ui.Window({
            id: 'assigmentUserWindow',
            title: 'Assign Users and Groups as Supervisors'.translate(),
            width: DEFAULT_WINDOW_WIDTH + 1,
            height: DEFAULT_WINDOW_HEIGHT,
            footerHeight: 'auto',
            bodyHeight: 'auto',
            modal: true
        });
        restClient = new PMRestClient({
            endpoint: 'projects',
            typeRequest: 'get',
            messageError: 'There are problems, please try again.'.translate(),
            functionFailure: function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            }
        });

        assigmentPanelUser = new PMUI.core.Panel({
            layout: "vbox",
            width: "49%",
            style: {
                cssClasses: [
                    'mafe-panel-assignment-white'
                ]
            }
        });
        assigmentPanelUserList = new PMUI.core.Panel({
            layout: "vbox",
            width: "50%",
            style: {
                cssClasses: [
                    'mafe-panel-assignment-smooth'
                ]
            }
        });
        assigmentPanelGlobal = new PMUI.core.Panel({
            layout: 'hbox',
            width: DEFAULT_WINDOW_WIDTH,
            style: {
                cssClasses: [
                    'mafe-assigment-panel-global'
                ]
            }
        });

        panelTitleUser = new PMUI.core.Panel({
            layout: 'hbox'
        });
        titleUser = new PMUI.ui.TextLabel({
            id: 'titleUser',
            label: ' ',
            textMode: 'plain',
            text: 'Available Users List'.translate(),
            style: {
                cssClasses: [
                    'mafe-designer-assigment-title'
                ]
            }
        });
        panelSearchUser = new PMUI.core.Panel({
            layout: 'hbox'
        });
        searchGridUser = new PMUI.field.TextField({
            id: 'searchGridUser',
            label: ' ',
            placeholder: 'Search ...'.translate()
        });
        panelGridUser = new PMUI.core.Panel({layout: 'hbox'});
        buttonsUsers = new PMUI.field.OptionsSelectorField({
            id: 'buttonsUsers',
            orientation: 'horizontal',
            items: [{
                text: 'All'.translate(),
                selected: true,
                value: 'all',
                style: {
                    cssClasses: [
                        'pmui-switch-icon-all'
                    ]
                }
            }, {
                text: 'Users'.translate(),
                value: 'user',
                style: {
                    cssClasses: [
                        'pmui-switch-icon-user'
                    ]
                }
            }, {
                text: 'Groups'.translate(),
                value: 'group',
                style: {
                    cssClasses: [
                        'pmui-switch-icon-group'
                    ]
                }
            }
            ],
            listeners: {
                select: function (item, event) {
                    groupRows(gridUsers, item.value);
                }
            },
            style: {
                cssClasses: [
                    'mafe-assigment-buttons'
                ]
            }
        });
        gridUsers = new PMUI.grid.GridPanel({
            id: 'gridUsers',
            pageSize: pageSizeAssignment,
            behavior: 'dragdropsort',
            filterable: true,
            filterPlaceholder: 'Search ...'.translate(),
            emptyMessage: 'No records found'.translate(),
            nextLabel: 'Next'.translate(),
            previousLabel: 'Previous'.translate(),
            customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
                return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
            },
            columns: [{
                id: 'gridUsersButtonLabel',
                title: '',
                dataType: 'button',
                width: '10%',
                buttonLabel: function (row, data) {
                    return data.lastName;
                },
                buttonStyle: {
                    cssClasses: [
                        'mafe-grid-button'
                    ]
                },
                onButtonClick: function (row, grid) {
                    if (row.data.customKeys.obj_type === 'group') {
                        restClient.setTypeRequest('get');
                        restClient.functionSuccess = function (xhr, response) {
                            var stringUsers = '', i;
                            for (i = 0; i < response.length; i += 1) {
                                stringUsers = stringUsers + response[i].usr_firstname + ' ' + response[i].usr_lastname + ', ';
                            }
                            stringUsers = stringUsers.substring(0, stringUsers.length - 2);
                            if (stringUsers === '') {
                                stringUsers = 'No users';
                            }
                            quickMessageWindow.show($(row.html).find('a')[0], stringUsers);
                        };
                        restClient.setBaseEndPoint('group/' + row.data.customKeys.sup_uid + '/users');
                        restClient.executeRestClient();
                    }
                }
            }, {
                title: '',
                dataType: 'string',
                columnData: 'sup_name',
                alignmentCell: 'left',
                width: '80%'
            }, {
                id: 'gridUsersButtonAssign',
                title: '',
                dataType: 'button',
                width: '19%',
                buttonStyle: {
                    cssClasses: [
                        'mafe-button-edit'
                    ]
                },
                buttonLabel: function (row, data) {
                    row.getCells()[0].content.style.addClasses([row.data.customKeys.obj_type === 'user' ? 'button-icon-user' : 'button-icon-group']);
                    return 'Assign'.translate();
                },
                onButtonClick: function (row, grid) {
                    grid.removeItem(row);
                    grid.sort('sup_name', 'asc');
                    gridUserList.addItem(row);
                    buttonsUserList.setValue(buttonsUsers.getValue());
                    groupRows(gridUserList, buttonsUsers.getValue());
                    assignee(row);
                }
            }
            ],
            onDropOut: function (item, origin, destiny) {
                assignee(item);
            },
            onDrop: function (a, row) {
                buttonsUsers.setValue(buttonsUserList.getValue());
                groupRows(gridUsers, buttonsUserList.getValue());
            },
            style: {
                cssClasses: [
                    'mafe-designer-assigment-grid'
                ]
            }
        });

        panelTitleUserList = new PMUI.core.Panel({
            layout: 'hbox'
        });
        titleUserList = new PMUI.ui.TextLabel({
            id: 'titleUserList',
            textMode: 'plain',
            text: 'Assigned Users List'.translate(),
            style: {
                cssClasses: [
                    'mafe-designer-assigment-title'
                ]
            }
        });
        panelSearchUserList = new PMUI.core.Panel({
            layout: 'hbox'
        });
        searchGridUserList = new PMUI.field.TextField({
            id: 'searchGridUserList',
            label: ' ',
            placeholder: 'Search ...'.translate()
        });
        panelGridUserList = new PMUI.core.Panel({
            layout: 'hbox'
        });
        buttonsUserList = new PMUI.field.OptionsSelectorField({
            id: 'buttonsUserList',
            orientation: 'horizontal',
            items: [{
                text: 'All'.translate(),
                value: 'all',
                selected: true,
                style: {
                    cssClasses: [
                        'pmui-switch-icon-all'
                    ]
                }
            }, {
                text: 'Users'.translate(),
                value: 'user',
                style: {
                    cssClasses: [
                        'pmui-switch-icon-user'
                    ]
                }
            }, {
                text: 'Groups'.translate(),
                value: 'group',
                style: {
                    cssClasses: [
                        'pmui-switch-icon-group'
                    ]
                }
            }

            ],
            listeners: {
                select: function (item, event) {
                    groupRows(gridUserList, item.value);
                }
            },
            style: {
                cssClasses: [
                    'mafe-assigment-buttons'
                ]
            }
        });
        gridUserList = new PMUI.grid.GridPanel({
            id: 'gridUserList',
            pageSize: pageSizeAssignment,
            behavior: 'dragdropsort',
            filterPlaceholder: 'Search ...'.translate(),
            filterable: true,
            nextLabel: 'Next'.translate(),
            previousLabel: 'Previous'.translate(),
            customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
                return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
            },
            emptyMessage: function () {
                var div = document.createElement('div'),
                    span = document.createElement('span');
                div.appendChild(span);
                div.className = 'mafe-grid-panel-empty';
                span.innerHTML = 'Drag & Drop a User or a Group here'.translate();
                return div;
            },
            onEmpty: function (grid, cell) {
                gridUserList.dom.tableContainer.style.overflow = 'hidden';
            },
            columns: [{
                id: 'gridUserListButtonLabel',
                title: '',
                width: '10%',
                dataType: 'button',
                buttonLabel: function (row, data) {
                    return data.lastName;
                },
                buttonStyle: {
                    cssClasses: [
                        'mafe-grid-button'
                    ]
                },
                onButtonClick: function (row, grid) {
                    var stringUsers, i;
                    if (row.data.customKeys.obj_type === 'group') {
                        restClient.setTypeRequest('get');
                        restClient.functionSuccess = function (xhr, response) {
                            stringUsers = '';
                            for (i = 0; i < response.length; i += 1) {
                                stringUsers = stringUsers + response[i].usr_firstname + ' ' + response[i].usr_lastname + ', ';
                            }
                            stringUsers = stringUsers.substring(0, stringUsers.length - 2);
                            if (stringUsers === '') {
                                stringUsers = 'No users';
                            }
                            quickMessageWindow.show($(row.html).find('a')[0], stringUsers);
                        };
                        restClient.setBaseEndPoint('group/' + row.data.customKeys.sup_uid + '/users');
                        restClient.executeRestClient();
                    }
                }
            }, {
                title: '',
                dataType: 'string',
                columnData: 'sup_name',
                width: '80%',
                alignmentCell: 'left'
            }, {
                id: 'gridUserListButtonDelete',
                title: '',
                dataType: 'button',
                width: '10%',
                buttonStyle: {
                    cssClasses: [
                        'mafe-button-delete'
                    ]
                },
                buttonLabel: function (row, data) {
                    row.getCells()[0].content.style.addClasses([row.data.customKeys.obj_type === 'user' ? 'button-icon-user' : 'button-icon-group']);
                    return 'Remove'.translate();
                },
                onButtonClick: function (row, grid) {
                    grid.removeItem(row);
                    grid.sort('sup_name', 'asc');
                    gridUsers.addItem(row);
                    buttonsUsers.setValue(buttonsUserList.getValue());
                    groupRows(gridUsers, buttonsUserList.getValue());
                    remove(row);
                }
            }
            ],
            onDropOut: function (item, origin, destiny) {
                remove(item);
            },
            onDrop: function (a, row) {
                buttonsUserList.setValue(buttonsUsers.getValue());
                groupRows(gridUserList, buttonsUsers.getValue());
            },
            style: {
                cssClasses: [
                    'mafe-designer-assigment-grid'
                ]
            }
        });

        applyStyles = function () {
            gridUsers.dom.toolbar.appendChild(buttonsUsers.getHTML());
            gridUsers.dom.toolbar.style.height = "76px";
            gridUserList.dom.toolbar.appendChild(buttonsUserList.getHTML());
            gridUserList.dom.toolbar.style.height = "76px";
            buttonsUsers.dom.labelTextContainer.style.display = "none";
            buttonsUserList.dom.labelTextContainer.style.display = "none";
            gridUsers.hideHeaders();
            gridUserList.hideHeaders();
            assigmentPanelUserList.setHeight('100%');
            gridUsers.filterControl.html.style.width = "300px";
            gridUserList.filterControl.html.style.width = "300px";
        };
        loadServerData = function () {
            var restClient = new PMRestClient({
                typeRequest: 'post',
                multipart: true,
                data: {
                    calls: [
                        {
                            url: 'project/' + PMDesigner.project.id + '/available-process-supervisors',
                            method: 'GET'
                        }, {
                            url: 'project/' + PMDesigner.project.id + '/process-supervisors',
                            method: 'GET'
                        }
                    ]
                },
                functionSuccess: function (xhr, response) {
                    loadUsers(response[0].response);
                    loadUsersList(response[1].response);
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            });
            restClient.setBaseEndPoint('');
            restClient.executeRestClient();
        };
        loadUsers = function (response) {
            var i;
            for (i = 0; i < response.length; i += 1) {
                if (response[i].obj_type === 'user') {
                    gridUsers.addDataItem({
                        sup_uid: response[i].usr_uid,
                        sup_name: response[i].usr_firstname + ' ' + response[i].usr_lastname,
                        obj_type: response[i].obj_type
                    });
                }
                if (response[i].obj_type === 'group') {
                    gridUsers.addDataItem({
                        sup_uid: response[i].grp_uid,
                        sup_name: response[i].grp_name,
                        obj_type: response[i].obj_type
                    });
                }
            }
            gridUsers.sort('sup_name', 'asc');
        };
        loadUsersList = function (response) {
            var i;
            for (i = 0; i < response.length; i += 1) {
                if (response[i].pu_type === 'SUPERVISOR') {
                    gridUserList.addDataItem({
                        sup_uid: response[i].usr_uid,
                        sup_name: response[i].usr_firstname + ' ' + response[i].usr_lastname,
                        obj_type: 'user',
                        pu_type: response[i].pu_type,
                        pu_uid: response[i].pu_uid
                    });
                }
                if (response[i].pu_type === 'GROUP_SUPERVISOR') {
                    gridUserList.addDataItem({
                        sup_uid: response[i].grp_uid,
                        sup_name: response[i].grp_name,
                        obj_type: 'group',
                        pu_type: response[i].pu_type,
                        pu_uid: response[i].pu_uid
                    });
                }
            }
            gridUserList.sort('sup_name', 'asc');
        };
        groupRows = function (grid, value) {
            var i, items;
            if (grid.memorystack === undefined) {
                grid.memorystack = [];
            }
            items = grid.getItems();
            while (grid.memorystack.length > 0) {
                grid.addItem(grid.memorystack.pop());
            }
            if (value !== 'all') {
                for (i = 0; i < items.length; i += 1) {
                    if (items[i].getData().obj_type !== value) {
                        grid.memorystack.push(items[i]);
                        grid.removeItem(items[i]);
                    }
                }
            }
            grid.sort('sup_name', 'asc');
        };
        assignee = function (row) {
            var restClient = new PMRestClient({
                endpoint: 'process-supervisor',
                typeRequest: 'post',
                data: {
                    usr_uid: row.data.customKeys.sup_uid,
                    pu_type: row.data.customKeys.obj_type === 'group' ? 'GROUP_SUPERVISOR' : 'SUPERVISOR'
                },
                functionSuccess: function (xhr, response) {
                    row.data.customKeys.pu_type = response.pu_type;
                    row.data.customKeys.pu_uid = response.pu_uid;
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                },
                messageError: 'There are problems saving the assigned user, please try again.'.translate()
            });
            restClient.executeRestClient();
        };
        remove = function (row) {
            var restClient = new PMRestClient({
                endpoint: 'process-supervisor/' + row.data.customKeys.pu_uid,
                typeRequest: 'remove',
                functionSuccess: function () {
                },
                functionComplete: function () {
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            });
            restClient.executeRestClient();
        };

        assigmentWindow.open();
        panelTitleUser.addItem(titleUser);
        panelTitleUserList.addItem(titleUserList);
        assigmentWindow.body.style.overflow = "hidden";
        panelGridUser.addItem(gridUsers);
        panelGridUserList.addItem(gridUserList);

        assigmentPanelGlobal.addItem(assigmentPanelUser);
        assigmentPanelGlobal.addItem(assigmentPanelUserList);
        assigmentWindow.addItem(assigmentPanelGlobal);

        assigmentPanelUser.addItem(panelTitleUser);
        assigmentPanelUser.addItem(panelSearchUser);
        assigmentPanelUser.addItem(buttonsUsers);
        assigmentPanelUser.addItem(panelGridUser);

        assigmentPanelUserList.addItem(panelTitleUserList);
        assigmentPanelUserList.addItem(panelSearchUserList);
        assigmentPanelUserList.addItem(buttonsUserList);
        assigmentPanelUserList.addItem(panelGridUserList);

        assigmentWindow.defineEvents();
        applyStyles();

        loadServerData();
    };
}());
(function () {
    PMDesigner.assigmentSupervisors = function (event) {
        var assigmentWindow = null,
            pageSizeAssignment = 8,
            flagEdit = 0,
            i,
            index = 0,
            quickMessageWindow = new QuickMessageWindow(),
            arrayObjectUserList = new Array(),
            arrayObjectUsers = new Array(),
            arrayObjectUserList2 = new Array(),
            arrayObjectUsers2 = new Array(),
            arrayDynaformInputDocumentID = new Array(),
            arrayDynaformInputDocumentObject = new Array(),
            arrayObjectRemovedSteps = new Array(),
            message_window,
            restClient,
            assigmentPanelUser,
            assigmentPanelUserList,
            assigmentPanelGlobal,
            panelTitleUser,
            titleUser,
            panelSearchUser,
            searchGridUser,
            panelGridUser,
            buttonsUsers,
            gridUsers,
            panelTitleUserList,
            titleUserList,
            panelSearchUserList,
            searchGridUserList,
            panelGridUserList,
            radioUsersList,
            buttonsUserList,
            gridUserList,
            radioUsers,
            applyStylesRadioButtonGroupField,
            applyStylesForToolbar,
            applyStyles,
            loadUsers,
            loadUsersList,
            groupRows,
            assignee,
            remove,
            loadGridCaseTacker,
            orderDataTree,
            titleTreeObjects,
            titleGridObjects,
            panelLabelObjects,
            panelObjects,
            treePanelObjects,
            updateItem,
            usersgrid,
            saveItemDyanformInputDocuments,
            getValuesAssignmentSteps,
            gridPanelObjects,
            panelContainerObjects,
            userslist,
            gridpanelobj,
            flashMessage = new PMUI.ui.FlashMessage({
                message: '',
                appendTo: document.body,
                duration: 1000,
                severity: "success"
            });
        assigmentWindow = new PMUI.ui.Window({
            id: 'assigmentUserWindow',
            title: 'Supervisors'.translate(),
            width: DEFAULT_WINDOW_WIDTH + 1,
            height: DEFAULT_WINDOW_HEIGHT,
            footerHeight: 'auto',
            bodyHeight: 'auto',
            modal: true,
            onBeforeClose: function () {
                if (flagEdit != 0) {
                    message_window.open();
                    message_window.showFooter();
                } else {
                    assigmentWindow.close();
                }
            },
            footerItems: [
                {
                    text: "Cancel",
                    handler: function () {
                        if (flagEdit != 0) {
                            message_window.open();
                            message_window.showFooter();
                        } else {
                            assigmentWindow.close();
                        }
                    },
                    buttonType: 'error'
                },
                {
                    text: 'Save',
                    handler: function () {
                        var i, j, idObject, objType, baseEndPointID;
                        if (flagEdit != 0) {
                            //save Configuration Supervisors and Steps(Dyanform - Inputs Documents)                            
                            if (gridUserList.getItems().length > 0) {
                                grid = gridUserList;
                                for (i = 0; i < arrayObjectUserList.length; i += 1) {
                                    b = arrayObjectUserList[i];
                                    grid.removeItem(b);
                                    grid.sort('sup_name', 'asc');
                                    gridUserList.addItem(b);
                                    radioUsersList.setValue(radioUsers.getValue());
                                    groupRows(gridUserList, radioUsers.getValue());
                                    if (arrayObjectUserList2.indexOf(b) == -1) {
                                        assignee(b);
                                    }
                                }
                            }
                            if (gridUsers.getItems().length > 0) {
                                for (i = 0; i < arrayObjectUsers.length; i += 1) {
                                    b = arrayObjectUsers[i];
                                    if (arrayObjectUsers2.indexOf(b) == -1) {
                                        remove(arrayObjectUsers[i]);
                                    }
                                }
                            }

                            index = 0;
                            for (i = 0; i < gridPanelObjects.getItems().length; i += 1) {
                                idObject = gridPanelObjects.getItems()[i].getData().obj_uid;
                                index = arrayDynaformInputDocumentID.indexOf(idObject);
                                if (index <= -1) {
                                    saveItemDyanformInputDocuments(gridPanelObjects.getItems()[i]);
                                } else {
                                    updateItem(gridPanelObjects.getItems()[i], i);
                                }
                            }

                            for (i = 0; i < arrayObjectRemovedSteps.length; i += 1) {
                                for (j = 0; j < arrayDynaformInputDocumentObject.length; j += 1) {
                                    index = (arrayObjectRemovedSteps[i] == arrayDynaformInputDocumentObject[j].obj_uid) ? 0 : 1;
                                    if (index == 0) {
                                        objType = arrayDynaformInputDocumentObject[j].obj_type.toLowerCase();
                                        baseEndPointID = (objType === "dynaform") ? arrayDynaformInputDocumentObject[j].pud_uid : arrayDynaformInputDocumentObject[j].pui_uid;
                                        restClient = new PMRestClient({
                                            typeRequest: 'post',
                                            multipart: true,
                                            data: {
                                                "calls": [
                                                    {
                                                        "url": 'process-supervisor/' + objType + '/' + baseEndPointID,
                                                        "method": 'DELETE'
                                                    }
                                                ]
                                            },
                                            functionSuccess: function (xhr, response) {
                                            },
                                            functionFailure: function (xhr, response) {
                                            }
                                        });
                                        restClient.executeRestClient();
                                        break;
                                    }
                                }
                            }
                            assigmentWindow.close();
                        }
                        else {
                            assigmentWindow.close();
                        }
                    },
                    buttonType: "success"
                }
            ],
            visibleFooter: true,
            buttonPanelPosition: "bottom",
            footerAlign: "right"
        });

        message_window = new PMUI.ui.MessageWindow({
            id: "messageWindowCancel",
            width: 490,
            title: "Supervisors".translate(),
            windowMessageType: "warning",
            bodyHeight: "auto",
            message: 'Are you sure you want to discard your changes?'.translate(),
            footerItems: [{
                id: "messageWindowNo",
                text: "No".translate(),
                handler: function () {
                    message_window.close();
                },
                buttonType: "error"
            },
                {
                    id: "messageWindowYes",
                    text: "Yes".translate(),
                    handler: function () {
                        message_window.close();
                        assigmentWindow.close();
                    },
                    buttonType: "success"
                }
            ]
        });

        restClient = new PMRestClient({
            endpoint: 'projects',
            typeRequest: 'get',
            messageError: 'There are problems, please try again.'.translate(),
            functionFailure: function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            }
        });

        assigmentPanelUser = new PMUI.core.Panel({
            layout: "vbox",
            width: "49%",
            style: {
                cssClasses: [
                    'mafe-panel-assignment-white'
                ]
            }
        });
        assigmentPanelUserList = new PMUI.core.Panel({
            layout: "vbox",
            width: "50%",
            style: {
                cssClasses: [
                    'mafe-panel-assignment-smooth'
                ]
            }
        });
        assigmentPanelGlobal = new PMUI.core.Panel({
            layout: 'hbox',
            width: DEFAULT_WINDOW_WIDTH,
            style: {
                cssClasses: [
                    'mafe-assigment-panel-global'
                ]
            }
        });

        panelTitleUser = new PMUI.core.Panel({
            layout: 'hbox'
        });
        titleUser = new PMUI.ui.TextLabel({
            id: 'titleUser',
            label: ' ',
            textMode: 'plain',
            text: 'Available Users List'.translate(),
            style: {
                cssClasses: [
                    'mafe-designer-assigment-title'
                ]
            }
        });
        panelSearchUser = new PMUI.core.Panel({
            layout: 'hbox'
        });
        searchGridUser = new PMUI.field.TextField({
            id: 'searchGridUser',
            label: ' ',
            placeholder: 'Search ...'.translate()
        });
        panelGridUser = new PMUI.core.Panel({layout: 'hbox'});
        buttonsUsers = new PMUI.field.OptionsSelectorField({
            id: 'buttonsUsers',
            orientation: 'horizontal',
            items: [{
                text: 'All'.translate(),
                selected: true,
                value: 'all',
                style: {
                    cssClasses: [
                        'pmui-switch-icon-all'
                    ]
                }
            }, {
                text: 'Users'.translate(),
                value: 'user',
                style: {
                    cssClasses: [
                        'pmui-switch-icon-user'
                    ]
                }
            }, {
                text: 'Groups'.translate(),
                value: 'group',
                style: {
                    cssClasses: [
                        'pmui-switch-icon-group'
                    ]
                }
            }
            ],
            listeners: {
                select: function (item, event) {
                    groupRows(gridUsers, item.value);
                }
            },
            style: {
                cssClasses: [
                    'mafe-assigment-buttons'
                ]
            }
        });
        gridUsers = new PMUI.grid.GridPanel({
            id: 'gridUsers',
            pageSize: pageSizeAssignment,
            behavior: 'dragdropsort',
            filterable: true,
            filterPlaceholder: 'Search ...'.translate(),
            emptyMessage: 'No records found'.translate(),
            nextLabel: 'Next'.translate(),
            previousLabel: 'Previous'.translate(),
            customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
                return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
            },
            dynamicLoad: {
                keys: {
                    server: HTTP_SERVER_HOSTNAME,
                    projectID: PMDesigner.project.projectId,
                    workspace: WORKSPACE,
                    accessToken: PMDesigner.project.tokens.access_token,
                    endPoint: 'project/' + PMDesigner.project.id + '/available-process-supervisors/paged'
                }
            },
            customDataRest: function (data) {
                var i;
                for (i = 0; i < data.length; i += 1) {
                    if (data[i].obj_type == "group") {
                        data[i].available = data[i].grp_name;
                    } else {
                        data[i].available = data[i]["usr_firstname"] + " " + data[i]["usr_lastname"] + " (" + data[i]["usr_username"] + ")";
                    }
                }
                return data;
            },
            columns: [{
                id: 'gridUsersButtonLabel',
                title: '',
                dataType: 'button',
                width: '10%',
                buttonLabel: function (row, data) {
                    return data.lastName;
                },
                buttonStyle: {
                    cssClasses: [
                        'mafe-grid-button'
                    ]
                },
                onButtonClick: function (row, grid) {
                    var option, select;
                    select = document.createElement("span");
                    select.id = "list-usersIngroup";
                    option = document.createElement("span");
                    option.id = "list-usersIngroup-iem"
                    if (row.getData()["obj_type"] === 'group') {
                        var restClient = new PMRestClient({
                            typeRequest: 'get',
                            functionSuccess: function (xhr, response) {
                                var optionClone, i;
                                for (i = 0; i < response.length; i += 1) {
                                    if (i == 10) {
                                        optionClone = option.cloneNode(false);
                                        optionClone.innerHTML = "<b style='float: right'>. . .<b>";
                                        select.appendChild(optionClone);
                                    } else {
                                        optionClone = option.cloneNode(false);
                                        optionClone.textContent = "- " + response[i].usr_firstname + ' ' + response[i].usr_lastname;
                                        optionClone.title = response[i].usr_firstname + ' ' + response[i].usr_lastname;
                                        select.appendChild(optionClone);
                                    }
                                }
                                if (!optionClone) {
                                    optionClone = option.cloneNode(false);
                                    optionClone.textContent = "No users".translate();
                                    select.appendChild(optionClone);
                                }

                                quickMessageWindow.show($(row.html).find('a')[0], select);
                            },
                            functionFailure: function (xhr, response) {
                                PMDesigner.msgWinError(response.error.message);
                            },
                            messageError: 'There are problems saving the assigned user, please try again.'.translate()
                        });
                        restClient.setBaseEndPoint('group/' + row.getData()["grp_uid"] + '/supervisor-users?start=0&limit=11');
                        restClient.executeRestClient();
                    }
                }
            }, {
                title: '',
                dataType: 'string',
                columnData: 'available',
                alignmentCell: 'left',
                width: '330px'
            }, {
                id: 'gridUsersButtonAssign',
                title: '',
                dataType: 'button',
                width: '19%',
                buttonStyle: {
                    cssClasses: [
                        'mafe-button-edit-assign'
                    ]
                },
                buttonLabel: function (row, data) {
                    row.getCells()[0].content.style.addClasses([row.getData()["obj_type"] === 'user' ? 'button-icon-user' : 'button-icon-group']);
                    return '';
                },
                onButtonClick: function (row, grid) {
                    grid = (grid != null) ? grid : gridUsers;
                    assignee(row);
                    gridUserList.goToPage(gridUserList.currentPage);
                    grid.goToPage(grid.currentPage);
                    flashMessage.setMessage("The user/group was successfully removed".translate());
                    flashMessage.setAppendTo(assigmentWindow.getHTML());
                    flashMessage.show();
                }
            }
            ],
            onDropOut: function (item, origin, destiny) {
            },
            onDrop: function (a, row) {
                grid = this;
                remove(row);
                gridUserList.goToPage(gridUserList.currentPage);
                grid.goToPage(grid.currentPage);
                flashMessage.setMessage("The user/group was successfully removed".translate());
                flashMessage.setAppendTo(assigmentWindow.getHTML());
                flashMessage.show();
                return false;
            },
            style: {
                cssClasses: [
                    'mafe-designer-assigment-grid'
                ]
            }
        });

        panelTitleUserList = new PMUI.core.Panel({
            layout: 'hbox'
        });
        titleUserList = new PMUI.ui.TextLabel({
            id: 'titleUserList',
            textMode: 'plain',
            text: 'Assigned supervisors list'.translate(),
            style: {
                cssClasses: [
                    'mafe-designer-assigment-title'
                ]
            }
        });
        panelSearchUserList = new PMUI.core.Panel({
            layout: 'hbox'
        });
        searchGridUserList = new PMUI.field.TextField({
            id: 'searchGridUserList',
            label: ' ',
            placeholder: 'Search ...'.translate()
        });
        panelGridUserList = new PMUI.core.Panel({
            layout: 'hbox'
        });
        radioUsersList = new PMUI.field.RadioButtonGroupField({
            id: 'idRadioUsersList',
            controlPositioning: 'horizontal',
            maxDirectionOptions: 3,
            options: [
                {
                    label: "View all".translate(),
                    value: "all"
                },
                {
                    label: "View users".translate(),
                    value: "user"
                },
                {
                    label: "View groups".translate(),
                    value: "group"
                }
            ],
            onChange: function (newVal, oldVal) {
                switch (newVal) {
                    case "user" :
                        gridUserList.typeList = "user".translate();
                        break;
                    case "group":
                        gridUserList.typeList = "group".translate();
                        break;
                    default:
                        gridUserList.typeList = "";
                        break;
                }
                gridUserList.goToPage(0);
            },
            required: true,
            value: "all"
        });
        buttonsUserList = new PMUI.field.OptionsSelectorField({
            id: 'buttonsUserList',
            orientation: 'horizontal',
            items: [{
                text: 'All'.translate(),
                value: 'all',
                selected: true,
                style: {
                    cssClasses: [
                        'pmui-switch-icon-all'
                    ]
                }
            }, {
                text: 'Users'.translate(),
                value: 'user',
                style: {
                    cssClasses: [
                        'pmui-switch-icon-user'
                    ]
                }
            }, {
                text: 'Groups'.translate(),
                value: 'group',
                style: {
                    cssClasses: [
                        'pmui-switch-icon-group'
                    ]
                }
            }

            ],
            listeners: {
                select: function (item, event) {
                    groupRows(gridUserList, item.value);
                }
            },
            style: {
                cssClasses: [
                    'mafe-assigment-buttons'
                ]
            }
        });
        gridUserList = new PMUI.grid.GridPanel({
            id: 'gridUserList',
            pageSize: pageSizeAssignment,
            behavior: 'dragdropsort',
            filterPlaceholder: 'Search ...'.translate(),
            filterable: true,
            nextLabel: 'Next'.translate(),
            previousLabel: 'Previous'.translate(),
            customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
                return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
            },
            emptyMessage: function () {
                var div = document.createElement('div'),
                    span = document.createElement('span');
                div.appendChild(span);
                div.className = 'mafe-grid-panel-empty';
                span.innerHTML = 'Drag & Drop a User or a Group here'.translate();
                return div;
            },
            onEmpty: function (grid, cell) {
                gridUserList.dom.tableContainer.style.overflow = 'hidden';
            },
            dynamicLoad: {
                keys: {
                    server: HTTP_SERVER_HOSTNAME,
                    projectID: PMDesigner.project.projectId,
                    workspace: WORKSPACE,
                    accessToken: PMDesigner.project.tokens.access_token,
                    endPoint: 'project/' + PMDesigner.project.id + '/process-supervisors/paged'
                }
            },
            customDataRest: function (data) {
                var i;
                for (i = 0; i < data.length; i += 1) {
                    if (data[i].pu_type == "GROUP_SUPERVISOR") {
                        data[i].assignee = data[i].grp_name;
                    } else {
                        data[i].assignee = data[i]["usr_firstname"] + " " + data[i]["usr_lastname"] + " (" + data[i]["usr_username"] + ")";
                    }
                }
                return data;
            },
            columns: [{
                id: 'gridUserListButtonLabel',
                title: '',
                width: '10%',
                dataType: 'button',
                buttonLabel: function (row, data) {
                    return data.lastName;
                },
                buttonStyle: {
                    cssClasses: [
                        'mafe-grid-button'
                    ]
                },
                onButtonClick: function (row, grid) {
                    var option, select;
                    select = document.createElement("span");
                    select.id = "list-usersIngroup";
                    option = document.createElement("span");
                    option.id = "list-usersIngroup-iem"
                    if (row.getData()["pu_type"] === "GROUP_SUPERVISOR") {
                        var restClient = new PMRestClient({
                            typeRequest: 'get',
                            functionSuccess: function (xhr, response) {
                                var optionClone, i;
                                for (i = 0; i < response.length; i += 1) {
                                    if (i == 10) {
                                        optionClone = option.cloneNode(false);
                                        optionClone.innerHTML = "<b style='float: right'>. . .<b>";
                                        select.appendChild(optionClone);
                                    } else {
                                        optionClone = option.cloneNode(false);
                                        optionClone.textContent = "- " + response[i].usr_firstname + ' ' + response[i].usr_lastname;
                                        optionClone.title = response[i].usr_firstname + ' ' + response[i].usr_lastname;
                                        select.appendChild(optionClone);
                                    }
                                }
                                if (!optionClone) {
                                    optionClone = option.cloneNode(false);
                                    optionClone.textContent = "No users".translate();
                                    select.appendChild(optionClone);
                                }

                                quickMessageWindow.show($(row.html).find('a')[0], select);
                            },
                            functionFailure: function (xhr, response) {
                                PMDesigner.msgWinError(response.error.message);
                            },
                            messageError: 'There are problems saving the assigned user, please try again.'.translate()
                        });
                        restClient.setBaseEndPoint('group/' + row.getData()["grp_uid"] + '/supervisor-users?start=0&limit=11');
                        restClient.executeRestClient();
                    }
                }
            }, {
                title: '',
                dataType: 'string',
                columnData: 'assignee',
                width: '330px',
                alignmentCell: 'left'
            }, {
                id: 'gridUserListButtonDelete',
                title: '',
                dataType: 'button',
                width: '10%',
                buttonStyle: {
                    cssClasses: [
                        'mafe-button-delete-assign'
                    ]
                },
                buttonLabel: function (row, data) {
                    row.getCells()[0].content.style.addClasses([row.getData()["pu_type"] === "SUPERVISOR" ? 'button-icon-user' : 'button-icon-group']);
                    return '';
                },
                onButtonClick: function (row, grid) {
                    grid = (grid != null) ? grid : gridUsers;
                    remove(row);
                    gridUsers.goToPage(gridUsers.currentPage);
                    grid.goToPage(grid.currentPage);
                    flashMessage.setMessage("Assignee saved successfully".translate());
                    flashMessage.setAppendTo(assigmentWindow.getHTML());
                    flashMessage.show();
                }
            }
            ],
            onDropOut: function (item, origin, destiny) {
            },
            onDrop: function (grid, row) {
                var dataRow = {};
                grid = this;
                assignee(row);
                gridUsers.goToPage(gridUsers.currentPage);
                grid.goToPage(grid.currentPage);
                flashMessage.setMessage("Assignee saved successfully".translate());
                flashMessage.setAppendTo(assigmentWindow.getHTML());
                flashMessage.show();
                return false;
            },
            style: {
                cssClasses: [
                    'mafe-designer-assigment-grid'
                ]
            }
        });
        radioUsers = new PMUI.field.RadioButtonGroupField({
            id: 'idRadioUsers',
            controlPositioning: 'horizontal',
            maxDirectionOptions: 3,
            options: [
                {
                    label: "View all".translate(),
                    value: "all"
                },
                {
                    label: "View users".translate(),
                    value: "user"
                },
                {
                    label: "View groups".translate(),
                    value: "group"
                }
            ],
            onChange: function (newVal, oldVal) {
                switch (newVal) {
                    case "user" :
                        gridUsers.typeList = "user";
                        break;
                    case "group":
                        gridUsers.typeList = "group";
                        break;
                    default:
                        gridUsers.typeList = "";
                        break;
                }
                gridUsers.goToPage(0);
            },
            required: true,
            value: "all",
            style: {
                cssProperties: {
                    'margin-top': '15px'
                }
            }
        });
        applyStylesRadioButtonGroupField = function () {
            $('#idRadioUsers .pmui-field-control-table').css("border", "none");
            $('#idRadioUsers').css("margin-top", "12px");
            radioUsers.defineEvents();

            $('#idRadioUsersList .pmui-field-control-table').css("border", "none");
            $('#idRadioUsersList').css("margin-top", "12px");
            radioUsersList.defineEvents();
        };

        applyStylesForToolbar = function () {
            $('.pmui-gridpanel-toolbar')[0].childNodes[0].style.width = "300px";
            $('.pmui-gridpanel-toolbar')[1].childNodes[0].style.width = "300px";
            $('.pmui-gridpanel-toolbar')[0].childNodes[0].style.backgroundColor = "#f5f5f5";
            $('.pmui-gridpanel-toolbar')[1].childNodes[0].style.backgroundColor = "#f5f5f5";
            $('#assigmentUserWindow')[0].childNodes[1].childNodes[0].childNodes[1].style.backgroundColor = "white";
            $('#gridUserList')[0].style.backgroundColor = "white";
            $('#gridUserList').css("width", "452px");
            $('#gridUsers').css({"width": "452px", "height": "175px"});
            $('.pmui-window-body .pmui-panel:eq(9)').css({
                'width': '1000px',
                'border-top': '1px solid #cdd2d5',
                'margin-top': '36px',
                'height': '80px'
            });
        };

        applyStyles = function () {
            applyStylesRadioButtonGroupField();
            gridUsers.dom.toolbar.appendChild(radioUsers.getHTML());
            gridUsers.dom.toolbar.style.height = "76px";
            gridUserList.dom.toolbar.appendChild(radioUsersList.getHTML());
            gridUserList.dom.toolbar.style.height = "76px";
            radioUsers.dom.labelTextContainer.style.display = "none";
            radioUsersList.dom.labelTextContainer.style.display = "none";
            gridUsers.hideHeaders();
            gridUserList.hideHeaders();
            assigmentPanelUserList.setHeight('100%');
            gridUsers.filterControl.html.style.width = "300px";
            gridUserList.filterControl.html.style.width = "300px";
            assigmentWindow.getItems()[0].getItems()[0].html.style.borderRight = "1px solid #cdd2d5";
            assigmentWindow.getItems()[0].getItems()[0].html.style.borderBottom = "1px solid #cdd2d5";
            assigmentWindow.getItems()[0].getItems()[1].html.style.borderBottom = "1px solid #cdd2d5";
            assigmentWindow.getItems()[0].getItems()[0].html.style.height = "415px";
            assigmentWindow.getItems()[0].getItems()[1].html.style.height = "415px";
            assigmentWindow.getItems()[0].getItems()[1].html.style.paddingLeft = "10px";
            $('#gridPanelObjects .pmui-gridpanel-tableContainer').css({'height': '245px'});
            $(".pmui-gridpanel-footer").addClass("pmui-gridpanel-footer-dinamic");
        };
        loadUsers = function (response) {
            var i;
            for (i = 0; i < response.length; i += 1) {
                if (response[i].obj_type === 'user') {
                    gridUsers.addDataItem({
                        sup_uid: response[i].usr_uid,
                        sup_name: response[i].usr_firstname + ' ' + response[i].usr_lastname,
                        obj_type: response[i].obj_type
                    });
                }
                if (response[i].obj_type === 'group') {
                    gridUsers.addDataItem({
                        sup_uid: response[i].grp_uid,
                        sup_name: response[i].grp_name,
                        obj_type: response[i].obj_type
                    });
                }
            }
            gridUsers.sort('sup_name', 'asc');
        };
        loadUsersList = function (response) {
            var i;
            for (i = 0; i < response.length; i += 1) {
                if (response[i].pu_type === 'SUPERVISOR') {
                    gridUserList.addDataItem({
                        sup_uid: response[i].usr_uid,
                        sup_name: response[i].usr_firstname + ' ' + response[i].usr_lastname,
                        obj_type: 'user',
                        pu_type: response[i].pu_type,
                        pu_uid: response[i].pu_uid
                    });
                }
                if (response[i].pu_type === 'GROUP_SUPERVISOR') {
                    gridUserList.addDataItem({
                        sup_uid: response[i].grp_uid,
                        sup_name: response[i].grp_name,
                        obj_type: 'group',
                        pu_type: response[i].pu_type,
                        pu_uid: response[i].pu_uid
                    });
                }
            }
            gridUserList.sort('sup_name', 'asc');
        };
        groupRows = function (grid, value) {
            var i, items;
            if (grid.memorystack === undefined) {
                grid.memorystack = [];
            }
            items = grid.getItems();
            while (grid.memorystack.length > 0) {
                grid.addItem(grid.memorystack.pop());
            }
            if (value !== 'all') {
                for (i = 0; i < items.length; i += 1) {
                    if (items[i].getData().obj_type !== value) {
                        grid.memorystack.push(items[i]);
                        grid.removeItem(items[i]);
                    }
                }
            }
            grid.sort('sup_name', 'asc');
        };
        assignee = function (row) {
            var restClient = new PMRestClient({
                endpoint: 'process-supervisor',
                typeRequest: 'post',
                data: {
                    usr_uid: row.getData()["obj_type"] == "group" ? row.getData()["grp_uid"] : row.getData()["usr_uid"],
                    pu_type: row.getData()["obj_type"] === 'group' ? 'GROUP_SUPERVISOR' : 'SUPERVISOR'
                },
                functionSuccess: function (xhr, response) {
                    row.data.customKeys.pu_type = response.pu_type;
                    row.data.customKeys.pu_uid = response.pu_uid;
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                },
                messageError: 'There are problems saving the assigned user, please try again.'.translate()
            });
            restClient.executeRestClient();
        };
        remove = function (row) {
            var restClient = new PMRestClient({
                endpoint: 'process-supervisor/' + row.getData()["pu_uid"],
                typeRequest: 'remove',
                functionSuccess: function () {
                },
                functionComplete: function () {
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            });
            restClient.executeRestClient();
        };

        //steps Dynaforms and InputDocuments
        titleTreeObjects = new PMUI.ui.TextLabel({
            id: "titleTreeObjects",
            textMode: 'plain',
            text: 'Available Objects'.translate(),
            style: {
                cssClasses: [
                    'mafe-designer-assigment-title'
                ]
            }
        });

        titleGridObjects = new PMUI.ui.TextLabel({
            id: "titleGridObjects",
            textMode: 'plain',
            text: 'Assigned objects'.translate(),
            style: {
                cssClasses: [
                    'mafe-designer-assigment-title'
                ],
                cssProperties: {
                    'left': 40
                }
            }
        });

        panelLabelObjects = new PMUI.core.Panel({
            width: DEFAULT_WINDOW_WIDTH * 0.94,
            fieldset: true,
            items: [
                titleTreeObjects,
                titleGridObjects
            ],
            style: {
                cssProperties: {
                    'margin-bottom': 2,
                    'margin-left': 50
                }
            },
            layout: "hbox"
        });

        panelObjects = new PMUI.core.Panel({
            width: DEFAULT_WINDOW_WIDTH * 0.94,
            height: 30,
            fieldset: true,
            items: [
                panelLabelObjects
            ],
            layout: "vbox"
        });

        //Objects
        orderDataTree = function (data) {
            var items = [], i,
                type = ['DYNAFORM', 'INPUT-DOCUMENT'],
                label = ['Dynaform', 'Input Document'];
            for (i = 0; i < type.length; i += 1) {
                items = [];
                for (var j = 0; j < data.length; j += 1) {
                    if (type[i] === data[j].obj_type) {
                        if (data[j].obj_type == "DYNAFORM") {
                            items.push({
                                step_type_obj: label[i].translate(),
                                obj_label: label[i].translate(),
                                obj_title: data[j]['dyn_title'],
                                obj_type: data[j]['obj_type'],
                                obj_uid: data[j]['obj_uid'],
                                dyn_uid: data[j]['dyn_uid']
                            });
                        } else {
                            if (data[j].obj_type == "INPUT-DOCUMENT") {
                                items.push({
                                    step_type_obj: label[i].translate(),
                                    obj_label: label[i].translate(),
                                    obj_title: data[j]['inp_doc_title'],
                                    obj_type: data[j]['obj_type'],
                                    obj_uid: data[j]['obj_uid'],
                                    obj_uid: data[j]['obj_uid'],
                                    inp_uid: data[j]['inp_doc_uid']
                                });
                            }
                        }
                    }
                }
                if (items.length === 0) {
                    dataTree.push({
                        obj_title: label[i].translate(),
                        items: [{obj_title: 'N/A'.translate(), obj_uid: ''}]
                    });
                } else {
                    dataTree.push({
                        obj_title: label[i].translate(),
                        items: items
                    });
                }
            }
        };

        loadGridCaseTacker = function (data) {
            var dataOrder = new Array(), i, j;
            for (i = 0; i < data.length; i += 1) {
                for (j = 0; j < data.length; j += 1) {
                    positionIndex = (data[j]['obj_type'] == 'DYNAFORM') ? data[j].pud_position : data[j].pui_position;
                    if (positionIndex == (i + 1)) {
                        dataOrder.push(data[j]);
                        switch (dataOrder[i]['obj_type']) {
                            case 'DYNAFORM':
                                label = dataOrder[i]['dyn_title'].translate();
                                break;
                            case 'INPUT-DOCUMENT':
                                label = dataOrder[i]['input_doc_title'].translate();
                                break;
                            default:
                                break;
                        }
                        dataOrder[i]['obj_title'] = label;
                        break;
                    }
                }
            }
            gridPanelObjects.setDataItems(dataOrder);
        };

        getValuesAssignmentSteps = function () {
            restClient = new PMRestClient({
                typeRequest: 'post',
                multipart: true,
                data: {
                    "calls": [
                        {
                            "url": "process-supervisor/available-assignmentsteps",
                            "method": 'GET'
                        },
                        {
                            "url": "process-supervisor/assignmentsteps",
                            "method": 'GET'
                        }
                    ]
                },
                functionSuccess: function (xhr, response) {
                    dataTree = [];
                    orderDataTree(response[0].response);
                    treePanelObjects.setDataItems(dataTree);
                    loadGridCaseTacker(response[1].response);
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            });
            restClient.executeRestClient();
        };

        treePanelObjects = new PMUI.panel.TreePanel({
            id: 'treePanelObjects',
            proportion: 0.5,
            filterable: true,
            filterPlaceholder: 'Text to search'.translate(),
            emptyMessage: 'No records found'.translate(),
            style: {cssClasses: ['itemsSteps']},
            nodeDefaultSettings: {
                behavior: "drag",
                labelDataBind: 'obj_title',
                itemsDataBind: 'items',
                collapsed: false,
                childrenDefaultSettings: {
                    labelDataBind: 'obj_title',
                    autoBind: true
                },
                autoBind: true
            }
        });

        //Save Item (Drop)
        saveItemDyanformInputDocuments = function (rowStep) {
            rowStep = rowStep.getData();
            if (rowStep.obj_type === "DYNAFORM") {
                data = {
                    "dyn_uid": rowStep.obj_uid,
                    "pud_position": rowStep.obj_position
                };
            } else {
                data = {
                    "inp_doc_uid": rowStep.obj_uid,
                    "pui_position": rowStep.obj_position
                };
            }

            restClient = new PMRestClient({
                endpoint: 'process-supervisor/' + rowStep.obj_type.toLowerCase(),
                typeRequest: 'post',
                data: data,
                functionSuccess: function (xhr, response) {
                },
                functionFailure: function (xhr, response) {
                }
            });
            restClient.executeRestClient();
            return data;
        };

        //Update SORT tree
        updateItem = function (rowStep, i) {
            var objType,
                baseEndPointType,
                baseEndPointID;
            rowStep = rowStep.getData();
            rowStep.obj_position = i + 1;
            objType = rowStep.obj_type.toLowerCase(),
            baseEndPointType = (objType === "dynaform") ? 'dynaforms' : 'input-documents';
            baseEndPointID = (objType === "dynaform") ? rowStep.pud_uid : rowStep.pui_uid;
            if (objType === "dynaform") {
                rowStep.pud_position = rowStep.obj_position;
            } else {
                rowStep.pui_position = rowStep.obj_position;
            }

            restClient = new PMRestClient({
                typeRequest: 'post',
                multipart: true,
                data: {
                    "calls": [
                        {
                            "url": 'process-supervisor/' + objType + "/" + baseEndPointID,
                            "method": 'PUT',
                            "data": rowStep
                        }
                    ]
                },
                functionSuccess: function (xhr, response) {
                },
                functionFailure: function (xhr, response) {
                }
            });
            restClient.executeRestClient();
        };
        gridPanelObjects = new PMUI.grid.GridPanel({
            id: 'gridPanelObjects',
            proportion: 1.5,
            visibleFooter: false,
            filterable: false,
            style: {
                cssClasses: ['itemsSteps']
            },
            filterPlaceholder: 'Search ...'.translate(),
            emptyMessage: 'No records found'.translate(),
            nextLabel: 'Next'.translate(),
            previousLabel: 'Previous'.translate(),
            customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
                return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
            },
            behavior: 'dragdropsort',
            columns: [
                {
                    title: 'Title'.translate(),
                    dataType: 'string',
                    width: 330,
                    alignment: "left",
                    columnData: "obj_title",
                    sortable: false,
                    alignmentCell: 'left'
                },
                {
                    title: 'Type'.translate(),
                    dataType: 'string',
                    width: 120,
                    alignment: "left",
                    columnData: "obj_type",
                    sortable: false,
                    alignmentCell: 'left'
                },
                {
                    id: 'gridPanelObjectsButtonDelete',
                    title: '',
                    dataType: 'button',
                    buttonLabel: '',
                    buttonStyle: {cssClasses: ['mafe-button-delete-assign']},
                    buttonTooltip: 'Remove Object'.translate(),
                    onButtonClick: function (row, grid) {
                        var rowStep, treePanelItems;
                        flagEdit = 1;
                        index = arrayDynaformInputDocumentID.indexOf(row.getData().obj_uid);
                        rowStep = row.getData();
                        treePanelItems = treePanelObjects.getItems();

                        if (index > -1) {
                            arrayObjectRemovedSteps.push(row.getData().obj_uid);
                            arrayDynaformInputDocumentID.splice(index, 1);
                        }
                        function removeRowClass(treeNode) {
                            var childNodeEl = $(treeNode.html).find('.pmui-gridpanelrow');
                            childNodeEl.removeClass('pmui-gridpanelrow');
                        }

                        function removeEmptyNode(indexNode) {
                            var parentNode = treePanelItems[indexNode];
                            if (parentNode.getItems().length == 1 &&
                                parentNode.getItems()[0].getData()['obj_uid'] == "") {
                                parentNode.removeItem(0);
                            }
                            parentNode.addItem(row);
                            removeRowClass(parentNode);
                        }

                        if (rowStep.obj_type === "DYNAFORM") {
                            removeEmptyNode(0);
                        } else {
                            removeEmptyNode(1);
                        }
                        grid.removeItem(row);
                    }
                }
            ],
            onDrop: function (grid, item, index) {
                var parentItems;
                if (item.getData()['obj_uid'] == "") {
                    return false;

                }
                parentItems = item.parent.getItems();
                if (parentItems.length == 1 && item.getData()['obj_uid'] != "") {
                    item.parent.addDataItem(
                        {obj_title: 'N/A'.translate(), obj_uid: ''}
                    );
                    item.parent.behaviorObject.draggedObject = item;
                }
                flagEdit = 1;
                rowStep = item.getData();
                rowStep.obj_position = index + 1;
                item.setData(rowStep);
                index = arrayObjectRemovedSteps.indexOf(item.getData().obj_uid);

                if (index > -1) {
                    arrayDynaformInputDocumentID.push(item.getData().obj_uid);
                    arrayObjectRemovedSteps.splice(index, 1);
                }
            },
            onSort: function (grid, item, index) {
                flagEdit = 1;
                rowStep = item.getData();
                rowStep.obj_position = index + 1;
            }
        });

        //principal Container Steps
        panelContainerObjects = new PMUI.core.Panel({
            id: "panelContainerObjects",
            width: DEFAULT_WINDOW_WIDTH,
            height: 250,
            fieldset: true,
            items: [treePanelObjects, gridPanelObjects],
            layout: "hbox",
            style: {
                cssProperties: {
                    'margin-top': '-40px'
                }
            }
        });

        assigmentWindow.open();
        panelTitleUser.addItem(titleUser);
        panelTitleUserList.addItem(titleUserList);
        assigmentWindow.body.style.overflowX = "hidden";

        panelGridUser.addItem(gridUsers);
        panelGridUserList.addItem(gridUserList);

        assigmentPanelGlobal.addItem(assigmentPanelUser);
        assigmentPanelGlobal.addItem(assigmentPanelUserList);
        assigmentWindow.addItem(assigmentPanelGlobal);

        assigmentWindow.addItem(panelObjects);
        assigmentWindow.addItem(panelContainerObjects);
        getValuesAssignmentSteps();

        gridPanelObjects.style.addProperties({overflow: 'auto'});
        gridPanelObjects.style.addProperties({float: 'right'});
        gridPanelObjects.setWidth(630);
        gridPanelObjects.setHeight(250);
        $('#gridPanelObjects').css("margin-right", "32px");
        $('#treePanelObjects').css("margin-left", "10px");

        assigmentPanelUser.addItem(panelTitleUser);
        assigmentPanelUser.addItem(panelSearchUser);
        assigmentPanelUser.addItem(radioUsers);
        assigmentPanelUser.addItem(panelGridUser);

        assigmentPanelUserList.addItem(panelTitleUserList);
        assigmentPanelUserList.addItem(panelSearchUserList);
        assigmentPanelUserList.addItem(radioUsersList);
        assigmentPanelUserList.addItem(panelGridUserList);
        gridUsers.dom.tableContainer.style.height = "245px";
        gridUserList.dom.tableContainer.style.height = "245px";
        gridUsers.goToPage(0);
        gridUserList.goToPage(0);
        assigmentWindow.defineEvents();
        applyStyles();
        treePanelObjects.style.addProperties({overflow: 'auto'});

        applyStylesForToolbar();
        usersgrid = gridUsers;
        for (i = 0; i < usersgrid.getItems().length; i += 1) {
            arrayObjectUsers2[i] = usersgrid.getItems()[i];
        }
        userslist = gridUserList;
        for (i = 0; i < userslist.getItems().length; i += 1) {
            arrayObjectUserList2[i] = userslist.getItems()[i];
        }
        gridpanelobj = gridPanelObjects;
        for (i = 0; i < gridpanelobj.getItems().length; i += 1) {
            arrayDynaformInputDocumentID[i] = gridpanelobj.getItems()[i].getData().obj_uid;
            arrayDynaformInputDocumentObject[i] = gridpanelobj.getItems()[i].getData();
        }
    };
}());
(
    function () {
        PMDesigner.propertiesProcess = function () {
            var responseProperties = null,
                getValuesProperties,
                isDirtyFormProcess,
                saveProperties,
                propertiesWindow,
                processUID,
                textTitle,
                textDescription,
                processOwner,
                dropCalendar,
                dropProcessCat,
                dropDynaform,
                dropRouting,
                checkDebug,
                checkHideCase,
                checkSubProcess,
                dropCaseCreated,
                dropCaseDeleted,
                dropCaseCancelled,
                dropCasePaused,
                dropCaseUnpaused,
                dropCaseReassigned,
                dropCaseOpen,
                dropTypeProcess,
                proCost,
                loadProperties,
                proUnitCost,
                formEditProcess,
                loadDynaforms,
                loadCalendar,
                loadTemplate,
                loadTriggers,
                loadTypeProcess,
                loadCategory,
                notification,
                notificationText = "Fields marked with an asterisk (%%ASTERISK%%) are required.".translate()
                    .replace(/%%ASTERISK%%/g, '<span style="color: #e84c3d">*</span>'),
                clickedClose;

            getValuesProperties = function () {
                var restClient = new PMRestClient({
                    typeRequest: 'post',
                    multipart: true,
                    data: {
                        "calls": [
                            {
                                "url": "project/" + PMDesigner.project.id + "/dynaforms",
                                "method": 'GET'
                            },
                            {
                                "url": "calendars",
                                "method": 'GET'
                            },
                            {
                                "url": "project/categories",
                                "method": 'GET'
                            },
                            {
                                "url": "project/" + PMDesigner.project.id + "/file-manager?path=templates",
                                "method": 'GET'
                            },
                            {
                                "url": "project/" + PMDesigner.project.id + "/triggers",
                                "method": 'GET'
                            },
                            {
                                "url": "project/" + PMDesigner.project.id + "/process",
                                "method": 'GET'
                            }
                        ]
                    },
                    functionSuccess: function (xhr, response) {
                        loadDynaforms(response[0].response);
                        loadCalendar(response[1].response);
                        loadCategory(response[2].response);
                        loadTemplate(response[3].response);
                        loadTriggers(response[4].response);
                        loadTypeProcess(response[5].response);
                        loadProperties(response[5].response);
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    }
                });
                restClient.setBaseEndPoint('');
                restClient.executeRestClient();
            };
            isDirtyFormProcess = function () {
                var message_window
                if (formEditProcess.isDirty()) {
                    message_window = new PMUI.ui.MessageWindow({
                        id: "cancelMessageTriggers",
                        windowMessageType: 'warning',
                        width: 490,
                        title: "Edit process".translate(),
                        message: 'Are you sure you want to discard your changes?'.translate(),
                        footerItems: [
                            {
                                text: "No".translate(),
                                handler: function () {
                                    message_window.close();
                                },
                                buttonType: "error"
                            },
                            {
                                text: "Yes".translate(),
                                handler: function () {
                                    message_window.close();
                                    propertiesWindow.close();
                                },
                                buttonType: "success"
                            }
                        ]
                    });
                    message_window.open();
                    message_window.showFooter();
                } else {
                    propertiesWindow.close();
                }
            };
            saveProperties = function (data) {
                var restClient = new PMRestClient({
                    typeRequest: 'update',
                    data: data,
                    messageSuccess: "Properties saved successfully".translate(),
                    functionSuccess: function (xhr, response) {
                        propertiesWindow.close();

                        PMDesigner.project.setProjectName(data.pro_title);
                        PMDesigner.project.setDescription(data.pro_description);
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);

                        PMDesigner.project.dirty = false;
                    }
                });
                restClient.setEndpoint("process");
                restClient.executeRestClient();
            };

            propertiesWindow = new PMUI.ui.Window({
                id: "propertiesProcessWindow",
                title: "Edit process".translate(),
                width: DEFAULT_WINDOW_WIDTH,
                height: DEFAULT_WINDOW_HEIGHT,
                footerHeight: 'auto',
                bodyHeight: "auto",
                modal: true,
                buttonPanelPosition: "bottom",
                footerAlign: "right",
                onBeforeClose: function () {
                    clickClose = true;
                    isDirtyFormProcess();
                },
                buttons: [
                    {
                        id: 'outputWindowDocButtonCancel',
                        text: "Cancel".translate(),
                        buttonType: "error",
                        handler: function () {
                            clickedClose = false;
                            isDirtyFormProcess();
                        }
                    },
                    {
                        id: 'outputWindowDocButtonSave',
                        text: "Save".translate(),
                        buttonType: "success",
                        handler: function () {
                            var dataForm;
                            if (formEditProcess.isValid() && processOwner.isValid()) {
                                if ((navigator.userAgent.indexOf("MSIE") != -1) || (navigator.userAgent.indexOf("Trident") != -1)) {
                                    dataForm = getData2PMUI(formEditProcess.html);
                                } else {
                                    dataForm = formEditProcess.getData();
                                    dataForm.pro_process_owner = processOwner.get("value");
                                }
                                dataForm.pro_debug = checkDebug.controls[0].selected ? 1 : 0;
                                dataForm.pro_show_message = checkHideCase.controls[0].selected ? 1 : 0;
                                dataForm.pro_subprocess = checkSubProcess.controls[0].selected ? 1 : 0;
                                functionAssignmentUsers = function (xhr, response) {
                                };
                                saveProperties(dataForm);
                            } else {
                                if (!(processOwner.isValid()) && formEditProcess.isValid()) {
                                    formEditProcess.addItem(notification);
                                } else {
                                    formEditProcess.removeItem(notification);
                                }
                            }
                        }
                    }
                ]
            });

            processUID = new PMUI.field.TextField({
                label: "UID".translate(),
                id: "process_uid",
                name: "pro_uid",
                labelWidth: "35%",
                controlsWidth: "300px"
            });

            textTitle = new PMUI.field.TextField({
                label: "Title".translate(),
                id: 'textTitle',
                name: 'pro_title',
                labelWidth: "35%",
                placeholder: 'a text here'.translate(),
                controlsWidth: "300px",
                required: true
            });

            textDescription = new PMUI.field.TextAreaField({
                id: 'textDescription',
                name: 'pro_description',
                label: "Description".translate(),
                labelWidth: "35%",
                controlsWidth: "500px",
                rows: 150,
                style: {cssClasses: ['mafe-textarea-resize']}
            });

            processOwner = new SuggestField({
                id: 'processOwner',
                name: 'pro_owner',
                label: "Process Owner".translate(),
                required: true,
                width: 500,
                placeholder: "suggest users".translate(),
                separatingText: ["Users".translate()],
                dynamicLoad: {
                    data: [
                        {
                            key: "usr_uid",
                            label: ["usr_firstname", "usr_lastname", "(", "usr_username", ")"]
                        }
                    ],
                    keys: {
                        url: HTTP_SERVER_HOSTNAME + "/api/1.0/" + WORKSPACE,
                        accessToken: PMDesigner.project.tokens.access_token,
                        endpoints: [
                            {
                                method: "GET",
                                url: 'users'
                            }
                        ]
                    }
                }
            });

            dropCalendar = new PMUI.field.DropDownListField({
                id: "dropCalendar",
                name: "pro_calendar",
                labelWidth: "35%",
                label: "Calendar".translate(),
                controlsWidth: "300px",
                valueType: 'string',
                onChange: function (newValue, prevValue) {
                }
            });

            dropProcessCat = new PMUI.field.DropDownListField({
                id: "dropProcessCat",
                name: "pro_category",
                controlsWidth: "300px",
                labelWidth: "35%",
                label: "Process Category".translate(),
                valueType: 'string',
                onChange: function (newValue, prevValue) {
                }
            });

            dropDynaform = new PMUI.field.DropDownListField({
                id: "dropDynaform",
                name: "pro_summary_dynaform",
                controlsWidth: "300px",
                labelWidth: "35%",
                label: "Dynaform to show a case summary".translate(),
                valueType: 'string',
                onChange: function (newValue, prevValue) {
                }
            });

            dropRouting = new PMUI.field.DropDownListField({
                id: "dropRouting",
                name: "pro_derivation_screen_tpl",
                controlsWidth: "300px",
                labelWidth: "35%",
                label: "Routing Screen Template".translate(),
                valueType: 'string',
                onChange: function (newValue, prevValue) {
                }
            });

            checkDebug = new PMUI.field.CheckBoxGroupField({
                id: 'checkDebug',
                labelWidth: "35%",
                label: "Debug".translate(),
                name: "pro_debug",
                value: '1',
                controlPositioning: 'vertical',
                options: [
                    {
                        id: 'pro_debug',
                        disabled: false,
                        value: '1',
                        selected: false
                    }
                ],
                onChange: function (newVal, oldVal) {
                }
            });

            checkHideCase = new PMUI.field.CheckBoxGroupField({
                id: 'checkHideCase',
                labelWidth: "35%",
                label: "Hide the case number and the case title in the steps".translate(),
                value: '1',
                name: "pro_show_message",
                controlPositioning: 'vertical',
                options: [
                    {
                        disabled: false,
                        value: '1',
                        selected: false
                    }
                ],
                onChange: function (newVal, oldVal) {
                }
            });

            checkSubProcess = new PMUI.field.CheckBoxGroupField({
                id: 'checkSubProcess',
                labelWidth: "35%",
                label: "This a sub-process".translate(),
                value: '1',
                name: "pro_subprocess",
                controlPositioning: 'vertical',
                options: [
                    {
                        disabled: false,
                        value: '1',
                        selected: false
                    }
                ],
                onChange: function (newVal, oldVal) {
                }
            });
            dropCaseCreated = new PMUI.field.DropDownListField({
                id: "dropCaseCreated",
                name: "pro_tri_create",
                labelWidth: "35%",
                label: "Execute a trigger when a case is created".translate(),
                valueType: 'string',
                controlsWidth: "300px",
                onChange: function (newValue, prevValue) {
                }
            });

            dropCaseDeleted = new PMUI.field.DropDownListField({
                id: "dropCaseDeleted",
                name: "pro_tri_deleted",
                labelWidth: "35%",
                label: "Execute a trigger when a case is deleted".translate(),
                valueType: 'string',
                controlsWidth: "300px",
                onChange: function (newValue, prevValue) {
                }
            });

            dropCaseCancelled = new PMUI.field.DropDownListField({
                id: "dropCaseCancelled",
                name: "pro_tri_canceled",
                labelWidth: "35%",
                label: "Execute a trigger when a case is cancelled".translate(),
                valueType: 'string',
                controlsWidth: "300px",
                onChange: function (newValue, prevValue) {
                }
            });

            dropCasePaused = new PMUI.field.DropDownListField({
                id: "dropCasePaused",
                name: "pro_tri_paused",
                labelWidth: "35%",
                label: "Execute a trigger when a case is paused".translate(),
                valueType: 'string',
                controlsWidth: "300px",
                onChange: function (newValue, prevValue) {
                }
            });

            dropCaseUnpaused = new PMUI.field.DropDownListField({
                id: "dropCaseUnpaused",
                name: "pro_tri_unpaused",
                labelWidth: "35%",
                label: "Execute a trigger when a case is unpaused".translate(),
                valueType: "string",
                controlsWidth: "300px"
            });

            dropCaseReassigned = new PMUI.field.DropDownListField({
                id: "dropCaseReassigned",
                name: "pro_tri_reassigned",
                labelWidth: "35%",
                label: "Execute a trigger when a case is reassigned".translate(),
                valueType: 'string',
                controlsWidth: "300px",
                onChange: function (newValue, prevValue) {
                }
            });

            dropCaseOpen = new PMUI.field.DropDownListField({
                id: "dropCaseOpen",
                name: "pro_tri_open",
                label: "Execute a trigger when a case is opened".translate(),
                labelWidth: "35%",
                valueType: "string",
                controlsWidth: "300px",

                onChange: function (newValue, prevValue) {
                }
            });

            dropTypeProcess = new PMUI.field.DropDownListField({
                id: "dropTypeProcess",
                name: "pro_type_process",
                labelWidth: "35%",
                label: "Process Design Access: Public / Private (Owner)".translate(),
                valueType: 'string',
                controlsWidth: "300px",
                onChange: function (newValue, prevValue) {
                }
            });

            proCost = new PMUI.field.TextField({
                id: 'proCost',
                name: 'pro_cost',
                labelWidth: "35%",
                value: 0,
                placeholder: 'a cost here'.translate(),
                label: "Cost".translate(),
                valueType: 'integer',
                controlsWidth: "300px",
                required: window.enterprise === "1" ? true : false,
                style: {
                    cssProperties: {
                        float: "left"
                    }
                }
            });

            proUnitCost = new PMUI.field.TextField({
                id: 'proUnitCost',
                name: 'pro_unit_cost',
                labelWidth: "35%",
                value: '$',
                placeholder: 'a unit cost here'.translate(),
                controlsWidth: "300px",
                label: "Units".translate(),
                valueType: 'string',
                required: window.enterprise === "1" ? true : false,
                style: {
                    cssProperties: {
                        float: "left"
                    }
                }
            });

            notification = new PMUI.field.TextAnnotationField({
                id: "requiredMessage",
                name: "Message",
                textType: PMUI.field.TextAnnotationField.TEXT_TYPES.HTML,
                text: notificationText,
                text_Align: "center"
            });

            formEditProcess = new PMUI.form.Form({
                id: 'formEditProcess',
                fieldset: true,
                title: "Process Information".translate(),
                width: DEFAULT_WINDOW_WIDTH - DEFAULT_WINDOW_WIDTH * 0.10,
                items: [
                    processUID,
                    textTitle,
                    textDescription,
                    dropCalendar,
                    dropProcessCat,
                    dropDynaform,
                    dropRouting,
                    checkDebug,
                    checkHideCase,
                    checkSubProcess,
                    dropCaseCreated,
                    dropCaseDeleted,
                    dropCaseCancelled,
                    dropCasePaused,
                    dropCaseUnpaused,
                    dropCaseReassigned,
                    dropCaseOpen,
                    dropTypeProcess,
                    proCost,
                    proUnitCost
                ]
            });

            //Load Dynaforms
            loadDynaforms = function (response) {
                var i;
                dropDynaform.addOption({
                    label: "None".translate(),
                    value: "",
                    select: false
                });
                for (i = 0; i < response.length; i += 1) {
                    dropDynaform.addOption({
                        label: response[i].dyn_title,
                        value: response[i].dyn_uid,
                        select: false
                    });
                }
            };

            //Load calendar
            loadCalendar = function (response) {
                var i;
                dropCalendar.addOption({
                    label: "None".translate(),
                    value: "",
                    select: false
                });
                for (i = 0; i < response.length; i += 1) {
                    dropCalendar.addOption({
                        label: response[i].cal_name,
                        value: response[i].cal_uid,
                        select: false
                    });
                }
            };

            //Load category
            loadCategory = function (response) {
                var i;
                dropProcessCat.addOption({
                    label: "None".translate(),
                    value: "",
                    select: false
                });
                for (i = 0; i < response.length; i += 1) {
                    dropProcessCat.addOption({
                        label: response[i].cat_name,
                        value: response[i].cat_uid,
                        select: false
                    });
                }
            };

            //Load Templates
            loadTemplate = function (response) {
                var i;
                dropRouting.addOption({
                    label: "None".translate(),
                    value: "",
                    select: false
                });
                for (i = 0; i < response.length; i += 1) {
                    if (response[i].prf_filename != "alert_message.html") {
                        dropRouting.addOption({
                            label: response[i].prf_filename,
                            value: response[i].prf_filename,
                            select: false
                        });
                    }
                }
            };

            //Load triggers
            loadTriggers = function (response) {
                var i;
                dropCaseCreated.addOption({
                    label: "None".translate(),
                    value: "",
                    select: false
                });
                dropCaseDeleted.addOption({
                    label: "None".translate(),
                    value: "",
                    select: false
                });
                dropCaseCancelled.addOption({
                    label: "None".translate(),
                    value: "",
                    select: false
                });
                dropCasePaused.addOption({
                    label: "None".translate(),
                    value: "",
                    select: false
                });
                dropCaseUnpaused.addOption({
                    label: "None".translate(),
                    value: "",
                    select: false
                });
                dropCaseReassigned.addOption({
                    label: "None".translate(),
                    value: "",
                    select: false
                });

                dropCaseOpen.addOption({
                    label: "None".translate(),
                    value: "",
                    select: false
                });

                for (i = 0; i < response.length; i += 1) {
                    dropCaseCreated.addOption({
                        label: response[i].tri_title,
                        value: response[i].tri_uid,
                        select: false
                    });
                    dropCaseDeleted.addOption({
                        label: response[i].tri_title,
                        value: response[i].tri_uid,
                        select: false
                    });
                    dropCaseCancelled.addOption({
                        label: response[i].tri_title,
                        value: response[i].tri_uid,
                        select: false
                    });
                    dropCasePaused.addOption({
                        label: response[i].tri_title,
                        value: response[i].tri_uid,
                        select: false
                    });
                    dropCaseUnpaused.addOption({
                        label: response[i].tri_title,
                        value: response[i].tri_uid,
                        select: false
                    });
                    dropCaseReassigned.addOption({
                        label: response[i].tri_title,
                        value: response[i].tri_uid,
                        select: false
                    });

                    dropCaseOpen.addOption({
                        label: response[i].tri_title,
                        value: response[i].tri_uid,
                        select: false
                    });
                }
            };

            // Load type of process
            loadTypeProcess = function (response) {
                dropTypeProcess.addOption({
                    label: "Public".translate(),
                    value: "PUBLIC",
                    select: false
                });
                dropTypeProcess.addOption({
                    label: "Private".translate(),
                    value: "PRIVATE",
                    select: false
                });
            };

            // Load properties of process
            loadProperties = function (response) {
                propertiesWindow.addItem(formEditProcess);
                propertiesWindow.open();
                $(processOwner.createHTML()).insertBefore(dropCalendar.html);
                processOwner.html.find("input").blur(function () {
                    if (!(processOwner.isValid())) {
                        processOwner.showMessageRequiredExtended();
                    } else {
                        processOwner.repaint("1px solid #adafb2", "2px", "", "1px solid white");
                    }
                });
                processOwner.html.find("input").focusin(function () {
                    if (processOwner.isValid()) {
                        processOwner.repaint("1px solid #adafb2", "2px", "#000", "-webkit-focus-ring-color auto 1px");
                    }
                });
                processOwner.containerLabel.css({ width: "35%" });
                processOwner.repaint("1px solid #adafb2", "2px", "", "");
                formEditProcess.getField("pro_type_process").hideColon();
                formEditProcess.reset();
                responseProperties = response;
                processUID.setValue(response.pro_uid);
                processUID.setReadOnly(true);
                textTitle.setValue(response.pro_title);
                textDescription.setValue(response.pro_description);
                processOwner.set("value", response.pro_create_user);
                processOwner.html.find("input").val(response.pro_create_firstname + " " + response.pro_create_lastname + " " + "(" + response.pro_create_username + ")");
                dropDynaform.setValue(response.pro_summary_dynaform);
                dropCaseCancelled.setValue(response.pro_tri_canceled);
                dropCaseCreated.setValue(response.pro_tri_create);
                dropCaseDeleted.setValue(response.pro_tri_deleted);
                dropCasePaused.setValue(response.pro_tri_paused);
                dropCaseUnpaused.setValue(response.pro_tri_unpaused);
                dropCaseReassigned.setValue(response.pro_tri_reassigned);
                dropCaseOpen.setValue(response.pro_tri_open);
                dropRouting.setValue(response.pro_derivation_screen_tpl);
                dropCalendar.setValue(response.pro_calendar);
                dropProcessCat.setValue(response.pro_category);
                dropTypeProcess.setValue(response.pro_type_process);
                checkHideCase.setHeight(57)
                if (response.pro_debug == 1) {
                    checkDebug.getControls()[0].select();
                }
                if (response.pro_show_message == 1) {
                    checkHideCase.getControls()[0].select();
                }
                if (response.pro_subprocess == 1) {
                    checkSubProcess.getControls()[0].select();
                }
                proCost.setValue(response.pro_cost);
                if (response.pro_unit_cost != null && response.pro_unit_cost != '') {
                    proUnitCost.setValue(response.pro_unit_cost);
                }
            };

            getValuesProperties();

            propertiesWindow.showFooter();
            propertiesWindow.defineEvents();
            applyStyleWindowForm(propertiesWindow);
            formEditProcess.getField('pro_title').setFocus();
            formEditProcess.getField("pro_type_process").html.style.float = "left";

            dropCaseCancelled.style.addProperties({"float": "left"});
            dropCasePaused.style.addProperties({"float": "left"});
            dropCaseUnpaused.style.addProperties({"float": "left"});
            dropCaseReassigned.style.addProperties({"float": "left"});
            dropCaseOpen.style.addProperties({"float": "left"});

            $("#dropCaseCancelled,#dropCasePaused,#dropCaseReassigned,#dropTypeProcess,#dropCaseOpen").find("select:eq(0)").css("z-index", 1);

            if (window.enterprise === "1") {
                proCost.setVisible(true);
                proUnitCost.setVisible(true);
            } else {
                proCost.setVisible(false);
                proUnitCost.setVisible(false);
            }
        };

    }()
);
(function () {
    PMDesigner.caseTracker = function () {
        var caseTrackerForm,
            index,
            flagEdit = 0,
            caseTrackerWindow,
            dataCaseTracker,
            dataTree,
            conditionform,
            conditionWindows,
            disableAllItems,
            formIsDirty,
            conditionformIsDirty,
            orderDataTree,
            saveItem,
            updateItem,
            treePanelObjects,
            loadGridCaseTacker,
            editCondition,
            gridPanelObjects,
            titleTreeObjects,
            getValuesCaseTrackerObjects,
            updateCaseTrackerPropertiesAndObjects,
            loadPropertiesCaseTracker,
            titleGridObjects,
            panelLabelObjects,
            panelContainerObjects,
            panelObjects,
            applyStylesWindow,
            showObjects,
            arrayObjectDropAssignedObjects = new Array(),
            arrayObjectAvailableObjects = new Array(),
            arrayObjectStepsCaseTracker = new Array();

        disableAllItems = function () {
            caseTrackerWindow.getItems()[0].setVisible(false);
            caseTrackerWindow.getItems()[1].setVisible(false);
            caseTrackerWindow.hideFooter();
        };
        formIsDirty = function () {
            if (caseTrackerForm.isDirty() || flagEdit == 1) {
                var message_window = new PMUI.ui.MessageWindow({
                    id: "cancelMessageTriggers",
                    windowMessageType: 'warning',
                    width: 490,
                    title: "Case Tracker".translate(),
                    message: 'Are you sure you want to discard your changes?'.translate(),
                    footerItems: [
                        {
                            text: "No".translate(),
                            handler: function () {
                                message_window.close();
                            },
                            buttonType: "error"
                        },
                        {
                            text: "Yes".translate(),
                            handler: function () {
                                message_window.close();
                                caseTrackerWindow.close();
                            },
                            buttonType: "success"
                        }
                    ]
                });
                message_window.open();
                message_window.showFooter();
            } else {
                caseTrackerWindow.close();
            }
        };

        conditionformIsDirty = function () {
            if (conditionform.isDirty()) {
                var message_window = new PMUI.ui.MessageWindow({
                    id: "cancelMessageTriggers",
                    windowMessageType: 'warning',
                    width: 490,
                    title: "Case Tracker".translate(),
                    message: 'Are you sure you want to discard your changes?'.translate(),
                    footerItems: [
                        {
                            text: "No".translate(),
                            handler: function () {
                                message_window.close();
                            },
                            buttonType: "error"
                        },
                        {
                            text: "Yes".translate(),
                            handler: function () {
                                message_window.close();
                                conditionWindows.close();
                            },
                            buttonType: "success"
                        }
                    ]
                });
                message_window.open();
                message_window.showFooter();
            } else {
                conditionWindows.close();
            }

        };
        orderDataTree = function (data) {
            var items = [];
            var type = ['DYNAFORM', 'INPUT_DOCUMENT', 'OUTPUT_DOCUMENT', 'EXTERNAL_STEP'];
            var label = ['Dynaform', 'Input Document', 'OutPut Document', 'External Step'];
            for (var i = 0; i < type.length; i += 1) {
                items = [];
                for (var j = 0; j < data.length; j += 1) {
                    if (type[i] === data[j].obj_type) {
                        items.push({
                            step_type_obj: label[i].translate(),
                            obj_label: label[i].translate(),
                            obj_title: data[j]['obj_title'],
                            obj_type: data[j]['obj_type'],
                            obj_uid: data[j]['obj_uid']
                        });
                    }
                }
                if (items.length === 0) {
                    dataTree.push({
                        obj_title: label[i].translate(),
                        items: []
                    });
                } else {
                    dataTree.push({
                        obj_title: label[i].translate(),
                        items: items
                    });
                }
            }
        };

        //Properties
        loadPropertiesCaseTracker = function () {
            dataCaseTracker = [];
            restClient = new PMRestClient({
                endpoint: 'case-tracker/property',
                typeRequest: 'get',
                functionSuccess: function (xhr, response) {
                    dataCaseTracker = response;
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            });
            restClient.executeRestClient();
        };

        updateCaseTrackerPropertiesAndObjects = function (data) {
            //save steps Objects Case Tracker
            var i, j;
            index = 0;
            for (i = 0; i < gridPanelObjects.getItems().length; i += 1) {
                idObject = (typeof gridPanelObjects.getItems()[i].getData().cto_uid_obj != 'undefined') ? gridPanelObjects.getItems()[i].getData().cto_uid_obj : gridPanelObjects.getItems()[i].getData().obj_uid;
                index = arrayObjectStepsCaseTracker.indexOf(idObject);
                if (index <= -1) {
                    saveItem(gridPanelObjects.getItems()[i]);
                } else {
                    updateItem(gridPanelObjects.getItems()[i], i);
                }
            }
            ;
            for (i = 0; i < arrayObjectAvailableObjects.length; i += 1) {
                for (j = 0; j < arrayObjectDropAssignedObjects.length; j += 1) {
                    index = (arrayObjectAvailableObjects[i] == arrayObjectDropAssignedObjects[j].cto_uid_obj) ? 0 : 1;
                    if (index == 0) {
                        restClient = new PMRestClient({
                            typeRequest: 'post',
                            multipart: true,
                            data: {
                                "calls": [
                                    {
                                        "url": 'case-tracker/object/' + arrayObjectDropAssignedObjects[j].cto_uid,
                                        "method": 'DELETE'
                                    }
                                ]
                            },
                            functionSuccess: function (xhr, response) {
                            },
                            functionFailure: function (xhr, response) {
                            }
                        });
                        restClient.executeRestClient();
                        break;
                    }
                }
            }

            //save Properties Case Tracker
            data ['map_type'] = data ['map_type'] == '["1"]' ? "PROCESSMAP" : "NONE";
            data ['routing_history'] = data ['routing_history'] == '["1"]' ? 1 : 0;
            data ['message_history'] = data ['message_history'] == '["1"]' ? 1 : 0;
            restClient = new PMRestClient({
                endpoint: 'case-tracker/property',
                typeRequest: 'update',
                data: data,
                functionSuccess: function (xhr, response) {
                    caseTrackerWindow.close();
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                },
                messageError: "There are problems updating the Case Tracker, please try again.".translate(),
                messageSuccess: 'Case Tracker updated successfully'.translate(),
                flashContainer: document.body
            });
            restClient.executeRestClient();
        };

        caseTrackerForm = new PMUI.form.Form({
            id: 'caseTrackerForm',
            fieldset: true,
            title: "",
            width: DEFAULT_WINDOW_WIDTH - 70,
            height: 30,
            layout: "hbox",
            items: [
                {
                    pmType: "annotation",
                    text: "Display :".translate(),
                    id: "DisplayMessage",
                    name: "DisplayMessage"
                },
                {
                    id: 'map_type',
                    pmType: 'checkbox',
                    labelVisible: false,
                    options: [
                        {
                            label: 'Processmap'.translate(),
                            value: '1'
                        }
                    ]
                },
                {
                    id: 'routing_history',
                    pmType: 'checkbox',
                    labelVisible: false,
                    options: [
                        {
                            label: 'Routing History'.translate(),
                            value: '1'
                        }
                    ]
                },
                {
                    id: 'message_history',
                    pmType: 'checkbox',
                    labelVisible: false,
                    options: [
                        {
                            id: 'message_history',
                            label: 'Messages'.translate(),
                            value: '1'
                        }
                    ]
                }
            ],
            style: {
                cssProperties: {
                    'margin-bottom': '70px'
                }
            }
        });

        //objects
        loadGridCaseTacker = function (data) {
            var i;
            for (i = 0; i < data.length; i += 1) {
                switch (data[i]['cto_type_obj']) {
                    case 'DYNAFORM':
                        label = 'Dynaform'.translate();
                        break;
                    case 'INPUT_DOCUMENT':
                        label = 'Input Document'.translate();
                        break;
                    case 'OUTPUT_DOCUMENT':
                        label = 'OutPut Document'.translate();
                        break;
                    case 'EXTERNAL_STEP':
                        label = 'External Step'.translate();
                        break;
                    default:
                        label = data[i]['tri_type'];
                        break;
                }
                data[i]['obj_label'] = label;
            }
            gridPanelObjects.setDataItems(data);
        };

        getValuesCaseTrackerObjects = function () {
            restClient = new PMRestClient({
                typeRequest: 'post',
                multipart: true,
                data: {
                    "calls": [
                        {
                            "url": "case-tracker/available-objects",
                            "method": 'GET'
                        },
                        {
                            "url": "case-tracker/objects",
                            "method": 'GET'
                        }
                    ]
                },
                functionSuccess: function (xhr, response) {
                    dataTree = [];
                    orderDataTree(response[0].response);
                    treePanelObjects.setDataItems(dataTree);
                    loadGridCaseTacker(response[1].response);
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            });
            restClient.executeRestClient();
        };
        saveItem = function (rowStep) {
            rowStep = rowStep.getData();
            data = {
                "cto_type_obj": rowStep.obj_type,
                "cto_uid_obj": rowStep.obj_uid,
                "cto_condition": (typeof rowStep.cto_condition != 'undefined') ? rowStep.cto_condition : '',
                "cto_position": rowStep.cto_position
            };
            restClient = new PMRestClient({
                endpoint: 'case-tracker/object',
                typeRequest: 'post',
                data: data,
                functionSuccess: function (xhr, response) {
                    data = response;
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                },
                messageError: "There are problems saved, please try again.".translate()
            });
            restClient.executeRestClient();
            return data;
        };

        updateItem = function (rowStep, i) {
            rowStep = rowStep.getData();
            rowStep.cto_position = i + 1;
            restClient = new PMRestClient({
                typeRequest: 'post',
                multipart: true,
                data: {
                    "calls": [
                        {
                            "url": 'case-tracker/object/' + rowStep.cto_uid,
                            "method": 'PUT',
                            "data": rowStep
                        }
                    ]
                },
                functionSuccess: function (xhr, response) {
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            });
            restClient.executeRestClient();
        };

        treePanelObjects = new PMUI.panel.TreePanel({
            id: 'treePanelObjects',
            proportion: 0.5,
            filterable: true,
            filterPlaceholder: 'Search ...'.translate(),
            emptyMessage: 'No records found'.translate(),
            style: {cssClasses: ['itemsSteps']},
            nodeDefaultSettings: {
                behavior: "drag",
                labelDataBind: 'obj_title',
                itemsDataBind: 'items',
                collapsed: false,
                childrenDefaultSettings: {
                    labelDataBind: 'obj_title',
                    autoBind: true
                },
                autoBind: true
            }
        });

        editCondition = function () {
            var visible, dataEdit;

            conditionform = new PMUI.form.Form({
                id: 'conditionform',
                title: "",
                fieldset: true,
                visibleHeader: false,
                width: 500,
                items: [
                    new CriteriaField({
                        id: 'cto_condition',
                        pmType: 'textarea',
                        name: 'cto_condition',
                        valueType: 'string',
                        label: 'Condition'.translate(),
                        placeholder: 'Insert a condition'.translate(),
                        rows: 200,
                        width: 250,
                        controlsWidth: 285,
                        renderType: 'textarea'
                    })
                ]
            });

            if (rowStep != '' && rowStep != undefined) {
                dataEdit = conditionform.getFields();
                dataEdit[0].setValue(rowStep['cto_condition']);
            }

            conditionWindows = new PMUI.ui.Window({
                id: 'conditionWindows',
                title: 'Condition'.translate(),
                width: 500,
                height: 'auto',
                footerHeight: 'auto',
                bodyHeight: 'auto',
                modal: true,
                buttonPanelPosition: 'bottom',
                footerAlign: "right",
                onBeforeClose: conditionformIsDirty,
                buttons: [
                    {
                        id: 'conditionObjectWindowButtonClose',
                        text: "Cancel".translate(),
                        handler: conditionformIsDirty,
                        buttonType: 'error'
                    },
                    {
                        id: 'conditionObjectWindowButtonSave',
                        text: "Save".translate(),
                        handler: function () {
                            var i, item;
                            if (conditionform.isValid()) {
                                idrowStep = (typeof rowStep.cto_uid_obj != 'undefined') ? rowStep.cto_uid_obj : rowStep.obj_uid;
                                for (i = 0; i < gridPanelObjects.getItems().length; i += 1) {
                                    item = gridPanelObjects.getItems()[i].getData();
                                    idObj = (typeof item.cto_uid_obj != 'undefined') ? item.cto_uid_obj : item.obj_uid;
                                    if (idObj == idrowStep) {
                                        rowStep.cto_condition = conditionform.getData()['cto_condition'];
                                        gridPanelObjects.getItems()[i].setData(rowStep);
                                        break;
                                    }
                                }
                                conditionWindows.close();
                            }
                        },
                        buttonType: 'success'
                    }
                ]
            });
            conditionWindows.addItem(conditionform);
            conditionWindows.open();
            conditionWindows.showFooter();
            applyStyleWindowForm(conditionWindows);
            conditionWindows.defineEvents();
            conditionWindows.footer.html.style.textAlign = 'right';
            conditionform.setFocus();
            style = $('#cto_condition .pmui-field-label').attr("style");
            style = style + ' float: left;';
            $('#cto_condition .pmui-field-label').attr("style", style);
        };

        gridPanelObjects = new PMUI.grid.GridPanel({
            id: 'gridPanelObjects',
            proportion: 1.5,
            visibleFooter: false,
            filterable: false,
            width: '640px',
            style: {cssClasses: ['itemsSteps']},
            filterPlaceholder: 'Search ...'.translate(),
            emptyMessage: 'No records found'.translate(),
            nextLabel: 'Next'.translate(),
            previousLabel: 'Previous'.translate(),
            customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
                return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
            },
            behavior: 'dragdropsort',
            columns: [
                {
                    title: 'Title'.translate(),
                    dataType: 'string',
                    width: 330,
                    alignment: "left",
                    columnData: "obj_title",
                    sortable: false,
                    alignmentCell: 'left'
                },
                {
                    title: 'Type'.translate(),
                    dataType: 'string',
                    width: 120,
                    alignment: "left",
                    columnData: "obj_label",
                    sortable: false,
                    alignmentCell: 'left'
                },
                {
                    id: 'gridPanelObjectsButtonProperties',
                    title: '',
                    dataType: 'button',
                    buttonLabel: "Condition".translate(),
                    iconPosition: "center",
                    buttonStyle: {cssClasses: ['mafe-button-editstep']},
                    buttonTooltip: 'Edit Properties'.translate(),
                    onButtonClick: function (row, grid) {
                        rowStep = row.getData();
                        editCondition();
                    }
                },
                {
                    id: 'gridPanelObjectsButtonDelete',
                    title: '',
                    dataType: 'button',
                    buttonLabel: '',
                    buttonStyle: {cssClasses: ['mafe-button-delete-assign']},
                    buttonTooltip: 'Remove Object'.translate(),
                    onButtonClick: function (row, grid) {
                        flagEdit = 1;
                        rowStep = row.getData();
                        index = (row.getData().cto_uid_obj != 'undefined') ? arrayObjectStepsCaseTracker.indexOf(row.getData().cto_uid_obj) : arrayObjectStepsCaseTracker.indexOf(row.getData().obj_uid);
                        if (index > -1) {
                            arrayObjectAvailableObjects.push(row.getData().cto_uid_obj);
                            arrayObjectStepsCaseTracker.splice(index, 1);
                        }
                        objType = (typeof row.getData().cto_type_obj != 'undefined') ? row.getData().cto_type_obj : row.getData().obj_type;
                        grid.removeItem(row);
                        switch (objType) {
                            case 'DYNAFORM':
                                treePanelObjects.getItems()[0].addItem(row);
                                break;
                            case 'INPUT_DOCUMENT':
                                treePanelObjects.getItems()[1].addItem(row);
                                break;
                            case 'OUTPUT_DOCUMENT':
                                treePanelObjects.getItems()[2].addItem(row);
                                break;
                            case 'EXTERNAL_STEP':
                                treePanelObjects.getItems()[3].addItem(row);
                                break;
                            default:
                                break;
                        }
                    }
                }
            ],
            onDrop: function (grid, item, index) {
                flagEdit = 1;
                if (item.data.customKeys.obj_uid === '') {
                    return false;
                }
                rowStep = item.getData();
                rowStep.cto_position = index + 1;
                item.setData(rowStep);

                index = (typeof item.getData().cto_uid_obj != 'undefined') ? arrayObjectAvailableObjects.indexOf(item.getData().cto_uid_obj) : arrayObjectAvailableObjects.indexOf(item.getData().obj_uid);

                if (index > -1) {
                    itemPush = (typeof item.getData().cto_uid_obj != 'undefined') ? item.getData().cto_uid_obj : item.getData().obj_uid;
                    arrayObjectStepsCaseTracker.push(itemPush);
                    arrayObjectAvailableObjects.splice(index, 1);
                }
            },
            onSort: function (grid, item, index) {
                rowStep = item.getData();
            }
        });

        titleTreeObjects = new PMUI.ui.TextLabel({
            id: "titleTreeObjects",
            textMode: 'plain',
            text: 'Available Objects'.translate(),
            style: {
                cssClasses: [
                    'mafe-designer-steps-tree'
                ]
            }
        });

        titleGridObjects = new PMUI.ui.TextLabel({
            id: "titleGridObjects",
            textMode: 'plain',
            text: 'Assigned objects'.translate(),
            style: {
                cssClasses: [
                    'mafe-designer-stesp-grid'
                ]
            }
        });

        panelLabelObjects = new PMUI.core.Panel({
            width: DEFAULT_WINDOW_WIDTH * 0.94,
            fieldset: true,
            items: [
                titleTreeObjects,
                titleGridObjects
            ],
            style: {
                cssProperties: {
                    'margin-bottom': 4,
                    'margin-top': 4,
                    'margin-left': 4
                }
            },
            layout: "hbox"
        });

        panelContainerObjects = new PMUI.core.Panel({
            width: DEFAULT_WINDOW_WIDTH * 0.94,
            height: 320,
            fieldset: true,
            items: [
                treePanelObjects,
                gridPanelObjects
            ],
            layout: "hbox"
        });

        panelObjects = new PMUI.core.Panel({
            width: DEFAULT_WINDOW_WIDTH * 0.94,
            height: DEFAULT_WINDOW_HEIGHT * 0.70,
            fieldset: true,
            items: [
                panelLabelObjects,
                panelContainerObjects
            ],
            layout: "vbox"
        });

        caseTrackerWindow = new PMUI.ui.Window({
            id: 'caseTrackerWindow',
            title: "Case Tracker".translate(),
            width: DEFAULT_WINDOW_WIDTH,
            height: DEFAULT_WINDOW_HEIGHT,
            footerHeight: 'auto',
            bodyHeight: 'auto',
            modal: true,
            buttonPanelPosition: "bottom",
            onBeforeClose: formIsDirty,
            visibleFooter: false,
            footerAling: "right",
            footerItems: [{
                id: 'btnCloseCaseTracker',
                text: 'Cancel'.translate(),
                buttonType: "error",
                handler: formIsDirty
            },
                {
                    id: 'btnSaveCaseTracker',
                    text: "Save".translate(),
                    buttonType: "success",
                    handler: function () {
                        if (caseTrackerForm.isValid()) {
                            data = caseTrackerForm.getData();
                            updateCaseTrackerPropertiesAndObjects(data);
                        }
                    }
                }
            ],
            spaceButtons: 30
        });

        caseTrackerWindow.addItem(caseTrackerForm);
        caseTrackerWindow.addItem(panelObjects);
        caseTrackerWindow.open();
        caseTrackerWindow.showFooter();
        applyStyleWindowForm(caseTrackerWindow);
        caseTrackerWindow.defineEvents();
        caseTrackerWindow.footer.html.style.textAlign = 'right';
        caseTrackerForm.setFocus();

        applyStylesWindow = function () {
            $('#gridPanelObjects .pmui-gridpanel-tableContainer').css({'height': 'auto'});
            $('#caseTrackerForm :eq(2)').css({'padding': '0px 10px 0px 10px'});
            $('#caseTrackerForm :eq(0)').remove();
            items = caseTrackerWindow.getItems()[0].getItems();
            $(items[1].getHTML()).find('table').css('border', 'none');
            $(items[2].getHTML()).find('table').css('border', 'none');
            $(items[3].getHTML()).find('table').css('border', 'none');
        };

        showObjects = function () {
            disableAllItems();

            loadPropertiesCaseTracker();
            caseTrackerWindow.getItems()[0].setVisible(true);
            caseTrackerWindow.showFooter();
            caseTrackerWindow.setTitle("Case Tracker Properties".translate());

            if (dataCaseTracker != '') {
                var dataEdit = caseTrackerForm.getFields();
                dataEdit[1].setValue((dataCaseTracker['map_type'] == 'PROCESSMAP') ? '["1"]' : '[]');
                dataEdit[2].setValue((dataCaseTracker['routing_history'] == 1) ? '["1"]' : '[]');
                dataEdit[3].setValue((dataCaseTracker['message_history'] == 1) ? '["1"]' : '[]');
            }


            getValuesCaseTrackerObjects();
            caseTrackerWindow.getItems()[1].setVisible(true);
            caseTrackerWindow.setTitle("Case Tracker".translate());

            caseTrackerWindow.body.style.overflow = 'initial';

            gridPanelObjects.style.addProperties({overflow: 'auto'});
            gridPanelObjects.style.addProperties({float: 'right'});
            gridPanelObjects.setWidth(640);
            gridPanelObjects.setHeight(270);
            applyStylesWindow();
            treePanelObjects.style.addProperties({float: 'left'});
            treePanelObjects.style.addProperties({overflow: 'auto'});
            treePanelObjects.setWidth(200);
            treePanelObjects.setHeight(270);

            applyStyleTreePanel(treePanelObjects);

            panelLabelObjects.setHeight(15);
            caseTrackerWindow.defineEvents();

            caseTrackerWindow.setBodyPadding(5);

            panelObjects.style.addProperties({marginLeft: '15px'});
            for (var i = 0; i < gridPanelObjects.getItems().length; i += 1) {
                arrayObjectStepsCaseTracker[i] = gridPanelObjects.getItems()[i].getData().cto_uid_obj;
                arrayObjectDropAssignedObjects[i] = gridPanelObjects.getItems()[i].getData();
            }
        };
        showObjects();
    };

    PMDesigner.caseTracker.showObjects = function () {
        PMDesigner.caseTracker();
    };

}());
PMDesigner.gatewayProperties = function (gateway) {
    if (gateway.getGatewayType() !== "PARALLEL") {
        PMDesigner.RoutingRule(gateway);
    }
};

PMDesigner.RoutingRule = function (shape) {
    var formRoutingRule,
        formPanelSelected,
        arrayShapeIdRemoved = [],
        buttonAdd,
        windowConnections,
        warningMessageWindowDelete,
        warningMessageWindowDirty,
        containerLabels,
        deleteButton,
        buttonSave,
        buttonCancel,
        labelNextTask,
        labelCondition,
        typeShapeValueText,
        dataRouteGroup = [],
        arrayElementName = [],
        availableShapes  = [];

    warningMessageWindowDelete = new PMUI.ui.MessageWindow({
        id: 'warningMessageWindowDelete',
        windowMessageType: 'warning',
        width: 490,
        bodyHeight: 'auto',
        title: 'Routing Rule'.translate(),
        message: 'Do you want to delete this routing rule?'.translate(),
        footerItems: [
            {
                id: 'warningMessageWindowDeleteButtonNo',
                text: 'No'.translate(),
                visible: true,
                handler: function () {
                    warningMessageWindowDelete.close();
                },
                buttonType: "error"
            },
            {
                id: 'warningMessageWindowDeleteButtonYes',
                text: 'Yes'.translate(),
                visible: true,
                handler: function () {
                    deleteRow();
                    warningMessageWindowDelete.close();
                },
                buttonType: "success"
            }
        ]
    });
    warningMessageWindowDirty = new PMUI.ui.MessageWindow({
        id: 'warningMessageWindowDirty',
        windowMessageType: 'warning',
        width: 490,
        bodyHeight: 'auto',
        title: 'Routing Rule'.translate(),
        message: 'Are you sure you want to discard your changes?'.translate(),
        footerItems: [
            {
                id: 'warningMessageWindowDirtyButtonNo',
                text: 'No'.translate(),
                visible: true,
                handler: function () {
                    warningMessageWindowDirty.close();
                },
                buttonType: "error"
            }, {
                id: 'warningMessageWindowDirtyButtonYes',
                text: 'Yes'.translate(),
                visible: true,
                handler: function () {
                    warningMessageWindowDirty.close();
                    windowConnections.close();
                },
                buttonType: "success"
            }
        ]
    });

    buttonAdd = new PMUI.ui.Button({
        id: 'routingRuleButtonAdd',
        text: 'Add Routing Rule'.translate(),
        style: {
            cssProperties: {
                marginLeft: '50px',
                marginTop: '10px',
                marginBottom: '10px',
                padding: "5px"
            }
        },
        buttonType: 'success',
        handler: function () {
            // to add a new row
            addRow();
            enableSorting();
        }
    });
    buttonSave = new PMUI.ui.Button({
        id: 'windowConnectionsButtonSave',
        text: 'Save'.translate(),
        handler: function () {
            //validate routing rules form
            if (isValidRoutingRules()) {
                //remove all flows an get points
                var allPoints = removeConnectionsIntoCanvas();
                saveConnections(allPoints);
            }

        },
        buttonType: 'success'
    });
    buttonCancel = new PMUI.ui.Button({
        id: 'windowConnectionsButtonCancel',
        text: 'Cancel'.translate(),
        buttonType: 'error',
        handler: function () {
            if (formRoutingRule.isDirty()) {
                warningMessageWindowDirty.open();
                warningMessageWindowDirty.showFooter();
            } else {
                windowConnections.close();
            }
        }
    });

    labelNextTask = new PMUI.ui.TextLabel({
        text: 'Next Task'.translate()
    });
    labelNextTask.setWidth(382);
    labelNextTask.style.addProperties({padding: '5px 0px 0px 50px'});

    labelCondition = new PMUI.ui.TextLabel({
        text: 'Condition'.translate()
    });
    labelCondition.setWidth(410);
    labelCondition.style.addProperties({padding: '5px 0px 0px 5px'});
    labelCondition.style.addProperties({marginLeft: '31%'});

    containerLabels = new PMUI.core.Panel({
        layout: 'hbox',
        width: 'auto',
        height: 29,
        style: {
            cssProperties: {
                'border-bottom': '1px solid #c0c0c0'
            }
        }
    });
    containerLabels.addItem(labelNextTask);
    containerLabels.addItem(labelCondition);
    containerLabels.style.addProperties({'border-bottom': '1px solid #e7e7e7'});

    formRoutingRule = new PMUI.form.Form({
        id: 'formRoutingRule',
        visibleHeader: false,
        width: DEFAULT_WINDOW_WIDTH - 60,
        height: 'auto',
        items: [],
        style: {
            cssProperties: {
                'margin-left': '35px'
            }
        }
    });
    formRoutingRule.hideFooter();

    windowConnections = new PMUI.ui.Window({
        id: 'windowConnections',
        title: 'Routing Rule'.translate(),
        height: DEFAULT_WINDOW_HEIGHT,
        width: DEFAULT_WINDOW_WIDTH,
        footerAlign: 'right',
        buttonPanelPosition: 'bottom',
        items: [
            buttonAdd
        ],
        buttons: [
            buttonCancel,
            buttonSave
        ]
    });

    typeShapeValueText = (shape.gat_type === 'EXCLUSIVE') ? 'EXCLUSIVE'.translate() : 'INCLUSIVE'.translate();
    windowConnections.setTitle('Routing Rule'.translate() + ' - ' + typeShapeValueText);
    windowConnections.showFooter();
    windowConnections.addItem(containerLabels);
    if (formRoutingRule.dirty === null) {
        formRoutingRule.dirty = false;
    }
    windowConnections.addItem(formRoutingRule);
    windowConnections.open();

    loadConnections();
    enableSorting();
    /**
     * add a new row
     * @returns {PMUI.form.FormPanel|*}
     */
    function addRow() {
        var dropDownControl,
            criteriaField,
            deleteButton,
            newRow,
            max,
            i;
        newRow = new PMUI.form.FormPanel({
            layout: 'hbox'
        });

        dropDownControl = new PMUI.field.DropDownListField({
            id: 'dropdownNextTask',
            name: 'act_name',
            valueType: 'string',
            label: 'Next Task'.translate(),
            labelPosition: 'top',
            labelVisible: false,
            value: '',
            readOnly: false,
            controlsWidth: 360,
            proportion: 0.9,
            style: {
                cssProperties: {
                    'vertical-align': 'top'
                }
            }
        });

        criteriaField = new CriteriaField({
            id: 'textCondition',
            pmType: 'text',
            renderType: 'textarea',
            name: 'flo_condition',
            valueType: 'string',
            label: 'Condition'.translate(),
            labelPosition: 'top',
            labelVisible: false,
            controlsWidth: 345,
            required: false,
            style: {
                cssProperties: {
                    'vertical-align': 'top'
                }
            }
        });
        deleteButton = new PMUI.field.ButtonField({
            id: 'buttonDelete',
            value: 'Delete'.translate(),
            handler: function (e, a) {
                formPanelSelected = this.getParent();
                warningMessageWindowDelete.open();
                warningMessageWindowDelete.dom.titleContainer.style.height = '17px';
                warningMessageWindowDelete.showFooter();
                enableSorting();
            },
            name: 'delete',
            labelVisible: false,
            buttonAling: 'left',
            controlsWidth: 60,
            proportion: 0.1,
            style: {
                cssProperties: {
                    'vertical-align': 'top'
                }
            }
        });

        newRow.addItem(dropDownControl);
        newRow.addItem(criteriaField);
        newRow.addItem(deleteButton);
        loadOptions(dropDownControl);
        //apply styles;
        if (availableShapes && availableShapes.length > 0) {
            deleteButton.controls[0].button.setButtonType('error');
            $(deleteButton.getHTML()).find("a").css({
                padding: "5px"
            });
            formRoutingRule.addItem(newRow);
            for (i = 0, max = formRoutingRule.getItems().length; i < max; i += 1) {
                formRoutingRule.getItems()[i].style.addProperties({'padding': 'initial'});
            }
            dropDownControl.dom.labelTextContainer.style.display = 'none';
            criteriaField.dom.labelTextContainer.style.display = 'none';
            criteriaField.setValue(true);
        } else {
            PMDesigner.msgFlash('There are no items.'.translate(), windowConnections.footer, 'error');
        }
        return newRow;
    }

    function loadOptions(dropdown) {
        var i,
            customShapes,
            element,
            nameGroup,
            evnLabelMap;
        //Important! Any changes to synchronize the assessment of the condition
        //of the functions: countActivities and loadActivities
        dropdown.clearOptions();
        dropdown.setUID = function (uid) {
            this.uid = uid;
        };
        dropdown.getUID = function () {
            return this.uid;
        };
        dropdown.addOptionGroup({
            label: 'Task'.translate(),
            selected: true,
            options: []
        });
        dropdown.addOptionGroup({
            label: 'Sub-process'.translate(),
            options: []
        });
        dropdown.addOptionGroup({
            label: 'Event'.translate(),
            options: []
        });
        dropdown.addOptionGroup({
            label: 'Gateway'.translate(),
            options: []
        });
        customShapes = PMUI.getActiveCanvas().getCustomShapes();

        for (i = 0; i < customShapes.getSize(); i += 1) {
            element = customShapes.get(i);
            // verify pool and participant
            if (element.getType() !== 'PMParticipant' && element.getType() !== 'PMPool') {
                //itself verify and same parent
                if (shape.getID() !== element.getID()
                    && element.businessObject
                    && shape.businessObject
                    && shape.businessObject.elem.$parent
                    && element.businessObject.elem.$parent
                    && element.businessObject.elem.$parent.id === shape.businessObject.elem.$parent.id) {
                    switch (element.type) {
                        case 'PMActivity':
                            nameGroup = element.act_type === 'TASK' ? 'Task'.translate() : 'Sub-process'.translate();
                            dropdown.addOption({
                                value: element.act_uid,
                                label: element.act_name
                            }, nameGroup);
                            arrayElementName[element.act_uid] = element.act_name;
                            availableShapes.push(element);
                            break;
                        case 'PMEvent':
                            evnLabelMap = {
                                'END': 'End Event'.translate(),
                                'INTERMEDIATE': 'Intermediate Event'.translate()
                            };

                            if (element.evn_type !== 'START') {
                                dropdown.addOption({
                                    value: element.evn_uid,
                                    label: element.evn_name || evnLabelMap[element.evn_type]
                                }, 'Event'.translate());

                                arrayElementName[element.evn_uid] = element.evn_name || evnLabelMap[element.evn_type];
                                availableShapes.push(element);
                            }
                            break;
                        case 'PMGateway':
                            dropdown.addOption({
                                value: element.gat_uid,
                                label: element.gat_name || 'Gateway'.translate()
                            }, 'Gateway');
                            arrayElementName[element.gat_uid] = element.gat_name || 'Gateway'.translate();
                            availableShapes.push(element);
                            break;
                    }
                }
            }
        }
    }

    function removeConnectionsIntoCanvas() {
        var shapeDest, connection, dt, allPoints = {}, i, j;
        for (j = 0; j < arrayShapeIdRemoved.length; j += 1) {
            shapeDest = PMUI.getActiveCanvas().getCustomShapes().find('id', arrayShapeIdRemoved[j]);
            dt = shape.getPorts().asArray();
            for (i = 0; i < dt.length; i += 1) {
                connection = dt[i].getConnection();
                if (shape.getID() === connection.getSrcPort().getParent().getID() &&
                    shapeDest.getID() === connection.getDestPort().getParent().getID()) {
                    //caching points
                    allPoints[shapeDest.getID()] = getPoints(connection);
                    removeConnection(connection);

                    break;
                }
            }
        }
        arrayShapeIdRemoved = [];
        return allPoints;
    }

    /**
     * to remove a individual connection and getting points
     * @param connection
     */
    function removeConnection(connection) {
        PMUI.getActiveCanvas().emptyCurrentSelection();
        PMUI.getActiveCanvas().setCurrentConnection(connection);
        PMUI.getActiveCanvas().executeCommandDelete();
        connection.saveAndDestroy();
        PMUI.getActiveCanvas().removeConnection(connection);
    }

    /**
     * gets conenctions inital and final points
     * @param connection
     */
    function getPoints(connection) {
        var result = [];
        result[0] = connection.points[0];
        result[1] = connection.points[connection.points.length - 1];
        return result;
    }


    function editAllConnections(connArray) {
        var i,
            elem,
            canvas = PMUI.getActiveCanvas();
        for(i = 0; i < connArray.length; i += 1) {
            elem = connArray[i];
            if (elem && elem.connection) {
                elem.oldShapeDest.removePort(elem.connection.destPort);
                elem.shapeDest.addPort(elem.connection.destPort, 100, 100,
                    false, elem.connection.srcPort);

                elem.connection.lineSegments.clear();
                canvas.commandStack.add(new PMUI.command.CommandConnect(elem.connection));
                elem.connection.connect();
                canvas.triggerPortChangeEvent(elem.connection.destPort);
            }
        }
    }


    function createEndShape() {
        var customShape, canvas = PMUI.getActiveCanvas(), command, x, y;
        customShape = canvas.shapeFactory('END');

        x = shape.getX() + shape.getWidth();
        y = shape.getY() + shape.getHeight() + 20;

        canvas.addElement(customShape, x, y,
            customShape.topLeftOnCreation);

        //since it is a new element in the designer, we triggered the
        //custom on create element event
        canvas.updatedElement = customShape;

        // create the command for this new shape
        command = new PMUI.command.CommandCreate(customShape);
        canvas.commandStack.add(command);
        command.execute();
        return customShape;
    }

    /**
     * new method to validate routing rules
     * @returns {boolean}
     */
    function isValidRoutingRules() {
        var result = true,
            arrayAux = [],
            i,
            max,
            id,
            dt = formRoutingRule.getItems();
        if (!formRoutingRule.isValid()) {
            result = false;
            return;
        }

        for (i = 0, max = dt.length; i < max; i += 1) {
            id = dt[i].getField("act_name").getValue();
            if (typeof(arrayAux[id]) === "undefined") {
                arrayAux[id] = "1";
            } else {
                result = false;
                PMDesigner.msgWinError("The routing rule to \"{0}\" already exists".translate([arrayElementName[id]]));
                return;
            }
        }
        return result;
    }

    function saveConnections(allPoints) {
        var dt = formRoutingRule.getItems(),
            id,
            i,
            oldId,
            shapeDest,
            oldShapeDest,
            connection,
            newConnection,
            dataRouteAll,
            dataRoute,
            restClient,
            newPoints,
            conectionsArray = [];


        for (i = 0; i < dt.length; i += 1) {
            id = dt[i].getField('act_name').getValue();
            oldId = dt[i].getField('act_name').getUID();
            if (id !== '0') {
                dataRouteGroup.push(id);
                if (dt[i].getField('flo_condition').getValue() == "") {
                    dt[i].getField('flo_condition').setValue(true);
                }
                if (oldId === undefined) {
                    shapeDest = PMUI.getActiveCanvas().getCustomShapes().find('id', id);
                    //getting current connection

                    newPoints = allPoints[id];

                    newConnection = createConnection(shape, shapeDest, newPoints);
                    newConnection.setFlowCondition(dt[i].getField('flo_condition').getValue());
                }
                if (oldId !== undefined && id === oldId) {
                    shapeDest = PMUI.getActiveCanvas().getCustomShapes().find('id', id);
                    connection = isConnection(shape, shapeDest);

                    if (typeof(connection) != "object") {
                        connection = createConnection(shape, shapeDest);
                    }

                    connection.setFlowCondition(dt[i].getField('flo_condition').getValue());
                }
                if (oldId !== undefined && id !== oldId) {
                    shapeDest = PMUI.getActiveCanvas().getCustomShapes().find('id', id);
                    oldShapeDest = PMUI.getActiveCanvas().getCustomShapes().find('id', oldId);
                    connection = isConnection(shape, oldShapeDest);

                    if (typeof(connection) != "object") {
                        connection = createConnection(shape, shapeDest);
                    }

                    connection.setFlowCondition(dt[i].getField('flo_condition').getValue());

                    if (typeof(connection) != "object") {
                        connection = createConnection(shape, shapeDest);
                    }

                    connection.setFlowCondition(dt[i].getField('flo_condition').getValue());

                    conectionsArray.push({
                        "connection" : isConnection(shape, oldShapeDest) || null,
                        "shapeDest": shapeDest,
                        "oldShapeDest": oldShapeDest
                    });
                }
            } else {
                var customShape = createEndShape();
                createConnection(shape, customShape);
            }
        }
        editAllConnections(conectionsArray);

        /*update routing order*/
        dataRouteAll = [];
        for (i = 0; i < dataRouteGroup.length; i += 1) {
            dataRoute = {
                'rou_case': parseInt(i) + 1,
                'rou_next_task': dataRouteGroup[i],
                'pro_uid': PMDesigner.project.id
            };
            dataRouteAll.push(dataRoute);
        }
        restClient = new PMRestClient({
            endpoint: 'update-route-order',
            typeRequest: 'update',
            data: dataRouteAll,
            functionSuccess: function () {
                PMDesigner.msgFlash('Saved correctly'.translate(), document.body, 'success', 3000, 5);
            },
            functionFailure: function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
                PMDesigner.msgFlash('There are problems updating the routing rule, please try again.'.translate(), document.body, 'error', 3000, 5);
            }
        });
        restClient.executeRestClient();
        /*end update routing order*/

        windowConnections.close();
        PMDesigner.msgFlash('Saved correctly'.translate(), document.body);
        PMDesigner.project.dirty = true;
        PMDesigner.project.setDirty(true);
    }

    function createConnection(sourceShape, shape, points) {
        var sourcePort, endPort, connection, canvas = PMUI.getActiveCanvas(), points;
        sourcePort = new PMUI.draw.Port({
            width: 10,
            height: 10
        });
        endPort = new PMUI.draw.Port({
            width: 10,
            height: 10
        });
        if (!points) {
            points = findBestPorts(sourceShape, shape);
        }
        sourceShape.addPort(sourcePort, points[0].x - sourceShape.getZoomX(), points[0].y - sourceShape.getZoomY());
        shape.addPort(endPort, points[1].x - shape.getZoomX(), points[1].y - shape.getZoomY(), false, sourcePort);

        //add ports to the canvas array for regularShapes
        //shape.canvas.regularShapes.insert(sourcePort).insert(endPort);
        //create the connection
        connection = new PMFlow({
            srcPort: sourcePort,
            destPort: endPort,
            segmentColor: new PMUI.util.Color(0, 0, 0),
            name: '',
            canvas: shape.canvas,
            segmentStyle: shape.connectionType.segmentStyle,
            flo_type: shape.connectionType.type
        });
        connection.setSrcDecorator(new PMUI.draw.ConnectionDecorator({
            width: 11,
            height: 11,
            canvas: canvas,
            decoratorPrefix: (typeof shape.connectionType.srcDecorator !== 'undefined'
            && shape.connectionType.srcDecorator !== null) ?
                shape.connectionType.srcDecorator : 'mafe-sequence',
            decoratorType: 'source',
            parent: connection
        }));
        connection.setDestDecorator(new PMUI.draw.ConnectionDecorator({
            width: 11,
            height: 11,
            canvas: canvas,
            decoratorPrefix: (typeof shape.connectionType.destDecorator !== 'undefined'
            && shape.connectionType.destDecorator !== null) ?
                shape.connectionType.destDecorator : 'mafe-sequence',
            decoratorType: 'target',
            parent: connection
        }));
        connection.canvas.commandStack.add(new PMUI.command.CommandConnect(connection));

        //connect the two ports
        connection.connect();
        connection.setSegmentMoveHandlers();

        //add the connection to the canvas, that means insert its html to
        // the DOM and adding it to the connections array
        canvas.addConnection(connection);

        // Filling PMFlow fields
        connection.setTargetShape(endPort.parent);
        connection.setOriginShape(sourcePort.parent);
        connection.savePoints();

        // now that the connection was drawn try to create the intersections
        connection.checkAndCreateIntersectionsWithAll();

        //attaching port listeners
        sourcePort.attachListeners(sourcePort);
        endPort.attachListeners(endPort);

        // finally trigger createEvent
        canvas.triggerCreateEvent(connection, []);
        connection.flo_state = connection.points;
        return connection;
    }

    function findBestPorts(sourceShape, shape) {
        var result = [], i, j,
            distance = 99999999,
            initPoint,
            secondPoint,
            midPoints = getMiddlePoints(sourceShape),
            midPoints2 = getMiddlePoints(shape);
        for (i = 0; i < midPoints.length; i += 1) {
            initPoint = midPoints[i];
            for (j = 0; j < midPoints2.length; j += 1) {
                secondPoint = midPoints2[j];
                if (distance > initPoint.getManhattanDistance(secondPoint)) {
                    distance = initPoint.getManhattanDistance(midPoints2[j]);
                    result[0] = (initPoint);
                    result[1] = (midPoints2[j]);
                }
            }
        }
        return result;
    }

    function getMiddlePoints(shape) {
        return [
            new PMUI.util.Point(Math.round(shape.zoomWidth / 2) + shape.getZoomX(), 0 + shape.getZoomY()), // TOP
            new PMUI.util.Point(shape.zoomWidth + shape.getZoomX(), Math.round(shape.zoomHeight / 2) + shape.getZoomY()), // RIGHT
            new PMUI.util.Point(Math.round(shape.zoomWidth / 2) + shape.getZoomX(), shape.zoomHeight + shape.getZoomY()), // BOTTOM
            new PMUI.util.Point(0 + shape.getZoomX(), Math.round(shape.zoomHeight / 2) + shape.getZoomY())               // LEFT
        ];
    }

    function loadConnections() {
        var row, connection, dt = shape.getPorts().asArray(),
            i, j;
        for (i = 0; i < dt.length; i += 1) {
            connection = dt[i].getConnection();
            if (shape.getID() !== connection.getDestPort().getParent().getID() && shape.gat_default_flow !== connection.flo_uid) {
                row = addRow();
                row.getField('act_name').setValue(connection.getDestPort().getParent().getID());
                row.getField('act_name').setUID(connection.getDestPort().getParent().getID());
                row.getField('flo_condition').setValue(connection.getFlowCondition());

                row.getItems()[2].style.addProperties({display: 'none'});
                row.getItems()[2].controls[0].button.setButtonType('error');
                $(row.getItems()[2].getHTML()).find("a").css({
                    padding: "5px"
                });
                formRoutingRule.addItem(row);
                for (j = 0; j < formRoutingRule.getItems().length; j += 1) {
                    formRoutingRule.getItems()[j].style.addProperties({'padding': 'initial'});
                }
                row.getItems()[0].dom.labelTextContainer.style.display = 'none';
                row.getItems()[1].dom.labelTextContainer.style.display = 'none';
            }
        }
    }

    function isConnection(sourceShape, shape) {
        var connection,
            i,
            dt = sourceShape.getPorts().asArray();
        for (i = 0; i < dt.length; i += 1) {
            connection = dt[i].getConnection();
            if (sourceShape.getID() === connection.getSrcPort().getParent().getID() &&
                shape.getID() === connection.getDestPort().getParent().getID()) {
                return connection;
            }
        }
        return false;
    }

    function deleteRow() {
        arrayShapeIdRemoved.push(formPanelSelected.getField('act_name').getValue());
        formRoutingRule.removeItem(formPanelSelected);
        PMDesigner.msgFlash('Routing rule removed correctly'.translate(), windowConnections.footer);
    }

    function enableSorting() {
        var index = 0,
            div = $(formRoutingRule.getHTML()).find(">div:nth-child(2)").css({"overflow": "initial"});
        div.sortable({
            items: '>div',
            placeholder: 'steps-placeholder',
            cursor: "move",
            change: function (event, ui) {
                index = ui.placeholder.index();
            },
            start: function (event, ui) {
            },
            stop: function (event, ui) {
                var dt = [],
                    row,
                    formPanelSelected,
                    id,
                    i,
                    j,
                    shapeDest,
                    connection;

                ui.item.parent().find(">div").each(function (i, e) {
                    dt.push(PMUI.getPMUIObject(e));
                });
                for (i = 0; i < dt.length; i += 1) {
                    formPanelSelected = dt[i];
                    arrayShapeIdRemoved.push(formPanelSelected.getField('act_name').getValue());
                    formRoutingRule.removeItem(formPanelSelected);
                }
                for (i = 0; i < dt.length; i += 1) {
                    row = addRow();
                    row.getItems()[2].style.addProperties({display: 'none'});
                    row.getItems()[2].controls[0].button.setButtonType('error');
                    $(row.getItems()[2].getHTML()).find("a").css({
                        padding: "5px"
                    });
                    formRoutingRule.addItem(row);
                    for (j = 0; j < formRoutingRule.getItems().length; j += 1) {
                        formRoutingRule.getItems()[j].style.addProperties({'padding': 'initial'});
                    }
                    row.getItems()[0].dom.labelTextContainer.style.display = 'none';
                    row.getItems()[1].dom.labelTextContainer.style.display = 'none';
                    row.getField('act_name').setValue(dt[i].getField('act_name').getValue());
                    row.getField('flo_condition').setValue(dt[i].getField('flo_condition').getValue());
                    id = dt[i].getField('act_name').getValue();
                    shapeDest = PMUI.getActiveCanvas().getCustomShapes().find('id', id);
                    connection = isConnection(shape, shapeDest);
                }
                enableSorting();
            }
        });
    }
};

PMDesigner.RoutingRuleDeleteAllFlow = function (shape) {
    var warningMessageWindowDelete = new PMUI.ui.MessageWindow({
        windowMessageType: 'warning',
        width: 490,
        bodyHeight: 'auto',
        title: 'Routing Rule'.translate(),
        id: 'warningMessageWindowDelete',
        message: 'Do you want to delete all routing rules?'.translate(),
        footerItems: [
            {
                id: 'warningMessageWindowDeleteButtonNo',
                text: 'No'.translate(),
                visible: true,
                handler: function () {
                    warningMessageWindowDelete.close();
                },
                buttonType: "error"
            }, {
                id: 'warningMessageWindowDeleteButtonYes',
                text: 'Yes'.translate(),
                visible: true,
                handler: function () {
                    deleteAllConnection();
                    warningMessageWindowDelete.close();
                },
                buttonType: "success"
            }
        ]
    });

    function deleteAllConnection() {
        var connection, elements = [],
            i,
            sw,
            msg,
            ports;
        PMUI.getActiveCanvas().emptyCurrentSelection();
        //IMPORTANT: You must empty elements in another array due to the array reference indices managed.
        //referer: PMDesigner.canvas.removeConnection & element.getPorts().asArray()

        ports = shape.getPorts().asArray();
        for (i = 0; i < ports.length; i += 1) {
            elements.push(ports[i]);
        }
        sw = false;
        for (i = 0; i < elements.length; i += 1) {
            connection = elements[i].getConnection();
            if (shape.getID() !== connection.getDestPort().getParent().getID()) {
                PMUI.getActiveCanvas().setCurrentConnection(connection);
                PMUI.getActiveCanvas().removeElements();
                connection.saveAndDestroy();
                PMUI.getActiveCanvas().removeConnection(connection);
                sw = true;
            }
        }
        msg = sw ? 'Routing rules deleted successfully' : 'There aren\'t routing rules';
        PMDesigner.msgFlash(msg.translate(), document.body);
    }

    warningMessageWindowDelete.open();
    warningMessageWindowDelete.showFooter();
    warningMessageWindowDelete.dom.titleContainer.style.height = '17px';
};

PMDesigner.RoutingRuleSetOrder = function (diagram) {
    var restClient = new PMRestClient({
        endpoint: 'update-route-order-from-project',
        typeRequest: 'update',
        data: {},
        functionSuccess: function () {
            PMDesigner.msgFlash('Saved correctly'.translate(), document.body, 'success', 3000, 5);
        }
    });
    restClient.executeRestClient();
};
PMDesigner.dynaformDesigner = function (data) {
    var old = PMUI.activeCanvas, a;
    PMUI.activeCanvas = false;
    a = new FormDesigner.main.Designer(data);
    a.show();
    a.onHide = function () {
        PMUI.activeCanvas = old;
    };
    return a;
};

PMDesigner.complexRoutingRule = function (shape) {
    var formRoutingRule,
        formPanelSelected,
        arrayShapeIdRemoved = [],
        buttonAdd,
        windowConnections,
        warningMessageWindowDelete,
        containerLabels,
        buttonSave,
        buttonCancel,
        labelNextTask,
        labelCondition,
        warningMessageWindowDirty,
        deleteButton;

    //Window
    //button add routing rule
    buttonAdd = new PMUI.ui.Button({
        id: 'routingRuleButtonAdd',
        text: 'Add Routing Rule'.translate(),
        style: {
            cssProperties: {
                marginLeft: '50px',
                marginTop: '10px',
                marginBottom: '10px',
                fontSize: '16px'
            }
        },
        buttonType: 'success',
        height: 31,
        handler: function () {
            var item, btnDel;
            if (countActivities() > 0) {
                item = addRow();
                item.getItems()[2].style.addProperties({display: 'none'});
                item.getItems()[2].controls[0].button.setButtonType('error');
                formRoutingRule.addItem(item);
                for (var i = 0; i < formRoutingRule.getItems().length; i += 1) {
                    //formRoutingRule.getItems()[i].style.addProperties({'box-sizing': 'initial'});
                    //item.style.addProperties({padding : 'initial'});
                    formRoutingRule.getItems()[i].style.addProperties({'padding': 'initial'});
                }
                item.getItems()[0].dom.labelTextContainer.style.display = "none";
                item.getItems()[1].dom.labelTextContainer.style.display = "none";

            } else {
                PMDesigner.msgFlash('There are no items.'.translate(), windowConnections.footer);
            }
        }
    });
    //button Save
    buttonSave = new PMUI.ui.Button({
        id: 'windowConnectionsButtonSave',
        text: 'Apply'.translate(),
        handler: function () {
            removeConnectionsIntoCanvas();
            saveConnections();
        },
        buttonType: 'success',
        height: 31
    });
    //Button cancel
    buttonCancel = new PMUI.ui.Button({
        id: 'windowConnectionsButtonCancel',
        text: 'Cancel'.translate(),
        buttonType: 'error',
        handler: function () {
            if (formRoutingRule.isDirty()) {
                warningMessageWindowDirty.open();
                warningMessageWindowDirty.showFooter();
            } else {
                windowConnections.close();
            }
        }
    });

    windowConnections = new PMUI.ui.Window({
        id: 'windowConnections',
        title: 'Routing Rule'.translate(),
        height: DEFAULT_WINDOW_HEIGHT,
        width: DEFAULT_WINDOW_WIDTH,
        footerAlign: 'right',
        buttonPanelPosition: 'top',
        items: [
            buttonAdd
        ],
        buttons: [
            buttonCancel,
            buttonSave
        ]
    });
    windowConnections.showFooter();
    //END WINDOW

    containerLabels = new PMUI.core.Panel({
        layout: 'hbox',
        width: 'auto',
        height: 29,
        style: {
            cssProperties: {
                'border-bottom': '1px solid #c0c0c0'
            }
        }
    });

    labelNextTask = new PMUI.ui.TextLabel({
        text: 'Next Task'.translate(),
        style: {
            cssProperties: {
                'font-weight': 'bold'
            }
        }
    });

    labelCondition = new PMUI.ui.TextLabel({
        text: 'Description'.translate(),
        style: {
            cssProperties: {
                'font-weight': 'bold'
            }
        }
    });

    containerLabels.addItem(labelNextTask);
    containerLabels.addItem(labelCondition);

    windowConnections.addItem(containerLabels);

    formRoutingRule = new PMUI.form.Form({
        id: 'formRoutingRule',
        visibleHeader: false,
        width: DEFAULT_WINDOW_WIDTH - 60,
        height: 'auto',
        items: [],
        style: {
            cssProperties: {
                'margin-left': '35px'
            }
        }
    });

    warningMessageWindowDelete = new PMUI.ui.MessageWindow({
        id: 'warningMessageWindowDelete',
        windowMessageType: 'warning',
        width: 490,
        title: "Routing Rule".translate(),
        bodyHeight: 'auto',
        message: 'Do you want to delete this routing rule?'.translate(),
        footerItems: [
            {
                id: 'warningMessageWindowDeleteButtonNo',
                text: 'No'.translate(),
                visible: true,
                handler: function () {
                    warningMessageWindowDelete.close();
                },
                buttonType: "error"
            }, {
                id: 'warningMessageWindowDeleteButtonYes',
                text: 'Yes'.translate(),
                visible: true,
                handler: function () {
                    deleteRow();
                    warningMessageWindowDelete.close();
                },
                buttonType: "success"
            }
        ]
    });

    warningMessageWindowDirty = new PMUI.ui.MessageWindow({
        id: 'warningMessageWindowDirty',
        windowMessageType: 'warning',
        width: 490,
        bodyHeight: 'auto',
        title: "Routing Rule".translate(),
        message: 'Are you sure you want to discard your changes?'.translate(),
        footerItems: [{
            id: 'warningMessageWindowDirtyButtonNo',
            text: 'No'.translate(),
            visible: true,
            handler: function () {
                warningMessageWindowDirty.close();
            },
            buttonType: "error"
        },
            {
                id: 'warningMessageWindowDirtyButtonYes',
                text: 'Yes'.translate(),
                visible: true,
                handler: function () {
                    warningMessageWindowDirty.close();
                    windowConnections.close();
                },
                buttonType: "success"
            }
        ]
    });

    //main
    formRoutingRule.hideFooter();
    windowConnections.addItem(formRoutingRule);
    windowConnections.open();
    labelNextTask.setWidth(382);
    labelNextTask.style.addProperties({padding: '5px 0px 0px 50px'});
    labelCondition.setWidth(410);
    labelCondition.style.addProperties({padding: '5px 0px 0px 0px'});
    containerLabels.style.addProperties({'border-bottom': '1px solid #e7e7e7'});
    windowConnections.setTitle('Routing Rule'.translate() + ' - ' + ((shape.gat_type === 'COMPLEX') ? 'Exclusive (Manual)'.translate() : shape.gat_type));
    loadConnections();

    function countActivities() {
        //Important! Any changes to synchronize the assessment of the condition 
        //of the functions: countActivities and loadActivities
        var n = 0, i, dt;
        dt = PMDesigner.project.getDirtyObject().diagrams[0].activities;
        for (i = 0; i < dt.length; i += 1) {
            n = n + 1;
        }

        dt = PMDesigner.project.getDirtyObject().diagrams[0].events;
        for (i = 0; i < dt.length; i += 1) {
            if (dt[i].evn_type !== 'START') {
                n = n + 1;
            }
        }
        return n;
    }

    function addRow() {

        var dropDownControl, description, deleteButton, newRow;

        newRow = new PMUI.form.FormPanel({
            layout: 'hbox'
        });

        dropDownControl = new PMUI.field.DropDownListField({
            id: 'dropdownNextTask',
            name: 'act_name',
            valueType: 'string',
            label: 'Next Task'.translate(),
            labelPosition: 'top',
            labelVisible: false,
            value: '',
            readOnly: true,
            controlsWidth: 360,
            proportion: 1.1,
            style: {
                cssProperties: {
                    'vertical-align': 'top'
                }
            }
        });

        description = new PMUI.field.TextField({
            id: 'textCondition',
            pmType: 'text',
            name: 'flo_description',
            valueType: 'string',
            label: 'Description'.translate(),
            labelPosition: 'top',
            labelVisible: false,
            controlsWidth: 320,
            required: true,
            style: {
                cssProperties: {
                    'vertical-align': 'top'
                }
            }
        });

        deleteButton = new PMUI.field.ButtonField({
            id: 'buttonDelete',
            value: 'Delete'.translate(),
            handler: function (e, a) {
                var i;
                for (i = 0; i < formRoutingRule.getItems().length; i += 1) {
                    if (formRoutingRule.getItems()[i].getItems()[2].controls[0].button.id == this.id) {
                        formPanelSelected = formRoutingRule.getItems()[i];
                        warningMessageWindowDelete.open();
                        warningMessageWindowDelete.dom.titleContainer.style.height = "17px";
                        warningMessageWindowDelete.showFooter();
                    }
                }

            },
            name: 'delete',
            labelVisible: false,
            buttonAling: 'left',
            controlsWidth: 100,
            proportion: 0.3,
            style: {
                cssProperties: {
                    'vertical-align': 'top'
                }
            }
        });

        newRow.addItem(dropDownControl);
        newRow.addItem(description);
        newRow.addItem(deleteButton);
        loadActivities(dropDownControl);
        return newRow;
    }

    function loadActivities(dropdown) {
        var i, dt;
        //Important! Any changes to synchronize the assessment of the condition
        //of the functions: countActivities and loadActivities
        dropdown.clearOptions();
        dropdown.setUID = function (uid) {
            this.uid = uid;
        };
        dropdown.getUID = function () {
            return this.uid;
        };
        dropdown.addOptionGroup({
            label: 'Task'.translate(),
            selected: true,
            options: []
        });
        dropdown.addOptionGroup({
            label: 'Sub-process'.translate(),
            options: []
        });
        dt = PMDesigner.project.getDirtyObject().diagrams[0].activities, nameGroup;
        dt = dt.sort(function (a, b) {
            return a.act_name.toString().toLowerCase() > b.act_name.toString().toLowerCase();
        });
        for (i = 0; i < dt.length; i += 1) {
            nameGroup = dt[i].act_type === 'TASK' ? 'Task'.translate() : 'Sub-process'.translate();
            dropdown.addOption({
                value: dt[i].act_uid,
                label: dt[i].act_name
            }, nameGroup);
        }

        dropdown.addOptionGroup({
            label: 'End of process'.translate(),
            options: []
        });
        dt = PMDesigner.project.getDirtyObject().diagrams[0].events;
        dt = dt.sort(function (a, b) {
            return a.evn_name.toString().toLowerCase() > b.evn_name.toString().toLowerCase();
        });
        for (i = 0; i < dt.length; i += 1) {
            if (dt[i].evn_type !== 'START') {
                dropdown.addOption({
                    value: dt[i].evn_uid,
                    label: dt[i].evn_name
                }, 'End of process'.translate());
            }
        }
    }

    function removeConnectionsIntoCanvas() {
        var shapeDest, connection, dt, i, j;
        for (j = 0; j < arrayShapeIdRemoved.length; j += 1) {
            shapeDest = getShapeForId(arrayShapeIdRemoved[j]);
            dt = shape.getPorts().asArray();
            for (i = 0; i < dt.length; i += 1) {
                connection = dt[i].getConnection();
                if (shape.getID() === connection.getSrcPort().getParent().getID() &&
                    shapeDest.getID() === connection.getDestPort().getParent().getID()) {
                    PMDesigner.canvas.emptyCurrentSelection();
                    PMDesigner.canvas.setCurrentConnection(connection);
                    PMDesigner.canvas.removeElements();
                    connection.saveAndDestroy();
                    PMDesigner.canvas.removeConnection(connection);
                    break;
                }
            }
        }
        arrayShapeIdRemoved = [];
    }

    function editShapeDestConnection(shapeDest, oldShape) {
        var connection, canvas = PMDesigner.canvas;
        connection = isConnection(shape, oldShape);
        oldShape.removePort(connection.destPort);
        shapeDest.addPort(connection.destPort, 100, 100,
            false, connection.srcPort);

        connection.canvas.commandStack.add(new PMUI.command.CommandConnect(connection));
        connection.connect();
        canvas.triggerPortChangeEvent(connection.destPort);
    }

    function createEndShape() {
        var customShape, canvas = PMDesigner.canvas, command, x, y;
        customShape = canvas.shapeFactory('END');

        x = shape.getX() + shape.getWidth();
        y = shape.getY() + shape.getHeight() + 20;

        canvas.addElement(customShape, x, y,
            customShape.topLeftOnCreation);

        //since it is a new element in the designer, we triggered the
        //custom on create element event
        canvas.updatedElement = customShape;

        // create the command for this new shape
        command = new PMUI.command.CommandCreate(customShape);
        canvas.commandStack.add(command);
        command.execute();
        return customShape;
    }

    function saveConnections() {
        var dt, i;
        if (!formRoutingRule.isValid()) {
            return;
        }
        dt = formRoutingRule.getItems(), id, oldId, shapeDest, oldShapeDest, connection;
        for (i = 0; i < dt.length; i += 1) {
            id = dt[i].getField('act_name').getValue();
            oldId = dt[i].getField('act_name').getUID();
            if (id !== '0') {
                if (oldId === undefined) {
                    shapeDest = getShapeForId(id);
                    createConnection(shape, shapeDest).setFlowCondition(dt[i].getField('flo_description').getValue());
                }
                if (oldId !== undefined && id === oldId) {
                    shapeDest = getShapeForId(id);
                    connection = isConnection(shape, shapeDest);
                    $a = connection;
                    connection.setFlowCondition(dt[i].getField('flo_description').getValue());
                }
                if (oldId !== undefined && id !== oldId) {
                    shapeDest = getShapeForId(id);
                    oldShapeDest = getShapeForId(oldId);
                    connection = isConnection(shape, oldShapeDest);
                    connection.setFlowCondition(dt[i].getField('flo_description').getValue());
                    editShapeDestConnection(shapeDest, oldShapeDest);
                }
            } else {
                var customShape = createEndShape();
                createConnection(shape, customShape);
            }
        }
        windowConnections.close();
        PMDesigner.msgFlash('Saved correctly'.translate(), document.body);
        PMDesigner.project.dirty = true;
    }

    function getShapeForId(id) {
        var dt = PMDesigner.canvas.getChildren().asArray(), i;
        for (i = 0; i < dt.length; i += 1) {
            if (dt[i].act_uid === id || dt[i].evn_uid === id) {
                return dt[i];
            }
        }
        return null;
    }

    function createConnection(sourceShape, shape) {
        //referer to /processmaker/vendor/colosa/MichelangeloFE/src/connectiondrop.js
        //method PMConnectionDropBehavior.prototype.onDrop
        var sourcePort, endPort, connection, canvas = PMDesigner.canvas;
        sourcePort = new PMUI.draw.Port({
            width: 10,
            height: 10
        });
        endPort = new PMUI.draw.Port({
            width: 10,
            height: 10
        });

        sourceShape.addPort(sourcePort, 100, 100);
        shape.addPort(endPort, 100, 100,
            false, sourcePort);

        //add ports to the canvas array for regularShapes
        //shape.canvas.regularShapes.insert(sourcePort).insert(endPort);
        //create the connection
        connection = new PMFlow({
            srcPort: sourcePort,
            destPort: endPort,
            segmentColor: new PMUI.util.Color(92, 156, 204),
            name: "",
            canvas: shape.canvas,
            segmentStyle: shape.connectionType.segmentStyle,
            flo_type: shape.connectionType.type
        });
        connection.setSrcDecorator(new PMUI.draw.ConnectionDecorator({
            width: 11,
            height: 11,
            canvas: canvas,
            decoratorPrefix: (typeof shape.connectionType.srcDecorator !== 'undefined'
            && shape.connectionType.srcDecorator !== null) ?
                shape.connectionType.srcDecorator : "mafe-sequence",
            decoratorType: "source",
            parent: connection
        }));
        connection.setDestDecorator(new PMUI.draw.ConnectionDecorator({
            width: 11,
            height: 11,
            canvas: canvas,
            decoratorPrefix: (typeof shape.connectionType.destDecorator !== 'undefined'
            && shape.connectionType.destDecorator !== null) ?
                shape.connectionType.destDecorator : "mafe-sequence",
            decoratorType: "target",
            parent: connection
        }));
        connection.canvas.commandStack.add(new PMUI.command.CommandConnect(connection));

        //connect the two ports
        connection.connect();
        connection.setSegmentMoveHandlers();

        //add the connection to the canvas, that means insert its html to
        // the DOM and adding it to the connections array
        canvas.addConnection(connection);

        // Filling PMFlow fields
        connection.setTargetShape(endPort.parent);
        connection.setOriginShape(sourcePort.parent);
        connection.savePoints();

        // now that the connection was drawn try to create the intersections
        connection.checkAndCreateIntersectionsWithAll();

        //attaching port listeners
        sourcePort.attachListeners(sourcePort);
        endPort.attachListeners(endPort);

        // finally trigger createEvent
        canvas.triggerCreateEvent(connection, []);
        return connection;
    }

    function loadConnections() {
        var row, connection, dt = shape.getPorts().asArray(), i, j;
        for (i = 0; i < dt.length; i += 1) {
            connection = dt[i].getConnection();
            if (shape.getID() !== connection.getDestPort().getParent().getID()) {
                row = addRow();
                row.getField('act_name').setValue(connection.getDestPort().getParent().getID());
                row.getField('act_name').setUID(connection.getDestPort().getParent().getID());
                row.getField('flo_description').setValue(connection.getFlowCondition());

                row.getItems()[2].style.addProperties({display: 'none'});
                row.getItems()[2].controls[0].button.setButtonType('error');
                formRoutingRule.addItem(row);
                for (j = 0; j < formRoutingRule.getItems().length; j += 1) {
                    formRoutingRule.getItems()[j].style.addProperties({'padding': 'initial'});
                }
                row.getItems()[0].dom.labelTextContainer.style.display = "none";
                row.getItems()[1].dom.labelTextContainer.style.display = "none";
            }
        }
    }

    function isConnection(sourceShape, shape) {
        var connection, dt, i;
        dt = sourceShape.getPorts().asArray();
        for (i = 0; i < dt.length; i += 1) {
            connection = dt[i].getConnection();
            if (sourceShape.getID() === connection.getSrcPort().getParent().getID() &&
                shape.getID() === connection.getDestPort().getParent().getID()) {
                return connection;
            }
        }
        return false;
    }

    function deleteRow() {
        arrayShapeIdRemoved.push(formPanelSelected.getField('act_name').getValue());
        formRoutingRule.removeItem(formPanelSelected);
        PMDesigner.msgFlash('Routing rule removed correctly'.translate(), windowConnections.footer);
    }

};

var PMVariables = function (options) {
    var that = this;
    this.initialFormAcceptedValuesKeyValue = null;
    this.initialFormAcceptedValuesValue = null;
    this.initialGridAcceptedValuesOrder = [];
    this.validateAcceptedValuesFlag = false;
    this.var_uid = null;
    this.var_name = null;
    this.edit = false;
    this.editRow = null;
    this.dirtyAcceptedValue = false;
    this.fieldInfo = null;
    this.onSave = new Function();
    this.onEdit = new Function();
    this.clickedClose = true;
    this.editingOptions = false;
    this.editRow = null;
    this.currentVariable = null;
    this.onWindowClose = new Function();
    this.buttonCreate = new PMUI.ui.Button({
        id: 'buttonCreate',
        text: 'Create'.translate(),
        height: '36px',
        width: 100,
        style: {
            cssClasses: [
                'mafe-button-create'
            ]

        },
        handler: function () {
            that.showForm();
        }
    });

    this.buttonCreateInputDocument = new PMUI.field.ButtonField({
        id: 'buttonCreateInputDocument',
        value: 'Create'.translate(),
        labelVisible: false,
        buttonAlign: 'center',
        proportion: 0.8,
        handler: function (field) {
            var inputDocument = new InputDocument();
            inputDocument.build();
            inputDocument.openFormInMainWindow();
            inputDocument.method = "POST";
        }
    });

    this.buttonEditInputDocument = new PMUI.field.ButtonField({
        id: 'buttonEditInputDocument',
        value: 'Edit'.translate(),
        labelVisible: false,
        buttonAlign: 'center',
        proportion: 0.8,
        handler: function (field) {
            var form = that.formVariables,
                fieldInpDoc = form.getField('inp_doc_uid'),
                inp_doc_uid = fieldInpDoc.getValue(),
                defaultText = "- Select an input document -".translate(),
                inputDocument;
            if (inp_doc_uid && inp_doc_uid !== defaultText) {
                inputDocument = new InputDocument();
                inputDocument.build();
                inputDocument.inputDocumentOriginDataForUpdate = {};
                inputDocument.openFormInMainWindow();
                inputDocument.inputDocumentFormGetProxy(inp_doc_uid);
            } else {
                fieldInpDoc.setValue("");
                form.isValid();
            }
        }
    });

    that.buttonCreateInputDocument.controls[0].button.setButtonType("success");
    that.buttonCreateInputDocument.controls[0].button.setStyle({
        cssClasses: ["mafe-button-create-variable", "pmui-success"],
        cssProperties: {padding: "8px 15px", border: "0px"}
    });
    that.buttonEditInputDocument.controls[0].button.setButtonType("success");
    that.buttonEditInputDocument.controls[0].button.setStyle({
        cssClasses: ["mafe-button-edit-variable", "pmui-success"],
        cssProperties: {padding: "8px 15px", border: "0px"}
    });

    var inp_doc_uid = new PMUI.field.DropDownListField({
        id: "inp_doc_uid",
        name: "inp_doc_uid",
        value: "",
        required: true,
        label: "Related Input Document".translate(),
        controlsWidth: 460,
        valueType: "string",
        labelPosition: "top",
        onChange: function () {
            that.validateInputDoc();
        }
    });

    this.buttonFieldAdd = new PMUI.field.ButtonField({
        id: 'buttonFieldAdd',
        pmType: 'buttonField',
        value: 'Create'.translate(),
        labelVisible: false,
        buttonAlign: 'center',
        controlsWidth: 50,
        proportion: 0.8,
        handler: function (field) {
            that.addAcceptedValue();
        },
        style: {
            cssProperties: {
                "margin-left": "10px"
            }
        }
    });
    this.buttonFieldAdd.getControl().button.setButtonType("success");
    this.buttonFieldCancel = new PMUI.field.ButtonField({
        id: 'buttonFieldCancel',
        pmType: 'buttonField',
        value: 'Cancel'.translate(),
        labelVisible: false,
        buttonAlign: 'center',
        controlsWidth: 55,
        proportion: 0.6,
        handler: function (field) {
            that.clickedClose = false;
            that.cancelAcceptedValue();
            that.editingOptions = false;
        }
    });
    this.buttonFieldCancel.getControl().button.setButtonType("error");

    this.formVariables = new PMUI.form.Form({
        id: 'formVariables',
        width: 'auto',
        title: '',
        visibleHeader: false,
        items: [
            {
                pmType: 'text',
                label: 'Variable Name'.translate(),
                placeholder: "Name".translate(),
                id: 'variableName',
                value: '',
                name: 'var_name',
                required: true,
                valueType: 'string',
                maxLength: 60,
                controlsWidth: 460,
                validators: [
                    {
                        pmType: "regexp",
                        criteria: /^[a-zA-Z\_]{1}\w+$/,
                        errorMessage: "A valid variable starts with a letter or underscore, followed by any number of letters, numbers, or underscores.".translate()
                    }
                ]
            }, {
                pmType: 'text',
                label: 'Label'.translate(),
                placeholder: 'Label'.translate(),
                id: 'variableLabel',
                value: 'label',
                name: 'var_label',
                valueType: 'string',
                maxLength: 60,
                controlsWidth: 460,
                visible: false
            }, {
                pmType: 'dropdown',
                label: 'Variable Type'.translate(),
                placeholder: 'Variable type'.translate(),
                id: 'varType',
                value: 'string',
                name: 'var_field_type',
                required: true,
                valueType: 'string',
                controlsWidth: 460,
                options: [
                    {
                        label: 'String',
                        value: 'string'
                    }, {
                        label: 'Integer',
                        value: 'integer'
                    }, {
                        label: 'Float',
                        value: 'float'
                    }, {
                        label: 'Boolean',
                        value: 'boolean'
                    }, {
                        label: 'Datetime',
                        value: 'datetime'
                    }, {
                        label: 'Grid',
                        value: 'grid'
                    }, {
                        label: 'Array',
                        value: 'array'
                    }, {
                        label: "File",
                        value: "file"
                    }, {
                        label: "Multiple File",
                        value: "multiplefile"
                    }, {
                        label: "Object",
                        value: "object"
                    }

                ],
                onChange: function (newValue, oldValue) {
                    var sw = that.gridAcceptedValues.visible === false ? true : (that.gridAcceptedValues.getData().length === 0);
                    var sw2 = that.formBooleanOptions.visible === false ? true : (that.formBooleanOptions.getField('trueOption').getValue() + that.formBooleanOptions.getField('falseOption').getValue()) === '';
                    if (sw && sw2) {
                        that.changeViewFieldType(newValue, oldValue);
                        that.resetAcceptedValuesPanel();
                        that.gridAcceptedValues.clearItems();
                        that.resetBooleanPanel();
                        return;
                    }
                    var message_window = new PMUI.ui.MessageWindow({
                        id: 'messageWindowCancel',
                        width: 490,
                        title: 'Variables'.translate(),
                        windowMessageType: 'warning',
                        bodyHeight: 'auto',
                        message: "This action will delete all options. Do you want to continue?".translate(),
                        footerItems: [
                            {
                                id: 'messageWindowNo',
                                text: 'No'.translate(),
                                handler: function () {
                                    message_window.close();
                                    that.formVariables.getField('var_field_type').setValue(oldValue);
                                },
                                buttonType: "error"
                            },
                            {
                                id: 'messageWindowYes',
                                text: 'Yes'.translate(),
                                handler: function () {
                                    message_window.close();
                                    that.changeViewFieldType(newValue, oldValue);
                                    that.resetAcceptedValuesPanel();
                                    that.gridAcceptedValues.clearItems();
                                    that.resetBooleanPanel();
                                },
                                buttonType: "success"
                            }
                        ],
                        onClose: function () {
                        }
                    });
                    message_window.open();
                    message_window.showFooter();
                }
            }, {
                pmType: "panel",
                id: "inp_doc_uidPanel",
                fieldset: false,
                layout: "hbox",
                items: [
                    inp_doc_uid,
                    that.buttonCreateInputDocument,
                    that.buttonEditInputDocument
                ]
            },
            {
                id: 'booleanPanel',
                pmType: 'panel',
                legend: 'Options'.translate(),
                fieldset: true,
                layout: 'vbox',
                items: [
                    {
                        pmType: 'panel',
                        layout: 'hbox',
                        items: [
                            new PMLabelField({
                                text: 'Key'.translate(),
                                textMode: 'plain',
                                style: {
                                    cssProperties: {
                                        color: '#AEAEAE',
                                        'font-weight': 'bold'
                                    }
                                },
                                proportion: 0.3
                            }),
                            new PMLabelField({
                                text: 'Label'.translate(),
                                textMode: 'plain',
                                style: {
                                    cssProperties: {
                                        color: '#AEAEAE',
                                        'font-weight': 'bold'
                                    }
                                }
                            })
                        ]
                    },
                    {
                        pmType: 'text',
                        name: 'trueOption',
                        label: 'True'.translate(),
                        controlsWidth: 460,
                        valueType: 'string',
                        maxLength: 100,
                        required: true
                    }, {
                        pmType: 'text',
                        name: 'falseOption',
                        label: 'False'.translate(),
                        controlsWidth: 460,
                        valueType: 'string',
                        maxLength: 100,
                        required: true
                    }
                ]
            },
            {
                pmType: 'dropdown',
                label: 'Database Connection'.translate(),
                placeholder: 'Database Connection'.translate(),
                id: 'varConnection',
                value: 'none',
                name: 'var_dbconnection',
                controlsWidth: 460,
                options: [{
                    label: 'PM Database',
                    value: 'workflow'
                }
                ],
                onChange: function (newValue, oldValue) {
                }
            }, {
                pmType: 'textarea',
                label: 'SQL'.translate(),
                placeholder: "Insert a SQL query like: SELECT [Key field], [Label field] FROM [Table name]".translate(),
                id: 'varSql',
                value: '',
                name: 'var_sql',
                valueType: 'string',
                controlsWidth: 460,
                style: {cssClasses: ['mafe-textarea-resize']}
            }, {
                pmType: 'checkbox',
                label: 'Define accepted variable values'.translate(),
                id: "chckboxOption",
                name: 'var_options_control',
                controlsWidth: 460,
                options: [{value: '1', label: ''}],
                onChange: function (newValue, oldValue) {
                    that.changeViewFieldType(that.formVariables.getField('var_field_type').getValue());
                }
            },
            {
                id: 'formAcceptedValues',
                pmType: 'panel',
                fieldset: false,
                layout: 'hbox',
                items: [
                    {
                        pmType: 'text',
                        name: 'keyValue',
                        id: "variable-keyvalue",
                        label: 'Key'.translate(),
                        labelWidth: '100%',
                        controlsWidth: 210,
                        proportion: 2.5,
                        valueType: 'string',
                        maxLength: 255,
                        labelPosition: "top"
                    },
                    {
                        pmType: 'text',
                        name: 'value',
                        id: "variable-value",
                        label: 'Label'.translate(),
                        labelWidth: '100%',
                        controlsWidth: 300,
                        valueType: 'string',
                        maxLength: 255,
                        proportion: 3.4,
                        labelPosition: "top"
                    },
                    that.buttonFieldCancel,
                    that.buttonFieldAdd
                ]
            }
        ]
    });
    this.formVariables.getData = function () {
        var data = getData2PMUI(that.formVariables.html);
        return data;
    };
    this.gridVariables = new PMUI.grid.GridPanel({
        id: 'gridVariables',
        pageSize: 10,
        width: '96%',
        style: {
            cssClasses: ['mafe-gridPanel']
        },
        filterPlaceholder: 'Search ...'.translate(),
        emptyMessage: 'No records found'.translate(),
        nextLabel: 'Next'.translate(),
        previousLabel: 'Previous'.translate(),
        tableContainerHeight: 374,
        customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
            return '';
        },
        columns: [{
            id: 'varName',
            title: 'Name'.translate(),
            dataType: 'string',
            columnData: 'var_name',
            alignmentCell: 'left',
            width: '300px',
            sortable: true
        }, {
            id: 'varType',
            title: 'Type'.translate(),
            dataType: 'string',
            alignmentCell: 'left',
            columnData: 'var_field_type',
            sortable: true
        }, {
            id: 'varEdit',
            dataType: 'button',
            title: '',
            buttonLabel: 'Edit'.translate(),
            width: '60px',
            buttonStyle: {
                cssClasses: [
                    'mafe-button-edit'
                ]
            },
            onButtonClick: function (row, grid) {
                that.showFormEdit(row.getData());
            }
        }, {
            id: 'varDelete',
            dataType: 'button',
            title: '',
            buttonLabel: function (row, data) {
                return 'Delete'.translate();
            },
            width: '70px',
            buttonStyle: {
                cssClasses: [
                    'mafe-button-delete'
                ]
            },
            onButtonClick: function (row, grid) {
                that.del(row.getData(), row, grid);
            }
        }
        ],
        dataItems: null
    });
    this.gridAcceptedValues = new PMUI.grid.GridPanel({
        id: 'gridAcceptedValues',
        pageSize: 5,
        style: {cssClasses: ['mafe-gridPanel']},
        filterPlaceholder: 'Text to Search'.translate(),
        emptyMessage: 'No records found'.translate(),
        nextLabel: 'Next'.translate(),
        previousLabel: 'Previous'.translate(),
        filterable: false,
        customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
            return '';
        },
        columns: [{
            id: 'keyvalue',
            title: 'Key'.translate(),
            columnData: 'keyValue',
            dataType: 'string',
            alignmentCell: 'left',
            width: 180
        }, {
            id: 'label',
            title: 'Label'.translate(),
            columnData: 'value',
            dataType: 'string',
            alignmentCell: 'left',
            width: 300
        }, {
            id: 'buttonEdit',
            dataType: 'button',
            title: '',
            buttonLabel: 'Edit'.translate(),
            width: 60,
            buttonStyle: {cssClasses: ['mafe-button-edit']},
            onButtonClick: function (row, grid) {
                that.editRow = row;
                that.editAcceptedValue(row);
            }
        }, {
            id: 'buttonDelete',
            dataType: 'button',
            title: '',
            buttonLabel: function (row, data) {
                return 'Delete'.translate();
            },
            width: 75,
            buttonStyle: {cssClasses: ['mafe-button-delete']},
            onButtonClick: function (row, grid) {
                if (row !== that.editRow) {
                    that.deleteAcceptedValue(row);
                } else {
                    PMDesigner.msgFlash('The row can not be removed, because is being edited.'.translate(), document.getElementById('windowVariables'), 'error', 1000, 5);
                }
            }
        }
        ],
        dataItems: null,
        behavior: 'dragdropsort'
    });
    this.isDirtyFormVariables = function () {
        $("input,select,textarea").blur();
        if (this.formVariables.isVisible()) {
            this.validateAcceptedValues();
            if (this.formVariables.isDirty() || this.dirtyAcceptedValue || this.validateAcceptedValuesFlag) {
                //if (this.formVariables.getField("var_options_control").controls[0].selected) {
                var message_window = new PMUI.ui.MessageWindow({
                    id: "messageWindowCancel",
                    width: 490,
                    title: "Variables".translate(),
                    windowMessageType: "warning",
                    bodyHeight: "auto",
                    message: 'Are you sure you want to discard your changes?'.translate(),
                    footerItems: [{
                        id: "messageWindowNo",
                        text: "No".translate(),
                        handler: function () {
                            message_window.close();
                        },
                        buttonType: "error"
                    },
                        {
                            id: "messageWindowYes",
                            text: "Yes".translate(),
                            handler: function () {
                                if (that.clickedClose) {
                                    that.windowVariables.close();
                                }
                                message_window.close();
                                that.showGrid();
                                that.windowVariables.hideFooter();
                            },
                            buttonType: "success"
                        },
                    ]
                });

                message_window.open();
                message_window.showFooter();
                /*} else {
                 that.showGrid();
                 }*/
            } else {
                if (that.clickedClose) {
                    that.windowVariables.close();
                } else {
                    that.showGrid();
                }
                this.initialFormAcceptedValuesKeyValue = null;
                this.initialFormAcceptedValuesValue = null;
                this.initialGridAcceptedValuesOrder = [];
                that.windowVariables.hideFooter();
            }
        } else {
            this.windowVariables.close();
        }
    };
    this.windowVariables = new PMUI.ui.Window({
        id: 'windowVariables',
        title: 'Variables'.translate(),
        height: DEFAULT_WINDOW_HEIGHT,
        width: DEFAULT_WINDOW_WIDTH,
        onBeforeClose: function () {
            that.clickedClose = true;
            that.isDirtyFormVariables();
        },
        footerItems: [
            new PMUI.ui.Button({
                id: "windowVariablesCancel",
                text: "Cancel".translate(),

                handler: function () {
                    that.clickedClose = false;
                    that.isDirtyFormVariables();
                },

                buttonType: "error"
            }),

            new PMUI.ui.Button({
                id: "windowVariablesSave",
                text: "Save".translate(),

                handler: function () {
                    if (that.edit) {
                        that.updateVariables();
                    } else {
                        that.saveVariables();
                    }
                },

                buttonType: "success"
            })
        ],
        onClose: function () {
            that.onWindowClose(that.currentVariable);
        },
        visibleFooter: true,
        buttonPanelPosition: "bottom"
    });
    PMVariables.prototype.init.call(this);
    that.setInputDocuments(inp_doc_uid);
};
PMVariables.prototype.init = function () {
    var that = this,
        acceptedValuesForm,
        label;

    that.buttonCreate.defineEvents();

    that.windowVariables.addItem(that.gridVariables);
    that.windowVariables.addItem(that.formVariables);
    that.windowVariables.addItem(that.gridAcceptedValues);
    that.windowVariables.hideFooter();
    that.windowVariables.open();
    label = $('#booleanPanel');
    acceptedValuesForm = $('#formAcceptedValues');
    that.customCss();
    acceptedValuesForm.find(".pmui-field-message").css("marginLeft", 10);
    $("#gridAcceptedValues").css({"height": "254px", "margin": "0 10px"});
    $("#requiredMessage").css({"margin-top": "10px"});
    $("#inp_doc_uid").find(".pmui-field-message:eq(0)").css("left", "226px");
    this.formAcceptedValues = PMUI.getPMUIObject(acceptedValuesForm.get(0));

    this.buttonFieldAdd.controls[0].button.setStyle({cssProperties: {padding: "6px 15px"}});
    this.buttonFieldCancel.controls[0].button.setStyle({cssProperties: {padding: "6px 15px"}});
    $('#gridVariables .pmui-textcontrol').css({'margin-top': '5px', width: '250px'});
    that.gridVariables.dom.toolbar.appendChild(that.buttonCreate.getHTML());

    this.formBooleanOptions = PMUI.getPMUIObject(label.get(0));
    that.showGrid();
    that.loadDataBaseConnections();

    validateKeysField(that.formVariables.getField('var_name').getControls()[0].getHTML(), ['isbackspace', 'isnumber', 'isletter', 'isunderscore']);

    that.resetAcceptedValuesPanel();
    label = $('#booleanPanel').css({'width': '675px', margin: '10px'}).find(".pmui-pmlabelfield");
    $(label[0]).replaceWith($(label[0]).find(".pmui-pmlabelcontrol").css({
        "font-size": "14px",
        "margin-right": "127px"
    }));
    $(label[1]).replaceWith($(label[1]).find(".pmui-pmlabelcontrol").css({
        "font-size": "14px",
        "margin-right": "127px"
    }));
    this.formVariables.panel.html.style.overflow = "scroll !important";
    $(this.formVariables.panel.html).removeClass("pmui-formpanel");
    $(this.formVariables.panel.html).append(that.formAcceptedValues.html);
    $(this.formVariables.panel.html).append(that.gridAcceptedValues.html);
    $(that.formAcceptedValues.html).find(".pmui-formpanel").css({"display": "inline-block"});
    that.windowVariables.footer.html.style.textAlign = "right";
};
PMVariables.prototype.saveVariables = function () {
    var that = this,
        data,
        inp_doc_uid_value = this.formVariables.getField("inp_doc_uid").controls[0].value;
    this.formVariables.getField("inp_doc_uid").setValue(inp_doc_uid_value);
    if (!this.formVariables.isValid()) {
        return;
    }
    if (this.formBooleanOptions.visible && !this.formBooleanOptions.isValid()) {
        return;
    }
    data = this.formVariables.getData();
    data.var_label = data.var_field_type;

    data.var_default = '';
    data.var_accepted_values = that.getDataAcceptedValues();
    data.var_field_size = 10;
    if (!this.formVariables.getField('var_sql').visible)
        data.var_sql = "";
    (new PMRestClient({
        endpoint: 'process-variable',
        typeRequest: 'post',
        functionSuccess: function (xhr, response) {
            if (that.onSave(xhr, response) === false) {
                that.var_uid = null;
                return;
            }
            that.showGrid();
            that.load();
            that.var_uid = null;
            that.windowVariables.hideFooter();
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        },
        messageError: ' ',
        data: data,
        messageSuccess: 'Variable saved successfully'.translate(),
        flashContainer: that.panel
    })).executeRestClient();
};
PMVariables.prototype.updateVariables = function () {
    var that = this,
        data,
        inp_doc_uid_value = this.formVariables.getField("inp_doc_uid").controls[0].value;
    this.formVariables.getField("inp_doc_uid").setValue(inp_doc_uid_value);
    if (!this.formVariables.isValid()) {
        return;
    }
    if (this.formBooleanOptions.visible && !this.formBooleanOptions.isValid()) {
        return;
    }
    data = this.formVariables.getData();
    data.var_label = data.var_field_type;
    data.var_field_size = 10;
    if (that.formVariables.getField('var_name').getValue().trim() === that.var_name.trim()) {
        delete data['var_name'];
    }
    data.var_default = '';
    data.var_accepted_values = that.getDataAcceptedValues();
    if (!this.formVariables.getField('var_sql').visible)
        data.var_sql = "";
    (new PMRestClient({
        endpoint: 'process-variable/' + that.var_uid,
        typeRequest: 'update',
        data: data,
        functionSuccess: function (xhr, response) {
            if (that.onEdit(xhr, response, data) === false) {
                that.var_uid = null;
                return;
            }
            var var_name_old = that.currentVariable.var_name;
            that.currentVariable = data;
            that.currentVariable.var_uid = that.var_uid;
            that.currentVariable.var_name_old = var_name_old;
            that.showGrid();
            that.load();
            that.var_uid = null;
            that.windowVariables.hideFooter();
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        },
        messageError: ' ',
        messageSuccess: 'Variable edited successfully'.translate(),
        flashContainer: that.panel
    })).executeRestClient();
};
PMVariables.prototype.deleteVariable = function (var_uid) {
    var that = this;
    (new PMRestClient({
        endpoint: 'process-variable/' + var_uid,
        typeRequest: 'remove',
        functionSuccess: function (xhr, response) {
            that.load();
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        },
        messageError: 'working...'.translate(),
        messageSuccess: 'Variable deleted successfully'.translate(),
        flashContainer: that.panel
    })).executeRestClient();
};
PMVariables.prototype.load = function () {
    var that = this;
    var restProxy = new PMRestClient({
        endpoint: 'process-variables',
        typeRequest: 'get',
        functionSuccess: function (xhr, response) {
            var listInputDocs = response;
            that.gridVariables.setDataItems(listInputDocs);
            that.gridVariables.sort('var_name', 'asc');
            $(that.gridVariables.dom.toolbar).find("input").val("");
            that.gridVariables.clearFilter();
            if (that.currentVariable !== null) {
                var var_name_old = that.currentVariable.var_name_old;
                for (var i = 0; i < response.length; i++) {
                    if (that.currentVariable.var_uid === response[i].var_uid) {
                        that.currentVariable = response[i];
                    }
                }
                that.currentVariable.var_name_old = var_name_old;
            }
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        }
    });
    restProxy.executeRestClient();
};
PMVariables.prototype.del = function (data, row, grid) {
    var that = this;
    var confirmWindow = new PMUI.ui.MessageWindow({
        id: 'confirmWindowDel',
        windowMessageType: 'warning',
        width: 490,
        bodyHeight: 'auto',
        title: "Variables".translate(),
        message: 'Do you want to delete this variable?'.translate(),
        footerItems: [{
            text: 'No'.translate(),
            visible: true,
            handler: function () {
                confirmWindow.close();
            },
            buttonType: "error"
        }, {
            text: 'Yes'.translate(),
            visible: true,
            handler: function () {
                confirmWindow.close();
                that.deleteVariable(data.var_uid, row);
            },
            buttonType: "success"
        }
        ]
    });
    confirmWindow.open();
    confirmWindow.dom.titleContainer.style.height = '17px';
    confirmWindow.showFooter();
};

PMVariables.prototype.customCss = function () {
    $("#inp_doc_uidPanel").css({padding: ""});
    $("#inp_doc_uid").css({width: "75%"}).find("label:eq(0)").css({float: "left", width: "31.5%"});

    var td = $("#chckboxOption .pmui-field-control-table td")[0];

    if (typeof(td) != "undefined") {
        td.setAttribute("style", "padding:0px !important");
    }

    this.formVariables.panel.getHTML().setAttribute("style", "overflow: initial");
};

PMVariables.prototype.showGrid = function () {
    var that = this;
    that.formVariables.setVisible(false);
    that.disableAcceptedValuesPanel();
    that.gridAcceptedValues.setVisible(false);
    that.disableBooleanPanel();
    $(that.gridVariables.dom.toolbar).find("input").val("");
    that.gridVariables.clearFilter();
    that.gridVariables.setVisible(true);
    that.windowVariables.setTitle('Variables'.translate());
};
PMVariables.prototype.showForm = function () {
    var that = this;
    that.edit = false;
    that.dirtyAcceptedValue = false;
    that.formVariables.setVisible(true);
    that.enableAcceptedValuesPanel();
    that.gridAcceptedValues.setVisible(true);
    that.enableBooleanPanel();
    that.gridVariables.setVisible(false);
    that.windowVariables.setTitle('Create Variable'.translate());
    that.formVariables.reset();
    that.formVariables.setFocus();
    that.changeViewFieldType('string');
    that.resetAcceptedValuesPanel();
    that.gridAcceptedValues.clearItems();
    that.resetBooleanPanel();
    that.buttonFieldCancel.setVisible(false);
    that.windowVariables.showFooter();
    that.buttonCreateInputDocument.setVisible(false);
    that.buttonEditInputDocument.setVisible(false);
};
PMVariables.prototype.showFormEdit = function (data) {
    var that = this;
    that.showForm();
    that.var_uid = data.var_uid;
    that.edit = true;
    that.var_name = data.var_name;
    that.windowVariables.setTitle('Edit Variable'.translate());
    that.formVariables.getField('var_dbconnection').setValue(data.var_dbconnection);
    that.formVariables.getField('var_field_type').setValue(data.var_field_type);
    that.formVariables.getField('var_label').setValue(data.var_label);
    that.formVariables.getField('var_name').setValue(data.var_name);
    that.formVariables.getField('var_sql').setValue(data.var_sql);
    that.formVariables.getField('inp_doc_uid').setValue(data.inp_doc_uid);

    that.setVarOptionsControl(data.var_accepted_values);
    that.changeViewFieldType(data.var_field_type);
    that.setDataAcceptedValues(data.var_accepted_values);
    that.currentVariable = data;
    that.currentVariable.var_name_old = that.currentVariable.var_name;
};
PMVariables.prototype.loadDataBaseConnections = function () {
    var that = this;
    var restProxy = new PMRestClient({
        endpoint: 'database-connections',
        typeRequest: 'get',
        functionSuccess: function (xhr, response) {
            var dropdown = that.formVariables.getField('var_dbconnection');
            for (var i = 0; i < response.length; i++) {
                if (response[i].dbs_connection_type == "TNS") {
                    dropdown.addOption({
                        label: "[" + response[i].dbs_tns + "] " + response[i].dbs_type + " : " + response[i].dbs_database_description,
                        value: response[i].dbs_uid
                    });
                } else {
                    dropdown.addOption({
                        label: "[" + response[i].dbs_server + ":" + response[i].dbs_port + "] " + response[i].dbs_type + ": " + response[i].dbs_database_name + response[i].dbs_database_description,
                        value: response[i].dbs_uid
                    });
                }
            }
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        }
    });
    restProxy.executeRestClient();
};
PMVariables.prototype.changeViewFieldType = function (newValue) {
    var that = this;
    var sw = that.formVariables.getField('var_options_control').controls[0].selected;

    that.formVariables.getField('var_dbconnection').setVisible(false);
    that.formVariables.getField('var_sql').setVisible(false);
    that.formVariables.getField('var_options_control').setVisible(false);
    that.formVariables.getField('inp_doc_uid').setVisible(false);
    that.formVariables.getField('inp_doc_uid').setRequired(false);
    that.disableAcceptedValuesPanel();
    that.gridAcceptedValues.setVisible(false);
    that.disableBooleanPanel();
    that.buttonCreateInputDocument.setVisible(false);
    that.buttonEditInputDocument.setVisible(false);

    validateKeysField(that.formAcceptedValues.getField('keyValue').getControls()[0].getHTML(), []);

    if (this.fieldInfo === null) {
        this.fieldInfo = document.createTextNode('');
        var a = that.formVariables.getField('var_field_type').getControls()[0];
        a.html.parentNode.appendChild(document.createElement('br'));
        a.html.parentNode.appendChild(this.fieldInfo);
    }

    switch (newValue) {
        case 'string':
            that.formVariables.getField('var_dbconnection').setVisible(true);
            that.formVariables.getField('var_sql').setVisible(true);
            that.formVariables.getField('var_options_control').setVisible(true);
            sw ? that.enableAcceptedValuesPanel() : that.disableAcceptedValuesPanel();
            that.gridAcceptedValues.setVisible(true && sw);
            this.fieldInfo.data = "Supported Controls: text, textarea, dropdown, radio, suggest, hidden.".translate();

            /*----------------------------------********---------------------------------*/
            /**features-begins**/
            this.fieldInfo.data = "Supported Controls: text, textarea, dropdown, radio, suggest, hidden, geo map, qr code.".translate();
            /**features-ends**/
            /*----------------------------------********---------------------------------*/

            break;
        case 'integer':
            that.formVariables.getField('var_dbconnection').setVisible(true);
            that.formVariables.getField('var_sql').setVisible(true);
            that.formVariables.getField('var_options_control').setVisible(true);
            sw ? that.enableAcceptedValuesPanel() : that.disableAcceptedValuesPanel();
            that.gridAcceptedValues.setVisible(true && sw);

            validateKeysField(that.formAcceptedValues.getField('keyValue').getControls()[0].getHTML(), ['isbackspace', 'isnumber', 'ishyphen']);
            this.fieldInfo.data = "Supported Controls: text, textarea, dropdown, radio, suggest, hidden.".translate();
            break;
        case 'float':
            that.formVariables.getField('var_dbconnection').setVisible(true);
            that.formVariables.getField('var_sql').setVisible(true);
            that.formVariables.getField('var_options_control').setVisible(true);
            sw ? that.enableAcceptedValuesPanel() : that.disableAcceptedValuesPanel();
            that.gridAcceptedValues.setVisible(true && sw);

            validateKeysField(that.formAcceptedValues.getField('keyValue').getControls()[0].getHTML(), ['isbackspace', 'isnumber', 'isperiod', 'ishyphen']);
            this.fieldInfo.data = "Supported Controls: text, textarea, dropdown, radio, suggest, hidden.".translate();
            break;
        case 'boolean':
            that.enableBooleanPanel();
            this.fieldInfo.data = "Supported Controls: checkbox, radio, hidden.".translate();
            break;
        case 'datetime':
            this.fieldInfo.data = "Supported Controls: datetime, hidden.".translate();
            break;
        case "grid":
            this.fieldInfo.data = "Supported Controls: grid.".translate();
            break;
        case "array":
            that.formVariables.getField('var_dbconnection').setVisible(true);
            that.formVariables.getField('var_sql').setVisible(true);
            that.formVariables.getField('var_options_control').setVisible(true);
            sw ? that.enableAcceptedValuesPanel() : that.disableAcceptedValuesPanel();
            that.gridAcceptedValues.setVisible(true && sw);
            this.fieldInfo.data = "Supported Controls: checkgroup.".translate();
            break;
        case 'file':
            that.formVariables.getField('inp_doc_uid').setVisible(true);
            that.buttonCreateInputDocument.setVisible(true);
            that.buttonEditInputDocument.setVisible(true);
            that.formVariables.getField('inp_doc_uid').setRequired(true);
            this.fieldInfo.data = "Supported Controls: file.".translate();
            that.validateInputDoc();
            break;
        case 'multiplefile':
            this.fieldInfo.data = "Supported Controls: Multiple File.".translate();
            break;
        case 'object':
            this.fieldInfo.data = "Supported Controls:".translate();
            break;
    }
};
/**
 * Enable or disable edit button of input document
 */
PMVariables.prototype.validateInputDoc = function () {
    var form = this.formVariables,
        fieldInpDoc = form.getField("inp_doc_uid"),
        defaultText = "- Select an input document -".translate();
    if (fieldInpDoc && fieldInpDoc.getValue() && fieldInpDoc.getValue() !== defaultText) {
        this.buttonEditInputDocument.enable();
    } else {
        this.buttonEditInputDocument.disable();
    }
};
PMVariables.prototype.addAcceptedValue = function () {
    var that = this,
        key = jQuery.trim(that.formAcceptedValues.getField('keyValue').getValue()),
        value = jQuery.trim(that.formAcceptedValues.getField('value').getValue());

    if (that.isAcceptedValueAdded()) {
        return PMDesigner.msgFlash('The key value already exists.'.translate(),
            document.getElementById('windowVariables'), 'error', 1000, 5);
    }
    if (!(key && value)) {
        return PMDesigner.msgFlash('The key and label must be supplied.'.translate(),
            document.getElementById('windowVariables'), 'error', 1000, 5);
    }
    if (that.editRow === null) {
        that.gridAcceptedValues.addItem(new PMUI.grid.GridPanelRow({
            data: {
                keyValue: key,
                value: value
            }
        }));
    } else {
        this.editingOptions = false;
        that.editRow.setData({
            keyValue: key,
            value: value
        });
    }
    that.dirtyAcceptedValue = true;
    that.cancelAcceptedValue();
};
PMVariables.prototype.editAcceptedValue = function (row) {
    var that = this;
    this.editingOptions = true;
    that.editRow = row;
    var data = row.getData();
    that.formAcceptedValues.getField('keyValue').setValue(data.keyValue);
    that.formAcceptedValues.getField('value').setValue(data.value);
    that.buttonFieldAdd.setValue('Save'.translate());
    that.buttonFieldCancel.setVisible(true);

    that.initialFormAcceptedValuesKeyValue = data.keyValue;
    that.initialFormAcceptedValuesValue = data.value;
};
PMVariables.prototype.deleteAcceptedValue = function (row) {
    var that = this;
    var confirmWindow = new PMUI.ui.MessageWindow({
        id: 'confirmWindowDeleteAcceptedValue',
        windowMessageType: 'warning',
        width: 490,
        bodyHeight: 'auto',
        title: "Variables".translate(),
        message: 'Do you want to delete this Key Value?'.translate(),
        footerItems: [{
            text: 'No'.translate(),
            visible: true,
            handler: function () {
                confirmWindow.close();
            },
            buttonType: "error"
        }, {
            text: 'Yes'.translate(),
            visible: true,
            handler: function () {
                confirmWindow.close();
                that.gridAcceptedValues.removeItem(row);
                that.dirtyAcceptedValue = true;
            },
            buttonType: "success"
        }
        ]
    });
    confirmWindow.open();
    confirmWindow.dom.titleContainer.style.height = '17px';
    confirmWindow.showFooter();
};
PMVariables.prototype.getDataAcceptedValues = function () {
    var that = this, data = [], i, dt = [];
    if (that.gridAcceptedValues.visible) {
        dt = that.gridAcceptedValues.getData();
        for (i = 0; i < dt.length; i++) {
            delete dt[i].key;
            delete dt[i].type;
            data.push({
                value: dt[i].keyValue,
                label: dt[i].value
            });
        }
    }
    if (that.formBooleanOptions.visible) {
        var a = that.formBooleanOptions.getItems("fields").reduce(function (prev, curr) {
                prev[curr.getName()] = curr.getValue();
                return prev;
            }, {});

        data = [
            {value: '1', label: a.trueOption},
            {value: '0', label: a.falseOption}
        ];
    }
    return data;
};
PMVariables.prototype.setDataAcceptedValues = function (stringJsonData) {
    if (!stringJsonData) {
        return;
    }
    var that = this, i, data = stringJsonData;
    if (typeof stringJsonData === 'string') {
        data = JSON.parse(stringJsonData);
    }
    if (that.gridAcceptedValues.visible) {
        for (i = 0; i < data.length; i++) {
            that.gridAcceptedValues.addItem(new PMUI.grid.GridPanelRow({
                data: {
                    keyValue: data[i].keyValue ? data[i].keyValue : data[i].value,
                    value: data[i].keyValue ? data[i].value : data[i].label
                }
            }));
            that.initialGridAcceptedValuesOrder.push(data[i].keyValue ? data[i].keyValue : data[i].value);
        }
    }
    if (that.formBooleanOptions.visible) {
        that.formBooleanOptions.getField('trueOption').setValue(data[0].label);
        that.formBooleanOptions.getField('falseOption').setValue(data[1].label);
    }
};
PMVariables.prototype.validateAcceptedValues = function () {
    var that = this,
        dirty = false,
        finalVal1,
        finalVal2,
        initVal1,
        initVal2,
        gridAcceptedValues,
        finalGridAcceptedValuesOrder = [],
        key;
    if (that.initialFormAcceptedValuesValue && that.formAcceptedValues.getField('value').getValue() != '') {
        finalVal1 = that.formAcceptedValues.getField('value').getValue();
        finalVal2 = that.formAcceptedValues.getField('keyValue').getValue();
        initVal1 = that.initialFormAcceptedValuesValue;
        initVal2 = that.initialFormAcceptedValuesKeyValue;
        if (finalVal1 !== initVal1 || finalVal2 !== initVal2) {
            dirty = true;
        }
    } else if (that.formAcceptedValues.getField('value').getValue() !== '' || that.formAcceptedValues.getField('keyValue').getValue()) {
        dirty = true;
    }

    if (that.initialGridAcceptedValuesOrder.length) {
        gridAcceptedValues = that.gridAcceptedValues.getData();
        for (key in gridAcceptedValues) {
            if (gridAcceptedValues.hasOwnProperty(key)) {
                finalGridAcceptedValuesOrder.push(gridAcceptedValues[key].keyValue);
            }
        }
        if (JSON.stringify(finalGridAcceptedValuesOrder) !== JSON.stringify(that.initialGridAcceptedValuesOrder)) {
            dirty = true;
        }
    }
    this.validateAcceptedValuesFlag = dirty;
};
PMVariables.prototype.setVarOptionsControl = function (stringJsonData) {
    if (!stringJsonData) {
        return;
    }
    var that = this, i, data = stringJsonData;
    if (typeof stringJsonData === 'string') {
        data = JSON.parse(stringJsonData);
    }
    if (data.length > 0) {
        that.formVariables.getField('var_options_control').setValue("['1']");
    }
};
PMVariables.prototype.cancelAcceptedValue = function () {
    var that = this;
    that.editRow = null;
    that.buttonFieldAdd.setValue('Add'.translate());
    that.buttonFieldCancel.setVisible(false);
    that.resetAcceptedValuesPanel();
};
PMVariables.prototype.isAcceptedValueAdded = function () {
    var that = this, i, keyValue, data, exist, i, index, rowEditValue;
    data = that.gridAcceptedValues.getData();
    keyValue = that.formAcceptedValues.getField('keyValue').getValue() || "";
    if (this.editingOptions) {
        rowEditValue = this.editRow.getData()["keyValue"];
        for (i = 0; i < data.length; i++) {
            if (rowEditValue === data[i].keyValue) {
                index = i;
                break;
            }
        }
        for (i = 0; i < data.length; i++) {
            if (i !== index) {
                if (keyValue === data[i].keyValue) {
                    exist = true;
                }
            }
        }
        if (exist) {
            return true;
        } else {
            return false;
        }
    } else {
        for (i = 0; i < data.length; i++) {
            if (data[i].keyValue === keyValue) {
                return true;
            }
        }
    }
    return false;
};
PMVariables.prototype.setInputDocuments = function (inp_doc_uid) {
    var restClient = new PMRestClient({
        endpoint: 'input-documents',
        typeRequest: 'get',
        functionSuccess: function (xhr, response) {
            inputDocumentsData = response;
            var arrayOptions = [];
            arrayOptions[0] = {
                label: "- Select an input document -".translate(),
                value: "",
                disabled: true,
                selected: true
            };
            for (var i = 0; i <= inputDocumentsData.length - 1; i++) {
                arrayOptions.push(
                    {
                        value: inputDocumentsData[i].inp_doc_uid,
                        label: inputDocumentsData[i].inp_doc_title
                    }
                );
            }
            inp_doc_uid.setOptions(arrayOptions);
            inp_doc_uid.setValue(arrayOptions[0].value);
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        },
    });
    restClient.executeRestClient();
};
PMVariables.prototype.setInputDocumentsFromIDModule = function (inp_doc_uid, response) {
    var inp_doc_uid_val = $("#inp_doc_uid").find("select:eq(0) option:selected").val();
    var arrayOptions = [];
    inputDocumentsData = response;
    arrayOptions[0] = {
        label: "- Select an input document -".translate(),
        value: "",
        disabled: true,
        selected: true
    };
    for (var i = 0; i <= inputDocumentsData.length - 1; i++) {
        arrayOptions.push(
            {
                value: inputDocumentsData[i].inp_doc_uid,
                label: inputDocumentsData[i].inp_doc_title
            }
        );
    }
    inp_doc_uid.setOptions(arrayOptions);
    inp_doc_uid.setValue(arrayOptions[0].value);
    if (inp_doc_uid_val) {
        inp_doc_uid.setValue(inp_doc_uid_val);
    }
};
PMVariables.prototype.isWindowActive = function () {
    if ($("#formVariables").is(":visible")) {
        return true;
    }
    return false;
};
/**
 * Reset the fields from the form's boolean panel.
 * @returns {PMVariables}
 */
PMVariables.prototype.resetBooleanPanel = function () {
    if (this.formBooleanOptions) {
        this.formBooleanOptions.getItems("fields").forEach(function (i) {
            i.setValue("");
        });
    }
    return this;
};
/**
 * Disable the fields from the form's boolean panel.
 * @returns {PMVariables}
 */
PMVariables.prototype.disableBooleanPanel = function () {
    if (this.formBooleanOptions) {
        this.formBooleanOptions.setVisible(false)
            .getItems("fields").forEach(function (i) {
            i.disable();
        });
    }
    return this;
};
/**
 * Enable the fields from the form's boolean panel.
 * @returns {PMVariables}
 */
PMVariables.prototype.enableBooleanPanel = function () {
    if (this.formBooleanOptions) {
        this.formBooleanOptions.setVisible(true)
            .getItems("fields").forEach(function (i) {
            i.enable();
        });
    }
    return this;
};
/**
 * Reset the fields from the form's Accepted Values panel.
 * @returns {PMVariables}
 */
PMVariables.prototype.resetAcceptedValuesPanel = function () {
    if (this.formAcceptedValues) {
        this.formAcceptedValues.getItems().forEach(function (i) {
            i.setValue("");
        });
    }
    return this;
};
/**
 * Enable the fields from the form's Accepted Values panel.
 * @returns {PMVariables}
 */
PMVariables.prototype.enableAcceptedValuesPanel = function () {
    if (this.formAcceptedValues) {
        this.formAcceptedValues.setVisible(true)
            .getItems('fields').forEach(function (i) {
            i.enable("");
        });
    }
    return this;
};
/**
 * Disable the fields from the form's Accepted Values panel.
 * @returns {PMVariables}
 */
PMVariables.prototype.disableAcceptedValuesPanel = function () {
    if (this.formAcceptedValues) {
        this.formAcceptedValues.setVisible(false)
            .getItems('fields').forEach(function (i) {
            i.disable("");
        });
    }
    return this;
};
PMDesigner.variables = function () {
    var pmvariables = new PMVariables();
    pmvariables.load();
};
PMDesigner.variables.create = function () {
    var pmvariables = new PMVariables();
    pmvariables.showForm();
    pmvariables.load();
};

RowVariableCondition = function () {
    PMUI.form.FormPanel.call(this, {
        layout: 'hbox'
    });
    RowVariableCondition.prototype.init.call(this);
};
RowVariableCondition.prototype = new PMUI.form.FormPanel();
RowVariableCondition.prototype.createHTML = function () {
    var items;
    PMUI.form.FormPanel.prototype.createHTML.call(this);
    items = this.getItems();
    items[0].dom.labelTextContainer.style.display = 'none';
    items[1].dom.labelTextContainer.style.display = 'none';
    items[2].dom.labelTextContainer.style.display = 'none';
    items[2].style.addProperties({display: 'none'});
    items[2].controls[0].button.setButtonType('error');
    return this.html;
};
RowVariableCondition.prototype.init = function () {
    var that = this,
        typeData,
        field,
        remove;
    field = new PMUI.field.TextField({
        id: 'idField',
        name: '',
        required: true,
        controlsWidth: 425,
        proportion: 2.1,
        labelVisible: false
    });
    typeData = new PMUI.field.DropDownListField({
        id: 'idTypeData',
        name: '',
        valueType: 'string',
        value: '',
        readOnly: true,
        controlsWidth: 200,
        labelVisible: false,
        options: [{
            value: 'integer',
            label: 'Integer'.translate()
        }, {
            value: 'string',
            label: 'String'.translate()
        }, {
            value: 'float',
            label: 'Float'.translate()
        }, {
            value: 'boolean',
            label: 'Boolean'.translate()
        }, {
            value: 'date',
            label: 'Date'.translate()
        }, {
            value: 'datetime',
            label: 'Datetime'.translate()
        }
        ]
    });
    remove = new PMUI.field.ButtonField({
        id: 'idRemove',
        value: 'Delete'.translate(),
        labelVisible: false,
        handler: function (e, a) {
            that.getParent().removeItem(that);
        },
        name: 'delete',
        controlsWidth: 60
    });
    that.addItem(field);
    that.addItem(typeData);
    that.addItem(remove);
};

PropertiesReceiveMessage = function (menuOption) {
    this.variables = [];
    this.onApply = new Function();
    this.onCancel = new Function();
    this.menuOption = menuOption;
    Mafe.Window.call(this);
    PropertiesReceiveMessage.prototype.init.call(this);
};
PropertiesReceiveMessage.prototype = new Mafe.Window();
PropertiesReceiveMessage.prototype.init = function () {
    var that = this;
    that.setTitle(that.menuOption.getMenuTargetElement().evn_name);
    that.setButtons([
        new PMUI.ui.Button({
            id: 'btnClose',
            text: 'Cancel'.translate(),
            buttonType: 'error',
            height: 31,
            handler: function () {
                that.close();
                that.onCancel();
            }
        }),
        new PMUI.ui.Button({
            id: 'windowDynaformInformationSaveOpen',
            text: 'Apply'.translate(),
            buttonType: 'success',
            height: 31,
            handler: function () {
                that.onApply();
            }
        })
    ]);
    that.buttonAdd = new PMUI.ui.Button({
        text: 'Add Variable'.translate(),
        buttonType: 'success',
        height: 31,
        style: {cssProperties: {marginLeft: '50px', marginTop: '10px'}},
        handler: function () {
            that.addVariable();
        }
    });
    that.form = new Mafe.Form({
        title: that.menuOption.getMenuTargetElement().evn_name,
        width: DEFAULT_WINDOW_WIDTH - 60,
        style: {cssProperties: {'margin-left': '35px'}}
    });
    that.conditionForm = new Mafe.Form({
        visibleHeader: false,
        width: DEFAULT_WINDOW_WIDTH - 60,
        style: {cssProperties: {'margin-left': '35px'}},
        items: [{
            label: 'Condition',
            labelPosition: 'top',
            pmType: 'textarea',
            rows: 100,
            style: {cssClasses: ['mafe-textarea-resize']}
        }
        ]
    });

    that.addItem(that.buttonAdd);
    that.addItem(that.form);
    that.addItem(that.conditionForm);

    that.addVariable();
};
PropertiesReceiveMessage.prototype.addVariable = function () {
    var that = this, a, i;
    a = new RowVariableCondition(this.variables);
    that.form.addItem(a);
    //force padding
    for (i = 0; i < that.form.getItems().length; i += 1) {
        that.form.getItems()[i].style.addProperties({'padding': 'initial'});
    }
};

EventMessagesGrid = function () {
    this.onCreate = new Function();
    this.onEdit = new Function();
    this.onDel = new Function();
    Mafe.Grid.call(this);
    EventMessagesGrid.prototype.init.call(this);
};
EventMessagesGrid.prototype = new Mafe.Grid();
EventMessagesGrid.prototype.createHTML = function () {
    Mafe.Grid.prototype.createHTML.call(this);
    this.dom.toolbar.appendChild(this.buttonCreate.getHTML());
    return this.html;
};
EventMessagesGrid.prototype.init = function () {
    var that = this;
    that.buttonCreate = new PMUI.ui.Button({
        id: 'idButtonEventMessagesGrid',
        text: 'Create'.translate(),
        height: '36px',
        width: 100,
        style: {cssClasses: ['mafe-button-create']},
        handler: function (event) {
            that.onCreate(event);
        }
    });
    that.buttonCreate.defineEvents();
    that.setID('idEventMessagesGrid');
    that.setColumns([{
        id: '',
        title: 'Nombre',
        sortable: true,
        width: '460px',
        dataType: 'string',
        alignmentCell: 'left',
        columnData: 'mes_title'
    }, {
        id: 'dynaformGridPanelEdit',
        title: '',
        dataType: 'button',
        buttonStyle: {cssClasses: ['mafe-button-edit']},
        buttonLabel: function (row, data) {
            return 'Edit'.translate();
        },
        onButtonClick: function (row, grid) {
            that.onEdit(row, grid);
        }
    }, {
        id: 'dynaformGridPanelDelete',
        title: '',
        dataType: 'button',
        buttonStyle: {cssClasses: ['mafe-button-delete']},
        buttonLabel: function (row, data) {
            return 'Delete'.translate();
        },
        onButtonClick: function (row, grid) {
            that.onDel(row, grid);
        }
    }
    ]);
    that.load();
};
EventMessagesGrid.prototype.show = function () {

};
EventMessagesGrid.prototype.create = function () {

};
EventMessagesGrid.prototype.load = function () {
    var that = this,
        dt = [
            {mes_title: 'message1-1'},
            {mes_title: 'message1-2'},
            {mes_title: 'message1-3'},
            {mes_title: 'message1-4'}
        ];
    that.setDataItems(dt);
};

EventMessagesForm = function () {
    Mafe.Form.call(this);
    EventMessagesForm.prototype.init.call(this);
};
EventMessagesForm.prototype = new Mafe.Form();
EventMessagesForm.prototype.init = function () {
    var that = this;
    that.setID('idEventMessagesForm');
    that.setItems({
        id: '',
        pmType: 'text',
        label: 'Name'.translate(),
        value: '',
        maxLength: 100,
        placeholder: '',
        name: '',
        required: true,
        controlsWidth: 300
    });
    that.buttons = [
        new PMUI.ui.Button({
            id: 'btnClose',
            text: 'Cancel'.translate(),
            buttonType: 'error',
            height: 31,
            handler: function () {
                that.onCancel();
            }
        }),
        new PMUI.ui.Button({
            id: 'windowDynaformPmtableSave',
            text: 'Save'.translate(),
            buttonType: 'success',
            height: 31,
            handler: function () {
                that.onSave();
            }
        })
    ];
};
EventMessagesForm.prototype.getButtons = function () {
    return this.buttons;
};

EventMessages = function () {
    Mafe.Window.call(this);

    this.list = new EventMessagesGrid();
    this.form = new EventMessagesForm();

    EventMessages.prototype.init.call(this);
};
EventMessages.prototype = new Mafe.Window();
EventMessages.prototype.init = function () {
    var that = this;
    that.list.onCreate = function () {
        that.resetView();
        that.form.setVisible(true);
    };
    that.setTitle("Event Messages");
    that.addItem(that.list);
};
EventMessages.prototype.showForm = function () {
    this.list.show();
};

PMDesigner.eventMessages = function () {
    var a = new EventMessages();
    a.open();
};
PMDesigner.eventMessages.create = function () {
};

var UserPicker = function (options) {
    this.relatedField = null;
    this.processId = null;
    this.workspace = null;
    this.window = null;
    this.currentVariable = {};
    this.pageSize = 10;
    UserPicker.prototype.init.call(this, options);
};

UserPicker.prototype.type = 'UserPicker';

UserPicker.prototype.family = 'UserPicker';

UserPicker.prototype.init = function (options) {
    var defaults = {
        relatedField: null,
        processId: PMDesigner.project.projectId,
        workspace: WORKSPACE
    };

    jQuery.extend(true, defaults, options);

    this.setRelatedField(defaults.relatedField)
        .setProcessId(defaults.processId)
        .setWorkspace(defaults.workspace);
};

UserPicker.prototype.setRelatedField = function (field) {
    if (field instanceof PMUI.form.Field) {
        this.relatedField = field;
    }
    return this;
};

UserPicker.prototype.setProcessId = function (process) {
    this.processId = process;
    return this;
};

UserPicker.prototype.setWorkspace = function (workspace) {
    this.workspace = workspace;
    return this;
};

UserPicker.prototype.open = function (callback) {
    var w, rc, fieldC, dataGrid, panel, textField, that = this, button, panelFilter;
    button = new PMUI.ui.Button({
        id: 'insertUser',
        text: 'Insert User'.translate(),
        handler: function () {
            if (callback && callback.success && typeof callback.success === 'function') {
                callback.success.call(that, that.currentVariable);
            }
            that.close();
        },
        disabled: true
    });

    textField = new PMUI.field.TextField({
        id: 'textFieldSearch',
        label: '',
        placeholder: 'Search ...'.translate()
    });

    w = new PMUI.ui.Window({
        id: 'processVariables',
        title: 'Process Users'.translate(),
        width: 480,
        height: 420,
        closable: true,
        modal: true,
        buttons: [
            button
        ],
        buttonsPosition: 'center'
    });

    textField = new PMUI.field.TextField({
        id: 'textFieldSearch',
        label: '',
        placeholder: 'Search ...'.translate(),
        width: 150
    });

    dataGrid = new PMUI.grid.GridPanel({
        id: 'gridPanel',
        selectable: true,
        pageSize: this.pageSize,
        nextLabel: 'Next'.translate(),
        previousLabel: 'Previous'.translate(),
        customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
            return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
        },
        columns: [
            {
                id: 'gridPanelUserName',
                title: 'User Name'.translate(),
                columnData: 'usr_username',
                width: 150,
                sortable: true,
                alignmentCell: 'left'
            },
            {
                id: 'gridPanelRole',
                title: 'Role'.translate(),
                columnData: 'usr_role',
                width: 230,
                sortable: false,
                alignmentCell: 'left'
            }
        ],
        onRowClick: function (row, data) {
            button.enable();
            that.currentVariable.username = data.usr_username;
            that.currentVariable.uid = data.usr_uid;
        }

    });

    panelFilter = new PMUI.core.Panel({
        id: 'panelFilter',
        layout: 'vbox',
        items: [/*fieldC,*/ textField]
    });

    panel = new PMUI.core.Panel({
        id: 'paneldataGrid',
        layout: 'vbox',
        items: [panelFilter, dataGrid]
    });

    rc = new PMRestClient({
        typeRequest: 'get',
        functionSuccess: function (xhr, response) {
            console.log(response);
            that.window = w;
            dataGrid.setDataItems(response);
            w.open();
            w.showFooter();
            w.addItem(panel);
            panelFilter.setWidth(430);
            textField.controls[0].onKeyUp = function () {
                console.log(textField.controls[0].html.value);
                dataGrid.filter(textField.controls[0].html.value);
            };
            dataGrid.dom.toolbar.style.display = 'none';
            textField.dom.labelTextContainer.innerHTML = '';
            textField.dom.labelTextContainer.style.marginTop = 5;
            panel.style.addProperties({'padding-left': 20});
            textField.dom.labelTextContainer.style.display = 'none';
            textField.controls[0].setWidth(200);
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        }
    });
    rc.setBaseEndPoint('users').executeRestClient();
};

UserPicker.prototype.close = function () {
    if (this.window) {
        this.window.close();
        this.window = null;
    }
};

var CriteriaUserField = function (options) {
    this.renderType = (options && options.renderType) || "text";
    PMUI.field.TextField.call(this, options);
    this.process = null;
    this.workspace = null;
    this.buttonHTML = null;
    this.rows = options.rows;
    this.user_uid = null;
    CriteriaUserField.prototype.init.call(this, options);
};

CriteriaUserField.prototype = new PMUI.field.TextField();

CriteriaUserField.prototype.setProcess = function (process) {
    this.process = process;
    return this;
};

CriteriaUserField.prototype.setWorkspace = function (workspace) {
    this.workspace = workspace;
    return this;
};

CriteriaUserField.prototype.init = function (options) {
    var defaults = {
        process: PMDesigner.project.projectId,
        workspace: WORKSPACE
    };
    jQuery.extend(true, defaults, options);
    this.setProcess(defaults.process)
        .setWorkspace(defaults.workspace);
};

CriteriaUserField.prototype.createVariablePicker = function () {
    var vp = new UserPicker({
        relatedField: this,
        processId: this.process
    });
    return vp;
};

CriteriaUserField.prototype.setControls = function () {
    if (this.controls.length) {
        return this;
    }
    if (this.renderType === 'text') {
        this.controls.push(new PMUI.control.TextControl());
    } else {
        this.controls.push(new PMUI.control.TextAreaControl({style: {cssProperties: {resize: 'vertical'}}}));
    }
    return this;
};

CriteriaUserField.prototype.createCallBack = function () {
    var that = this, oldValue, newValue, init = 0;
    return {
        success: function (variable) {
            init = that.controls[0].html.selectionStart;
            prevText = that.controls[0].html.value.substr(0, init);
            lastText = that.controls[0].html.value.substr(that.controls[0].html.selectionEnd, that.controls[0].html.value.length);
            newValue = variable.username;

            that.setValue(newValue);
            that.user_uid = variable.uid;
            that.controls[0].html.selectionEnd = init + variable.username.length;
        }
    };
};

CriteriaUserField.prototype.createHTML = function () {
    var button, that = this;
    PMUI.field.TextField.prototype.createHTML.call(this);

    button = new PMUI.ui.Button({
        id: 'buttonCriteriaUserField',
        text: '...',
        handler: function () {
            that.createVariablePicker().open(that.createCallBack());
        },
        style: {
            cssProperties: {
                background: '#2d3e50',
                fontSize: 18,
                paddingLeft: '15px',
                paddingRight: '15px',
                borderRadius: '4px',
                verticalAlign: 'top'
            }
        }
    });

    this.buttonHTML = button;
    $(this.helper.html).before(button.getHTML())
    this.buttonHTML.style.addProperties({"margin-left": "10px"});
    this.buttonHTML.html.tabIndex = -1;

    if (this.rows != null)
        this.controls[0].setHeight(this.rows);
    button.defineEvents();

    return this.html;
};

// Overwrite original init function for FormItemFactory
PMUI.form.FormItemFactory.prototype.init = function () {
    var defaults = {
        products: {
            "criteria": CriteriaUserField,
            "field": PMUI.form.Field,
            "panel": PMUI.form.FormPanel,
            "text": PMUI.field.TextField,
            "password": PMUI.field.PasswordField,
            "dropdown": PMUI.field.DropDownListField,
            "radio": PMUI.field.RadioButtonGroupField,
            "checkbox": PMUI.field.CheckBoxGroupField,
            "textarea": PMUI.field.TextAreaField,
            "datetime": PMUI.field.DateTimeField,
            "optionsSelector": PMUI.field.OptionsSelectorField,
            "buttonField": PMUI.field.ButtonField,
            "annotation": PMUI.field.TextAnnotationField
        },
        defaultProduct: "panel"
    };
    this.setProducts(defaults.products)
        .setDefaultProduct(defaults.defaultProduct);
};

/**
 * Class representing a Web Entry
 * @param relatedShape
 * @constructor
 */
var WebEntry = function (relatedShape) {
    this.relatedShape = null;
    this.groupType = null;
    this.groupLabel = null;
    this.stepsType = null;
    this.actUid = null;
    this.evenUid = null;
    this.weeUid = null;
    this.stepsAssigned = null;
    this.elementAccordionOpen = null;
    this.configWebEntry = null;
    this.isNewWebEntry = null;
    this.windowWebEntry = null;
    this.windowAlternative = null;
    this.tabForm = null;
    this.tabProperties = null;
    this.tabLink = null;
    this.confirmWindow = null;
    this.tabPanelWindow = null;
    this.suggestUser = null;
    this.stepsAssignTree = null;
    this.stepsAssignAccordion = null;
    this.labelsPanel = null;
    this.userGuest = {};
    WebEntry.prototype.initialize.call(this, relatedShape);
};
/**
 * A module representing a Web Entry
 **/
WebEntry.prototype = {
    /**
     * Sets the actUid
     * @param {string} actUid
     */
    setActUid: function (actUid) {
        this.actUid = actUid;
        return this;
    },

    /**
     * Sets the evenUid
     * @param {string} evenUid
     */
    setEvnUid: function (evenUid) {
        this.evenUid = evenUid;
        return this;
    },

    /**
     * Sets the weeUid
     * @param {string} weeUid
     */
    setWeeUid: function (weeUid) {
        this.weeUid = weeUid;
        return this;
    },

    /**
     * Sets the configWebEntry
     * @param {object} configWebEntry
     */
    setConfigWebEntry: function (configWebEntry) {
        this.configWebEntry = configWebEntry;
        return this;
    },

    /**
     * Sets the isNewWebEntry
     * @param {boolean} isNewWebEntry
     */
    setIsNewWebEntry: function (isNewWebEntry) {
        this.isNewWebEntry = isNewWebEntry;
        return this;
    },

    /**
     * Sets the windowWebEntry
     * @param {object} windowWebEntry
     */
    setWindowWebEntry: function (windowWebEntry) {
        this.windowWebEntry = windowWebEntry;
        return this;
    },

    /**
     * Sets the windowAlternative
     * @param {object} windowAlternative
     */
    setWindowAlternative: function (windowAlternative) {
        this.windowAlternative = windowAlternative;
        return this;
    },

    /**
     * Sets the relatedShape
     * @param {object} relatedShape
     */
    setRelatedShape: function (relatedShape) {
        this.relatedShape = relatedShape;
        return this;
    },

    /**
     * Sets the tabForm
     * @param {object} tabForm
     */
    setTabForm: function (tabForm) {
        this.tabForm = tabForm;
        return this;
    },

    /**
     * Sets the tabProperties
     * @param {object} tabProperties
     */
    setTabProperty: function (tabProperties) {
        this.tabProperties = tabProperties;
        return this;
    },

    /**
     * Sets the tabLink
     * @param {object} tabLink
     */
    setTabLink: function (tabLink) {
        this.tabLink = tabLink;
        return this;
    },

    /**
     * Sets the confirmWindow
     * @param {object} confirmWindow
     */
    setConfirmWin: function (confirmWindow) {
        this.confirmWindow = confirmWindow;
        return this;
    },

    /**
     * Sets the tabPanelWindow
     * @param {object} tabPanelWindow
     */
    setTabPanelWindow: function (tabPanelWindow) {
        this.tabPanelWindow = tabPanelWindow;
        return this;
    },

    /**
     * Sets the suggestUser
     * @param {object} suggestUser
     */
    setSuggestUser: function (suggestUser) {
        this.suggestUser = suggestUser;
        return this;
    },

    /**
     * Sets the stepsAssignTree
     * @param {object} stepsAssignTree
     */
    setStepsTree: function (stepsAssignTree) {
        this.stepsAssignTree = stepsAssignTree;
        return this;
    },

    /**
     * Sets the stepsAssignAccordion
     * @param {object} stepsAssignAccordion
     */
    setStepsAccordion: function (stepsAssignAccordion) {
        this.stepsAssignAccordion = stepsAssignAccordion;
        return this;
    },

    /**
     * Sets the labelsPanel
     * @param {object} labelsPanel
     */
    setLabelPanel: function (labelsPanel) {
        this.labelsPanel = labelsPanel;
        return this;
    },

    /**
     * Sets the userGuest
     * @param userGuest
     */
    setUserGuest: function (userGuest) {
        this.userGuest = userGuest;
        return this;
    },

    /**
     * Get the userGuest
     * @returns {Object} userGuest
     */
    getUserGuest: function () {
        return this.userGuest || {};
    },

    /**
     * Get the actUid value
     * @returns {null|*|string} The actUid value
     */
    getActUid: function () {
        return this.actUid || "";
    },

    /**
     * Get the isNewWebEntry value
     * @returns {null|*|boolean} The isNewWebEntry value
     */
    getRelatedShape: function () {
        return this.relatedShape || [];
    },

    /**
     * Get the evenUid value
     * @returns {null|*|string} The evenUid value
     */
    getEvnUid: function () {
        return this.evenUid || "";
    },

    /**
     * Get the weeUid value
     * @returns {null|*|string} The weeUid value
     */
    getWeeUid: function () {
        return this.weeUid || "";
    },

    /**
     * Get the configWebEntry value
     * @returns {*|null|Array} The configWebEntry value
     */
    getConfigWebEntry: function () {
        return this.configWebEntry || [];
    },

    /**
     * Get the isNewWebEntry value
     * @returns {null|*|boolean} The isNewWebEntry value
     */
    getIsNewWebEntry: function () {
        return this.isNewWebEntry || false;
    },

    /**
     * Get the windowWebEntry value
     * @returns {null|*} The windowWebEntry value
     */
    getWindowWebEntry: function () {
        return this.windowWebEntry || null;
    },

    /**
     * Get the windowAlternative value
     * @returns {*|null} The windowAlternative value
     */
    getWindowAlternative: function () {
        return this.windowAlternative || null;
    },

    /**
     * Get the tabForm value
     * @returns {*|null} The tabForm value
     */
    getTabForm: function () {
        return this.tabForm || null;
    },

    /**
     * Get the tabProperties value
     * @returns {null|*} The tabProperties value
     */
    getTabProperty: function () {
        return this.tabProperties || null;
    },

    /**
     * Get the tablink value
     * @returns {null|*} The tablink value
     */
    getTabLink: function () {
        return this.tabLink || null;
    },

    /**
     * Get the confirmWindow value
     * @returns {null|*} The confirmWindow value
     */
    getConfirmWin: function () {
        return this.confirmWindow || null;
    },

    /**
     * Get the tabPanelWindow
     * @returns {*|null}
     */
    getTabPanelWindow: function () {
        return this.tabPanelWindow || null;
    },

    /**
     * get the suggestUser value
     * @returns {*|null} The suggestUser value
     */
    getSuggestUser: function () {
        return this.suggestUser || null;
    },

    /**
     * Sets the stepsAssignTree
     * @returns {*|null}
     */
    getStepsTree: function () {
        return this.stepsAssignTree || null;
    },

    /**
     * Get the stepsAssignAccordion value
     * @returns {*|null} The stepsAssignAccordion value
     */
    getStepsAccordion: function () {
        return this.stepsAssignAccordion || null;
    },

    /**
     * get the labelsPanel value
     * @returns {null|*} The labelsPanel value
     */
    getLabelPanel: function () {
        return this.labelsPanel || null;
    },

    /**
     * initialize App
     * @param relatedShape
     */
    initialize: function (relatedShape) {
        this.groupType = [
            'DYNAFORM', 'INPUT_DOCUMENT',
            'OUTPUT_DOCUMENT', 'EXTERNAL'
        ];
        this.groupLabel = [
            'Dynaform (s)'.translate(), 'Input Document (s)'.translate(),
            'OutPut Document (s)'.translate(), 'External (s)'.translate()
        ];
        this.stepsType = {
            'DYNAFORM': 'Dynaform'.translate(),
            'INPUT_DOCUMENT': 'Input Document'.translate(),
            'OUTPUT_DOCUMENT': 'Output Document'.translate(),
            'EXTERNAL': 'External'.translate()
        };

        this.setRelatedShape(relatedShape)
            .setEvnUid(relatedShape.evn_uid);

        if (this.getRelatedShape().getPorts().getFirst()) {
            this.setActUid(this.getRelatedShape().getPorts().getFirst().getConnection().getDestPort().getParent()
                .act_uid);
        }
        if (__env.USER_GUEST) {
            $.extend(true, this.userGuest, __env.USER_GUEST);
        }
        this.stepsAssigned = new PMUI.util.ArrayList();
        this.elementAccordionOpen = new PMUI.util.ArrayList();
    },

    /**
     * Render form
     * @returns {WebEntry}
     */
    render: function () {
        this.getWindow().addItem(this.getTabPanel());
        this.getWindow().open();
        this.getWindow().showFooter();
        this.initializeData();
        return this;
    },

    /**
     * Populate Web Entry data
     */
    initializeData: function () {
        //Pupulate web entry data
        this.getInstanceWebEntryData();
        this.setWebEntryConfiguration();
        this.initializeAccordionAndTreepanelData();
    },

    /**
     * Populate accordion and treepanel data
     */
    initializeAccordionAndTreepanelData: function () {
        var that = this;
        //clear global array stepsAssigned
        this.stepsAssigned.clear();
        //get accordion data
        this.getAccordionData(
            function (xhr, response) {
                //populate data accordion tabForms
                that.loadAccordionItems(response);
            }, function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            }
        );
        //get getTreePanelData data
        this.getTreePanelData(
            function (xhr, response) {
                //populate data treePanel tabForms
                that.loadTreePanelData(response);
            }, function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            }
        );
        this.addEventSortableInAccordionElements();
        this.addEventSortableInTreePanelElements();
    },

    /**
     * Get all dynaforms
     * Execute restClient(GET/project/dynaforms)
     * @param successCallback
     * @param failureCallback
     */
    getDynaforms: function (successCallback, failureCallback) {
        return new PMRestClient({
            typeRequest: 'post',
            multipart: true,
            data: {
                calls: [
                    {
                        url: 'dynaforms',
                        method: 'GET'
                    }
                ]
            },
            functionSuccess: function (xhr, response) {
                successCallback(xhr, response);
            },
            functionFailure: function (xhr, response) {
                failureCallback(xhr, response);
            }
        }).executeRestClient();
    },

    /**
     * Get users
     * Execute restClient(GET/users/uid_usr)
     * @param uidUser
     * @param successCallback
     * @param failureCallback
     */
    getUserData: function (uidUser, successCallback, failureCallback) {
        return new PMRestClient({
            typeRequest: 'post',
            multipart: true,
            data: {
                calls: [
                    {
                        url: 'users/' + uidUser,
                        method: 'GET'
                    }
                ]
            },
            functionSuccess: function (xhr, response) {
                successCallback(xhr, response);
            },
            functionFailure: function (xhr, response) {
                failureCallback(xhr, response);
            },
            messageError: 'There are problems getting the Steps, please try again.'.translate()
        }).setBaseEndPoint("").executeRestClient();
    },

    /**
     * Get WebEntry Configuration
     * Execute restClient(GET/web-entry-event/event/)
     * Execute restClient(POST/web-entry-event/)
     * @param successCallback
     * @returns {Array}
     */
    getWebEntryConfiguration: function (successCallback) {
        var that = this,
            restProxy = new PMRestClient({
                endpoint: 'web-entry-event/event/' + that.getEvnUid(),
                typeRequest: "get",
                functionSuccess: function (xhr, response) {
                    successCallback(response, false);
                    that.initializeSomeVariables(response, false);
                },
                functionFailure: function (xhr, response) {
                    restProxy = new PMRestClient({
                        endpoint: 'web-entry-event',
                        typeRequest: "post",
                        data: {
                            act_uid: that.getActUid(),
                            evn_uid: that.getEvnUid(),
                            wee_title: that.getEvnUid(),
                            we_type: "MULTIPLE",
                            we_authentication: "LOGIN_REQUIRED",
                            we_callback: "PROCESSMAKER",
                            we_callback_url: "",
                            we_show_in_new_case: "0",
                            usr_uid: that.getUserGuest().uid || ''
                        },
                        functionSuccess: function (xhr, response) {
                            successCallback(response, true);
                            that.initializeSomeVariables(response, true);
                        },
                        functionFailure: function (xhr, response) {
                            PMDesigner.msgWinError(response.error.message);
                        }
                    });
                    restProxy.executeRestClient();
                }
            });
        restProxy.executeRestClient();
        return this;
    },

    /**
     * Update data Web Entry Configuration
     * Execute restClient(PUT/web-entry-event/weeuid)
     * @param data
     * @param successCallback
     * @param failureCallback
     * @returns {PMRestClient}
     */
    updateWebEntryConfiguration: function (data, successCallback, failureCallback) {
        return new PMRestClient({
            endpoint: 'web-entry-event/' + this.weeUid,
            typeRequest: 'update',
            data: data,
            functionSuccess: function (xhr, response) {
                successCallback(xhr, response);
            },
            functionFailure: function (xhr, response) {
                failureCallback(xhr, response);
            }
        }).executeRestClient();
    },

    /**
     * Get Accordion Data (Tab Forms)
     * Execute restClient(GET/steps, GET/step/triggers)
     * @returns {Array}
     */
    getAccordionData: function (successCallback, failureCallback) {
        return new PMRestClient({
            typeRequest: 'post',
            multipart: true,
            data: {
                calls: [
                    {
                        url: 'activity/' + this.getConfigWebEntry().tas_uid + '/steps',
                        method: 'GET'
                    }, {
                        url: 'activity/' + this.getConfigWebEntry().tas_uid + '/step/triggers',
                        method: 'GET'
                    }
                ]
            },
            functionSuccess: function (xhr, response) {
                successCallback(xhr, response);
            },
            functionFailure: function (xhr, response) {
                failureCallback(xhr, response);
            },
            messageError: 'There are problems getting the Steps, please try again.'.translate()
        }).executeRestClient();
    },

    /**
     * Get TreePanel Data (TabForm)
     * Execute restClient(GET/available-steps GET/triggers)
     * @returns {Array}
     */
    getTreePanelData: function (successCallback, failureCallback) {
        return new PMRestClient({
            typeRequest: 'post',
            multipart: true,
            data: {
                calls: [
                    {
                        url: 'activity/' + this.getConfigWebEntry().tas_uid + '/available-steps',
                        method: 'GET'
                    }, {
                        url: 'triggers',
                        method: 'GET'
                    }
                ]
            },
            functionSuccess: function (xhr, response) {
                successCallback(xhr, response);
            },
            functionFailure: function (xhr, response) {
                failureCallback(xhr, response);
            }
        }).executeRestClient();
    },

    /**
     * Get steps Availables (TreePanel)
     * Execute restClient(GET/available-steps)
     * @param successCallback
     * @param failureCallback
     * @returns {Array}
     */
    getStepAvailables: function (successCallback, failureCallback) {
        return new PMRestClient({
            typeRequest: 'post',
            multipart: true,
            data: {
                calls: [
                    {
                        url: 'activity/' + this.getConfigWebEntry().tas_uid + '/available-steps',
                        method: 'GET'
                    }
                ]
            },
            functionSuccess: function (xhr, response) {
                successCallback(xhr, response);
            },
            functionFailure: function (xhr, response) {
                failureCallback(xhr, response);
            }
        }).executeRestClient();
    },

    /**
     * Load Skin and Languages
     * Execute restClient(GET/system/languages GET/system/skins)
     * @returns {Array}
     */
    getSkinLanguage: function (successCallback, failureCallback) {
        return new PMRestClient({
            typeRequest: 'post',
            multipart: true,
            data: {
                calls: [
                    {
                        url: 'system/languages',
                        method: 'GET'
                    }, {
                        url: 'system/skins',
                        method: 'GET'
                    }
                ]
            },
            functionSuccess: function (xhr, response) {
                successCallback(xhr, response);
            },
            functionFailure: function (xhr, response) {
                failureCallback(xhr, response);
            },
            messageError: 'There are problems getting the Steps, please try again.'.translate()
        }).setBaseEndPoint("").executeRestClient();
    },

    /**
     * Delete the WebEntry configuration.
     * @param successCallback
     * @param failureCallback
     */
    deleteWebEntryConfiguration: function (successCallback, failureCallback) {
        return new PMRestClient({
            endpoint: 'web-entry-event/' + this.weeUid,
            typeRequest: 'remove',
            functionSuccess: function (xhr, response) {
                successCallback(xhr, response);
            },
            functionFailure: function (xhr, response) {
                failureCallback(xhr, response);
            }
        }).executeRestClient();
    },

    /**
     * Generate webEntry Link
     * @param weeUid
     * @param successCallback
     * @param failureCallback
     */
    generateLink: function (weeUid, successCallback, failureCallback) {
        return new PMRestClient({
            endpoint: 'web-entry-event/' + weeUid + '/generate-link',
            typeRequest: 'get',
            functionSuccess: function (xhr, response) {
                successCallback(xhr, response);
            },
            functionFailure: function (xhr, response) {
                failureCallback(xhr, response);
            }
        }).executeRestClient();
    },

    /**
     * Creates an instance of the WebEntry class
     * @returns {null}
     */
    getInstanceWebEntryData: function () {
        this.getWebEntryConfiguration(
            function (webEntryEvent, isNew) {
                if (isNew) {
                    webEntryEvent.we_type = 'MULTIPLE';
                    webEntryEvent.we_authentication = 'ANONYMOUS';
                    webEntryEvent.wee_url = '';
                    webEntryEvent.wee_title = '';
                }
            }
        );
        return this;
    },

    /**
     * Get Main Container Window
     * @returns {PMUI.ui.Window}
     */
    getWindow: function () {
        if (this.getWindowWebEntry() === null) {
            this.setWindowWebEntry(this.buildWindow());
        }
        return this.getWindowWebEntry();
    },

    /**
     * Build Window Container
     * @returns {PMUI.ui.Window}
     */
    buildWindow: function () {
        var that = this;
        return new PMUI.ui.Window({
            id: 'windowWebEntry',
            title: 'Web Entry'.translate(),
            height: DEFAULT_WINDOW_HEIGHT,
            width: DEFAULT_WINDOW_WIDTH,
            footerAlign: 'right',
            onBeforeClose: function () {
                if (that.isNewWebEntry) {
                    that.deleteWebEntryConfiguration(
                        function () {
                            that.getWindow().close();
                        },
                        function (xhr, response) {
                            PMDesigner.msgWinError(response.error.message);
                        }
                    );
                } else {
                    that.getWindow().close();
                }
                that.getWindow().close();
            },
            buttonPanelPosition: 'bottom',
            buttonsPosition: 'right',
            buttons: [
                {
                    id: 'windowWebEntryButtonDelete',
                    text: 'Delete'.translate(),
                    handler: function () {
                        that.handlerDeleteWebEntry();
                    },
                    buttonType: "error"
                },
                {
                    id: 'windowWebEntryButtonCancel',
                    text: 'Cancel'.translate(),
                    handler: function () {
                        if (that.isNewWebEntry) {
                            that.deleteWebEntryConfiguration(
                                function () {
                                    that.getWindow().close();
                                },
                                function (xhr, response) {
                                    PMDesigner.msgWinError(response.error.message);
                                }
                            );
                        } else {
                            that.getWindow().close();
                        }
                    },
                    buttonType: "error"
                },
                {
                    id: 'windowWebEntryButtonSave',
                    text: 'Save'.translate(),
                    handler: function (e) {
                        that.checkUserGuest('saveConfig');
                    },
                    buttonType: 'success'
                }
            ]
        });
    },

    /**
     * Return Second Window Container
     * @returns {PMUI.ui.Window}
     */
    getWindowAlternativeForm: function () {
        if (this.getWindowAlternative() === null) {
            this.setWindowAlternative(this.buildWindowAlternative());
        }
        return this.getWindowAlternative();
    },

    /**
     * Build Second Window Container
     * @returns {PMUI.ui.Window}
     */
    buildWindowAlternative: function () {
        return new PMUI.ui.Window({
            visibleFooter: true,
            title: 'Trigger'.translate(),
            footerAlign: 'right',
            footerItems: [{
                text: "@@",
                id: "secondaryWindow-criteria",
                style: {
                    cssProperties: {
                        "background": "rgb(45, 62, 80)",
                        "border": "1px solid rgb(45, 62, 80)"
                    },
                    cssClasses: ["mafe-button-condition-trigger"]
                }
            }, {
                id: 'secondaryWindow-cancel',
                text: 'Cancel'.translate(),
                buttonType: 'error',
                height: 31,
                style: {
                    cssClasses: ["mafe-button-condition-trigger"]
                }
            }, {
                id: 'secondaryWindow-save',
                text: 'Save'.translate(),
                buttonType: 'success',
                height: 31,
                style: {
                    cssClasses: ["mafe-button-condition-trigger"]
                }
            }]
        });
    },

    /**
     * Get TabPanel Container
     * @returns {TabPanel}
     */
    getTabPanel: function () {
        if (this.getTabPanelWindow() === null) {
            this.setTabPanelWindow(this.buildPanelWindow());
        }
        return this.getTabPanelWindow();
    },

    /**
     * Build TabPanel (TabForms, TabProperties, TabLink)
     * @returns {PMUI.panel.TabPanel}
     */
    buildPanelWindow: function () {
        return new PMUI.panel.TabPanel({
            id: 'windowWebEntryTabPanel',
            width: DEFAULT_WINDOW_WIDTH - 50,
            items: [
                {
                    id: 'tabForms',
                    title: 'Forms'.translate(),
                    panel: this.getTabForms()
                },
                {
                    id: 'tabProperties',
                    title: 'Properties'.translate(),
                    panel: this.getTabProperties()
                },
                {
                    id: 'tabLink',
                    title: 'Link'.translate(),
                    panel: this.getTabLinkForm()
                }
            ],
            style: {
                cssProperties: {
                    'margin-left': '10px'
                }
            },
            itemsPosition: {
                position: 'left'
            }
        });
    },

    /**
     * Get Panel TabForms
     * @returns {Panel}
     */
    getTabForms: function () {
        if (this.getTabForm() === null) {
            this.setTabForm(this.buildTabForms());
        }
        return this.getTabForm();
    },

    /**
     * Build Tab Forms
     * @returns {PMUI.core.Panel}
     */
    buildTabForms: function () {
        var that = this,
            singleDynaform,
            stepsMainContainer;

        singleDynaform = new PMUI.form.Form({
            id: 'singleDynaform',
            width: DEFAULT_WINDOW_WIDTH - 220,
            height: 180,
            name: 'singleDynaform',
            visibleHeader: false,
            items: [
                {
                    id: 'multipleStepsRadio',
                    pmType: 'radio',
                    labelVisible: false,
                    value: '',
                    name: 'options',
                    required: false,
                    controlPositioning: 'vertical',
                    maxDirectionOptions: 4,
                    options: [
                        {
                            id: 'multipleSteps',
                            label: 'Multiple Steps'.translate(),
                            value: 'MULTIPLE'
                        }
                    ],
                    labelWidth: '0%',
                    visible: false
                }
            ]
        });

        stepsMainContainer = new PMUI.core.Panel({
            id: 'stepsMainContainer',
            layout: 'hbox',
            width: DEFAULT_WINDOW_WIDTH - 220,
            items: [
                that.getStepsAssignTree(),
                that.getStepsAssignAccordion()
            ]
        });

        return new PMUI.core.Panel({
            id: 'mainContainer',
            layout: 'vbox',
            width: DEFAULT_WINDOW_WIDTH - 220,
            items: [
                singleDynaform,
                that.getLabelsPanel(),
                stepsMainContainer
            ]
        });
    },

    /**
     * Get Panel TabProperties
     * @returns {Panel}
     */
    getTabProperties: function () {
        if (this.getTabProperty() === null) {
            this.setTabProperty(this.buildTabProperties());
        }
        return this.getTabProperty();
    },

    /**
     * Build TabProperties
     * @returns {PMUI.core.Panel}
     */
    buildTabProperties: function () {
        var that = this,
            propertiesForm;

        propertiesForm = new PMUI.form.Form({
            id: 'idTabFormProperties',
            width: DEFAULT_WINDOW_WIDTH - 220,
            visibleHeader: false,
            items: [
                {
                    id: 'tabPropertiesWebEntryTitle',
                    pmType: 'text',
                    name: 'tabPropertiesWebEntryTitle',
                    valueType: 'string',
                    label: 'Web Entry Title'.translate(),
                    placeholder: 'Enter a title displayed on web entry window (if applies)'.translate(),
                    helper: 'Enter a title displayed on web entry window (if applies).'.translate(),
                    required: false,
                    controlsWidth: 458,
                    labelWidth: '23%'
                },
                {
                    id: 'tabPropRadioAuthentication',
                    pmType: 'radio',
                    labelVisible: true,
                    label: 'Authentication'.translate(),
                    value: 'ANONYMOUS',
                    name: 'authentication',
                    required: true,
                    controlPositioning: 'horizontal',
                    maxDirectionOptions: 4,
                    options: [
                        {
                            id: 'tabPropertiesOptionRadioAnonymous',
                            label: 'Anonymous'.translate(),
                            value: 'ANONYMOUS',
                            selected: false
                        }
                    ],
                    onChange: function (newVal, oldVal) {
                        that.anonimusProcedure(newVal, oldVal);
                    },
                    labelWidth: '23%'
                },
                {
                    id: 'tabPropertiesRequireUserLogin',
                    pmType: 'radio',
                    labelVisible: false,
                    value: 'LOGIN_REQUIRED',
                    required: false,
                    name: 'authentication',
                    controlPositioning: 'horizontal',
                    maxDirectionOptions: 4,
                    options: [
                        {
                            id: 'tabPropertiesRadioRequireUserLogin',
                            label: 'Require user login'.translate(),
                            value: 'LOGIN_REQUIRED',
                            selected: false
                        }
                    ],
                    onChange: function (newVal, oldVal) {
                        that.loginRequired(newVal, oldVal);
                    },
                    labelWidth: '23%'
                },
                {
                    id: 'tabPropertiesHideLoogedInformationBar',
                    pmType: 'checkbox',
                    name: 'tabPropertiesHideLoogedInformationBar',
                    labelVisible: false,
                    disabled: true,
                    options: [
                        {
                            id: 'hideLoogedInformationBar',
                            label: 'Hide Logged Information Bar'.translate(),
                            value: '1',
                            selected: false
                        }
                    ],
                    style: {
                        cssProperties: {
                            'padding-left': '50px'
                        }
                    }
                },
                {
                    id: 'tabPropertiesHideActiveSessionWarning',
                    pmType: 'checkbox',
                    name: 'tabPropertiesHideActiveSessionWarning',
                    labelVisible: false,
                    disabled: true,
                    helper: "By disabling this option, there's a security vulnerability if any other user has access to your web browser. It might impersonate your current session.".translate(),
                    options: [
                        {
                            id: 'hideActiveSessionWarning',
                            label: 'Hide Active Session Warning'.translate(),
                            value: '1',
                            selected: false
                        }
                    ],
                    style: {
                        cssProperties: {
                            'padding-top': '-10px',
                            'padding-left': '50px'
                        }
                    }
                },
                {
                    id: 'tabPropertiesRadioCallback',
                    pmType: 'radio',
                    labelVisible: true,
                    label: 'Callback Action'.translate(),
                    value: 'PROCESSMAKER',
                    required: true,
                    disabled: false,
                    controlPositioning: 'vertical',
                    labelPosition: 'left',
                    helper: 'Callback Action...'.translate(),
                    maxDirectionOptions: 4,
                    options: [
                        {
                            id: 'redirectPM',
                            label: 'Redirect to ProcessMaker predefined response page'.translate(),
                            value: 'PROCESSMAKER',
                            selected: true
                        },
                        {
                            id: 'redirectURL',
                            label: 'Redirect to custom URL'.translate(),
                            value: 'CUSTOM'
                        },
                        {
                            id: 'redirectCustom',
                            label: 'Redirect to custom URL and clear login info'.translate(),
                            value: 'CUSTOM_CLEAR'
                        }
                    ],
                    onChange: function (newVal, oldVal) {
                        that.callbackActionChange(newVal, oldVal);

                    },
                    labelWidth: '23%'
                },
                new CriteriaField({
                    id: 'criteriaFieldCustomUrl',
                    pmType: 'text',
                    name: 'criteriaFieldCustomUrl',
                    label: 'Custom URL'.translate(),
                    placeholder: 'Enter a valid URL to be redirected when entry will be completed'.translate(),
                    labelWidth: '23%',
                    controlsWidth: 455,
                    required: true,
                    disabled: true
                }),
                {
                    id: 'showInNewCase',
                    pmType: 'checkbox',
                    name: 'showInNewCase',
                    label: 'Show task in New Case'.translate(),
                    labelVisible: true,
                    options: [
                        {
                            id: 'showTaskInNewCase',
                            value: 'showCase',
                            selected: false
                        }
                    ],
                    onChange: function (newValue, oldValue) {
                        this.setValue(newValue);
                    }
                }
            ]
        });

        $(propertiesForm.getItem('tabPropRadioAuthentication').getHTML())
            .append($(that.getSuggestField().createHTML()));

        return propertiesForm;
    },

    /**
     * Get Panel TabLink
     * @returns {Panel}
     */
    getTabLinkForm: function () {
        if (this.getTabLink() === null) {
            this.setTabLink(this.buildTabLink());
        }
        return this.getTabLink();
    },

    /**
     * Build TabLink
     * @returns {PMUI.core.Panel}
     */
    buildTabLink: function () {
        var that = this,
            tfromLink;

        tfromLink = new PMUI.form.Form({
            id: 'idTabFormLink',
            width: DEFAULT_WINDOW_WIDTH - 220,
            visibleHeader: false,
            items: [
                {
                    id: 'tabLinkRadioGeneration',
                    pmType: 'radio',
                    labelVisible: true,
                    label: 'Link Generation'.translate(),
                    value: 'DEFAULT',
                    name: 'options',
                    required: true,
                    controlPositioning: 'vertical',
                    labelPosition: 'left',
                    helper: 'Link Generation'.translate(),
                    controlsWidth: 485,
                    labelWidth: '24.5%',
                    maxDirectionOptions: 3,
                    options: [
                        {
                            id: 'generateLinkDefaultValues',
                            label: 'Generate link using workspace default values (skin, language)'.translate(),
                            value: 'DEFAULT',
                            selected: 'true'
                        },
                        {
                            id: 'advancedLinkGeneration',
                            label: 'Advanced link generation'.translate(),
                            value: 'ADVANCED'
                        }
                    ],
                    onChange: function (newVal) {
                        that.linkGenerationOnChange(newVal);
                    },
                    labelWidth: '18%'
                },
                {
                    id: 'tabLinkDropdownSkin',
                    name: 'tabLinkDropdownSkin',
                    pmType: 'dropdown',
                    label: 'Skin'.translate(),
                    helper: 'Select a Skin.'.translate(),
                    required: true,
                    controlsWidth: 485,
                    labelWidth: '24.5%',
                    onChange: function () {
                        that.setLinkText(tfromLink, '');
                    },
                    options: [
                        {
                            label: 'Select a Skin'.translate(),
                            value: ''
                        }
                    ]
                },
                {
                    id: 'tabLinkDropdownLanguage',
                    name: 'tabLinkDropdownLanguage',
                    pmType: 'dropdown',
                    label: 'Language'.translate(),
                    helper: 'Select a language.'.translate(),
                    required: true,
                    controlsWidth: 485,
                    labelWidth: '24.5%',
                    onChange: function () {
                        that.setLinkText(tfromLink, '');
                    },
                    options: [
                        {
                            label: 'Select a language'.translate(),
                            value: ''
                        }
                    ]
                },
                {
                    id: 'tablinkTextCustomDomain',
                    pmType: 'text',
                    name: 'tablinkTextCustomDomain',
                    valueType: 'string',
                    label: 'Custom Hostname'.translate(),
                    placeholder: 'https://example.com:8080'.translate(),
                    helper: 'Protocol and Hostname, port is optional.'.translate(),
                    required: true,
                    controlsWidth: 485,
                    labelWidth: "24.5%",
                    validators: [
                        {
                            pmType: "regexp",
                            criteria: /^(https?:\/\/)?(((\d{1,3}\.){3}\d{1,3})|(([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?))(:\d+)?$/i,
                            errorMessage: 'Enter a Protocol and Hostname valid value.'.translate()
                        }
                    ],
                    onChange: function () {
                        that.setLinkText(tfromLink, '');
                    }
                },
                {
                    id: 'panelLinkForm',
                    pmType: 'panel',
                    layout: 'hbox',
                    required: true,
                    width: '155px',
                    style: {
                        cssProperties: {
                            'margin-left': '-10px',
                            'margin-top': '-10px'
                        }
                    },
                    items: [
                        {
                            pmType: 'annotation',
                            text: 'Web Entry URL:'.translate(),
                            id: 'webEntryLinkLabel',
                            required: true,
                            name: 'webEntryLinkLabel'
                        },
                        {
                            pmType: 'annotation',
                            id: 'webEntryLink',
                            name: 'webEntryLink',
                            text: '',
                            required: true,
                            textType: 1,
                            style: {
                                cssProperties: {
                                    'margin-left': '-173px'
                                }
                            }
                        }
                    ]
                },
                new PMUI.field.ButtonField({
                    id: 'buttonFieldGenerateLink',
                    pmType: 'buttonField',
                    value: 'Generate Link'.translate(),
                    labelVisible: false,
                    buttonAlign: 'center',
                    controlsWidth: 180,
                    proportion: 0.6,
                    handler: function (field) {
                        that.checkUserGuest('generateLink');
                    },
                    buttonType: 'success',
                    style: {
                        cssProperties: {
                            'vertical-align': 'top',
                            'padding-top': '10px',
                            'padding-right': '0px',
                            'padding-bottom': '1px',
                            'padding-left': '130px'
                        }
                    }
                })
            ]
        });

        tfromLink.getItem('buttonFieldGenerateLink').getControl(0).button.setButtonType('success');

        return tfromLink;
    },

    /**
     * Get MessageWindow Container
     * @returns {PMUI.ui.MessageWindow}
     */
    getConfirmWindow: function () {
        if (this.getConfirmWin() === null) {
            this.setConfirmWin(this.buildConfirmWindow());
        }
        return this.getConfirmWin();
    },

    /**
     * Build MessageWindow
     * @returns {PMUI.ui.MessageWindow}
     */
    buildConfirmWindow: function () {
        return new PMUI.ui.MessageWindow({
            id: 'confirmWindowDeleteAcceptedValue',
            windowMessageType: 'warning',
            width: 490,
            bodyHeight: 'auto',
            title: '',
            message: '',
            footerItems: [
                {
                    id: 'confirmWindow-footer-no',
                    text: 'No'.translate(),
                    visible: true,
                    buttonType: 'error'
                }, {
                    id: 'confirmWindow-footer-yes',
                    text: 'Yes'.translate(),
                    visible: true,
                    buttonType: 'success'
                }
            ],
            visibleFooter: true
        });
    },

    /**
     * Handler button for delete web entry config
     */
    handlerDeleteWebEntry: function () {
        var that = this,
            confirmWindow,
            yesButton,
            noButton;
        confirmWindow = that.getConfirmWindow()
            .setMessage('Are you sure you want to delete the Web Entry configuration?'.translate());
        yesButton = that.getConfirmWindow().footer.getItem('confirmWindow-footer-yes');
        yesButton.setHandler(function () {
            confirmWindow.close();
            that.deleteWebEntryConfiguration(
                function () {
                    that.getWindow().close();
                },
                function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            );
        });
        noButton = that.getConfirmWindow().footer.getItem('confirmWindow-footer-no');
        noButton.setHandler(function () {
            that.getConfirmWindow().close();
        });
        that.getConfirmWindow().open();
        return this;
    },

    /**
     * Handler button for save web entry config
     * @param method
     */
    checkUserGuest: function (method) {
        var title = 'Web Entry Anonymous Authentication'.translate(),
            message = ('Current selected user to act as anonymous will be replaced by the Guest user. ' +
                'This action cannot be undone. Do you want to proceed?').translate();

        //Validate the data before saving the configuration or generate the link
        if (this.isValidWebEntryData(method)) {
            (method === 'saveConfig') ? this.handlerSaveButton(message, title) : this.handlerGenerateLinkButton(message, title);
        }
        return this;
    },

    /**
     * Check userGuestUID and save Config
     * @param message
     * @param title
     * @returns {WebEntry}
     */
    handlerSaveButton: function (message, title) {
        var that = this,
            messageWindow = this.getConfirmWindow();
        //Validation is done to be compatible with older versions of webEntry (Now exist guest-type user).
        if (this.getUserGuest().uid && this.getSuggestField().value !== this.getUserGuest().uid) {
            messageWindow.setMessage(message);
            messageWindow.setTitle(title);

            //handler for cancel button
            messageWindow.footer.getItem('confirmWindow-footer-no').setHandler(function () {
                messageWindow.close();
            });

            //handler for yes button
            messageWindow.footer.getItem('confirmWindow-footer-yes').setHandler(function () {
                messageWindow.close();
                that.getSuggestField().set('value', that.getUserGuest().uid);
                that.saveWebEntryConfiguration(
                    function () {
                        that.getWindow().close();
                    }, function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    }
                );
            });
            messageWindow.open();
        } else {
            //For new configurations we save the configuration without any GUEST user validation.
            this.saveWebEntryConfiguration(
                function () {
                    that.getWindow().close();
                }, function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            );
        }
        return this;
    },

    /**
     * Check userGuestUID and generate webEntry Link
     * @param message
     * @param title
     * @returns {WebEntry}
     */
    handlerGenerateLinkButton: function (message, title) {
        var that = this,
            formLink = this.getTabLinkForm(),
            messageWindow = this.getConfirmWindow();
        //Validation is done to be compatible with older versions of webEntry (Now exist guest-type user).
        if (this.getUserGuest().uid && this.getSuggestField().value !== this.getUserGuest().uid) {
            messageWindow.setMessage(message);
            messageWindow.setTitle(title);

            //handler for cancel button
            messageWindow.footer.getItem('confirmWindow-footer-no').setHandler(function () {
                messageWindow.close();
            });
            //handler for yes button

            messageWindow.footer.getItem('confirmWindow-footer-yes').setHandler(function () {
                messageWindow.close();
                that.getSuggestField().set('value', that.getUserGuest().uid);
                that.setLinkText(formLink, '');
                //save Web Entry Configuration
                that.saveWebEntryConfiguration(
                    function (xhr, response) {
                        that.getConfigWebEntry().usr_uid = response.usr_uid || that.getConfigWebEntry().usr_uid;
                        //generate webEntry Link
                        that.generateLink(
                            that.getWeeUid(),
                            function (xhr, response) {
                                that.setLinkText(formLink, (response.link) ? response.link : '');
                                that.initializeAccordionAndTreepanelData();
                                that.getSuggestField().html.find("input")
                                    .val(that.getUserGuest().firstname + " " + "(" + that.getUserGuest().username + ")");
                            },
                            function (xhr, response) {
                                PMDesigner.msgWinError(response.error.message);
                            }
                        );
                    },
                    function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    }
                );
            });
            messageWindow.open();
        } else {
            //For new configurations we generate the webEntry Link without any GUEST user validation.
            this.saveWebEntryConfiguration(
                function () {
                    that.generateLink(
                        that.getWeeUid(),
                        function (xhr, response) {
                            that.setLinkText(formLink, (response.link) ? response.link : '');
                            that.initializeAccordionAndTreepanelData();
                        },
                        function (xhr, response) {
                            that.setLinkText(formLink, '');
                            PMDesigner.msgWinError(response.error.message);
                        }
                    );
                },
                function (xhr, response) {
                    that.setLinkText(formLink, '');
                    PMDesigner.msgWinError(response.error.message);
                },
                {generateLink: true}
            );
        }
        return this;
    },

    /**
     * Get Steps AssignAccordion
     * @returns {PMUI.panel.AccordionPanel}
     */
    getStepsAssignAccordion: function () {
        if (this.getStepsAccordion() === null) {
            this.setStepsAccordion(this.buildStepsAssignAccordion());
        }
        return this.getStepsAccordion();
    },

    /**
     * Build Steps Assign Accordion
     * @returns {PMUI.panel.AccordionPanel}
     */
    buildStepsAssignAccordion: function () {
        var that = this;
        return new PMUI.panel.AccordionPanel({
            id: 'stepsAssignAccordion',
            multipleSelection: true,
            hiddenTitle: true,
            proportion: 1.5,
            style: {
                cssProperties: {
                    margin: '0px 0px 0px 0px'
                },
                cssClasses: ['mafe-border-panel']
            },
            listeners: {
                select: function (accordionItem, event) {
                    var buttonExpand, buttonCollapse, itemsAccod;
                    itemsAccod = that.getStepsAssignAccordion().items;
                    if (accordionItem.collapsed) {
                        if (that.elementAccordionOpen.indexOf(accordionItem) > -1) {
                            that.elementAccordionOpen.remove(accordionItem);
                        }
                    } else {
                        if (that.elementAccordionOpen.indexOf(accordionItem) === -1) {
                            that.elementAccordionOpen.insert(accordionItem);
                        }
                    }
                    buttonCollapse = that.getLabelsPanel().getItem('collapse-button');
                    buttonExpand = that.getLabelsPanel().getItem('expand-button');
                    if (that.elementAccordionOpen.getSize() === 0) {
                        buttonExpand.setDisabled(false);
                        buttonCollapse.setDisabled(true);
                    } else if (that.elementAccordionOpen.getSize() === itemsAccod.getSize()) {
                        buttonExpand.setDisabled(true);
                        buttonCollapse.setDisabled(false);
                    } else {
                        buttonExpand.setDisabled(false);
                        buttonCollapse.setDisabled(false);
                    }
                }
            }
        });
    },

    /**
     * Get steps AssignTree
     * @returns {PMUI.core.Panel}
     */
    getStepsAssignTree: function () {
        if (this.getStepsTree() === null) {
            this.setStepsTree(this.buildStepsAssignTree());
        }
        return this.getStepsTree();
    },

    /**
     * Build steps AssignTree
     * @returns {PMUI.panel.TreePanel}
     */
    buildStepsAssignTree: function () {
        return new PMUI.panel.TreePanel({
            id: 'stepsAssignTree',
            proportion: 0.5,
            height: 475,
            filterable: true,
            autoBind: true,
            filterPlaceholder: 'Search ...'.translate(),
            emptyMessage: 'No records found'.translate(),
            nodeDefaultSettings: {
                labelDataBind: 'obj_title',
                autoBind: true,
                collapsed: false,
                itemsDataBind: 'items',
                childrenDefaultSettings: {
                    labelDataBind: 'obj_title',
                    autoBind: true
                },
                behavior: 'drag'
            },
            style: {
                cssProperties: {
                    margin: '0px 0px 0px 0px',
                    float: 'left',
                    overflow: 'auto'
                },
                cssClasses: ['mafe-border-panel']
            }
        });
    },

    /**
     * Get Label Panel
     * @returns {PMUI.core.Panel}
     */
    getLabelsPanel: function () {
        if (this.getLabelPanel() === null) {
            this.setLabelPanel(this.buildLabelPanel());
        }
        return this.getLabelPanel();
    },

    /**
     * Build Label Panel
     * @returns {PMUI.core.Panel}
     */
    buildLabelPanel: function () {
        var that = this;
        return new PMUI.core.Panel({
            width: DEFAULT_WINDOW_WIDTH - 220,
            layout: "hbox",
            items: [
                new PMUI.field.TextAnnotationField({
                    text: 'Available Elements'.translate(),
                    proportion: 1.12,
                    text_Align: 'center'
                }),
                new PMUI.field.TextAnnotationField({
                    text: 'Assigned Elements (Drop here)'.translate(),
                    proportion: 1.3,
                    text_Align: 'center'
                }),
                new PMUI.ui.Button({
                    buttonType: 'link',
                    text: 'Expand all'.translate(),
                    id: 'expand-button',
                    proportion: 1.2,
                    handler: function () {
                        that.expandAndCollapseHandler('collapse-button', 'expand-button');
                    }
                }),
                new PMUI.ui.Button({
                    buttonType: 'link',
                    text: 'Collapse all'.translate(),
                    id: 'collapse-button',
                    proportion: 1.1,
                    disabled: true,
                    handler: function () {
                        that.expandAndCollapseHandler('expand-button', 'collapse-button');
                    }
                })
            ]
        });
    },

    /**
     * Get SuggestField Control
     * @returns {SuggestField}
     */
    getSuggestField: function () {
        if (this.getSuggestUser() === null) {
            this.setSuggestUser(this.createSugesstField());
        }
        return this.getSuggestUser();
    },

    /**
     * Create Field Suggest (User)
     * @returns {SuggestField}
     */
    createSugesstField: function () {
        return new SuggestField({
            id: 'idSuggestUser',
            label: 'Users'.translate(),
            width: 450,
            placeholder: 'Suggest users'.translate(),
            required: true,
            disabled: true,
            helper: 'When the form is submitted a new case is created with this user account.'.translate(),
            dynamicLoad: {
                data: [{
                    key: 'usr_uid',
                    label: ['usr_firstname', 'usr_lastname', '(', 'usr_username', ')']
                }],
                keys: {
                    url: HTTP_SERVER_HOSTNAME + '/api/1.0/' + WORKSPACE,
                    accessToken: credentials.access_token,
                    endpoints: [{
                        method: 'GET',
                        url: 'users'
                    }]
                }
            }
        });
    },

    /**
     * Generate Web Entry Link
     * @param formLink
     * @param linkText
     */
    setLinkText: function (formLink, linkText) {
        formLink.getItem('panelLinkForm').getItem('webEntryLink')
            .setText('<a href="' + linkText + '" target="_blank" ' + 'class="mafe-webentry-link">' + linkText + '</a>');
        return this;
    },

    /**
     * Initialize Some Variables for web Entry (weeUId, actUid, evnUid, response, isNewWebEntry)
     * @param response
     * @param newWebEntry
     */
    initializeSomeVariables: function (response, newWebEntry) {
        this.setWeeUid(response.wee_uid)
            .setActUid(response.act_uid)
            .setEvnUid(response.evn_uid)
            .setConfigWebEntry(response)
            .setIsNewWebEntry(newWebEntry);
        return this;
    },

    /**
     * Collapse and Expand Accordion Handler
     * @param enableItem
     * @param disableItem
     */
    expandAndCollapseHandler: function (enableItem, disableItem) {
        var items,
            i,
            item;
        items = this.getStepsAssignAccordion().getItems();
        this.getLabelsPanel().getItem(enableItem).setDisabled(false);
        this.getLabelsPanel().getItem(disableItem).setDisabled(true);
        if (enableItem === 'collapse-button') {
            this.elementAccordionOpen.clear();
            for (i = 0; i < items.length; i += 1) {
                item = items[i];
                item.expand();
                this.elementAccordionOpen.insert(item);
            }
        } else {
            for (i = 0; i < items.length; i += 1) {
                item = items[i];
                this.elementAccordionOpen.remove(item);
                item.collapse();
            }
        }
    },

    /**
     * Disable MultipleSteps (tabForms)
     * @param singleMultiple
     * @returns {WebEntry}
     */
    disableMultipleSteps: function (singleMultiple) {
        var singleDyna = this.getTabForms().getItem('singleDynaform');
        singleDyna.getItem('multipleStepsRadio').setValue('');
        //Hide step panel
        this.getLabelsPanel().setVisible(false);
        this.getTabForms().getItem('stepsMainContainer').setVisible(false);
        return this;
    },

    /**
     * Disable Form SingleDynaform (tabForms)
     * @param singleMultiple
     * @returns {WebEntry}
     */
    disableSingleDynaform: function (singleMultiple) {
        var singleDyna = this.getTabForms().getItem('singleDynaform');
        singleDyna.getItem('multipleStepsRadio').setValue(singleMultiple);
        singleDyna.getItem('multipleStepsRadio').getControl(0).select();
        //Show step panel
        this.getLabelsPanel().setVisible(true);
        this.getTabForms().getItem('stepsMainContainer').setVisible(true);
        return this;
    },

    /**
     * Change handler
     * @param newValue
     * @param oldValue
     * @returns {loginRequired}
     */
    anonimusProcedure: function (newValue, oldValue) {
        var propertiesForm = this.getTabProperties(),
            //The Callback Actions (PROCESSMAKER is 0, CUSTOM is 1, and CUSTOM_CLEAR is 2)
            callbackAction = 2;
        propertiesForm.getItem('tabPropertiesRequireUserLogin').setValue('[]');
        propertiesForm.getItem('tabPropertiesHideLoogedInformationBar').disable();
        propertiesForm.getItem('tabPropertiesHideActiveSessionWarning').disable();
        propertiesForm.getItem('tabPropRadioAuthentication').setRequired(true);
        this.getSuggestField().setRequired(true);
        this.getSuggestField().hideMessageRequired();
        propertiesForm.getItem('tabPropertiesRadioCallback').disableOption(callbackAction);
        if (propertiesForm.getItem('tabPropertiesRadioCallback').getValue() === 'CUSTOM_CLEAR') {
            propertiesForm.getItem('tabPropertiesRadioCallback').getControl(callbackAction).deselect();
            propertiesForm.getItem('tabPropertiesRadioCallback').setValue('');
        }
        propertiesForm.getItem('criteriaFieldCustomUrl').disable();
        propertiesForm.getItem('criteriaFieldCustomUrl').buttonHTML.disable();
        this.setLinkText(this.getTabLinkForm(), '');
        return this;
    },

    /**
     * Change chandler
     * @param newValue
     * @param oldValue
     * @returns {loginRequired}
     */
    loginRequired: function (newValue, oldValue) {
        var propertiesForm = this.getTabProperties(),
            //The Callback Actions (PROCESSMAKER is 0, CUSTOM is 1, and CUSTOM_CLEAR is 2)
            callbackAction = 2;
        propertiesForm.getItem('tabPropRadioAuthentication').setValue('[]');
        propertiesForm.getItem('tabPropRadioAuthentication').setRequired(false);

        this.getSuggestField().setRequired(false);
        this.getSuggestField().hideMessageRequired();
        propertiesForm.getItem('tabPropertiesHideLoogedInformationBar').enable();
        propertiesForm.getItem('tabPropertiesHideActiveSessionWarning').enable();
        propertiesForm.getItem('tabPropertiesRadioCallback').enableOption(callbackAction);
        this.callbackActionChange(propertiesForm.getItem('tabPropertiesRadioCallback').getValue(), '');
        this.setLinkText(this.getTabLinkForm(), '');
        return this;
    },

    /**
     * Change handler
     * @param newValue
     * @param oldValue
     * @returns {callbackActionChange}
     */
    callbackActionChange: function (newValue, oldValue) {
        var propertiesForm = this.getTabProperties();
        propertiesForm.getItem("tabPropertiesRadioCallback").setValue(newValue);
        switch (newValue) {
            case 'PROCESSMAKER':
                propertiesForm.getItem('criteriaFieldCustomUrl').disable();
                propertiesForm.getItem('criteriaFieldCustomUrl').setRequired(false);
                propertiesForm.getItem('criteriaFieldCustomUrl').buttonHTML.disable();
                break;
            case 'CUSTOM':
            case 'CUSTOM_CLEAR':
                propertiesForm.getItem('criteriaFieldCustomUrl').enable();
                propertiesForm.getItem('criteriaFieldCustomUrl').setRequired(true);
                propertiesForm.getItem('criteriaFieldCustomUrl').buttonHTML.enable();
                break;
        }
        return this;
    },

    /**
     * Change handler
     * @param newValue
     * @returns {linkGenerationOnChange}
     */
    linkGenerationOnChange: function (newValue) {
        var required,
            skin,
            language,
            domain,
            tfromLink;
        tfromLink = this.getTabLinkForm();
        required = newValue === 'ADVANCED';
        skin = tfromLink.getItem('tabLinkDropdownSkin');
        language = tfromLink.getItem('tabLinkDropdownLanguage');
        domain = tfromLink.getItem('tablinkTextCustomDomain');
        this.setLinkText(tfromLink, '');
        skin.setRequired(required);
        language.setRequired(required);
        domain.setRequired(required);
        if (required) {
            skin.enable();
            language.enable();
            domain.enable();
        } else {
            skin.disable();
            domain.hideMessage();
            language.disable();
            domain.disable();
            domain.getControl(0).style.removeClasses(['error']);
        }
        return this;
    },

    /**
     * Save webEntry Configuration
     * @param successCallback
     * @param failureCallback
     */
    saveWebEntryConfiguration: function (successCallback, failureCallback, extra) {
        var data,
            //tabs window web entry
            tabProperties = this.getTabPanel().getItem('tabProperties'),
            tabLink = this.getTabPanel().getItem('tabLink'),
            //form tabs
            dataTabSingleDyn = this.getTabForms().getItem('singleDynaform'),
            dataTabProperties = tabProperties.getPanel('idTabProperties'),
            dataTabLink = tabLink.getPanel('idTabLink');

        //Prepare Data
        data = this.prepareData(dataTabSingleDyn, dataTabProperties, dataTabLink);
        data['extra'] = extra || {};
        //Save web Entry configuration
        this.updateWebEntryConfiguration(data, successCallback, failureCallback);
        return this;
    },

    /**
     * Validate Web Entry Data
     * @param method
     */
    isValidWebEntryData: function (method) {
        var valid = true,
            //tabs window web entry
            tabForm = this.getTabPanel().getItem('tabForms'),
            tabProperties = this.getTabPanel().getItem('tabProperties'),
            tabLink = this.getTabPanel().getItem('tabLink'),
            //form tabs
            dataTabSingleDyn = this.getTabForms().getItem('singleDynaform'),
            dataTabProperties = tabProperties.getPanel('idTabProperties'),
            dataTabLink = tabLink.getPanel('idTabLink'),
            //selected tab
            selectedTab = null;

        // validate form tab
        if (dataTabSingleDyn instanceof PMUI.form.Form) {
            valid = valid && dataTabSingleDyn.isValid();
            selectedTab = !valid && !selectedTab ? tabForm : selectedTab;
        }
        //validate property tab
        if (dataTabProperties instanceof PMUI.form.Form) {
            valid = valid && dataTabProperties.isValid();
            selectedTab = !valid && !selectedTab ? tabProperties : selectedTab;
        }
        //validate suggestField
        if (!this.getSuggestField().isValid()) {
            this.getSuggestField().showMessageRequired();
            valid = valid && this.getSuggestField().isValid();
            selectedTab = !valid && !selectedTab ? tabProperties : selectedTab;
        }
        //validate link Tab
        if (dataTabLink instanceof PMUI.form.Form) {
            valid = valid && dataTabLink.isValid();
            selectedTab = !valid && !selectedTab ? tabLink : selectedTab;
        }
        if (!valid) {
            selectedTab.select();
        }
        //validate Link Generate Button
        if (!$(dataTabLink.getItem('panelLinkForm').getItem('webEntryLink').text).attr('href') &&
            method !== 'generateLink') {
            PMDesigner.msgFlash('Please press the \"Generate Link\" button.'.translate(),
                this.getWindow(), 'error', 3000, 5);
            valid = false;
            tabLink.select();
        }
        return valid;
    },

    /**
     * Prepare Data
     * @param dataTabSingleDyn
     * @param dataTabProperties
     * @param dataTabLink
     */
    prepareData : function (dataTabSingleDyn, dataTabProperties, dataTabLink) {
        var data = {};
        data['act_uid'] = this.getActUid();
        data['evn_uid'] = this.getEvnUid();
        data['wee_title'] = this.getEvnUid();
        data['we_type'] = 'MULTIPLE';
        data['dyn_uid'] = '';
        data['we_custom_title'] = dataTabProperties.getItem('tabPropertiesWebEntryTitle').getValue();
        data['we_authentication'] = dataTabProperties.getItem('tabPropRadioAuthentication').getValue() === '[]' ?
            'LOGIN_REQUIRED' : 'ANONYMOUS';
        data['usr_uid'] = this.getSuggestField().value;
        data['we_hide_information_bar'] = dataTabProperties.getItem('tabPropertiesHideLoogedInformationBar')
            .getValue() === '[]' ? '0' : '1';
        data['we_hide_active_session_warning'] = dataTabProperties.getItem('tabPropertiesHideActiveSessionWarning')
            .getValue() === '[]' ? '0' : '1';
        data['we_callback'] = dataTabProperties.getItem('tabPropertiesRadioCallback').getValue();
        data['we_callback_url'] = (data['we_callback'] !== 'PROCESSMAKER') ?
            dataTabProperties.getItem('criteriaFieldCustomUrl').getValue() : '';
        data['we_link_generation'] = dataTabLink.getItem('tabLinkRadioGeneration').getValue();
        data['we_link_skin'] = dataTabLink.getItem('tabLinkDropdownSkin').getValue();
        data['we_link_language'] = dataTabLink.getItem('tabLinkDropdownLanguage').getValue();
        data['we_link_domain'] = (data['we_link_generation'] === 'ADVANCED') ?
            dataTabLink.getItem('tablinkTextCustomDomain').getValue() : '';
        data['we_show_in_new_case'] = (dataTabProperties.getItem('showInNewCase').getValue() === '["showCase"]') ? 1 : 0;
        return data;
    },

    /**
     * Populate all tabPanels (TabForms, TabProperties, TabLink) with data config
     * return void
     */
    setWebEntryConfiguration: function () {
        this.setConfigDataTabForms();
        this.setConfigDataTabProperties();
        this.setConfigDataTabLink();
        this.getWindow().getItem('windowWebEntryTabPanel').getItem('tabForms').select();
        return this;
    },

    /**
     * Load and populate Dynaforms Items
     * @returns {*}
     */
    setConfigDataTabForms: function () {
        var that = this,
            i,
            data,
            options = [],
            dynaforms = [];

        //execute Rest (get Dynaforms)
        this.getDynaforms(
            function (xhr, response) {
                dynaforms = response[0].response;

                //Set data Dropdown Single Dynaform
                for (i = 0; i < dynaforms.length; i += 1) {
                    data = {};
                    data.label = dynaforms[i]['dyn_title'];
                    data.value = dynaforms[i]['dyn_uid'];
                    if (that.getConfigWebEntry().dyn_uid === data.value &&
                        that.getConfigWebEntry().evn_uid === that.getRelatedShape()['evn_uid']) {
                        data.selected = true;
                    }
                    options.push(data);
                }

                //set Disable/Enable single or multiple steps
                (that.getConfigWebEntry().we_type === 'SINGLE') ?
                    that.disableMultipleSteps(that.getConfigWebEntry().we_type) :
                    that.disableSingleDynaform(that.getConfigWebEntry().we_type);
            },
            function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            }
        );
    },

    /**
     * TabProperties Panel
     * Assigns values to tabProperties fields
     */
    setConfigDataTabProperties: function () {
        var that = this,
            webEntryTitle,
            radioAuthentication,
            radioRequiredLogin,
            informationBar,
            activeSessionWarning,
            radioCollback,
            customUrl,
            user,
            showInNewCase;

        //get Controls Tab-Properties
        this.getTabPanel().getItem('tabProperties').select();
        webEntryTitle = this.getTabProperties().getItem('tabPropertiesWebEntryTitle');
        radioAuthentication = this.getTabProperties().getItem('tabPropRadioAuthentication');
        radioRequiredLogin = this.getTabProperties().getItem('tabPropertiesRequireUserLogin');
        radioCollback = this.getTabProperties().getItem('tabPropertiesRadioCallback');
        customUrl = this.getTabProperties().getItem('criteriaFieldCustomUrl');
        informationBar = this.getTabProperties().getItem('tabPropertiesHideLoogedInformationBar');
        activeSessionWarning = this.getTabProperties().getItem('tabPropertiesHideActiveSessionWarning');
        showInNewCase = this.getTabProperties().getItem('showInNewCase');

        //set webentry Title
        webEntryTitle.setValue(this.getConfigWebEntry().we_custom_title);

        //Loggin required or Anonymous
        radioCollback.setValue(this.getConfigWebEntry().we_callback);
        if (this.getConfigWebEntry().we_authentication === 'LOGIN_REQUIRED') {
            radioRequiredLogin.getControl(0).select();
            radioRequiredLogin.setValue(this.getConfigWebEntry().we_authentication);
            this.loginRequired();
        } else {
            radioAuthentication.getControl(0).select();
            radioAuthentication.setValue(this.getConfigWebEntry().we_authentication);
            this.anonimusProcedure();
        }

        //set value suggest user
        this.getSuggestField().html.find('input').val('');
        this.getConfigWebEntry().usr_uid = this.getConfigWebEntry().usr_uid || this.getUserGuest().uid;
        if (this.getConfigWebEntry().usr_uid) {
            this.getUserData(
                this.getConfigWebEntry().usr_uid,
                function (xhr, response) {
                    user = response[0].response;
                    that.getSuggestField().html.find("input").val(user.usr_firstname + " "
                        + user.usr_lastname + " " + "(" + user.usr_username + ")");
                    that.getSuggestField().set("value", user.usr_uid);
                },
                function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            );
        }

        //set Hide Logged Information Bar
        if (this.getConfigWebEntry().we_hide_information_bar === '1') {
            informationBar.setValue('["1"]');
            informationBar.getControl(0).select();
        } else {
            informationBar.setValue('[]');
            informationBar.getControl(0).deselect();
        }

        //set Hide Active Session Warning
        if (this.getConfigWebEntry().we_hide_active_session_warning === '1') {
            activeSessionWarning.setValue('["1"]');
            activeSessionWarning.getControl(0).select();
        } else {
            activeSessionWarning.setValue('[]');
            activeSessionWarning.getControl(0).deselect();
        }

        //set Callback Action
        switch (this.getConfigWebEntry().we_callback) {
            case 'PROCESSMAKER':
                radioCollback.getControl(0).select();
                break;
            case 'CUSTOM':
                radioCollback.getControl(1).select();
                break;
            case 'CUSTOM_CLEAR':
                radioCollback.getControl(2).select();
                break;
        }

        //set custom URL
        customUrl.setValue(this.getConfigWebEntry().we_callback_url);

        //set show task in new case
        if (this.getConfigWebEntry().we_show_in_new_case === '1'){
            showInNewCase.setValue('["showCase"]');
        }else{
            showInNewCase.setValue('[]');
        }

        //customize suggest styles
        this.getSuggestField().inputField[0].style.width = '280px';
        this.getSuggestField().inputLabel[0].parentElement.style.width = '120px';
        this.getSuggestField().inputLabel[0].parentElement.style.marginLeft = '220px';
    },

    /**
     * TabLink Panel
     * Assigns values to tabLink fields
     */
    setConfigDataTabLink: function () {
        var that = this,
            options = [],
            customDomain,
            radioGeneration,
            languages = [],
            skins = [],
            dropDownLanguages,
            dropDownSkins,
            data,
            i;

        //execute Rest (Get languages and Skins)
        this.getSkinLanguage(
            function (xhr, response) {
                languages = response[0].response.data;
                skins = response[1].response.data;
                //get controls Tab-link
                that.getTabPanel().getItem('tabLink').select();
                radioGeneration = that.getTabLinkForm().getItem('tabLinkRadioGeneration');
                dropDownSkins = that.getTabLinkForm().getItem('tabLinkDropdownSkin');
                dropDownLanguages = that.getTabLinkForm().getItem('tabLinkDropdownLanguage');
                customDomain = that.getTabLinkForm().getItem('tablinkTextCustomDomain');

                //set Link generation (Default or Advanced)
                radioGeneration.setValue(that.getConfigWebEntry().we_link_generation);
                (that.getConfigWebEntry().we_link_generation === 'DEFAULT') ? radioGeneration.getControl(0).select() :
                    radioGeneration.getControl(1).select();

                //set Languages Dropdown
                if (jQuery.isArray(languages)) {
                    for (i = 0; i < languages.length; i += 1) {
                        data = {};
                        data.label = languages[i].LANG_NAME;
                        data.value = languages[i].LANG_ID;
                        if (languages[i].LANG_ID === that.getConfigWebEntry().we_link_language) {
                            data.selected = true;
                        }
                        options.push(data);
                    }
                    dropDownLanguages.setOptions(options);
                }
                //set Skins Dropdown
                if (jQuery.isArray(skins)) {
                    options = [];
                    for (i = 0; i < skins.length; i += 1) {
                        data = {};
                        data.label = skins[i].SKIN_NAME;
                        data.value = skins[i].SKIN_FOLDER_ID;
                        if (that.getConfigWebEntry().we_link_skin === skins[i].SKIN_FOLDER_ID) {
                            data.selected = true;
                        }
                        options.push(data);
                    }
                    dropDownSkins.setOptions(options);
                }

                //set Custom Domain
                customDomain.setValue(that.getConfigWebEntry().we_link_domain);
                //Enable or Disable (Skin, Language, CustomDomain)
                that.linkGenerationOnChange(that.getConfigWebEntry().we_link_generation);
                //set Link text
                that.setLinkText(that.getTabLinkForm(), that.getConfigWebEntry().wee_url);
            },
            function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            }
        );
    },

    /**
     * Load and populate Accordion Data Items
     * @param response
     */
    loadAccordionItems: function (response) {
        var firstResp = [],
            secondResp = [],
            i,
            item,
            assigmentConfig,
            firstRes = 0,
            secondRes = 1;
        this.getStepsAssignAccordion().clearItems();
        if (jQuery.isArray(response) && response.length) {
            if (typeof response[firstRes] === "object") {
                firstResp = response[firstRes].response ? response[firstRes].response : [];
            }
            if (typeof response[secondRes] === "object") {
                secondResp = response[secondRes].response ? response[secondRes].response : [];
            }
        }
        if (firstResp.length) {
            for (i = 0; i < firstResp.length; i += 1) {
                item = this.createAccordionItem(firstResp[i], true, true);
                this.getStepsAssignAccordion().addItem(item);
                item.dataItem = firstResp[i];
                this.customAccordionItemButtons(item.html, firstResp[i], item);
            }
        }
        assigmentConfig = {
            step_type_obj: "Assignment".translate(),
            triggers: secondResp,
            st_type: "ASSIGNMENT",
            obj_title: "Assignment".translate(),
            step_uid_obj: "Assignment"
        };
        item = this.createAccordionItem(assigmentConfig);
        this.getStepsAssignAccordion().addItem(item);
        item.dataItem = assigmentConfig;
        assigmentConfig = {
            step_type_obj: "Routing".translate(),
            triggers: secondResp,
            obj_title: "Routing".translate(),
            st_type: 'ROUTING',
            step_uid_obj: "Routing"
        };
        item = this.createAccordionItem(assigmentConfig);
        this.getStepsAssignAccordion().addItem(item);
        item.dataItem = assigmentConfig;
        this.getStepsAssignAccordion().defineEvents();
    },

    /**
     * Create Accordion
     * @param data
     * @returns {PMUI.item.AccordionItem}
     */
    createAccordionItem: function (data) {
        var that = this,
            gridBefore,
            gridAfter,
            beforeTitle,
            afterTitle,
            i,
            textLabel;
        if (this.stepsType[data.step_type_obj]) {
            textLabel = this.stepsType[data.step_type_obj];
        } else {
            textLabel = data.step_type_obj;
        }
        beforeTitle = new PMUI.field.TextAnnotationField({
            text: 'Before'.translate() + ' ' + textLabel,
            proportion: 0.5,
            text_Align: 'left'
        });
        afterTitle = new PMUI.field.TextAnnotationField({
            text: 'After'.translate() + ' ' + textLabel,
            proportion: 0.5,
            text_Align: 'left',
            visible: data.st_type === "ASSIGNMENT" ? false : true
        });

        gridBefore = new PMUI.grid.GridPanel({
            behavior: 'dragdropsort',
            filterable: false,
            visibleHeaders: false,
            data: data.triggers,
            st_type: 'BEFORE',
            step_uid: data.step_uid,
            visibleFooter: false,
            width: '96%',
            emptyMessage: 'No records found'.translate(),
            style: {
                cssClasses: ['mafe-gridPanel']
            },
            customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
                return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
            },
            columns: [{
                title: '',
                dataType: 'string',
                alignmentCell: 'center',
                columnData: "st_position",
                width: 20
            }, {
                title: 'Before Output Document'.translate(),
                dataType: 'string',
                alignmentCell: 'left',
                columnData: 'tri_title',
                width: 210
            }, {
                title: '',
                dataType: 'button',
                buttonLabel: function (row, data) {
                    return data.st_condition === '' ? 'Condition'.translate() : 'Condition *'.translate();
                },
                buttonStyle: {
                    cssClasses: ['mafe-button-edit']
                },
                onButtonClick: function (row, grid) {
                    var data = row.getData();
                    that.editCondition(grid.step_uid, data.tri_uid, data.st_type, row);
                }
            }, {
                title: '',
                dataType: 'button',
                buttonLabel: 'Edit'.translate(),
                buttonStyle: {
                    cssClasses: ['mafe-button-edit']
                },
                onButtonClick: function (row, grid) {
                    var data = row.getData(),
                        restClient;
                    restClient = new PMRestClient({
                        endpoint: 'trigger/' + data.tri_uid,
                        typeRequest: 'get',
                        functionSuccess: function (xhr, response) {
                            that.editTrigger(response.tri_webbot, response.tri_uid);
                        },
                        functionFailure: function (xhr, response) {
                            PMDesigner.msgWinError(response.error.message);
                        }
                    });
                    restClient.executeRestClient();
                }
            }, {
                title: '',
                dataType: 'button',
                buttonLabel: 'Remove'.translate(),
                buttonStyle: {
                    cssClasses: ['mafe-button-delete']
                },
                onButtonClick: function (row, grid) {
                    that.removeTrigger(row, grid);
                }
            }],
            onDrop: function (container, draggableItem, index) {
                var receiveData = draggableItem.getData();
                if (draggableItem instanceof PMUI.item.TreeNode) {
                    that.receiveTreeNodeItem(receiveData, this, index);
                } else {
                    that.receiveRowItem(receiveData, this, index, draggableItem);
                }
                that.updateIndexToGrid(this);
                return false;
            },
            onSort: function (container, item, index) {
                var receiveData = item.getData();
                that.sortableRowHandler(receiveData, this, index);
                that.updateIndexToGrid(this);
            },
            onDragStart: function (grid, row) {
                var items;
                items = grid.getItems();
                if (jQuery.isArray(items)) {
                    if (items.length === 1) {
                        grid.showEmptyCell();
                    }
                }
            }
        });
        if (data.st_type !== "ROUTING" && data.st_type !== "ASSIGNMENT") {
            gridBefore.st_type = 'BEFORE';
        } else if (data.st_type === "ROUTING") {
            gridBefore.st_type = "BEFORE_ROUTING";
        } else {
            gridBefore.st_type = "BEFORE_ASSIGNMENT";
        }
        gridBefore.step_uid = data.step_uid;
        gridBefore.clearItems();
        if (jQuery.isArray(data.triggers)) {
            for (i = 0; i < data.triggers.length; i += 1) {
                if (gridBefore.st_type === data.triggers[i].st_type) {
                    gridBefore.addDataItem({
                        st_condition: data.triggers[i].st_condition,
                        st_position: data.triggers[i].st_position,
                        st_type: data.triggers[i].st_type,
                        tri_description: data.triggers[i].tri_description,
                        tri_title: data.triggers[i].tri_title,
                        tri_uid: data.triggers[i].tri_uid,
                        obj_title: data.triggers[i].tri_title,
                        obj_uid: data.triggers[i].tri_uid
                    });
                }
            }
        }
        gridAfter = new PMUI.grid.GridPanel({
            behavior: 'dragdropsort',
            filterable: false,
            visibleHeaders: false,
            data: data.triggers,
            visibleFooter: false,
            width: '96%',
            visible: data.st_type === "ASSIGNMENT" ? false : true,
            emptyMessage: 'No records found'.translate(),
            style: {
                cssClasses: ['mafe-gridPanel']
            },
            customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
                return messagePageGrid(currentPage, pageSize, numberItems, criteria, filter);
            },
            columns: [{
                title: '',
                dataType: 'string',
                alignmentCell: 'center',
                columnData: 'st_position',
                width: 20
            }, {
                title: 'Before Output Document'.translate(),
                dataType: 'string',
                alignmentCell: 'left',
                columnData: 'tri_title',
                width: 210
            }, {
                title: '',
                dataType: 'button',
                buttonLabel: function (row, data) {
                    return data.st_condition === '' ? 'Condition'.translate() : 'Condition *'.translate();
                },
                buttonStyle: {
                    cssClasses: ['mafe-button-edit']
                },
                onButtonClick: function (row, grid) {
                    var data = row.getData();
                    that.editCondition(grid.step_uid, data.tri_uid, data.st_type, row);
                }
            }, {
                title: '',
                dataType: 'button',
                buttonLabel: 'Edit'.translate(),
                buttonStyle: {
                    cssClasses: ['mafe-button-edit']
                },
                onButtonClick: function (row, grid) {
                    var data = row.getData(),
                        restClient;
                    restClient = new PMRestClient({
                        endpoint: 'trigger/' + data.tri_uid,
                        typeRequest: 'get',
                        functionSuccess: function (xhr, response) {
                            that.editTrigger(response.tri_webbot, response.tri_uid);
                        },
                        functionFailure: function (xhr, response) {
                            PMDesigner.msgWinError(response.error.message);
                        }
                    });
                    restClient.executeRestClient();
                }
            }, {
                title: '',
                dataType: 'button',
                buttonLabel: 'Remove'.translate(),
                buttonStyle: {
                    cssClasses: ['mafe-button-delete']
                },
                onButtonClick: function (row, grid) {
                    that.removeTrigger(row, grid);
                }
            }],
            onDrop: function (container, draggableItem, index) {
                var receiveData = draggableItem.getData();
                if (draggableItem instanceof PMUI.item.TreeNode) {
                    that.receiveTreeNodeItem(receiveData, this, index);
                } else {
                    that.receiveRowItem(receiveData, this, index, draggableItem);
                }
                that.updateIndexToGrid(this);
                return false;
            },
            onSort: function (container, item, index) {
                var receiveData = item.getData();
                that.sortableRowHandler(receiveData, this, index);
                that.updateIndexToGrid(this);
            },
            onDragStart: function (grid, row) {
                var items;
                items = grid.getItems();
                if (jQuery.isArray(items)) {
                    if (items.length === 1) {
                        grid.showEmptyCell();
                    }
                }
            }
        });
        if (data.st_type !== "ROUTING" && data.st_type !== "ASSIGNMENT") {
            gridAfter.st_type = 'AFTER';
        } else if (data.st_type == "ROUTING") {
            gridAfter.st_type = "AFTER_ROUTING";
        } else {
            gridAfter.st_type = "AFTER_ASSIGNMENT";
        }
        gridAfter.step_uid = data.step_uid;
        if (jQuery.isArray(data.triggers)) {
            for (i = 0; i < data.triggers.length; i += 1) {
                if (gridAfter.st_type === data.triggers[i].st_type) {
                    gridAfter.addDataItem({
                        st_condition: data.triggers[i].st_condition,
                        st_position: data.triggers[i].st_position,
                        st_type: data.triggers[i].st_type,
                        tri_description: data.triggers[i].tri_description,
                        tri_title: data.triggers[i].tri_title,
                        tri_uid: data.triggers[i].tri_uid,
                        obj_title: data.triggers[i].tri_title,
                        obj_uid: data.triggers[i].tri_uid
                    });
                }
            }
        }
        var accordionItem = new PMUI.item.AccordionItem({
            id: 'id' + data.step_uid_obj,
            dataStep: data,
            closeable: true,
            body: new PMUI.core.Panel({
                layout: 'vbox',
                items: [
                    beforeTitle,
                    gridBefore,
                    afterTitle,
                    gridAfter
                ]
            })
        });
        if (this.stepsType[data.step_type_obj]) {
            accordionItem.setTitle(data.step_position + ".  " + data.obj_title + ' ('
                + this.stepsType[data.step_type_obj] + ')');
            this.stepsAssigned.insert(accordionItem);
        } else {
            accordionItem.setTitle((this.getStepsAssignAccordion().items.getSize() + 1) + ". " + data.obj_title);
        }
        return accordionItem;
    },

    /**
     * This method is executed when a row is drop in another grid
     * @param receiveData, data of the droppable item
     * @param grid, the affected grid
     * @param index, the index position row
     * @param draggableItem
     * @returns {*}
     */
    receiveRowItem: function (receiveData, grid, index, draggableItem) {
        var receiveParent = draggableItem.getParent(),
            message,
            restClient,
            that = this;
        if (this.isTriggerAssigned(grid, receiveData.obj_uid)) {
            message = new PMUI.ui.FlashMessage({
                message: 'Trigger is assigned.'.translate(),
                duration: 3000,
                severity: 'error',
                appendTo: this.getWindow()
            });
            index = receiveParent.items.indexOf(draggableItem);
            receiveParent.items.remove(draggableItem);
            receiveParent.addItem(draggableItem, index);
            message.show();
            return false;
        }
        restClient = new PMRestClient({
            typeRequest: 'post',
            multipart: true,
            data: {
                calls: [{
                    url: grid.step_uid === undefined ?
                        'activity/' + that.getConfigWebEntry().tas_uid + '/step/trigger' : 'activity/'
                        + that.getConfigWebEntry().tas_uid + '/step/' + grid.step_uid + '/trigger',
                    method: 'POST',
                    data: {
                        tri_uid: receiveData.obj_uid,
                        st_type: grid.st_type,
                        st_condition: receiveData.st_condition,
                        st_position: index + 1
                    }
                }, {
                    url: receiveParent.step_uid === undefined ?
                        'activity/' + that.getConfigWebEntry().tas_uid + '/step/trigger/' + receiveData.obj_uid
                        + '/' + that.getStepType(receiveParent.st_type) :
                        'activity/' + that.getConfigWebEntry().tas_uid + '/step/' + receiveParent.step_uid
                        + '/trigger/' + receiveData.obj_uid + '/' + receiveParent.st_type.toLowerCase(),
                    method: 'DELETE'
                }]
            },
            functionSuccess: function (xhr, response) {
                var data;
                data = receiveData;
                if (data.hasOwnProperty("st_type")) {
                    data.st_type = grid.st_type;
                    grid.addDataItem(receiveData, index);
                }
                receiveParent.removeItem(draggableItem);
            },
            functionFailure: function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            },
            flashContainer: that.getWindow(),
            messageError: [
                'An unexpected error while assigning the trigger, please try again later.'.translate()
            ],
            messageSuccess: [
                'Trigger assigned successfully.'.translate()
            ]
        });
        restClient.executeRestClient();
        return this;
    },

    /**
     * This method is executed when a row is sorted in the grid
     * @param receiveData, data of the droppable item
     * @param grid, the affected grid
     * @param index, the new index position row
     * @returns {stepsTask}
     */
    sortableRowHandler: function (receiveData, grid, index) {
        return new PMRestClient({
            endpoint: grid.step_uid === undefined ?
                'activity/' + this.getConfigWebEntry().tas_uid + "/step/trigger/" + receiveData.tri_uid : 'activity/' +
                this.getConfigWebEntry().tas_uid + "/step/" + grid.step_uid + "/trigger/" + receiveData.tri_uid,
            typeRequest: 'update',
            data: {
                st_type: receiveData.st_type,
                st_condition: receiveData.st_condition,
                st_position: index + 1
            },
            functionFailure: function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            },
            flashContainer: this.getWindow(),
            messageError: 'An unexpected error while assigning the trigger, please try again later.'.translate(),
            messageSuccess: 'Trigger assigned successfully.'.translate()
        }).executeRestClient();
    },

    /**
     * Return the not items config.
     * @returns {{obj_title: *, obj_uid: string, id: string}}
     */
    notItemConfig: function () {
        var config = {
            obj_title: 'N/A'.translate(),
            obj_uid: '',
            id: "notItem"
        };
        return config;
    },

    /**
     * Get the steps assigned by a search criterion
     * @param criteria, search filter, after running the endpoint getAccordionData method
     * @returns {Array}, response with criteria
     */
    getStepsAssignedByCriteria: function (criteria) {
        var allAssigned = [],
            i,
            elements,
            j,
            resp,
            data,
            respon = [];

        this.getAccordionData(
            function (xhr, response) {
                allAssigned = response;
                if (jQuery.isArray(allAssigned)) {
                    for (i = 0; i < allAssigned.length; i += 1) {
                        resp = allAssigned[i];
                        if (typeof resp === "object") {
                            elements = resp.response ? resp.response : [];
                            for (j = 0; j < elements.length; j += 1) {
                                data = elements[j];
                                if (typeof data === "object") {
                                    if (data.step_type_obj && data.step_type_obj === criteria) {
                                        respon.push(data);
                                    }
                                }
                            }
                        }
                    }
                }
            },
            function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            }
        );
        return respon;
    },

    /**
     * Opens the properties of the selected step with the current settings
     * @param step, is the data of selected step
     * @chainable
     */
    propertiesStepShow: function (step) {
        var that = this,
            form,
            saveButton,
            cancelButton,
            restClient,
            criteriaButton;
        this.setWindowAlternative(null);
        this.getWindowAlternativeForm().setWidth(520);
        this.getWindowAlternativeForm().setHeight(370);
        this.getWindowAlternativeForm().setTitle('Step Properties'.translate());
        form = new PMUI.form.Form({
            id: 'stepsEditCondition',
            width: 500,
            title: 'Condition Trigger'.translate(),
            visibleHeader: false,
            items: [
                {
                    id: 'step_mode',
                    pmType: 'radio',
                    label: 'Mode'.translate(),
                    value: '',
                    visible: step.step_type_obj === "DYNAFORM" ? true : false,
                    name: 'step_mode',
                    options: [{
                        id: 'modeEdit',
                        label: 'Edit'.translate(),
                        value: 'EDIT',
                        selected: true
                    }, {
                        id: 'modeView',
                        label: 'View'.translate(),
                        value: 'VIEW'
                    }]
                },
                new CriteriaField({
                    id: 'step_condition',
                    pmType: 'textarea',
                    name: 'step_condition',
                    valueType: 'string',
                    label: 'Condition'.translate(),
                    placeholder: 'Insert a condition'.translate(),
                    rows: 150,
                    controlsWidth: 250,
                    renderType: 'textarea'
                })
            ]
        });
        this.getWindowAlternativeForm().addItem(form);

        restClient = new PMRestClient({
            endpoint: 'activity/' + that.getConfigWebEntry().tas_uid + '/step/' + step.step_uid,
            typeRequest: 'get',
            functionSuccess: function (xhr, response) {
                form.getField('step_mode').setValue(response.step_mode);
                form.getField('step_condition').setValue(response.step_condition);
            },
            functionFailure: function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            }
        });
        restClient.executeRestClient();
        saveButton = this.getWindowAlternativeForm().footer.getItem("secondaryWindow-save");
        cancelButton = this.getWindowAlternativeForm().footer.getItem("secondaryWindow-cancel");
        criteriaButton = this.getWindowAlternativeForm().footer.getItem("secondaryWindow-criteria");
        if (saveButton) {
            saveButton.setHandler(function () {
                var restClient;
                if (form.isValid()) {
                    restClient = new PMRestClient({
                        endpoint: 'activity/' + that.getConfigWebEntry().tas_uid + '/step/' + step.step_uid,
                        typeRequest: 'update',
                        data: form.getData(),
                        functionSuccess: function () {
                            that.getWindowAlternativeForm().close();
                        },
                        functionFailure: function (xhr, response) {
                            PMDesigner.msgWinError(response.error.message);
                        },
                        messageError: 'There are problems update the Step Trigger, please try again.'.translate()
                    });
                    restClient.executeRestClient();
                }
            });
        }
        if (cancelButton) {
            cancelButton.setHandler(function () {
                that.getWindowAlternativeForm().close();
            });
        }
        if (criteriaButton) {
            criteriaButton.handler = null;
            criteriaButton.setVisible(false);
        }
        this.getWindowAlternativeForm().open();
    },

    /**
     * Opens the step of the selected step with the current settings
     * @param step, is the data of selected step
     * @param accordioItem
     * @chainable
     */
    editStepShow: function (step, accordioItem) {
        var that = this,
            inputDocument;
        switch (step.step_type_obj) {
            case 'DYNAFORM':
                var restProxy = new PMRestClient({
                    endpoint: 'dynaform/' + step.step_uid_obj,
                    typeRequest: 'get',
                    functionSuccess: function (xhr, response) {
                        var old = PMUI.activeCanvas,
                            formDesigner;
                        PMUI.activeCanvas = false;
                        formDesigner = PMDesigner.dynaformDesigner(response);
                        formDesigner.onHide = function () {
                            var assignedDynaform,
                                i,
                                data,
                                title;
                            assignedDynaform = that.getStepsAssignedByCriteria("DYNAFORM");
                            if (jQuery.isArray(assignedDynaform)) {
                                for (i = 0; i < assignedDynaform.length; i += 1) {
                                    data = assignedDynaform[i];
                                    if (typeof data === "object") {
                                        if (data.step_uid === step.step_uid) {
                                            title = data.step_position + ". " + data.obj_title;
                                            title = title + ' (' + that.stepsType["DYNAFORM"] + ')';
                                            accordioItem.setTitle(title);
                                            accordioItem.dataItem = data;
                                        }
                                    }
                                }
                            }
                            PMUI.activeCanvas = old;
                        };
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    }
                });
                restProxy.executeRestClient();
                break;
            case 'OUTPUT_DOCUMENT':
                PMDesigner.output();
                PMDesigner.output.showTiny(step.step_uid_obj);
                break;
            case 'INPUT_DOCUMENT':
                inputDocument = new InputDocument({
                    onUpdateInputDocumentHandler: function (data, inputDoc) {
                        var position, title;
                        position = accordioItem.dataItem.step_position;
                        title = position + ". " + data.inp_doc_title;
                        title = title + ' (' + this.stepsType["INPUT_DOCUMENT"] + ')';
                        accordioItem.dataItem.obj_title = data.inp_doc_title;
                        accordioItem.setTitle(title);
                        inputDoc.winMainInputDocument.close();
                    }
                });
                inputDocument.build();
                inputDocument.openFormInMainWindow();
                inputDocument.inputDocumentFormGetProxy(step.step_uid_obj);
                break;
        }
    },

    /**
     * Edit the selected trigger condition
     * @param stepID, It is the id of the step to upgrade
     * @param triggerID, is the id of the trigger to update
     * @param stepType, It is the kind of step to update
     * @param row, PMUI.grid.GridPanelRow, is the row affected
     */
    editCondition: function (stepID, triggerID, stepType, row) {
        var that = this,
            saveButton,
            cancelButton,
            criteriaButton,
            form,
            dataRow;
        dataRow = row.getData();
        this.setWindowAlternative(null);
        this.getWindowAlternativeForm().setWidth(500);
        this.getWindowAlternativeForm().setHeight(350);
        this.getWindowAlternativeForm().setTitle('Condition Trigger'.translate());
        this.getWindowAlternativeForm().setTitle("Trigger".translate());
        form = new PMUI.form.Form({
            id: 'idFormEditCondition',
            width: 500,
            title: 'Condition Trigger'.translate(),
            visibleHeader: false,
            items: [
                new CriteriaField({
                    id: 'st_condition',
                    pmType: 'textarea',
                    name: 'st_condition',
                    valueType: 'string',
                    label: 'Condition'.translate(),
                    placeholder: 'Insert a condition'.translate(),
                    rows: 150,
                    controlsWidth: 250,
                    renderType: 'textarea',
                    value: dataRow.st_condition
                })
            ]
        });
        this.getWindowAlternativeForm().addItem(form);
        saveButton = this.getWindowAlternativeForm().footer.getItem("secondaryWindow-save");
        cancelButton = this.getWindowAlternativeForm().footer.getItem("secondaryWindow-cancel");
        criteriaButton = this.getWindowAlternativeForm().footer.getItem("secondaryWindow-criteria");
        if (saveButton) {
            saveButton.setHandler(function () {
                var data,
                    restClient;
                data = form.getData();
                data.st_type = stepType;
                restClient = new PMRestClient({
                    endpoint: 'activity/' + that.getConfigWebEntry().tas_uid + '/step/'
                    + ((typeof (stepID) != "undefined") ? stepID + "/" : "") + 'trigger/' + triggerID,
                    typeRequest: 'update',
                    data: data,
                    messageError: 'There are problems update the Step Trigger, please try again.'.translate(),
                    functionSuccess: function (xhr, response) {
                        dataRow.st_condition = data.st_condition;
                        row.setData(dataRow);
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    }
                });
                restClient.executeRestClient();
                that.getWindowAlternativeForm().close();
            });
        }
        if (cancelButton) {
            cancelButton.setHandler(function () {
                that.getWindowAlternativeForm().close();
            });
        }
        if (criteriaButton) {
            criteriaButton.setVisible(false);
            criteriaButton.handler = null;
        }
        this.getWindowAlternativeForm().open();
    },

    /**
     * This method is executed when editing a "trigger" in a row of the grid.
     * secondary window opens with the current configuration of the trigger
     * @param trigger, is the return value when is update 'trigger' action  in the enpoint
     * @param triggerID, is the id of the trigger to update
     * @chainable
     */
    editTrigger: function (trigger, triggerID) {
        var that = this,
            codeMirror,
            saveButton,
            cancelButton,
            criteriaButton;
        this.setWindowAlternative(null);
        codeMirror = new PMCodeMirror({
            id: "codeMirror"
        });
        CodeMirror.commands.autocomplete = function (cm) {
            CodeMirror.showHint(cm, CodeMirror.phpHint);
        };
        codeMirror.setValue(trigger);
        this.getWindowAlternativeForm().setWidth(DEFAULT_WINDOW_WIDTH);
        this.getWindowAlternativeForm().setHeight(DEFAULT_WINDOW_HEIGHT);
        this.getWindowAlternativeForm().setTitle("Trigger".translate());
        saveButton = this.getWindowAlternativeForm().footer.getItem("secondaryWindow-save");
        cancelButton = this.getWindowAlternativeForm().footer.getItem("secondaryWindow-cancel");
        criteriaButton = this.getWindowAlternativeForm().footer.getItem("secondaryWindow-criteria");
        if (saveButton) {
            saveButton.setHandler(function () {
                var restClient = new PMRestClient({
                    endpoint: 'trigger/' + triggerID,
                    typeRequest: 'update',
                    data: {
                        tri_param: '',
                        tri_webbot: codeMirror.getValue()
                    },
                    messageError: 'There are problems updating the trigger, please try again.'.translate(),
                    messageSuccess: 'Trigger updated correctly'.translate(),
                    flashContainer: that.getWindow(),
                    functionSuccess: function () {
                        that.getWindowAlternativeForm().close();
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    }
                });
                restClient.executeRestClient();
            });
        }
        if (cancelButton) {
            cancelButton.setHandler(function () {
                that.getWindowAlternativeForm().close();
            });
        }
        if (criteriaButton) {
            criteriaButton.setVisible(true);
            criteriaButton.setHandler(function () {
                var picker = new VariablePicker();
                picker.open({
                    success: function (variable) {
                        var cursorPos,
                            codemirror;
                        codemirror = codeMirror.cm;
                        cursorPos = codemirror.getCursor();
                        codemirror.replaceSelection(variable);
                        codemirror.setCursor(cursorPos.line, cursorPos.ch);
                    }
                });
            });
        }
        this.getWindowAlternativeForm().open();
        this.getWindowAlternativeForm().addItem(codeMirror);
        codeMirror.cm.setSize(this.getWindowAlternativeForm().getWidth(), 380);
        $(".CodeMirror.cm-s-default.CodeMirror-wrap").after($ctrlSpaceMessage.css({
            "padding-left": "10px",
            "margin": "3px 0px 0px 0px"
        }));
        codeMirror.cm.refresh();
    },

    /**
     * Returns the type of step, for the execution of "endpoint"
     * @param st_type, this a step type, the accepted parameters are:
     *  - BEFORE_ASSIGNMENT
     *  - BEFORE_ROUTING
     *  - AFTER_ROUTING
     *  - BEFORE
     *  - AFTER
     * @returns {string}
     */
    getStepType: function (st_type) {
        var value;
        switch (st_type) {
            case 'BEFORE_ASSIGNMENT':
                value = 'before-assignment';
                break;
            case 'BEFORE_ROUTING':
                value = 'before-routing';
                break;
            case 'AFTER_ROUTING':
                value = 'after-routing';
                break;
            case 'BEFORE':
                value = 'before';
                break;
            case 'AFTER':
                value = 'after';
                break;
            default:
                value = '';
                break;
        }
        return value;
    },

    /**
     * Checks whether a trigger is already assigned in a grid
     * @param grid, is instanceof PMUI.grid.Grid, in conducting the search
     * @param tri_uid, search parameter in the rows of the grid
     * @returns {boolean}
     */
    isTriggerAssigned: function (grid, tri_uid) {
        var data, i, exist = false;
        data = grid.getData();
        if (grid && jQuery.isArray(data)) {
            for (i = 0; i < data.length; i += 1) {
                if (data[i].tri_uid === tri_uid) {
                    exist = true;
                    break;
                }
            }
        }
        return exist;
    },

    /**
     * This method eliminates the list of triggers trigger an assigned step
     * @param row, the row affected or selected
     * @param grid, It is affected or grid to remove selected row
     */
    removeTrigger: function (row, grid) {
        var that = this,
            message = 'Do you want to remove the trigger "',
            messageData = row.getData().tri_title ? row.getData().tri_title : "",
            yesButton,
            noButton,
            restClient;
        message = message + messageData + '"?';
        this.getConfirmWindow().setMessage(message.translate());
        yesButton = this.getConfirmWindow().footer.getItem("confirmWindow-footer-yes");
        if (yesButton) {
            yesButton.setHandler(function () {
                restClient = new PMRestClient({
                    endpoint: grid.step_uid === undefined ?
                        'activity/' + that.getConfigWebEntry().tas_uid + '/step/trigger/' + row.getData().tri_uid + '/'
                        + that.getStepType(row.getData().st_type) : 'activity/' + that.getConfigWebEntry().tas_uid
                        + '/step/' + grid.step_uid + '/trigger/' + row.getData().tri_uid
                        + '/' + that.getStepType(row.getData().st_type),
                    typeRequest: 'remove',
                    functionSuccess: function (xhr, response) {
                        grid.removeItem(row);
                        that.getConfirmWindow().close();
                        that.updateIndexToGrid(grid);
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    },
                    flashContainer: that.getWindow(),
                    messageError: 'An unexpected error while deleting the trigger, please try again later.'.translate(),
                    messageSuccess: 'Trigger removed successfully'.translate()
                });
                restClient.executeRestClient();
            });
        }
        noButton = this.getConfirmWindow().footer.getItem("confirmWindow-footer-no");
        if (noButton) {
            noButton.setHandler(function () {
                that.getConfirmWindow().close();
            });
        }
        this.getConfirmWindow().open();
    },

    /**
     * Updates indexes of elements selected grid
     * @param grid, It is affected or grid to remove selected row
     * @returns {stepsTask}
     */
    updateIndexToGrid: function (grid) {
        var cell, rows, i, row;
        if (grid) {
            rows = grid.getItems();
            if (jQuery.isArray(rows)) {
                for (i = 0; i < rows.length; i += 1) {
                    row = rows[i];
                    cell = row.cells.find("columnData");
                    if (cell) {
                        cell.setContent(i + 1);
                    }
                }
            }
        }
        return this;
    },

    /**
     * Add custom buttons on the head of an element of stepsAssignAccordion
     * are three buttons
     * properties
     * edit
     * remove
     * @param html, is the html of the header accordion item
     * @param step, the data of the step selected
     * @param accordionItem
     */
    customAccordionItemButtons: function (html, step, accordionItem) {
        var that = this,
            propertiesStep,
            editStep,
            removeStep,
            $html,
            containerButtons,
            title;
        if (html) {
            $html = jQuery(html.getElementsByClassName("pmui-accordion-item-header"));
            title = step.obj_title + ' (' + step.step_type_obj + ')';
            $html.find(".pmui-accordion-item-title").get(0).title = title;
            containerButtons = $('<div></div>');
            containerButtons.addClass("propertiesTask-accordionItem");
            propertiesStep = $('<a>' + 'Properties'.translate() + '</a>');
            propertiesStep.addClass("mafe-button-edit propertiesTask-accordionButton");
            editStep = $('<a>' + 'Edit'.translate() + '</a>');
            editStep.addClass("mafe-button-edit propertiesTask-accordionButton");
            removeStep = $('<a>' + 'Remove'.translate() + '</a>');
            removeStep.addClass("mafe-button-delete propertiesTask-accordionButton");

            propertiesStep.click(function (e) {
                e.preventDefault();
                e.stopPropagation();
                that.propertiesStepShow(step);
                return false;
            });

            editStep.click(function (e) {
                e.preventDefault();
                e.stopPropagation();
                that.editStepShow(step, accordionItem);
                return false;
            });

            removeStep.click(function (e) {
                e.preventDefault();
                e.stopPropagation();
                that.removeStepShow(step);
                return false;
            });
            containerButtons.append(propertiesStep);
            containerButtons.append(editStep);
            containerButtons.append(removeStep);
            $html.append(containerButtons);
        }
    },

    /**
     * Add tooltip in treeNode elements
     * @returns {stepsTask}
     */
    updateIndexPosition: function (treeNode) {
        var items,
            i,
            item,
            $item,
            text,
            data;
        if (treeNode && treeNode.html) {
            items = treeNode.getItems();
            if (jQuery.isArray(items)) {
                for (i = 0; i < items.length; i += 1) {
                    item = items[i];
                    if (item.html) {
                        $item = $(item.html);
                        data = item.getData();
                        text = $item.find("a").get(0);
                        text.title = data.obj_title;
                    }
                }
            }
        }
        return this;
    },

    /**
     * This method loads the data to stepsAssignTree
     * @param response, the answer is an array containing all the elements
     * that will be loaded into the step stepsAssignTree
     * @chainable
     */
    loadTreePanelData: function (response) {
        var data,
            i,
            j,
            type,
            label,
            items = [],
            dataTree = [],
            treeNode;
        data = response[1].response;

        dataTree.push({
            obj_title: 'Trigger (s)'.translate(),
            items: [this.notItemConfig()]
        });

        for (i = 0; i < data.length; i += 1) {
            items.push({
                obj_title: data[i]['tri_title'],
                obj_type: data[i]['tri_type'],
                obj_uid: data[i]['tri_uid']
            });
        }

        if (items.length) {
            dataTree.shift();
            dataTree.push({
                obj_title: 'Trigger (s)'.translate(),
                items: items,
                id: "TRIGGER"
            });
        }

        data = response[0].response;
        type = this.groupType;
        label = this.groupLabel;
        items = [];
        for (i = 0; i < type.length; i += 1) {
            items = [];
            for (j = 0; j < data.length; j += 1) {
                if (type[i] === data[j].obj_type) {
                    items.push({
                        obj_title: data[j]['obj_title'],
                        obj_type: data[j]['obj_type'],
                        obj_uid: data[j]['obj_uid']
                    });
                }
            }
            if (items.length === 0) {
                dataTree.push({
                    obj_title: label[i].translate(),
                    items: [this.notItemConfig()],
                    behavior: '',
                    id: type[i]
                });
            } else {
                dataTree.push({
                    obj_title: label[i].translate(),
                    items: items,
                    behavior: 'drag',
                    id: type[i]
                });
            }
        }
        this.getStepsAssignTree().clearItems();
        for (i = 0; i < dataTree.length; i += 1) {
            this.getStepsAssignTree().addDataItem(dataTree[i]);
            treeNode = this.getStepsAssignTree().getItem(i);
            treeNode.setID(dataTree[i].id);
            this.updateIndexPosition(treeNode);
        }
        return this;
    },

    /**
     * It is an extension to add the "sortable" event "stepsAssignTree".
     * when choosing a node treePanel and you want to add to the accordion or the grid
     * @chainable
     */
    addEventSortableInTreePanelElements: function () {
        var that = this,
            items = this.getStepsAssignTree().getItems(),
            connect,
            i,
            sw,
            nodeItems;
        for (i = 0; i < items.length; i += 1) {
            nodeItems = items[i].getItems();
            if (nodeItems.length && nodeItems[0].getData().obj_type) {
                sw = items[i].getItems()[0].getData().obj_type === "SCRIPT";
                connect = sw ? ".pmui-gridpanel-tbody" : ".pmui-accordion-panel-body";
                $(items[i].html).find('ul').find('>li').draggable({
                    appendTo: document.body,
                    revert: "invalid",
                    helper: "clone".translate(),
                    cursor: "move",
                    zIndex: 1000,
                    connectToSortable: connect,
                    start: function (e) {
                        var i, nodeTag, node, nodeData, accordionItems, item;
                        nodeTag = e.target;
                        node = PMUI.getPMUIObject(nodeTag);
                        nodeData = node.getData();
                        accordionItems = that.getStepsAssignAccordion().getItems();
                        $(that.getStepsAssignAccordion().body).hide();
                        if (nodeData.obj_type !== "SCRIPT") {
                            for (i = 0; i < accordionItems.length; i += 1) {
                                item = accordionItems[i];
                                item.collapse();
                            }
                        }
                        $(that.getStepsAssignAccordion().body).show();
                    },
                    stop: function () {
                        var i = 0,
                            max;
                        if (that.elementAccordionOpen) {
                            max = that.elementAccordionOpen.getSize();
                            for (i = 0; i < max; i += 1) {
                                that.elementAccordionOpen.get(i).expand();
                            }
                        }
                    }
                });
            } else {
                $(nodeItems[0].html).draggable("disable");
            }
        }
    },

    /**
     * It is an extension to add the "sortable" event "stepAssignAccordion".
     * when a node "treePanel" is added to stop runs and is where you choose if it's a sort or aggregation.
     * @chainable
     */
    addEventSortableInAccordionElements: function () {
        var that = this,
            tagContainer,
            newIndex,
            index,
            treeNodeObject,
            treeNodeData;
        if (this.getStepsAssignAccordion() && this.getStepsAssignAccordion().html) {
            tagContainer = this.getStepsAssignAccordion().body;
            $(tagContainer).sortable({
                items: '>div:not(#idAssignment,#idRouting)',
                placeholder: 'steps-placeholder',
                receive: function (event, ui) {
                    var item = ui ? ui.item : null;
                    if (item && item instanceof jQuery && item.length) {
                        treeNodeObject = PMUI.getPMUIObject(item.get(0));
                        treeNodeData = treeNodeObject.getData();
                    }
                },
                stop: function (event, ui) {
                    var itemClone = ui ? ui.item : null,
                        accordionItems,
                        accordionItem,
                        dataEdited,
                        restClientMultipart,
                        restClient;
                    var newIndex = ui.item.index();
                    accordionItems = that.getStepsAssignAccordion().getItems();
                    if (itemClone && itemClone instanceof jQuery && itemClone.length) {
                        if (treeNodeObject) {
                            itemClone.remove();
                            if (newIndex + 1 > accordionItems.length) {
                                newIndex = that.stepsAssigned.getSize();
                            }
                            restClient = new PMRestClient({
                                endpoint: 'activity/' + that.getConfigWebEntry().tas_uid + '/step',
                                typeRequest: 'post',
                                data: {
                                    step_type_obj: treeNodeData.obj_type,
                                    step_uid_obj: treeNodeData.obj_uid,
                                    step_condition: '',
                                    step_position: newIndex + 1,
                                    step_mode: 'EDIT'
                                },
                                functionSuccess: function (xhr, response) {
                                    var item, buttonAfected, treeNode;
                                    that.getStepsAssignTree().removeItem(treeNodeObject);
                                    treeNode = that.getStepsAssignTree().items.find("id", response.step_type_obj);
                                    if (treeNode.items.getSize() === 0) {
                                        treeNode.addDataItem(that.notItemConfig());
                                    }
                                    response.obj_description = '';
                                    response.obj_title = treeNodeData.obj_title;
                                    response.triggers = [];
                                    item = that.createAccordionItem(response, true, true);
                                    item.dataItem = response;
                                    if (that.getStepsAssignAccordion().items.getSize() === 2) {
                                        that.getStepsAssignAccordion().addItem(item, 0);
                                    } else {
                                        that.getStepsAssignAccordion().addItem(item, newIndex);
                                    }
                                    that.getStepsAssignAccordion().defineEvents();
                                    that.customAccordionItemButtons(item.html, response, item);
                                    that.updateItemIndexToAccordion();
                                    that.addEventSortableInAccordionElements();
                                    that.addEventSortableInTreePanelElements();
                                    buttonAfected = that.getLabelsPanel().getItem("expand-button");
                                    buttonAfected.setDisabled(false);
                                },
                                functionFailure: function (xhr, response) {
                                    PMDesigner.msgWinError(response.error.message);
                                },
                                messageError: 'An unexpected error while assigning the step, please try again later.'
                                    .translate(),
                                messageSuccess: 'Step assigned successfully.'.translate(),
                                flashContainer: that.getStepsAssignAccordion().getParent()
                            });
                            restClient.executeRestClient();
                        } else {
                            accordionItem = PMUI.getPMUIObject(ui.item.get(0));
                            index = that.getStepsAssignAccordion().items.indexOf(accordionItem);
                            if (newIndex !== index) {
                                that.getStepsAssignAccordion().items.remove(accordionItem);
                                that.getStepsAssignAccordion().items.insertAt(accordionItem, newIndex);
                                dataEdited = {
                                    step_position: newIndex + 1,
                                    step_uid: accordionItem.dataItem.step_uid,
                                    step_type_obj: accordionItem.dataItem.step_type_obj,
                                    step_uid_obj: accordionItem.dataItem.step_uid_obj
                                };
                                restClientMultipart = new PMRestClient({
                                    endpoint: 'activity/' + that.getConfigWebEntry().tas_uid + '/step/'
                                    + accordionItem.dataItem.step_uid,
                                    typeRequest: 'update',
                                    data: dataEdited,
                                    functionSuccess: function (xhr, response) {
                                        that.updateItemIndexToAccordion();
                                    },
                                    functionFailure: function (xhr, response) {
                                        PMDesigner.msgWinError(response.error.message);
                                    },
                                    messageError: 'An unexpected error while editing the step, please try again later.'
                                        .translate(),
                                    messageSuccess: 'Step editing successfully.'.translate(),
                                    flashContainer: that.getWindow()
                                });
                                restClientMultipart.executeRestClient();
                            }
                        }
                    }
                },
                start: function (e, ui) {
                    newIndex = ui.item.index();
                }
            });
        }
    },

    /**
     * Updates indexes of elements assigned
     * @returns {stepsTask}
     */
    updateItemIndexToAccordion: function () {
        var title,
            i,
            item,
            dataItem,
            items = this.getStepsAssignAccordion().items,
            position,
            max;
        max = items.getSize();
        for (i = 0; i < max; i += 1) {
            item = items.get(i);
            position = items.indexOf(item);
            dataItem = item.dataItem;
            title = (position + 1) + ".  " + dataItem.obj_title;
            if (this.stepsType[dataItem.step_type_obj]) {
                title = title + ' (' + this.stepsType[dataItem.step_type_obj] + ')';
            }
            item.dataItem.step_position = i + 1;
            item.setTitle(title);
        }
        return this;
    },

    /**
     * The window opens for confirmation of the removal step
     * @param step, the current step to remove
     * @chainable
     */
    removeStepShow: function (step) {
        var that = this,
            title,
            yesButton,
            noButton,
            restClient;
        if (this.stepsType[step.step_type_obj] !== undefined) {
            title = "Step {0} ( {1} )".translate([step.obj_title, this.stepsType[step.step_type_obj]]);
            this.getConfirmWindow().setTitle(title);
        } else {
            this.getConfirmWindow().setTitle("Step " + step.step_type_obj.capitalize());
        }
        this.getConfirmWindow().setMessage("Do you want to remove the step '{0}'?".translate([step.obj_title]));
        yesButton = this.getConfirmWindow().footer.getItem("confirmWindow-footer-yes");
        noButton = this.getConfirmWindow().footer.getItem("confirmWindow-footer-no");
        if (yesButton) {
            yesButton.setHandler(function () {
                restClient = new PMRestClient({
                    endpoint: 'activity/' + that.getConfigWebEntry().tas_uid + '/step/' + step.step_uid,
                    typeRequest: 'remove',
                    functionSuccess: function (xhr, response) {
                        that.removingStepTask(step, response);
                        that.getConfirmWindow().close();
                        that.updateItemIndexToAccordion();
                    },
                    functionFailure: function (xhr, response) {
                        PMDesigner.msgWinError(response.error.message);
                    },
                    messageError: 'An unexpected error while deleting the step, please try again later.'.translate(),
                    messageSuccess: 'Step removed successfully'.translate(),
                    flashContainer: that.getWindow().getParent()
                });
                restClient.executeRestClient();
            });
        }
        if (noButton) {
            noButton.setHandler(function () {
                that.getConfirmWindow().close();
            });
        }
        this.getConfirmWindow().open();
    },

    /**
     * Get the steps is not assigned by a criterion
     * @param criteria, It is the filter criteria search
     * @param stepAvailable, all steps Unassigned
     * @returns {Array}, filtered items
     */
    getAvailablesStepsByCriteria: function (criteria, stepAvailable) {
        var items = [],
            i;
        if (jQuery.isArray(stepAvailable)) {
            for (i = 0; i < stepAvailable.length; i += 1) {
                if (stepAvailable[i].obj_type === criteria) {
                    items.push(stepAvailable[i]);
                }
            }
        }
        return items;
    },

    /**
     * Eliminates the step of step Assign Accordion
     * @param step, the current step to remove
     * @param response, data from the endpoint
     */
    removingStepTask: function (step, response) {
        var stepObject,
            stepAvailable,
            treeNodeObject,
            stepAvailables,
            i,
            itemsTreeNode = [],
            items = [];
        stepObject = this.getStepsAssignAccordion().getItem("id" + step.step_uid_obj);
        this.elementAccordionOpen.remove(stepObject);
        this.stepsAssigned.remove(stepObject);
        this.getStepsAssignAccordion().removeItem(stepObject);
        if (stepObject) {
            this.getStepAvailables(
                function (xhr, response) {
                    stepAvailable = response[0] ? response[0].response : [];
                },
                function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            );
            stepAvailables = this.getAvailablesStepsByCriteria(step.step_type_obj, stepAvailable);
            for (i = 0; i < stepAvailables.length; i += 1) {
                items.push({
                    obj_title: stepAvailables[i]['obj_title'],
                    obj_type: stepAvailables[i]['obj_type'],
                    obj_uid: stepAvailables[i]['obj_uid']
                });
            }
            treeNodeObject = this.getStepsAssignTree().getItem(step.step_type_obj);
            itemsTreeNode = treeNodeObject.getItems();
            for (i = 0; i < itemsTreeNode.length; i += 1) {
                treeNodeObject.removeItem(itemsTreeNode[i]);
            }
            treeNodeObject.clearItems();
            treeNodeObject.setDataItems(items);
            this.updateIndexPosition(treeNodeObject);
            this.addEventSortableInTreePanelElements();
            this.addEventSortableInAccordionElements();
        }
    },

    /**
     * This method is executed when an element stepsAssignTree, is assigned in a grid
     * @param receiveData, data of the droppable item
     * @param grid, the affected grid
     * @param index, the index position row
     * @returns {stepsTask}
     */
    receiveTreeNodeItem: function (receiveData, grid, index) {
        var restClient,
            message,
            that = this;
        if (this.isTriggerAssigned(grid, receiveData.obj_uid)) {
            message = new PMUI.ui.FlashMessage({
                message: 'Trigger is assigned.'.translate(),
                duration: 3000,
                severity: 'error',
                appendTo: this.getWindow()
            });
            message.show();
            return;
        }
        restClient = new PMRestClient({
            endpoint: grid.step_uid === undefined ?
                'activity/' + that.getConfigWebEntry().tas_uid + '/step/trigger' : 'activity/'
                + that.getConfigWebEntry().tas_uid + '/step/' + grid.step_uid + '/trigger',
            typeRequest: 'post',
            data: {
                tri_uid: receiveData.obj_uid,
                st_type: grid.st_type,
                st_condition: '',
                st_position: index + 1
            },
            functionSuccess: function (xhr, response) {
                grid.addDataItem({
                    st_condition: '',
                    st_position: index + 1,
                    st_type: grid.st_type,
                    tri_description: '',
                    tri_title: receiveData.obj_title,
                    tri_uid: receiveData.obj_uid,
                    obj_title: receiveData.obj_title,
                    obj_uid: receiveData.obj_uid
                }, index);
            },
            functionFailure: function (xhr, response) {
                PMDesigner.msgWinError(response.error.message);
            }
        });
        restClient.executeRestClient();
        return this;
    }
};
var PMMessageType = function (options) {
    var that = this;
    this.msgt_uid = null;
    this.edit = false;
    this.editRow = null;
    this.dirtyAcceptedValue = false;
    this.fieldInfo = null;
    this.onSave = new Function();
    this.onEdit = new Function();
    this.requiredMessage = null;
    this.clickedClose = true;
    this.previousMessageValue = null;
    this.buttonCreate = new PMUI.ui.Button({
        id: 'buttonCreate',
        text: "Create".translate(),
        height: '36px',
        width: 100,
        style: {
            cssClasses: [
                'mafe-button-create'
            ]
        },
        handler: function () {
            that.showForm();
        }
    });

    this.buttonFieldAdd = new PMUI.field.ButtonField({
        id: 'buttonFieldAdd',
        pmType: 'buttonField',
        value: 'Create'.translate(),
        labelVisible: false,
        buttonAlign: 'center',
        controlsWidth: 50,
        proportion: 0.6,
        handler: function (field) {
            that.addAcceptedValue();
        },
        buttonType: "success",
        style: {
            cssProperties: {
                'vertical-align': 'top',
                'padding-top': '37px',
                'padding-right': '1px',
                'padding-bottom': '1px',
                'padding-left': '1px'
            }
        }
    });
    this.buttonFieldAdd.getControl().button.setButtonType("success");

    this.buttonFieldCancel = new PMUI.field.ButtonField({
        id: 'buttonFieldCancel',
        pmType: 'buttonField',
        value: 'Cancel'.translate(),
        labelVisible: false,
        buttonAlign: 'center',
        controlsWidth: 50,
        proportion: 0.6,
        handler: function (field) {
            that.cancelAcceptedValue();
        },
        style: {
            cssProperties: {
                'vertical-align': 'top',
                'padding-top': '37px',
                'padding-right': '1px',
                'padding-bottom': '1px',
                'padding-left': '1px'
            }
        }
    });
    this.buttonFieldCancel.getControl().button.setButtonType("error");

    this.frmMessageType = new PMUI.form.Form({
        id: 'frmMessageType',
        width: 'auto',
        title: '',
        visibleHeader: false,
        items: [
            {
                id: 'txtMessageTypeName',
                name: 'txtMessageTypeName',
                pmType: 'text',
                label: "Name".translate(),
                placeholder: "Name".translate(),
                value: '',
                required: true,
                valueType: 'string',
                maxLength: 60,
                controlsWidth: 460
            },
            {
                id: "frmAcceptedValues",
                pmType: 'panel',
                legend: "Message Field".translate(),
                fieldset: true,
                layout: 'hbox',
                items: [
                    {
                        pmType: "text",
                        name: "txtMessageTypeVariableName",
                        label: "Message Field Name".translate(),
                        labelWidth: "100%",
                        controlsWidth: "400px",
                        proportion: 2.5,
                        valueType: "string",
                        maxLength: 255,
                        required: true,
                        labelPosition: "top",
                        validators: [
                            {
                                pmType: "regexp",
                                criteria: /^[a-zA-Z_]+[0-9a-zA-Z_]+$/,
                                errorMessage: "A valid variable starts with a letter or underscore, followed by any number of letters, numbers, or underscores.".translate()
                            }
                        ]
                    },
                    that.buttonFieldCancel,
                    that.buttonFieldAdd
                ]
            }
        ]
    });

    this.frmMessageType.getData = function () {
        var data = getData2PMUI(that.frmMessageType.html);
        return data;
    };

    this.gridMessages = new PMUI.grid.GridPanel({
        id: 'gridMessages',
        pageSize: 10,
        width: '96%',
        style: {
            cssClasses: ['mafe-gridPanel']
        },
        filterPlaceholder: 'Search ...'.translate(),
        emptyMessage: 'No records found'.translate(),
        nextLabel: 'Next'.translate(),
        previousLabel: 'Previous'.translate(),
        tableContainerHeight: 374,
        customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
            return '';
        },
        columns: [{
            id: "msgtName",
            title: "Name".translate(),
            dataType: "string",
            columnData: "msgt_name",
            alignmentCell: "left",
            width: "330px",
            sortable: true
        }, {
            id: 'msgtEdit',
            dataType: 'button',
            title: '',
            buttonLabel: 'Edit'.translate(),
            width: '60px',
            buttonStyle: {
                cssClasses: [
                    'mafe-button-edit'
                ]
            },
            onButtonClick: function (row, grid) {
                that.showFormEdit(row.getData());
            }
        }, {
            id: 'msgtDelete',
            dataType: 'button',
            title: '',
            buttonLabel: function (row, data) {
                return 'Delete'.translate();
            },
            width: '70px',
            buttonStyle: {
                cssClasses: [
                    'mafe-button-delete'
                ]
            },
            onButtonClick: function (row, grid) {
                that.del(row.getData(), row, grid);
            }
        }
        ],
        dataItems: null
    });

    this.gridAcceptedValues = new PMUI.grid.GridPanel({
        id: 'gridAcceptedValues',
        pageSize: 5,
        height: 240,
        style: {cssClasses: ['mafe-gridPanel']},
        filterPlaceholder: 'Search ...'.translate(),
        emptyMessage: 'No records found'.translate(),
        nextLabel: 'Next'.translate(),
        previousLabel: 'Previous'.translate(),
        filterable: false,
        tableContainerHeight: 200,
        customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
            return '';
        },
        columns: [
            {
                id: "msgtv_name",
                title: "Message Field Name".translate(),
                columnData: "msgtv_name",
                dataType: "string",
                alignmentCell: "left",
                width: 180,
                sortable: true
            },
            {
                id: "btnEdit",
                dataType: "button",
                title: "",
                buttonLabel: "Edit".translate(),
                width: 60,
                buttonStyle: {cssClasses: ["mafe-button-edit"]},

                onButtonClick: function (row, grid) {
                    that.editAcceptedValue(row);
                }
            },
            {
                id: "btnDelete",
                dataType: "button",
                title: "",
                buttonLabel: function (row, data) {
                    return "Delete".translate();
                },
                width: 75,
                buttonStyle: {cssClasses: ["mafe-button-delete"]},
                onButtonClick: function (row, grid) {
                    that.deleteAcceptedValue(row);
                }
            }
        ],
        dataItems: null
    });

    this.isDirtyFrmMessageType = function () {
        $("input,select,textarea").blur();
        if (this.frmMessageType.isVisible()) {
            if (this.frmMessageType.isDirty() || this.dirtyAcceptedValue) {
                var message_window = new PMUI.ui.MessageWindow({
                    id: 'messageWindowCancel',
                    width: 490,
                    title: "Message Types".translate(),
                    windowMessageType: 'warning',
                    bodyHeight: 'auto',
                    message: 'Are you sure you want to discard your changes?'.translate(),
                    footerItems: [
                        {
                            id: 'messageWindowNo',
                            text: 'No'.translate(),
                            handler: function () {
                                message_window.close();
                            },
                            buttonType: "error"
                        },
                        {
                            id: 'messageWindowYes',
                            text: 'Yes'.translate(),
                            handler: function () {
                                that.requiredMessage.hide();
                                if (that.clickedClose) {
                                    that.winMessageType.close();
                                }
                                message_window.close();
                                that.showGrid();
                            },
                            buttonType: "success"
                        }
                    ]
                });
                message_window.open();
                message_window.showFooter();
            } else {
                if (this.clickedClose) {
                    this.winMessageType.close();
                } else {
                    this.showGrid();
                    this.requiredMessage.hide();
                }
            }
        } else {
            this.winMessageType.close();
        }
    };
    this.winMessageType = new PMUI.ui.Window({
        id: 'winMessageType',
        title: '',
        height: DEFAULT_WINDOW_HEIGHT,
        width: DEFAULT_WINDOW_WIDTH,
        buttonsPosition: 'right',
        onBeforeClose: function () {
            that.clickedClose = true;
            that.isDirtyFrmMessageType();
        },
        footerItems: [
            new PMUI.ui.Button({
                id: 'winMessageTypeCancel',
                text: 'Cancel'.translate(),
                handler: function () {
                    that.clickedClose = false;
                    that.isDirtyFrmMessageType();
                },
                buttonType: 'error'
            }),
            new PMUI.ui.Button({
                id: 'winMessageTypeSave',
                text: "Save".translate(),
                handler: function () {
                    that.frmAcceptedValues.getItems("fields").forEach(function (i) {
                        i.disable();
                    });
                    if (that.edit) {
                        that.updateMessageType();
                    } else {
                        that.createMessageType();
                    }
                    that.frmAcceptedValues.getItems("fields").forEach(function (i) {
                        i.enable();
                    });
                },
                buttonType: 'success'
            })
        ],
        buttonPanelPosition: 'bottom'
    });
    PMMessageType.prototype.init.call(this);
};

PMMessageType.prototype.init = function () {
    var that = this;

    that.buttonCreate.defineEvents();
    that.winMessageType.addItem(that.gridMessages);
    that.winMessageType.addItem(that.frmMessageType);
    that.winMessageType.addItem(that.gridAcceptedValues);
    that.winMessageType.hideFooter();
    that.winMessageType.open();
    that.requiredMessage = $(document.getElementById("requiredMessage"));

    this.buttonFieldAdd.controls[0].button.setStyle({cssProperties: {padding: "6px 15px"}});
    this.buttonFieldCancel.controls[0].button.setStyle({cssProperties: {padding: "6px 15px"}});
    that.requiredMessage.css({float: "none"});
    that.winMessageType.footer.html.style.textAlign = 'right';

    that.frmAcceptedValues = PMUI.getPMUIObject($('#frmAcceptedValues').css({'width': '690px'}).get(0));
    $('#gridMessages .pmui-textcontrol').css({'margin-top': '5px', width: '250px'});

    //$(that.frmAcceptedValues.getHTML()).find("#requiredMessage").empty();
    that.requiredMessage.hide();
    //that.winMessageType.body.appendChild(that.requiredMessage[0]);
    that.gridMessages.dom.toolbar.appendChild(that.buttonCreate.getHTML());
    that.showGrid();

    validateKeysField(that.frmMessageType.getField('txtMessageTypeName').getControls()[0].getHTML(), ['isbackspace', 'isnumber', 'isletter', 'isunderscore']);
    that.resetFrmAcceptedValues();
};

PMMessageType.prototype.createMessageType = function () {
    var that = this,
        data;

    if (!this.frmMessageType.isValid()) {
        return;
    }
    if (that.gridAcceptedValues.getData().length == 0) {
        PMDesigner.msgFlash('Add at least one variable.'.translate(), winMessageType, 'error', 3000, 5);
        return;
    }

    data = this.frmMessageType.getData();
    data.msgt_name = data.txtMessageTypeName;
    data.msgt_variables = that.getDataAcceptedValues();

    // This returned data is not necessary at sendind, so it is deleted
    delete data.txtMessageTypeVariableName;
    delete data.buttonFieldCancel;
    delete data.buttonFieldAdd;

    (new PMRestClient({
        endpoint: 'message-type',
        typeRequest: 'post',
        functionSuccess: function (xhr, response) {
            if (that.onSave(xhr, response) === false) {
                that.msgt_uid = null;
                return;
            }
            that.showGrid();
            that.load();
            that.msgt_uid = null;
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        },

        messageError: ' ',
        data: data,
        messageSuccess: "Message Type Saved successfully.".translate(),
        flashContainer: that.panel

    })).executeRestClient();
};

PMMessageType.prototype.updateMessageType = function () {
    var that = this,
        data;
    if (!this.frmMessageType.isValid()) {
        return;
    }
    if (that.gridAcceptedValues.getData().length == 0) {
        if (!this.frmAcceptedValues.isValid()) {
            return;
        } else {
            PMDesigner.msgFlash('Add at least one variable.'.translate(), winMessageType, 'error', 3000, 5);
            return;
        }
    }

    data = this.frmMessageType.getData();
    data.msgt_name = data.txtMessageTypeName;
    data.msgt_variables = that.getDataAcceptedValues();

    (new PMRestClient({
        endpoint: "message-type/" + that.msgt_uid,
        typeRequest: "update",
        data: data,

        functionSuccess: function (xhr, response) {
            if (that.onEdit(xhr, response, data) === false) {
                that.msgt_uid = null;
                return;
            }

            that.showGrid();
            that.load();
            that.msgt_uid = null;
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        },

        messageError: ' ',
        messageSuccess: "Message Type edited successfully.".translate(),
        flashContainer: that.panel
    })).executeRestClient();
};

PMMessageType.prototype.deleteMessage = function (msgt_uid) {
    var that = this;

    (new PMRestClient({
        endpoint: 'message-type/' + msgt_uid,
        typeRequest: 'remove',

        functionSuccess: function (xhr, response) {
            that.load();
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        },

        messageError: 'Working...'.translate(),
        messageSuccess: 'Message Type Deleted successfully'.translate(),
        flashContainer: that.panel
    })).executeRestClient();
};

PMMessageType.prototype.load = function () {
    var that = this, restProxy;

    restProxy = new PMRestClient({
        endpoint: 'message-types',
        typeRequest: 'get',

        functionSuccess: function (xhr, response) {
            that.gridMessages.setDataItems(response);
            that.gridMessages.sort('msgt_name', 'asc');

            $(that.gridMessages.dom.toolbar).find("input").val("");
            that.gridMessages.clearFilter();
            document.getElementById("requiredMessage").style.marginTop = "16px";
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        }
    });

    restProxy.executeRestClient();
};

PMMessageType.prototype.del = function (data, row, grid) {
    var that = this,
        confirmWindow = new PMUI.ui.MessageWindow({
            id: 'confirmWindowDel',
            windowMessageType: 'warning',
            width: 490,
            bodyHeight: 'auto',
            title: "Message Types".translate(),
            message: "Do you want to delete this Message Type?".translate(),
            footerItems: [
                {
                    text: 'No'.translate(),
                    visible: true,
                    handler: function () {
                        confirmWindow.close();
                    },
                    buttonType: "error"
                }, {
                    text: 'Yes'.translate(),
                    visible: true,
                    handler: function () {
                        confirmWindow.close();
                        that.deleteMessage(data.msgt_uid, row);
                    },
                    buttonType: "success"
                }
            ]
        });

    confirmWindow.open();
    confirmWindow.dom.titleContainer.style.height = '17px';
    confirmWindow.showFooter();
};

PMMessageType.prototype.showGrid = function () {
    var that = this;
    that.frmMessageType.setVisible(false);
    that.frmAcceptedValues.setVisible(false);
    that.gridAcceptedValues.setVisible(false);
    $(that.gridMessages.dom.toolbar).find("input").val("");
    that.gridMessages.clearFilter();
    that.gridMessages.setVisible(true);
    that.winMessageType.setTitle("Message Types".translate());
    that.winMessageType.hideFooter();
};

PMMessageType.prototype.showForm = function () {
    var that = this;
    this.requiredMessage.show();
    that.edit = false;
    that.dirtyAcceptedValue = false;
    that.frmMessageType.setVisible(true);
    that.frmAcceptedValues.setVisible(true);
    that.gridAcceptedValues.setVisible(true);
    that.gridMessages.setVisible(false);
    that.winMessageType.setTitle("Create Message Type".translate());
    that.winMessageType.showFooter();
    that.frmMessageType.reset();
    that.frmMessageType.setFocus();
    that.changeViewFieldType();
    that.resetFrmAcceptedValues();
    that.gridAcceptedValues.clearItems();
    that.buttonFieldCancel.setVisible(false);
};

PMMessageType.prototype.showFormEdit = function (data) {
    var that = this;
    that.showForm();
    that.msgt_uid = data.msgt_uid;
    that.edit = true;

    that.winMessageType.setTitle('Edit Message Type'.translate());
    that.frmMessageType.getField('txtMessageTypeName').setValue(data.msgt_name);

    that.setDataAcceptedValues(data.msgt_variables);
    document.getElementById("requiredMessage").style.marginTop = "16px";
};

PMMessageType.prototype.changeViewFieldType = function () {
    var that = this, sw = true;

    that.frmAcceptedValues.setVisible(true);
    that.gridAcceptedValues.setVisible(true);

    validateKeysField(that.frmAcceptedValues.getField('txtMessageTypeVariableName').getControls()[0].getHTML(), ['isbackspace', 'isnumber', 'isletter', 'isunderscore']);

    if (this.fieldInfo === null) {
        this.fieldInfo = document.createTextNode('');
    }
};

PMMessageType.prototype.addAcceptedValue = function () {
    var that = this,
        value = $.trim(that.frmAcceptedValues.getField('txtMessageTypeVariableName').getValue()),
        message;

    // if the form (form field's RegEx) is invalid, add a Message Field will not be allowed.
    if (!that.frmAcceptedValues.isValid()) {
        return;
     }

    if (that.previousMessageValue !== value && that.isAcceptedValueAdded(value)) {
        message = new PMUI.ui.FlashMessage({
            message: "The variable Name already exists.".translate(),
            duration: 3000,
            severity: 'error',
            appendTo: that.winMessageType.footer
        });
        message.show();
        return;
    } else if (!value) {
        message = new PMUI.ui.FlashMessage({
            message: "Please, specify a name for the Message Field.".translate(),
            duration: 3000,
            severity: 'error',
            appendTo: that.winMessageType.footer
        });
        return message.show();
    }
    that.previousMessageValue = null;
    if (that.editRow === null) {
        that.gridAcceptedValues.addItem(new PMUI.grid.GridPanelRow({
            data: {
                msgtv_name: value
            }
        }));
    } else {
        that.editRow.setData({
            msgtv_name: value
        });
    }

    that.dirtyAcceptedValue = true;
    that.cancelAcceptedValue();
};

PMMessageType.prototype.editAcceptedValue = function (row) {
    var that = this, data;
    that.editRow = row;
    data = row.getData();

    that.previousMessageValue = data.msgtv_name;
    that.frmAcceptedValues.getField('txtMessageTypeVariableName').setValue(data.msgtv_name);
    that.buttonFieldAdd.setValue('Save'.translate());
    that.buttonFieldCancel.setVisible(true);
};

PMMessageType.prototype.deleteAcceptedValue = function (row) {
    var that = this,
        confirmWindow = new PMUI.ui.MessageWindow({
            id: 'confirmWindowDeleteAcceptedValue',
            windowMessageType: 'warning',
            width: 490,
            bodyHeight: 'auto',
            title: "Message Type".translate(),
            message: "Are you sure you want to delete the \"variable\"?".translate(),
            footerItems: [
                {
                    text: 'No'.translate(),
                    visible: true,
                    handler: function () {
                        confirmWindow.close();
                    },
                    buttonType: "error"
                }, {
                    text: 'Yes'.translate(),
                    visible: true,
                    handler: function () {
                        confirmWindow.close();
                        that.gridAcceptedValues.removeItem(row);
                        that.dirtyAcceptedValue = true;
                    },
                    buttonType: "success"
                }
            ]
        });

    confirmWindow.open();
    confirmWindow.dom.titleContainer.style.height = '17px';
    confirmWindow.showFooter();
};

PMMessageType.prototype.getDataAcceptedValues = function () {
    var that = this, data = [], i;

    if (that.gridAcceptedValues.visible) {
        data = that.gridAcceptedValues.getData();

        for (i = 0; i < data.length; i += 1) {
            delete data[i].key;
            delete data[i].type;
        }
    }
    return data;
};

PMMessageType.prototype.setDataAcceptedValues = function (stringJsonData) {
    var that = this, i, data;
    if (!stringJsonData) {
        return;
    }

    data = stringJsonData;
    if (typeof stringJsonData === 'string') {
        data = JSON.parse(stringJsonData);
    }

    if (that.gridAcceptedValues.visible) {
        for (i = 0; i < data.length; i += 1) {
            that.gridAcceptedValues.addItem(new PMUI.grid.GridPanelRow({
                data: {
                    msgtv_name: data[i].msgtv_name,
                    msgtv_default_value: data[i].msgtv_default_value
                }
            }));
        }
    }
};

PMMessageType.prototype.cancelAcceptedValue = function () {
    var that = this;
    that.editRow = null;
    that.buttonFieldAdd.setValue("Create".translate());
    that.buttonFieldCancel.setVisible(false);
    that.resetFrmAcceptedValues();
};
/**
 * Validate if the value is present in the data collection.
 * @param value
 * @returns {boolean}
 */
PMMessageType.prototype.isAcceptedValueAdded = function (value) {
    var that = this, i,
        data = that.gridAcceptedValues.getData();

    for (i = 0; i < data.length; i += 1) {
        if (data[i].msgtv_name === value) {
            return true;
        }
    }
    return false;
};
/**
 * Resets the fields from the form's panel for accepted values.
 */
PMMessageType.prototype.resetFrmAcceptedValues = function () {
    this.frmAcceptedValues.getItems('fields').forEach(function (i) {
        i.setValue("");
    });
};

PMDesigner.messageType = function () {
    var pmvariables = new PMMessageType();
    pmvariables.load();
};

PMDesigner.messageType.create = function () {
    var pmvariables = new PMMessageType();
    pmvariables.showForm();
    pmvariables.load();
};

var MessageEventDefinition = function (bpmnEvent) {
    var that = this,
        msgNameField,
        variableSelector;

    this.bpmnEvent = bpmnEvent;
    this.arrayMessageType = [];
    this.messageEventDefinitionOption = "";
    this.messageEventDefinitionUid = "";
    this.editRow = null;
    this.eventType = bpmnEvent.evn_behavior;
    this.gridCurrent = null;
    this.editMessageForm = null;
    this.dirtyGrid = false;
    this.myTitle = "";

    variableSelector = new CriteriaField({
        id: "txtMessageTypeVariableDefaultValue",
        name: "txtMessageTypeVariableDefaultValue",
        valueType: "string",
        label: that.bpmnEvent.evn_marker == "MESSAGECATCH" ? "Store value in".translate() : "Get value from".translate(),
        maxLength: 200,
        labelWidth: "50%",
        controlsWidth: 300,
        proportion: 3.4,
        required: false
    });

    msgNameField = {
        pmType: "text",
        id: "txtMessageTypeVariableName",
        name: "txtMessageTypeVariableName",
        label: (that.bpmnEvent.evn_marker === "MESSAGECATCH" ? "Value" : "Name").translate(),
        labelWidth: "40%",
        controlsWidth: 200,
        proportion: 2.5,
        valueType: "string",
        maxLength: 255,
        readOnly: true,
        required: false
    };

    this.cboMessageType = new PMUI.field.DropDownListField({
        id: "cboMessageType",
        name: "cboMessageType",
        label: "Message Type".translate(),
        options: [],
        required: true,
        controlsWidth: 150,
        onChange: function (newValue, prevValue) {
            var messageTypeData = that.getMessageTypeByIndex(that.cboMessageType.getValue());
            if (messageTypeData != null) {
                that.gridCurrent.setDataItems(messageTypeData.msgt_variables);

                that._resetEditMessageForm();
            }
        }
    });

    this.isDirtyFormMessageEvent = function () {
        if (that.frmMessageEventDefinition1.isDirty() || that.dirtyGrid) {
            var message_window = new PMUI.ui.MessageWindow({
                id: "cancelMessageType",
                width: 490,
                title: that.myTitle.translate(),
                windowMessageType: "warning",
                bodyHeight: 'auto',
                message: 'Are you sure you want to discard your changes?'.translate(),
                footerItems: [
                    {
                        text: "No".translate(),
                        handler: function () {
                            message_window.close();
                        },
                        buttonType: "error"
                    },
                    {
                        text: "Yes".translate(),
                        handler: function () {
                            message_window.close();
                            that.winMessageEventDefinition.close();
                        },
                        buttonType: "success"
                    }
                ]
            });
            message_window.open();
            message_window.showFooter();
        } else {
            that.winMessageEventDefinition.close();
        }
    };

    this.txtCaseTitle = new CriteriaField({
        id: "txtCaseTitle",
        name: "txtCaseTitle",
        valueType: "string",
        label: "Case Title".translate(),
        maxLength: 200,
        value: "",
        controlsWidth: 380
    });

    this.txtCorrelationValue = new CriteriaField({
        id: "txtCorrelationValue",
        name: "txtCorrelationValue",
        valueType: "string",
        label: "Correlation Value".translate(),
        maxLength: 200,
        value: "",
        controlsWidth: 380
    });

    this.btnSaveVariable = new PMUI.field.ButtonField({
        id: "btnSaveVariable",
        pmType: "buttonField",
        value: 'Save'.translate(),
        labelVisible: false,
        buttonAlign: 'center',
        controlsWidth: 50,
        proportion: 0.8,
        handler: function (field) {
            that.addVariableInGrdPnlVariable3();
            that.dirtyGrid = true;
        },
        buttonType: "success"
    });

    this.btnCancelVariable = new PMUI.field.ButtonField({
        id: "btnCancelVariable",
        pmType: "buttonField",
        value: "Cancel".translate(),
        labelVisible: false,
        buttonAlign: 'center',
        controlsWidth: 55,
        proportion: 0.6,

        handler: function (field) {
            that.cancelAcceptedValue();
        },
        buttonType: "error"
    });

    this.frmMessageEventDefinition1 = new PMUI.form.Form({
        id: "frmMessageEventDefinition1",
        title: "",
        width: DEFAULT_WINDOW_WIDTH - 70,
        visibleHeader: false,
        items: [
            that.txtCaseTitle,
            that.cboMessageType,
            that.txtCorrelationValue,
            {
                id: "edit-panel",
                pmType: "panel",
                legend: "Message content".translate(),
                fieldset: true,
                layout: "hbox",
                items: [
                    that.bpmnEvent.evn_marker === "MESSAGECATCH" ? variableSelector : msgNameField,
                    that.bpmnEvent.evn_marker === "MESSAGECATCH" ? msgNameField : variableSelector,
                    that.btnCancelVariable,
                    that.btnSaveVariable
                ]
            }
        ],
        style: {
            cssProperties: {
                marginBottom: '15px'
            }
        }
    });

    this.editMessageForm = this.frmMessageEventDefinition1.getItem("edit-panel");

    this.grdPnlVariable3 = new PMUI.grid.GridPanel({
        id: "grdPnlVariable3",
        pageSize: 5,
        style: {cssClasses: ["mafe-gridPanel"]},
        emptyMessage: "No records found".translate(),
        filterable: false,

        customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
            return "";
        },
        columns: [
            {
                id: "msgtv_name",
                title: "Name".translate(),
                columnData: "msgtv_name",
                dataType: "string",
                alignmentCell: "left",
                width: 180
            },
            {
                id: "msgtv_default_value",
                title: "Get value from".translate(),
                columnData: "msgtv_default_value",
                dataType: "string",
                alignmentCell: "left",
                width: 300
            },
            {
                id: "btnEdit",
                dataType: "button",
                title: "",
                buttonLabel: "Edit".translate(),
                width: 60,
                buttonStyle: {cssClasses: ["mafe-button-edit"]},

                onButtonClick: function (row, grid) {
                    that.setValueMessageEventDefinition(row);
                }
            }
        ],

        dataItems: null
    });

    this.grdPnlReceive = new PMUI.grid.GridPanel({
        id: "grdPnlVariable3",
        pageSize: 5,
        style: {cssClasses: ["mafe-gridPanel"]},
        emptyMessage: "No records found".translate(),
        filterable: false,
        tableContainerHeight: 204,
        customStatusBar: function (currentPage, pageSize, numberItems, criteria, filter) {
            return "";
        },

        columns: [
            {
                id: "msgtv_default_value",
                title: "Store value in".translate(),
                columnData: "msgtv_default_value",
                dataType: "string",
                alignmentCell: "left",
                width: 200
            },
            {
                id: "msgtv_name",
                title: "Name Value".translate(),
                columnData: "msgtv_name",
                dataType: "string",
                alignmentCell: "left",
                width: 280
            },
            {
                id: "btnEdit",
                dataType: "button",
                title: "",
                buttonLabel: "Edit".translate(),
                width: 60,
                buttonStyle: {cssClasses: ["mafe-button-edit"]},

                onButtonClick: function (row, grid) {
                    that.setValueMessageEventDefinition(row);
                }
            }
        ],
        dataItems: null
    });

    this.gridCurrent = this.grdPnlVariable3;

    MessageEventDefinition.prototype.init.call(this);
};

MessageEventDefinition.prototype.init = function () {
    var that = this;

    that.createWindow();
    that.winMessageEventDefinition.addItem(that.frmMessageEventDefinition1);
    that.winMessageEventDefinition.addItem(that.gridCurrent);

    that.winMessageEventDefinition.open();
    this.editMessageForm.setVisible(false);
    this.applyStylesPost();

    that.load();
    document.getElementById("requiredMessage").style.marginTop = "15px";
};

MessageEventDefinition.prototype.applyStylesPost = function () {
    var that = this;
    that.btnSaveVariable.controls[0].addCSSClasses(["pmui pmui-button pmui-success"]);
    that.btnCancelVariable.controls[0].addCSSClasses(["pmui pmui-button pmui-error"]);
    that.btnSaveVariable.controls[0].html.style.padding = "5px";
    $(that.btnCancelVariable.controls[0].html).css({
        padding: "5px",
        width: "auto"
    });
};

MessageEventDefinition.prototype.createWindow = function () {
    var that = this;

    if (that.bpmnEvent.evn_marker === "MESSAGECATCH") {
        that.myTitle = (that.bpmnEvent.evn_type === "START" ? "Start Message Event" : "Intermediate Receive Message Event").translate();
        that.gridCurrent = this.grdPnlReceive;
    } else if (that.bpmnEvent.evn_marker === "MESSAGETHROW") {
        that.myTitle = (that.bpmnEvent.evn_type == "END" ? "End Message Event" : "Intermediate Send Message Event").translate();
        that.gridCurrent = this.grdPnlVariable3;
    }

    that.winMessageEventDefinition = new PMUI.ui.Window({
        id: "winMessageEventDefinition",
        title: that.myTitle.translate(),

        height: DEFAULT_WINDOW_HEIGHT,
        width: DEFAULT_WINDOW_WIDTH,
        modal: true,
        onBeforeClose: function () {
            that.isDirtyFormMessageEvent();
        },
        footerItems: [
            {
                pmType: "button",
                buttonType: "error",
                text: "Cancel".translate(),
                handler: function () {
                    that.isDirtyFormMessageEvent();
                }
            },
            {
                pmType: "button",
                buttonType: "success",
                text: "Save".translate(),

                handler: function () {
                    var correlationValueAux, data;
                    if (!that.frmMessageEventDefinition1.isValid()) {
                        return;
                    }
                    correlationValueAux = that.frmMessageEventDefinition1.getData();
                    data = {
                        evn_uid: that.bpmnEvent.evn_uid,
                        msgt_uid: that.cboMessageType.getValue(),
                        msged_variables: that.getVariablesByArray(that.grdPnlVariable3GetData()),
                        msged_correlation: correlationValueAux.txtCorrelationValue
                    };

                    data.case_title = that.txtCaseTitle.value;
                    switch (that.messageEventDefinitionOption) {
                        case "POST":
                            that.messageEventDefintionPostRestProxy(data);

                            that.gridCurrent.clearItems();
                            that.winMessageEventDefinition.close();
                            break;
                        case "PUT":
                            that.messageEventDefintionPutRestProxy(data, that.messageEventDefinitionUid);
                            that.gridCurrent.clearItems();
                            that.winMessageEventDefinition.close();
                            break;
                    }
                }
            }
        ],

        footerAlign: "right",
        visibleFooter: true,
        closable: true,
        buttonPanelPosition: "bottom"
    });
};

MessageEventDefinition.prototype.getMessageTypeByIndex = function (messageTypeUid) {
    var that = this,
        i,
        messageTypeData = null;

    if (that.arrayMessageType.length > 0) {
        for (i = 0; i <= that.arrayMessageType.length - 1; i += 1) {
            if (that.arrayMessageType[i].msgt_uid == messageTypeUid) {
                messageTypeData = that.arrayMessageType[i];
                break;
            }
        }
    }
    return messageTypeData;
};

MessageEventDefinition.prototype.getVariablesByArray = function (arrayVariables) {
    var variables = {}, i;

    for (i = 0; i <= arrayVariables.length - 1; i += 1) {
        variables[arrayVariables[i].msgtv_name] = arrayVariables[i].msgtv_default_value;
    }

    return variables;
};

MessageEventDefinition.prototype.getVariablesByObject = function (objectVariable) {
    var that = this,
        key, aux,
        arrayData = [];

    for (key in objectVariable) {
        aux = {};
        aux["msgtv_name"] = key;
        aux["msgtv_default_value"] = objectVariable[key];
        arrayData.push(aux);
    }
    return arrayData;
};

MessageEventDefinition.prototype.load = function () {
    var that = this, restProxy;

    that.messageEventDefinitionUid = "";
    that.messageEventDefinitionOption = "";

    restProxy = new PMRestClient({
        typeRequest: "get",
        endpoint: "message-event-definitions",

        functionSuccess: function (xhr, response) {
            var dataResponse = response,
                i,
                arrayMessageEventDefinition = dataResponse,
                arrayMessageEventDefinitionData = {};

            for (i = 0; i <= arrayMessageEventDefinition.length - 1; i += 1) {
                if (arrayMessageEventDefinition[i].evn_uid == that.bpmnEvent.evn_uid) {
                    that.messageEventDefinitionUid = arrayMessageEventDefinition[i].msged_uid
                    arrayMessageEventDefinitionData = arrayMessageEventDefinition[i];
                    break;
                }
            }

            that.cboMessageTypeSetOptionsRestProxy(that.cboMessageType, (typeof(arrayMessageEventDefinitionData.msgt_uid) != "undefined") ? arrayMessageEventDefinitionData.msgt_uid : "");

            if (that.messageEventDefinitionUid == "") {
                //POST
                that.messageEventDefinitionOption = "POST";

                that.editMessageForm.getField("txtMessageTypeVariableName").setValue("");
                that.editMessageForm.getField("txtMessageTypeVariableDefaultValue").setValue("");
            } else {
                //PUT
                that.messageEventDefinitionOption = "PUT";

                that.gridCurrent.setDataItems(that.getVariablesByObject(arrayMessageEventDefinitionData.msged_variables));
                that.frmMessageEventDefinition1.getField("txtCorrelationValue").setValue(arrayMessageEventDefinitionData.msged_correlation);
                that.frmMessageEventDefinition1.getField("txtCaseTitle").setValue(arrayMessageEventDefinitionData.tas_def_title);
            }
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        }
    });

    restProxy.executeRestClient();
};

MessageEventDefinition.prototype.setValueMessageEventDefinition = function (row) {
    var that = this, data;

    that.editRow = row;
    data = row.getData();

    that.editMessageForm.getField("txtMessageTypeVariableName").setValue(data.msgtv_name);
    that.editMessageForm.getField("txtMessageTypeVariableDefaultValue").setValue(data.msgtv_default_value);

    that.editMessageForm.setVisible(true);
};

MessageEventDefinition.prototype.addVariableInGrdPnlVariable3 = function () {
    var that = this,
        data = {
            msgtv_name: that.editMessageForm.getField("txtMessageTypeVariableName").getValue(),
            msgtv_default_value: that.editMessageForm.getField("txtMessageTypeVariableDefaultValue").getValue()
        };

    if (that.editRow == null) {
        that.gridCurrent.addItem(new PMUI.grid.GridPanelRow({
            data: data
        }));
    } else {
        that.editRow.setData(data);
    }

    that.cancelAcceptedValue();
};

MessageEventDefinition.prototype.cancelAcceptedValue = function () {
    var that = this;
    that.editRow = null;
    that._resetEditMessageForm();
    that.editMessageForm.setVisible(false);
};

MessageEventDefinition.prototype.grdPnlVariable3GetData = function () {
    var that = this,
        i,
        data = [];

    if (that.gridCurrent.visible) {
        data = that.gridCurrent.getData();

        for (i = 0; i <= data.length - 1; i += 1) {
            delete data[i].key;
            delete data[i].type;
        }
    }
    return data;
};

MessageEventDefinition.prototype.cboMessageTypeSetOptionsRestProxy = function (cboMessageType, messageTypeUidSelected) {
    var that = this, restProxy, iAux;

    cboMessageType.clearOptions();
    iAux = 0;
    restProxy = new PMRestClient({
        typeRequest: "get",
        endpoint: "message-types",

        functionSuccess: function (xhr, response) {
            var dataResponse = response, messageTypeData, i, arrayOptions, iAux;
            that.arrayMessageType = dataResponse;
            arrayOptions = [];
            for (i = 0; i <= dataResponse.length - 1; i += 1) {
                if (dataResponse[i].msgt_uid == messageTypeUidSelected) {
                    iAux = i;
                }

                arrayOptions.push(
                    {
                        value: dataResponse[i].msgt_uid,
                        label: dataResponse[i].msgt_name
                    }
                );
            }

            if (arrayOptions.length > 0) {
                cboMessageType.setOptions(arrayOptions);

                if (messageTypeUidSelected == "") {
                    //POST
                    cboMessageType.setValue(arrayOptions[0].value);
                    messageTypeData = that.getMessageTypeByIndex(arrayOptions[0].value);

                    if (messageTypeData != null) {
                        that.gridCurrent.setDataItems(messageTypeData.msgt_variables);
                    }
                } else {
                    //PUT
                    cboMessageType.setValue(arrayOptions[iAux].value);
                }
            }
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        }
    });

    restProxy.executeRestClient();
};

MessageEventDefinition.prototype.messageEventDefintionPostRestProxy = function (data) {
    var restProxy = new PMRestClient({
        endpoint: "message-event-definition",
        typeRequest: "post",
        data: data,

        functionSuccess: function (xhr, response) {
            var dataResponse = response;
            PMDesigner.msgFlash('The property event was saved successfully.'.translate(), document.body, 'success', 3000, 5);
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        }
    });

    restProxy.executeRestClient();
};

MessageEventDefinition.prototype.messageEventDefintionPutRestProxy = function (data, messageEventDefinitionUid) {
    var restProxy = new PMRestClient({
        endpoint: "message-event-definition/" + messageEventDefinitionUid,
        typeRequest: "update",
        data: data,

        functionSuccess: function (xhr, response) {
            var dataResponse = response;
            PMDesigner.msgFlash('The property event was saved successfully.'.translate(), document.body, 'success', 3000, 5);
        },
        functionFailure: function (xhr, response) {
            PMDesigner.msgWinError(response.error.message);
        }
    });

    restProxy.executeRestClient();
};
/**
 * Reset the fields from the form's Edit panel.
 * @returns {MessageEventDefinition}
 * @private
 */
MessageEventDefinition.prototype._resetEditMessageForm = function () {
    if (this.editMessageForm) {
        this.editMessageForm.getItems().map(function (i) {
            i.setValue("");
        });
    }
    return this;
};

/*----------------------------------********---------------------------------*/
var FeatureUtils = function () {

};

FeatureUtils.prototype.generateUId = function () {
    var text = "",
        i,
        possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    for (i = 0; i < 16; i += 1) {
        text += possible.charAt(Math.floor(Math.random() * possible.length));
    }
    return text;
};

var ElementFactory = function () {
    this.utils = new FeatureUtils();
};

ElementFactory.prototype.getElement = function (data) {
    var element = {};
    element.id = this.utils.generateUId();
    element.name = data.name;
    element.label = data.label;
    element.valueType = data.valueType;
    element.controlsWidth = data.controlsWidth;
    element.required = data.required;
    element.events = data.events;
    element.listeners = data.listeners;

    switch (data.type) {
        case 'text':
            element.pmType = data.type;
            element.maxLength = 3;
            break;
        case 'hidden':
            element.pmType = data.type;
            break;
        case 'textarea':
            element.pmType = data.type;
            element.rows = data.rows;
            break;
        case 'dropdown':
            element.pmType = data.type;
            element.options = data.options;
            break;
        case 'checkbox':
            element.pmType = data.type;
            break;
        case 'link':
            element.pmType = data.type;
            break;
        case 'button':
            element.pmType = data.type;
            break;
        case 'criteria':
            element.pmType = data.type;
            element.rows = data.rows;
            break;
        default:
            break;
    }

    return element;
};

var FeaturesHandler = function () {
    this.elementFactory = new ElementFactory();
    this.utils = new FeatureUtils();
    this.forms = [];
};

FeaturesHandler.prototype.registerEvents = function (items) {

};

FeaturesHandler.prototype.registerListeners = function (items) {

};

FeaturesHandler.prototype.wireEvents = function (items) {
    this.registerEvents(items);
    this.registerListeners(items);
};

FeaturesHandler.prototype.parseForm = function (formData) {
    var form = {};
    form.id = this.utils.generateUId();
    form.title = formData.title;
    form.icon = (formData.icon || '');
    form.action = formData.action;
    form.panel = new PMUI.form.Form({
        id: this.utils.generateUId(),
        visibleHeader: false,
        width: DEFAULT_WINDOW_WIDTH - 250,
        items: this.parseItems(formData)
    });
    return form;
};

FeaturesHandler.prototype.parseItems = function (formData) {
    var items = [];
    if (this.validateData(formData)) {
        for (var i in formData.fields) {
            items.push(
                this.elementFactory.getElement(formData.fields[i])
            );
        }
    }
    this.wireEvents(items);
    return items;
};

FeaturesHandler.prototype.validateData = function (formData) {
    switch (true) {
        case !(formData.type == 'form'):
            return false;
            break;
        case formData.language == undefined:
            return false;
            break;
        default:
            return true;
            break;
    }
};

FeaturesHandler.prototype.getForms = function () {
    return [];
};

PMDesigner.featuresHandler = new FeaturesHandler();
/*----------------------------------********---------------------------------*/
/**
 * @class IntroHelper
 * Handle Intro helper
 *
 * @constructor
 * Creates a new instance of the class
 * @param {Object} options
 */
var IntroHelper = function (options) {
    this.steps = [];
    this.onExit = null;
    this.introjs = null;
    IntroHelper.prototype.initObject.call(this, options);
};
IntroHelper.prototype.type = 'IntroHelper';

IntroHelper.prototype.initObject = function (options) {
    var defaults = {
        steps: [],
        skipLabel: "Skip",
        onExit : new Function()
    };
    $.extend(true, defaults, options);
    this.setSteps(defaults.steps);

    this.setSkipLabel(defaults.skipLabel);
    this.setNextLabel(defaults.nextLabel);
    this.setPrevLabel(defaults.prevLabel);
    this.setDoneLabel(defaults.doneLabel);
    this.setOnExit(defaults.onExit);
};

IntroHelper.prototype.setSteps = function (steps) {
    this.steps = steps;
    return this;
};

IntroHelper.prototype.setSkipLabel = function (label) {
    this.skipLabel = label;
    return this;
};
IntroHelper.prototype.setNextLabel = function (label) {
    this.nextLabel = label;
    return this;
};

IntroHelper.prototype.setPrevLabel = function (label) {
    this.prevLabel = label;
    return this;
};

IntroHelper.prototype.setDoneLabel = function (label) {
    this.doneLabel = label;
    return this;
};

IntroHelper.prototype.setSkipLabel = function (label) {
    this.skipLabel = label;
    return this;
};

IntroHelper.prototype.setOnExit = function (callback) {
    if (callback && typeof callback == "function") {
        this.onExit = callback;
    }
    return this;
};

IntroHelper.prototype.startIntro = function () {
    var that = this;
    this.introjs = introJs();
    this.introjs.setOptions({
        steps: this.steps,
        skipLabel: this.skipLabel,
        nextLabel: this.nextLabel,
        prevLabel: this.prevLabel,
        doneLabel: this.doneLabel
    });
    this.introjs.onexit(function () {
        that.onExit();
    });

    this.introjs.start();
};
(function () {
    PMDesigner.scriptTaskProperties = function (activity) {
        var that = this,
            taskUid,
            taskType,
            triggerSelectedData,
            oldValue,
            uidProj,
            scrtas_uid,
            buttonCancel,
            restClientNewScriptTask,
            restClientUpdateScriptTask,
            restClientNewTrigger,
            restClientUpdateTrigger,
            $ctrlSpaceMessage,
            triggerEngine,
            getListTrigger,
            getScriptTask,
            formScriptTask,
            buttonSave,
            domSettings,
            scriptTaskPropertiesWindow,
            triggerUid;

        taskUid = activity.getID();
        taskType = activity.getTaskType();
        taskType = "TRIGGER";
        oldValue = "";
        uidProj = PMDesigner.project.id;
        scrtas_uid = "";
        triggerUid = "";
        $ctrlSpaceMessage = $("<p class='ctrlSpaceMessage'>" + "Press".translate() + " <strong>ctrl+space</strong> " + "to get the function list".translate() + ".</p>");

        /*window*/
        buttonCancel = new PMUI.ui.Button({
            id: 'cancelScriptButton',
            text: "Cancel".translate(),
            buttonType: 'error',
            handler: function (event) {
                clickedClose = false;
                scriptTaskPropertiesWindow.isDirtyFormScript();
            }
        });

        restClientNewScriptTask = function (triggerUid) {
            var restClient = new PMRestClient({
                endpoint: 'script-task',
                typeRequest: 'post',
                data: {
                    scrtas_obj_uid: triggerUid,
                    act_uid: taskUid,
                    scrtas_obj_type: taskType
                },
                functionSuccess: function () {
                    scriptTaskPropertiesWindow.close();
                    PMDesigner.msgFlash('Script Task saved correctly'.translate(), document.body, 'success', 3000, 5);
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                    PMDesigner.msgFlash('There are problems updating the Script Task, please try again.'.translate(), document.body, 'error', 3000, 5);
                }
            });
            restClient.executeRestClient();
        };

        restClientUpdateScriptTask = function (triggerUid) {
            var restClient = new PMRestClient({
                endpoint: 'script-task/' + formScriptTask.getItems()[0].items.get(4).getValue(),
                typeRequest: 'update',
                data: {
                    scrtas_obj_uid: triggerUid, /*trigger uid*/
                    act_uid: taskUid,
                    scrtas_obj_type: taskType
                },
                functionSuccess: function () {
                    scriptTaskPropertiesWindow.close();
                    PMDesigner.msgFlash('Script Task saved correctly'.translate(), document.body, 'success', 3000, 5);
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                    PMDesigner.msgFlash('There are problems updating the Script Task, please try again.'.translate(), document.body, 'error', 3000, 5);
                }
            });
            restClient.executeRestClient();
        };

        restClientNewTrigger = function (dataToSend, newScriptTask) {
            var restClient = new PMRestClient({
                endpoint: 'trigger',
                typeRequest: 'post',
                data: dataToSend,
                functionSuccess: function (xhr, response) {
                    triggerUid = response.tri_uid;
                    if (triggerUid != "" && typeof triggerUid != "undefinied") {
                        if (newScriptTask) {
                            restClientNewScriptTask(triggerUid);
                        } else {
                            restClientUpdateScriptTask(triggerUid);
                        }
                    }
                },
                functionFailure: function (xhr, response) {
                    var msg = response.error.message,
                        arrayMatch = [];

                    if ((arrayMatch = /^[\w\s]+\:\s*(.*)$/i.exec(msg))) {
                        msg = arrayMatch[1];
                    }

                    PMDesigner.msgWinError(msg);
                }
            });
            restClient.executeRestClient();
        };

        restClientUpdateTrigger = function (newScriptTask) {
            var restClient = new PMRestClient({
                endpoint: 'trigger/' + formScriptTask.getItems()[0].items.get(1).getValue(),
                typeRequest: 'update',
                data: {
                    scrtas_obj_uid: formScriptTask.getItems()[0].items.get(1).getValue(),
                    tri_webbot: formScriptTask.getItems()[1].controls[0].cm.getValue(),
                    act_uid: taskUid,
                    scrtas_obj_type: taskType
                },
                functionSuccess: function (xhr, response) {
                    triggerUid = response.tri_uid;
                    if (newScriptTask) {
                        restClientNewScriptTask(formScriptTask.getItems()[0].items.get(1).getValue());
                    } else {
                        restClientUpdateScriptTask(formScriptTask.getItems()[0].items.get(1).getValue());
                    }
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                    PMDesigner.msgFlash('There are problems updating the Script Task, please try again.'.translate(), document.body, 'error', 3000, 5);
                }
            });
            restClient.executeRestClient();
        };

        buttonSave = new PMUI.ui.Button({
            id: 'saveScriptButton',
            text: "Save".translate(),
            handler: function (event) {
                var newScriptTask, dataToSend;
                if (formScriptTask.isValid()) {
                    if (formScriptTask.getItems()[0].items.get(4).getValue() === "") { /*new*/
                        newScriptTask = true;
                        if (formScriptTask.getItems()[0].items.get(1).getValue() != "") {
                            restClientUpdateTrigger(newScriptTask);
                        } else {
                            dataToSend = {
                                tri_title: formScriptTask.getItems()[0].items.get(2).getValue(),
                                tri_description: "",
                                tri_webbot: formScriptTask.getItems()[1].controls[0].cm.getValue(),
                                tri_param: ""
                            };
                            restClientNewTrigger(dataToSend, newScriptTask);
                        }
                    } else {
                        newScriptTask = false;
                        if (formScriptTask.getItems()[0].items.get(1).getValue() !== "") {
                            restClientUpdateTrigger(newScriptTask);
                        } else {
                            dataToSend = {
                                tri_title: formScriptTask.getItems()[0].items.get(2).getValue(),
                                tri_description: "",
                                tri_webbot: formScriptTask.getItems()[1].controls[0].cm.getValue(),
                                tri_param: ""
                            };
                            restClientNewTrigger(dataToSend, newScriptTask);
                        }
                    }
                }
            },
            buttonType: 'success'
        });

        scriptTaskPropertiesWindow = new PMUI.ui.Window({
            id: "scriptTaskPropertiesWindow",
            title: "Script Task Properties".translate(),
            width: DEFAULT_WINDOW_WIDTH,
            height: DEFAULT_WINDOW_HEIGHT,
            footerItems: [
                {
                    text: "@@",
                    id: "selectPickerButton",
                    handler: function () {
                        var picker = new VariablePicker();
                        picker.open({
                            success: function (variable) {
                                var codeMirror = formScriptTask.getItems()[1].controls[0].cm;
                                var cursorPos = codeMirror.getCursor();
                                codeMirror.replaceSelection(variable);
                                codeMirror.setCursor(cursorPos.line, cursorPos.ch);
                            }
                        });
                    },
                    style: {
                        cssProperties: {
                            "margin-left": '208px',
                            "background": "rgb(45, 62, 80)",
                            "border": "1px solid rgb(45, 62, 80)"
                        }
                    }
                },
                buttonCancel,
                buttonSave
            ],
            buttonPanelPosition: "bottom",
            footerAling: "right",
            onBeforeClose: function () {
                clickedClose = true;
                scriptTaskPropertiesWindow.isDirtyFormScript();
            }
        });

        scriptTaskPropertiesWindow.isDirtyFormScript = function () {
            var that = this,
                message_window,
                title = "Script Task".translate();
            if (oldValue !== formScriptTask.getItems()[1].controls[0].cm.getValue()) {
                var message_window = new PMUI.ui.MessageWindow({
                    id: "cancelMessageTriggers",
                    windowMessageType: 'warning',
                    width: 490,
                    title: title,
                    message: 'Are you sure you want to discard your changes?'.translate(),
                    footerItems: [
                        {
                            text: "No".translate(),
                            handler: function () {
                                message_window.close();
                            },
                            buttonType: "error"
                        },
                        {
                            text: "Yes".translate(),
                            handler: function () {
                                message_window.close();
                                that.close();
                            },
                            buttonType: "success"
                        }
                    ]
                });
                message_window.open();
                message_window.showFooter();
            } else {
                that.close();
            }
        };
        /*end window*/

        /*form*/

        triggerEngine = new PMUI.field.DropDownListField({
            id: "triggerEngine",
            name: "triggerEngine",
            label: "Title".translate(),
            options: null,
            controlsWidth: 400,
            required: true,
            onChange: function (newValue, prevValue) {
                var uidTri = newValue, i;
                for (i = 0; i < triggerSelectedData.length; i += 1) {
                    if (triggerSelectedData[i].tri_uid == uidTri) {
                        formScriptTask.getItems()[1].controls[0].cm.setValue(triggerSelectedData[i].tri_webbot);
                    }
                }
            }
        });

        getListTrigger = function (triggerEngine) {
            var restClient = new PMRestClient({
                endpoint: 'triggers',
                typeRequest: 'get',
                functionSuccess: function (xhr, response) {
                    var arrayOptions = [], i;
                    triggerSelectedData = response;
                    arrayOptions[0] = {
                        label: "- Select a trigger -".translate(),
                        value: "",
                        disabled: true,
                        selected: true
                    };
                    for (i = 0; i <= triggerSelectedData.length - 1; i += 1) {
                        arrayOptions.push(
                            {
                                value: triggerSelectedData[i].tri_uid,
                                label: triggerSelectedData[i].tri_title
                            }
                        );
                    }
                    triggerEngine.setOptions(arrayOptions);
                    triggerEngine.setValue(arrayOptions[0].value);
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                },
                messageError: "There are problems getting the Triggers list, please try again.".translate()
            });
            restClient.executeRestClient();
        };

        getScriptTask = function () {
            var restClient = new PMRestClient({
                endpoint: 'script-task/activity/' + taskUid,
                typeRequest: 'get',
                functionSuccess: function (xhr, response) {
                    var i;
                    if (typeof response == "object") {
                        triggerUid = response.scrtas_obj_uid;
                        if (triggerUid != "" && typeof triggerUid != "undefinied") {
                            for (i = 0; i < triggerSelectedData.length; i += 1) {
                                if (triggerSelectedData[i].tri_uid == triggerUid) {
                                    formScriptTask.getItems()[1].controls[0].setValue(triggerSelectedData[i].tri_webbot);
                                    oldValue = triggerSelectedData[i].tri_webbot;
                                    formScriptTask.getItems()[0].items.get(4).setValue(response.scrtas_uid);
                                    formScriptTask.getItems()[0].items.get(1).setValue(triggerUid);
                                }
                            }
                        }
                    }
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            });
            restClient.executeRestClient();
        };

        formScriptTask = new PMUI.form.Form({
            id: "formScriptTask",
            border: true,
            visibleHeader: false,
            width: '900px',
            height: "300px",
            name: "formScriptTask",
            title: '',
            items: [
                {
                    id: "panelDetailsCustom",
                    pmType: "panel",
                    layout: 'vbox',
                    fieldset: false,
                    height: '380px',
                    legend: "DETAILS".translate(),
                    items: [
                        {
                            id: "taskUid",
                            pmType: "text",
                            label: "ID".translate(),
                            value: taskUid,
                            name: "taskUid",
                            readOnly: true,
                            visible: false,
                            valueType: 'string'
                        },
                        triggerEngine,
                        {
                            id: "newScript",
                            pmType: "text",
                            label: "Title".translate(),
                            controlsWidth: 400,
                            value: "",
                            name: "newScript",
                            required: false,
                            visible: false,
                            valueType: 'string'
                        },
                        {
                            id: "taskType",
                            pmType: "text",
                            value: taskType,
                            name: "taskType",
                            visible: false,
                            valueType: 'string'
                        },
                        {
                            id: "scrtas_uid",
                            pmType: "text",
                            value: scrtas_uid,
                            name: "scrtas_uid",
                            visible: false,
                            valueType: 'string'
                        }
                    ]
                }
            ]
        });

        that.cd = new PMCodeMirrorField({
            labelWidth: '23.8%'
        });
        that.cd.setLabel("Code".translate());
        formScriptTask.addItem(that.cd);
        formScriptTask.getItems()[1].setName('code');
        formScriptTask.getItems()[1].setID('code');
        formScriptTask.getItems()[1].setHeight(300);
        CodeMirror.commands.autocomplete = function (cm) {
            CodeMirror.showHint(cm, CodeMirror.phpHint);
        };
        
        getListTrigger(triggerEngine);
        getScriptTask();
        /*end form*/

        domSettings = function () {
            var codeMirrorControl, requiredMessage, titleAdd, titleBack;
            codeMirrorControl = formScriptTask.getItems()[1].controls[0].cm;
            formScriptTask.getItems()[1].html.style.padding = "10px";
            if (codeMirrorControl != undefined) {
                codeMirrorControl.setSize(650, 255); //CodeMirror Size
                $("#code").find(".CodeMirror-wrap").css({'border': '1px solid #c0c0c0'});
                codeMirrorControl.refresh();
            }
            requiredMessage = $(document.getElementById("requiredMessage"));
            scriptTaskPropertiesWindow.body.appendChild(requiredMessage[0]);
            requiredMessage[0].style['marginTop'] = '65px';

            scriptTaskPropertiesWindow.footer.html.style.textAlign = 'right';
            $(".CodeMirror.cm-s-default.CodeMirror-wrap").after($ctrlSpaceMessage.css({
                "padding-left": "10px",
                "margin": "3px 0px 0px 0px"
            }));

            titleAdd = "Add new".translate();
            titleBack = "Back to list".translate();

            $("#triggerEngine").find("select").after('&nbsp;&nbsp;<a id="titleAdd" title="' + titleAdd + '" class="showHideScript pmui pmui-button mafe-button-edit-assign-no-hover" href="#" style="left: 0px; top: 0px; width: auto; line-height: normal; position: relative; height: auto; z-index: auto;"><span class="button-label"></span><span class="button-icon " style="display: inline-block;"></span></a>');
            $("#newScript").find("input").after('&nbsp;&nbsp;<a id="titleBack" title="' + titleBack + '" class="showHideScript pmui pmui-button mafe-button-delete-assign-no-hover" href="#" style="left: 0px; top: 0px; width: auto; line-height: normal; position: relative; height: auto; z-index: auto;"><span class="button-label"></span><span class="button-icon " style="display: inline-block;"></span></a>');
            $("#titleAdd, #titleBack").tooltip();
        };

        scriptTaskPropertiesWindow.addItem(formScriptTask);
        scriptTaskPropertiesWindow.open();
        scriptTaskPropertiesWindow.showFooter();
        domSettings();
        
        $(".showHideScript").on("click", function () {
            if (formScriptTask.getItems()[0].items.get(1).isVisible()) {
                formScriptTask.getItems()[0].items.get(1).setVisible(false);
            } else {
                formScriptTask.getItems()[0].items.get(1).setVisible(true);
                formScriptTask.getItems()[0].items.get(1).setValue("");
                formScriptTask.getItems()[0].items.get(2).setRequired(false);
                formScriptTask.getItems()[0].items.get(1).setRequired(true);
                $(formScriptTask.getItems()[0].items.get(1).html).find("select").focus();
            }

            if (!formScriptTask.getItems()[0].items.get(2).isVisible()) {
                formScriptTask.getItems()[0].items.get(2).setVisible(true);
                formScriptTask.getItems()[0].items.get(1).setRequired(false);
                formScriptTask.getItems()[0].items.get(1).setValue("");
                formScriptTask.getItems()[0].items.get(2).setRequired(true);
                formScriptTask.getItems()[0].items.get(2).setValue("");
                $(formScriptTask.getItems()[0].items.get(2).html).find("input").focus();
            } else {
                formScriptTask.getItems()[0].items.get(2).setVisible(false);
            }
            formScriptTask.getItems()[1].controls[0].cm.setValue("");
            oldValue = "";

        });

    };
}());

(function () {
    PMDesigner.timerEventProperties = function (activity) {
        var that = this,
            evnUid = activity.getID(),
            activityType = activity.getEventMarker(),
            uidProj = PMDesigner.project.id,
            oldValues,
            tmrevn_uid = "",
            dataTimer = "",
            buttonCancel,
            restClientNewTimerEvent,
            buttonSave,
            restClientUpdateTimerEvent,
            timerEventPropertiesWindow,
            showHourlyItems,
            showDailyItems,
            showMonthlyItems,
            showOneDateTimeItems,
            showEveryItems,
            showWaitForItems,
            showWaitUntilItems,
            varshowHourlyItems,
            endDate,
            oneDateTime,
            daysGroup,
            monthsGroup,
            radioGroup,
            dateTimeVariablePicker,
            formTimerEvent,
            getFormData,
            getTimerEventData,
            validateItems,
            domSettings,
            eventType = activity.getEventType(),
            regexDay = new RegExp(/^(((0|1|2)?[0-9])|(3[01]))$/),
            regexHour = new RegExp(/^(((0|1)?[0-9])|(2[0-4]))$/),
            regexMinute = new RegExp(/^([0-5]?[0-9])$/);

        /*window*/
        buttonCancel = new PMUI.ui.Button({
            id: 'cancelTimmerButton',
            text: "Cancel".translate(),
            buttonType: 'error',
            handler: function (event) {
                clickedClose = false;
                formTimerEvent.getField('startDate').controls[0].hideCalendar();
                formTimerEvent.getField('endDate').controls[0].hideCalendar();
                formTimerEvent.getField('oneDateTime').controls[0].hideCalendar();
                formTimerEvent.getField('dateTimeVariablePicker').controls[0].hideCalendar();
                timerEventPropertiesWindow.isDirtyFormScript();
            }
        });

        restClientNewTimerEvent = function (dataToSave) {
            var restClient = new PMRestClient({
                endpoint: 'timer-event',
                typeRequest: 'post',
                data: dataToSave,
                functionSuccess: function (xhr, response) {
                    timerEventPropertiesWindow.close();
                    PMDesigner.msgFlash('Timer Event saved correctly'.translate(), document.body, 'success', 3000, 5);
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                    PMDesigner.msgFlash('There are problems updating the Timer Event, please try again.'.translate(), document.body, 'error', 3000, 5);
                }
            });
            restClient.executeRestClient();
        };

        restClientUpdateTimerEvent = function (dataToSave) {
            var restClient = new PMRestClient({
                endpoint: 'timer-event/' + formTimerEvent.getField("tmrevn_uid").getValue(),
                typeRequest: 'update',
                data: dataToSave,
                functionSuccess: function (xhr, response) {
                    timerEventPropertiesWindow.close();
                    PMDesigner.msgFlash('Timer Event saved correctly'.translate(), document.body, 'success', 3000, 5);
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                    PMDesigner.msgFlash('There are problems updating the Timer Event, please try again.'.translate(), document.body, 'error', 3000, 5);
                }
            });
            restClient.executeRestClient();
        };

        buttonSave = new PMUI.ui.Button({
            id: 'saveTimmerButton',
            text: "Save".translate(),
            handler: function (event) {
                var i,
                    opt,
                    formData;

                formTimerEvent.getField("hourType").setValue(getData2PMUI(formTimerEvent.html).hourType);
                formTimerEvent.getField("minuteType").setValue(getData2PMUI(formTimerEvent.html).minuteType);
                formTimerEvent.getField("dayType").setValue(getData2PMUI(formTimerEvent.html).dayType);
                if (formTimerEvent.isValid()) {
                    opt = formTimerEvent.getField("radioGroup").getValue();
                    formData = formTimerEvent.getData();
                    switch (opt) {
                        case "1": /*hourly*/
                            dataTimer = {
                                evn_uid: evnUid,
                                tmrevn_option: "HOURLY",
                                tmrevn_start_date: formData.startDate.substring(0, 10),
                                tmrevn_end_date: formTimerEvent.getField("endDate").getValue().substring(0, 10),
                                tmrevn_minute: formData.minuteType.length == 1 ? "0" + formData.minuteType : (formData.minuteType.length == 0 ? "00" : formData.minuteType )
                            };
                            break;
                        case "2": /*daily*/
                            dataTimer = {
                                evn_uid: evnUid,
                                tmrevn_option: "DAILY",
                                tmrevn_start_date: formData.startDate.substring(0, 10),
                                tmrevn_end_date: formTimerEvent.getField("endDate").getValue().substring(0, 10),
                                tmrevn_hour: formData.hourType.length == 1 ? "0" + formData.hourType : (formData.hourType.length == 0 ? "00" : formData.hourType ),
                                tmrevn_minute: formData.minuteType.length == 1 ? "0" + formData.minuteType : (formData.minuteType.length == 0 ? "00" : formData.minuteType ),
                                tmrevn_configuration_data: JSON.parse(formData.daysGroup).map(function (n) {
                                    return Number(n);
                                })
                            };
                            break;
                        case "3": /*monthly*/
                            dataTimer = {
                                evn_uid: evnUid,
                                tmrevn_option: "MONTHLY",
                                tmrevn_start_date: formData.startDate.substring(0, 10),
                                tmrevn_end_date: formTimerEvent.getField("endDate").getValue().substring(0, 10),
                                tmrevn_day: formData.dayType.length == 1 ? "0" + formData.dayType : (formData.dayType.length == 0 ? "00" : formData.dayType ),
                                tmrevn_hour: formData.hourType.length == 1 ? "0" + formData.hourType : (formData.hourType.length == 0 ? "00" : formData.hourType ),
                                tmrevn_minute: formData.minuteType.length == 1 ? "0" + formData.minuteType : (formData.minuteType.length == 0 ? "00" : formData.minuteType ),
                                tmrevn_configuration_data: JSON.parse(formData.monthsGroup).map(function (n) {
                                    return Number(n);
                                })
                            };
                            break;
                        case "4": /*one-date-time*/
                            dataTimer = {
                                evn_uid: evnUid,
                                tmrevn_option: "ONE-DATE-TIME",
                                tmrevn_next_run_date: $("#oneDateTime").find("input:eq(0)").val()
                            };
                            break;
                        case "5": /*every*/
                            dataTimer = {
                                evn_uid: evnUid,
                                tmrevn_option: "EVERY",
                                tmrevn_hour: formData.hourType.length == 1 ? "0" + formData.hourType : (formData.hourType.length == 0 ? "00" : formData.hourType ),
                                tmrevn_minute: formData.minuteType.length == 1 ? "0" + formData.minuteType : (formData.minuteType.length == 0 ? "00" : formData.minuteType )
                            };
                            break;
                        case "6": /*wait for*/
                            if ((formData.dayType === '' || formData.dayType === '00' || formData.dayType === '0') &&
                                (formData.hourType === '' || formData.hourType === '00' || formData.hourType === '0') &&
                                (formData.minuteType === '' || formData.minuteType === '00' || formData.minuteType === '0')) {
                                PMDesigner.msgWinError("The amount of time entered is not valid. Please fill in at least one of the fields (day, hour, or minute)".translate());
                                return;
                            } else {
                                if (!regexDay.test(formData.dayType) || !regexHour.test(formData.hourType) || !regexMinute.test(formData.minuteType)) {
                                    PMDesigner.msgWinError("The amount of time entered is not valid. Please fill in at least one of the fields (day, hour, or minute)".translate());
                                    return;
                                }
                            }
                            dataTimer = {
                                evn_uid: evnUid,
                                tmrevn_option: "WAIT-FOR",
                                tmrevn_day: formData.dayType.length == 1 ? "0" + formData.dayType : (formData.dayType.length == 0 ? "00" : formData.dayType ),
                                tmrevn_hour: formData.hourType.length == 1 ? "0" + formData.hourType : (formData.hourType.length == 0 ? "00" : formData.hourType ),
                                tmrevn_minute: formData.minuteType.length == 1 ? "0" + formData.minuteType : (formData.minuteType.length == 0 ? "00" : formData.minuteType )
                            };
                            break;
                        case "7": /*wait until specified date time*/
                            dataTimer = {
                                evn_uid: evnUid,
                                tmrevn_option: "WAIT-UNTIL-SPECIFIED-DATE-TIME",
                                tmrevn_configuration_data: $("#dateTimeVariablePicker").find("input:eq(0)").val()
                            };
                            break;
                    }
                    dataTimer.caseTitle = formData.txtCaseTitle;
                    if (formTimerEvent.getField("tmrevn_uid").getValue() == "") {
                        restClientNewTimerEvent(dataTimer);
                    } else {
                        restClientUpdateTimerEvent(dataTimer);
                    }
                }
            },
            buttonType: 'success'
        });

        timerEventPropertiesWindow = new PMUI.ui.Window({
            id: "timerEventPropertiesWindow",
            title: "Timer Event Properties".translate(),
            width: DEFAULT_WINDOW_WIDTH,
            height: DEFAULT_WINDOW_HEIGHT,
            footerItems: [
                buttonCancel,
                buttonSave
            ],
            buttonPanelPosition: "bottom",
            footerAling: "right",
            onBeforeClose: function () {
                clickedClose = true;
                formTimerEvent.getField('startDate').controls[0].hideCalendar();
                formTimerEvent.getField('endDate').controls[0].hideCalendar();
                formTimerEvent.getField('oneDateTime').controls[0].hideCalendar();
                formTimerEvent.getField('dateTimeVariablePicker').controls[0].hideCalendar();
                timerEventPropertiesWindow.isDirtyFormScript();
            }
        });

        timerEventPropertiesWindow.isDirtyFormScript = function () {
            var that = this,
                title = "Timer Event".translate(),
                newValues = getFormData($("#formTimerEvent"));
            if (JSON.stringify(oldValues) !== JSON.stringify(newValues)) {
                var message_window = new PMUI.ui.MessageWindow({
                    id: "cancelMessageTriggers",
                    windowMessageType: 'warning',
                    width: 490,
                    title: title,
                    message: 'Are you sure you want to discard your changes?'.translate(),
                    footerItems: [
                        {
                            text: "No".translate(),
                            handler: function () {
                                message_window.close();
                            },
                            buttonType: "error"
                        },
                        {
                            text: "Yes".translate(),
                            handler: function () {
                                message_window.close();
                                that.close();
                            },
                            buttonType: "success"
                        }
                    ]
                });
                message_window.open();
                message_window.showFooter();
            } else {
                that.close();
            }
        };
        /*end window*/

        /*form*/
        showHourlyItems = function () {
            formTimerEvent.getField('startDate').setVisible(true);
            formTimerEvent.getField('startDate').setRequired(true);
            formTimerEvent.getField('endDateCheckbox').setVisible(true);
            formTimerEvent.getField('endDate').setVisible(true);
            formTimerEvent.getField('oneDateTime').setVisible(false);
            formTimerEvent.getField('oneDateTime').setRequired(false);
            formTimerEvent.getField('daysGroup').setVisible(false);
            formTimerEvent.getField('daysGroup').setRequired(false);
            formTimerEvent.getField('monthsGroup').setVisible(false);
            formTimerEvent.getField('monthsGroup').setRequired(false);
            formTimerEvent.getField('dateTimeVariablePicker').setVisible(false);
            if (formTimerEvent.getItems()[0].items !== undefined) {
                formTimerEvent.getItems()[0].items.get(4).getField('dayType').setVisible(false);
                formTimerEvent.getItems()[0].items.get(4).getField('dayType').setRequired(false);
                formTimerEvent.getItems()[0].items.get(4).getField('hourType').setVisible(false);
                formTimerEvent.getItems()[0].items.get(4).getField('hourType').setRequired(false);
                formTimerEvent.getItems()[0].items.get(4).getField('minuteType').setVisible(true);
                formTimerEvent.getItems()[0].items.get(4).getField('minuteType').setRequired(true);
            }
        };

        showDailyItems = function () {
            formTimerEvent.getField('startDate').setVisible(true);
            formTimerEvent.getField('startDate').setRequired(true);
            formTimerEvent.getField('endDateCheckbox').setVisible(true);
            formTimerEvent.getField('endDate').setVisible(true);
            formTimerEvent.getField('oneDateTime').setVisible(false);
            formTimerEvent.getField('oneDateTime').setRequired(false);
            formTimerEvent.getField('daysGroup').setVisible(true);
            formTimerEvent.getField('daysGroup').setRequired(true);
            formTimerEvent.getField('oneDateTime').setRequired(false);
            formTimerEvent.getField('monthsGroup').setVisible(false);
            formTimerEvent.getField('monthsGroup').setRequired(false);
            formTimerEvent.getField('dateTimeVariablePicker').setVisible(false);
            if (formTimerEvent.getItems()[0].items !== undefined) {
                formTimerEvent.getItems()[0].items.get(4).getField('dayType').setVisible(false);
                formTimerEvent.getItems()[0].items.get(4).getField('dayType').setRequired(false);
                formTimerEvent.getItems()[0].items.get(4).getField('hourType').setVisible(true);
                formTimerEvent.getItems()[0].items.get(4).getField('hourType').setRequired(true);
                formTimerEvent.getItems()[0].items.get(4).getField('minuteType').setVisible(true);
                formTimerEvent.getItems()[0].items.get(4).getField('minuteType').setRequired(true);
            }
        };

        showMonthlyItems = function () {
            formTimerEvent.getField('startDate').setVisible(true);
            formTimerEvent.getField('startDate').setRequired(true);
            formTimerEvent.getField('endDateCheckbox').setVisible(true);
            formTimerEvent.getField('endDate').setVisible(true);
            formTimerEvent.getField('oneDateTime').setVisible(false);
            formTimerEvent.getField('oneDateTime').setRequired(false);
            formTimerEvent.getField('daysGroup').setVisible(false);
            formTimerEvent.getField('daysGroup').setRequired(false);
            formTimerEvent.getField('monthsGroup').setVisible(true);
            formTimerEvent.getField('monthsGroup').setRequired(true);
            formTimerEvent.getField('dateTimeVariablePicker').setVisible(false);
            if (formTimerEvent.getItems()[0].items !== undefined) {
                formTimerEvent.getItems()[0].items.get(4).getField('dayType').setVisible(true);
                formTimerEvent.getItems()[0].items.get(4).getField('dayType').setRequired(true);
                formTimerEvent.getItems()[0].items.get(4).getField('hourType').setVisible(true);
                formTimerEvent.getItems()[0].items.get(4).getField('hourType').setRequired(true);
                formTimerEvent.getItems()[0].items.get(4).getField('minuteType').setVisible(true);
                formTimerEvent.getItems()[0].items.get(4).getField('minuteType').setRequired(true);
            }
        };

        showOneDateTimeItems = function () {
            formTimerEvent.getField('startDate').setVisible(false);
            formTimerEvent.getField('startDate').setRequired(false);
            formTimerEvent.getField('endDateCheckbox').setVisible(false);
            formTimerEvent.getField('endDate').setVisible(false);
            formTimerEvent.getField('oneDateTime').setVisible(true);
            formTimerEvent.getField('oneDateTime').setRequired(true);
            formTimerEvent.getField('daysGroup').setVisible(false);
            formTimerEvent.getField('daysGroup').setRequired(false);
            formTimerEvent.getField('monthsGroup').setVisible(false);
            formTimerEvent.getField('monthsGroup').setRequired(false);
            formTimerEvent.getField('dateTimeVariablePicker').setVisible(false);
            if (formTimerEvent.getItems()[0].items !== undefined) {
                formTimerEvent.getItems()[0].items.get(4).getField('dayType').setVisible(false);
                formTimerEvent.getItems()[0].items.get(4).getField('dayType').setRequired(false);
                formTimerEvent.getItems()[0].items.get(4).getField('hourType').setVisible(false);
                formTimerEvent.getItems()[0].items.get(4).getField('hourType').setRequired(false);
                formTimerEvent.getItems()[0].items.get(4).getField('minuteType').setVisible(false);
                formTimerEvent.getItems()[0].items.get(4).getField('minuteType').setRequired(false);
            }
        };

        showEveryItems = function () {
            formTimerEvent.getField('startDate').setVisible(false);
            formTimerEvent.getField('startDate').setRequired(false);
            formTimerEvent.getField('endDateCheckbox').setVisible(false);
            formTimerEvent.getField('endDate').setVisible(false);
            formTimerEvent.getField('oneDateTime').setVisible(false);
            formTimerEvent.getField('oneDateTime').setRequired(false);
            formTimerEvent.getField('daysGroup').setVisible(false);
            formTimerEvent.getField('daysGroup').setRequired(false);
            formTimerEvent.getField('monthsGroup').setVisible(false);
            formTimerEvent.getField('monthsGroup').setRequired(false);
            formTimerEvent.getField('dateTimeVariablePicker').setVisible(false);
            if (formTimerEvent.getItems()[0].items !== undefined) {
                formTimerEvent.getItems()[0].items.get(4).getField('dayType').setVisible(false);
                formTimerEvent.getItems()[0].items.get(4).getField('dayType').setRequired(false);
                formTimerEvent.getItems()[0].items.get(4).getField('hourType').setVisible(true);
                formTimerEvent.getItems()[0].items.get(4).getField('hourType').setRequired(true);
                formTimerEvent.getItems()[0].items.get(4).getField('minuteType').setVisible(true);
                formTimerEvent.getItems()[0].items.get(4).getField('minuteType').setRequired(true);
            }
        };
        /*intermediate*/
        showWaitForItems = function () {
            formTimerEvent.getField('startDate').setVisible(false);
            formTimerEvent.getField('endDateCheckbox').setVisible(false);
            formTimerEvent.getField('endDate').setVisible(false);
            formTimerEvent.getField('oneDateTime').setVisible(false);
            formTimerEvent.getField('daysGroup').setVisible(false);
            formTimerEvent.getField('daysGroup').setRequired(false);
            formTimerEvent.getField('monthsGroup').setVisible(false);
            formTimerEvent.getField('monthsGroup').setRequired(false);
            formTimerEvent.getField('dateTimeVariablePicker').setVisible(false);
            formTimerEvent.getField('dateTimeVariablePicker').setRequired(false);
            if (formTimerEvent.getItems()[0].items !== undefined) {
                formTimerEvent.getItems()[1].items.get(4).getField('dayType').setVisible(true);
                formTimerEvent.getItems()[1].items.get(4).getField('hourType').setVisible(true);
                formTimerEvent.getItems()[1].items.get(4).getField('minuteType').setVisible(true);
            }
        };

        showWaitUntilItems = function () {
            formTimerEvent.getField('startDate').setVisible(false);
            formTimerEvent.getField('endDateCheckbox').setVisible(false);
            formTimerEvent.getField('endDate').setVisible(false);
            formTimerEvent.getField('oneDateTime').setVisible(false);
            formTimerEvent.getField('daysGroup').setVisible(false);
            formTimerEvent.getField('daysGroup').setRequired(false);
            formTimerEvent.getField('monthsGroup').setVisible(false);
            formTimerEvent.getField('monthsGroup').setRequired(false);
            formTimerEvent.getField('dateTimeVariablePicker').setVisible(true);
            formTimerEvent.getField('dateTimeVariablePicker').setRequired(true);
            if (formTimerEvent.getItems()[0].items !== undefined) {
                formTimerEvent.getItems()[1].items.get(4).getField('dayType').setVisible(false);
                formTimerEvent.getItems()[1].items.get(4).getField('hourType').setVisible(false);
                formTimerEvent.getItems()[1].items.get(4).getField('minuteType').setVisible(false);
            }
        };

        this.txtCaseTitle = new CriteriaField({
            id: "txtCaseTitle",
            name: "txtCaseTitle",
            valueType: "string",
            label: "Case Title".translate(),
            maxLength: 200,
            value: "",
            controlsWidth: 500
        });

        radioGroup = new PMUI.field.RadioButtonGroupField({
            id: 'radioGroup',
            controlPositioning: 'horizontal',
            maxDirectionOptions: 7,
            options: [
                {
                    label: "Hourly".translate(),
                    value: "1"
                },
                {
                    label: "Daily".translate(),
                    value: "2"
                },
                {
                    label: "Monthly".translate(),
                    value: "3"
                },
                {
                    label: "One date/time".translate(),
                    value: "4"
                },
                {
                    label: "Every".translate(),
                    value: "5"
                },
                {
                    label: "Wait for".translate(),
                    value: "6"
                },
                {
                    label: "Wait until specified date/time".translate(),
                    value: "7"
                }

            ],
            onChange: function (newVal, oldVal) {
                switch (newVal) {
                    case "1":
                        showHourlyItems();
                        break;
                    case "2":
                        showDailyItems();
                        break;
                    case "3":
                        showMonthlyItems();
                        break;
                    case "4":
                        showOneDateTimeItems();
                        break;
                    case "5":
                        showEveryItems();
                        break;
                    case "6":
                        showWaitForItems();
                        break;
                    case "7":
                        showWaitUntilItems();
                        break;
                }
            },
            value: "1"
        });

        startDate = new PMUI.field.DateTimeField({
            id: 'startDate',
            label: 'Start date'.translate(),
            datetime: false,
            dateFormat: 'yy-mm-dd',
            firstDay: 1,
            controlsWidth: 100,
            required: false,
            readOnly: true,
            minDate: 0,
            maxDate: 1460
        });

        endDate = new PMUI.field.DateTimeField({
            id: 'endDate',
            label: "End date".translate(),
            value: '',
            disabled: true,
            datetime: false,
            dateFormat: 'yy-mm-dd',
            firstDay: 1,
            controlsWidth: 100,
            required: false,
            readOnly: true,
            minDate: 0,
            maxDate: 1460
        });

        oneDateTime = new PMUI.field.DateTimeField({
            id: 'oneDateTime',
            label: 'Date time'.translate(),
            datetime: true,
            dateFormat: 'yy-mm-dd HH:ii:ss',
            firstDay: 1,
            controlsWidth: 150,
            required: false,
            readOnly: true,
            minDate: 0,
            maxDate: 1460
        });

        daysGroup = new PMUI.field.CheckBoxGroupField({
            label: "Days".translate(),
            id: 'daysGroup',
            controlPositioning: 'vertical',
            maxDirectionOptions: 3,
            required: true,
            options: [
                {
                    label: 'Monday'.translate(),
                    value: 1,
                    name: 'monday',
                    selected: true
                },
                {
                    label: 'Tuesday'.translate(),
                    value: 2,
                    name: 'tuesday',
                    selected: true
                },
                {
                    label: 'Wednesday'.translate(),
                    value: 3,
                    name: 'wednesday',
                    selected: true
                },
                {
                    label: 'Thursday'.translate(),
                    value: 4,
                    name: 'thursday',
                    selected: true
                },
                {
                    label: 'Friday'.translate(),
                    value: 5,
                    name: 'friday',
                    selected: true
                },
                {
                    label: 'Saturday'.translate(),
                    value: 6,
                    name: 'saturday',
                    selected: true
                },
                {
                    label: 'Sunday'.translate(),
                    value: 7,
                    name: 'sunday',
                    selected: true
                }
            ],
            onChange: function (newVal, oldVal) {

            }
        });

        monthsGroup = new PMUI.field.CheckBoxGroupField({
            label: "Months".translate(),
            id: 'monthsGroup',
            controlPositioning: 'vertical',
            maxDirectionOptions: 3,
            required: true,
            options: [
                {
                    label: 'January'.translate(),
                    value: 1,
                    name: 'january',
                    selected: true
                },
                {
                    label: 'February'.translate(),
                    value: 2,
                    selected: true
                },
                {
                    label: 'March'.translate(),
                    value: 3,
                    selected: true
                },
                {
                    label: 'April'.translate(),
                    value: 4,
                    selected: true
                },
                {
                    label: 'May'.translate(),
                    value: 5,
                    selected: true
                },
                {
                    label: 'June'.translate(),
                    value: 6,
                    selected: true
                },
                {
                    label: 'July'.translate(),
                    value: 7,
                    selected: true
                },
                {
                    label: 'August'.translate(),
                    value: 8,
                    selected: true
                },
                {
                    label: 'September'.translate(),
                    value: 9,
                    selected: true
                },
                {
                    label: 'October'.translate(),
                    value: 10,
                    selected: true
                },
                {
                    label: 'November'.translate(),
                    value: 11,
                    selected: true
                },
                {
                    label: 'December'.translate(),
                    value: 12,
                    selected: true
                }
            ],
            onChange: function (newVal, oldVal) {

            }
        });

        dateTimeVariablePicker = new PMUI.field.DateTimeField({
            id: 'dateTimeVariablePicker',
            label: 'Date time'.translate(),
            datetime: true,
            dateFormat: 'yy-mm-dd HH:ii:ss',
            firstDay: 1,
            controlsWidth: 150,
            required: false,
            readOnly: true,
            minDate: 0,
            maxDate: 1460
        });

        formTimerEvent = new PMUI.form.Form({
            id: "formTimerEvent",
            border: true,
            visibleHeader: false,
            width: '900px',
            height: "300px",
            name: "formTimerEvent",
            title: '',
            items: [
                this.txtCaseTitle,
                {
                    id: "panelDetailsCustom",
                    pmType: "panel",
                    layout: 'vbox',
                    fieldset: false,
                    height: '380px',
                    legend: "DETAILS".translate(),
                    items: [
                        {
                            id: "evnUid",
                            pmType: "text",
                            value: evnUid,
                            name: "evnUid",
                            readOnly: true,
                            visible: false,
                            valueType: 'string'
                        },
                        {
                            id: "activityType",
                            pmType: "text",
                            value: activityType,
                            name: "activityType",
                            readOnly: true,
                            visible: false,
                            valueType: 'string'
                        },
                        radioGroup,
                        {
                            pmType: "panel",
                            id: "datesPanel",
                            layout: 'hbox',
                            items: [
                                startDate,
                                {
                                    pmType: "checkbox",
                                    id: "endDateCheckbox",
                                    label: "End date".translate(),
                                    controlPositioning: 'vertical',
                                    maxDirectionOptions: 2,
                                    value: '',
                                    options: [
                                        {
                                            label: "End date:".translate(),
                                            disabled: false,
                                            value: '1',
                                            selected: false
                                        }
                                    ],
                                    onChange: function (newVal, oldVal) {
                                        if (newVal[2] == "1") {
                                            $('#endDate').find('input:eq(0)').removeProp("disabled");
                                        } else {
                                            $('#endDate').find('input:eq(0)').val('').attr("disabled", "disabled");
                                            formTimerEvent.getField('endDate').setValue('');
                                        }
                                    }
                                },
                                endDate,
                                oneDateTime,
                                dateTimeVariablePicker
                            ]
                        },
                        {
                            pmType: "panel",
                            id: "dayHourMonthPanel",
                            layout: 'hbox',
                            items: [
                                {
                                    id: "dayType",
                                    label: "Day".translate(),
                                    pmType: "text",
                                    value: "",
                                    name: "dayType",
                                    visible: true,
                                    valueType: 'integer',
                                    controlsWidth: 50,
                                    maxLength: 2
                                },
                                {
                                    id: "hourType",
                                    label: "Hour".translate(),
                                    pmType: "text",
                                    value: "",
                                    name: "hourType",
                                    visible: true,
                                    valueType: 'integer',
                                    controlsWidth: 50,
                                    maxLength: 2
                                },
                                {
                                    id: "minuteType",
                                    label: "Minute".translate(),
                                    pmType: "text",
                                    value: "",
                                    name: "minuteType",
                                    visible: true,
                                    valueType: 'integer',
                                    controlsWidth: 50,
                                    maxLength: 2
                                }
                            ]
                        },
                        daysGroup,
                        monthsGroup,
                        {
                            id: "tmrevn_uid",
                            pmType: "text",
                            value: tmrevn_uid,
                            name: "tmrevn_uid",
                            visible: false,
                            valueType: 'string'
                        }
                    ]
                }
            ]
        });

        formTimerEvent.initialData = function () {
            var formElements = this.getItems()[0],
                datesPanelElements,
                radioGroupValues = {'radioGroup': formElements.items.get(2).getValue()};
            oldValues.push(radioGroupValues);
            datesPanelElements = formElements.items.get(3).getItems();

        };

        getFormData = function ($form) {
            var unindexed_array = $form.serializeArray(),
                indexed_array = {};

            $.map(unindexed_array, function (n, i) {
                indexed_array[n['name']] = n['value'];
            });
            return indexed_array;
        };

        getTimerEventData = function () {
            var restClient = new PMRestClient({
                endpoint: 'timer-event/event/' + formTimerEvent.getField("evnUid").getValue(),
                typeRequest: 'get',
                functionSuccess: function (xhr, response) {
                    if (typeof response === "object" && JSON.stringify(response).length > 2) {
                        var opt = "";
                        if (typeof response.tmrevn_option !== 'undefined') {
                            opt = response.tmrevn_option.toUpperCase();
                        }
                        formTimerEvent.getField("txtCaseTitle").setValue(response.tas_def_title);
                        switch (opt) {
                            case "HOURLY":
                                $("#radioGroup").find("input:eq(0)").trigger("click");
                                formTimerEvent.getField("tmrevn_uid").setValue(response.tmrevn_uid);
                                formTimerEvent.getField("startDate").setValue(response.tmrevn_start_date);
                                if (response.tmrevn_end_date != "") {
                                    formTimerEvent.getField("endDateCheckbox").setValue('["1"]');
                                    formTimerEvent.getField("endDate").setValue(response.tmrevn_end_date);
                                    formTimerEvent.getField("endDate").enable();
                                }
                                formTimerEvent.getField("minuteType").setValue(response.tmrevn_minute);
                                break;
                            case "DAILY":
                                $("#radioGroup").find("input:eq(1)").trigger("click");
                                formTimerEvent.getField("tmrevn_uid").setValue(response.tmrevn_uid);
                                formTimerEvent.getField("startDate").setValue(response.tmrevn_start_date);
                                if (response.tmrevn_end_date != "") {
                                    formTimerEvent.getField("endDateCheckbox").setValue('["1"]');
                                    formTimerEvent.getField("endDate").setValue(response.tmrevn_end_date);
                                    formTimerEvent.getField("endDate").enable();
                                }
                                formTimerEvent.getField("hourType").setValue(response.tmrevn_hour);
                                formTimerEvent.getField("minuteType").setValue(response.tmrevn_minute);
                                formTimerEvent.getField("daysGroup").setValue("");
                                formTimerEvent.getField("daysGroup").setValue(JSON.stringify(response.tmrevn_configuration_data.map(function (n) {
                                    return n.toString();
                                })));
                                break;
                            case "MONTHLY":
                                $("#radioGroup").find("input:eq(2)").trigger("click");
                                formTimerEvent.getField("tmrevn_uid").setValue(response.tmrevn_uid);
                                formTimerEvent.getField("startDate").setValue(response.tmrevn_start_date);
                                if (response.tmrevn_end_date != "") {
                                    formTimerEvent.getField("endDateCheckbox").setValue('["1"]');
                                    formTimerEvent.getField("endDate").setValue(response.tmrevn_end_date);
                                    formTimerEvent.getField("endDate").enable();
                                }
                                formTimerEvent.getField("dayType").setValue(response.tmrevn_day);
                                formTimerEvent.getField("hourType").setValue(response.tmrevn_hour);
                                formTimerEvent.getField("minuteType").setValue(response.tmrevn_minute);
                                formTimerEvent.getField("monthsGroup").setValue("");
                                formTimerEvent.getField("monthsGroup").setValue(JSON.stringify(response.tmrevn_configuration_data.map(function (n) {
                                    return n.toString();
                                })));
                                break;
                            case "ONE-DATE-TIME":
                                $("#radioGroup").find("input:eq(3)").trigger("click");
                                formTimerEvent.getField("tmrevn_uid").setValue(response.tmrevn_uid);
                                var d = response.tmrevn_next_run_date.replace(/-/g, "/");
                                for (var i in ENABLED_FEATURES) {
                                    if (ENABLED_FEATURES[i] == 'oq3S29xemxEZXJpZEIzN01qenJUaStSekY4cTdJVm5vbWtVM0d4S2lJSS9qUT0=') {
                                        d = response.tmrevn_next_run_date;
                                    }
                                }
                                d = new Date(d);
                                formTimerEvent.getField("oneDateTime").setValue(d);
                                break;
                            case "EVERY":
                                $("#radioGroup").find("input:eq(4)").trigger("click");
                                formTimerEvent.getField("tmrevn_uid").setValue(response.tmrevn_uid);
                                formTimerEvent.getField("hourType").setValue(response.tmrevn_hour);
                                formTimerEvent.getField("minuteType").setValue(response.tmrevn_minute);
                                break;
                            case "WAIT-FOR":
                                $("#radioGroup").find("input:eq(5)").trigger("click");
                                formTimerEvent.getField("tmrevn_uid").setValue(response.tmrevn_uid);
                                formTimerEvent.getField("dayType").setValue(response.tmrevn_day);
                                formTimerEvent.getField("hourType").setValue(response.tmrevn_hour);
                                formTimerEvent.getField("minuteType").setValue(response.tmrevn_minute);
                                break;
                            case "WAIT-UNTIL-SPECIFIED-DATE-TIME":
                                $("#radioGroup").find("input:eq(6)").trigger("click");
                                formTimerEvent.getField("tmrevn_uid").setValue(response.tmrevn_uid);
                                var d = response.tmrevn_configuration_data.replace(/-/g, "/");
                                d = new Date(d);
                                formTimerEvent.getField("dateTimeVariablePicker").setValue(d);
                                break;
                        }
                    } else {
                        if (eventType == "START") {
                            $("#radioGroup").find("input:eq(1)").trigger("click");
                        } else {
                            $("#radioGroup").find("input:eq(5)").trigger("click");
                        }
                    }
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                    PMDesigner.msgFlash('There are problems updating the Timer Event, please try again.'.translate(), document.body, 'error', 3000, 5);
                }
            });
            restClient.executeRestClient();
        };

        /*end form*/

        validateItems = function (itemId) {
            var regexTest,
                message,
                valueItem,
                regexTest;

            if (itemId === 'dayType') {
                regexTest = regexDay;
                message = "Error value: Day: 0 - 31".translate();
            } else if (itemId === 'hourType') {
                regexTest = regexHour;
                message = "Error value: Hour: 0 - 23".translate();
            } else if (itemId === 'minuteType') {
                regexTest = regexMinute;
                message = "Error value: Minute: 0 - 59".translate();
            }

            valueItem = $('#' + itemId).find('span input:eq(0)').val();

            if (!regexTest.test(valueItem)) {
                PMDesigner.msgFlash(message, timerEventPropertiesWindow, 'error', 3000, 5);
                $('#' + itemId).find('span input:eq(0)').val('');
                return false;
            }
        };

        domSettings = function () {
            var requiredMessage = $(document.getElementById("requiredMessage"));
            timerEventPropertiesWindow.body.appendChild(requiredMessage[0]);
            requiredMessage[0].style['marginTop'] = '70px';

            timerEventPropertiesWindow.footer.html.style.textAlign = 'right';

            $('#hourType, #dayType, #minuteType').find('span input:eq(0)').bind('blur change', function () {
                validateItems($(this).closest('div').attr('id'));
            });

            $("#dayType").find("input").attr({"type": "number", "maxlength": "2", "min": "0", "max": "31"});
            $("#hourType").find("input").attr({"type": "number", "maxlength": "2", "min": "0", "max": "23"});
            $("#minuteType").find("input").attr({"type": "number", "maxlength": "2", "min": "0", "max": "59"});

            $("#radioGroup").css({"text-align": "center", "margin-bottom": "20px"}).find("label:eq(0)").remove();
            $("#endDateCheckbox").css({"width": "170px", "top": "6px", "left": "28px"}).find("label:eq(0)").remove();
            $("#endDateCheckbox").find("table:eq(0)").css("border", "0px");
            $("#startDate").css("width", "");
            $("#endDate").css("width", "104px").find("label:eq(0)").remove();
            $("#oneDateTime").css("width", "");
            $("#datesPanel").css("text-align", "center").find("label").css({
                "width": "",
                "float": "",
                "text-align": "right"
            });
            $("#dayHourMonthPanel").css("text-align", "center").find("label").css({"float": "", "width": "34.5%"});

            $("#daysGroup").css("text-align", "center").find("label:eq(0)").remove();
            $("#monthsGroup").css("text-align", "center").find("label:eq(0)").remove();
            $("#daysGroup").find("input").each(function () {
                $(this).attr("name", $(this).val());
            });

            $("#dateTimeVariablePicker").css("width", "");


            if (eventType == "START") {
                $(formTimerEvent.getField("radioGroup").controls[0].html).parent().show();
                $(formTimerEvent.getField("radioGroup").controls[1].html).parent().show();
                $(formTimerEvent.getField("radioGroup").controls[2].html).parent().show();
                $(formTimerEvent.getField("radioGroup").controls[3].html).parent().show();
                $(formTimerEvent.getField("radioGroup").controls[4].html).parent().show();
                $(formTimerEvent.getField("radioGroup").controls[5].html).parent().hide();
                $(formTimerEvent.getField("radioGroup").controls[6].html).parent().hide();
                $("#radioGroup").find("input:eq(1)").trigger("click");
            } else {
                $(formTimerEvent.getField("radioGroup").controls[0].html).parent().hide();
                $(formTimerEvent.getField("radioGroup").controls[1].html).parent().hide();
                $(formTimerEvent.getField("radioGroup").controls[2].html).parent().hide();
                $(formTimerEvent.getField("radioGroup").controls[3].html).parent().hide();
                $(formTimerEvent.getField("radioGroup").controls[4].html).parent().hide();
                $(formTimerEvent.getField("radioGroup").controls[5].html).parent().show();
                $(formTimerEvent.getField("radioGroup").controls[6].html).parent().show();
                $("#radioGroup").find("input:eq(5)").trigger("click");
            }
        };

        timerEventPropertiesWindow.addItem(formTimerEvent);
        timerEventPropertiesWindow.open();
        formTimerEvent.eventsDefined = false;
        formTimerEvent.defineEvents();
        timerEventPropertiesWindow.showFooter();
        domSettings();
        getTimerEventData();
        oldValues = getFormData($("#formTimerEvent"));
    };
}());


(function () {
    PMDesigner.emailEventProperties = function (activity) {
        var that = this,
            buttonCancel,
            buttonSave,
            emailEventPropertiesWindow,
            emailAcounts,
            emailContent,
            getEmailAccounts,
            formEmailEvent,
            emailWindowTinyMCE,
            getFormData,
            getEmailEventData,
            domSettings,
            activityId = activity.getID(),
            activityType = activity.getEventMarker(),
            uidProj = PMDesigner.project.id,
            initTinyMCE = null,
            oldValues,
            emailEventId = "",
            prf_uid = "",
            ddSize = 21,
            auxFromMail = {},
            defaultServerlabel = "Mail (PHP)".translate(),
            triggerSelectedData;
        /*options to display in drop down*/

        /*window*/
        buttonCancel = new PMUI.ui.Button({
            id: 'cancelEmailEventsButton',
            text: "Cancel".translate(),
            buttonType: 'error',
            handler: function (event) {
                PMDesigner.hideAllTinyEditorControls();
                clickedClose = false;
                emailEventPropertiesWindow.isDirtyFormScript();
            }
        });

        buttonSave = new PMUI.ui.Button({
            id: 'saveEmailEventsButton',
            text: "Save".translate(),
            handler: function (event) {
                PMDesigner.hideAllTinyEditorControls();
                var dataForm = formEmailEvent.getData(),
                    selectedAccount = formEmailEvent.getField('emailAcounts').getValue();
                if (formEmailEvent.isValid()) {
                    if (dataForm.emailEventId == "") { /*insert*/
                        (new PMRestClient({
                            endpoint: 'file-manager',
                            typeRequest: 'post',
                            messageError: '',
                            data: {
                                prf_filename: "emailEvent_" + new Date().getTime() + ".html",
                                prf_path: "templates",
                                prf_content: dataForm.filecontent
                            },
                            functionSuccess: function (xhr, response) {
                                var restClient;
                                prf_uid = response.prf_uid;
                                if (prf_uid != "" && typeof prf_uid != "undefined") {
                                    restClient = new PMRestClient({
                                        endpoint: 'email-event',
                                        typeRequest: 'post',
                                        data: {
                                            evn_uid: activityId,
                                            email_event_from: auxFromMail[selectedAccount] || '',
                                            email_event_to: dataForm.ToEmail,
                                            email_event_subject: dataForm.subjectEmail,
                                            email_server_uid: dataForm.emailAcounts,
                                            prf_uid: prf_uid
                                        },
                                        functionSuccess: function () {
                                            emailEventPropertiesWindow.close();
                                            PMDesigner.msgFlash('Email Event saved correctly'.translate(), document.body, 'success', 3000, 5);
                                        },
                                        functionFailure: function (xhr, response) {
                                            PMDesigner.msgWinError(response.error.message);
                                            PMDesigner.msgFlash('There are problems updating the Email Event, please try again.'.translate(), document.body, 'error', 3000, 5);
                                        }
                                    });
                                    restClient.executeRestClient();
                                }
                            },
                            functionFailure: function (xhr, response) {
                                PMDesigner.msgWinError(response.error.message);
                            }
                        })).executeRestClient();


                    } else {
                        (new PMRestClient({
                            endpoint: 'file-manager/' + dataForm.prf_uid,
                            typeRequest: 'update',
                            messageError: '',
                            data: {
                                prf_content: tinyMCE.activeEditor.getContent()
                            },
                            functionSuccess: function (xhr, response) {
                                var restClient;
                                prf_uid = response.prf_uid;
                                if (prf_uid != "" && typeof prf_uid != "undefined") {
                                    restClient = new PMRestClient({
                                        endpoint: 'email-event/' + dataForm.emailEventId,
                                        typeRequest: 'update',
                                        data: {
                                            evn_uid: activityId,
                                            email_event_from: auxFromMail[selectedAccount] || '',
                                            email_event_to: dataForm.ToEmail,
                                            email_event_subject: dataForm.subjectEmail,
                                            email_server_uid: dataForm.emailAcounts,
                                            prf_uid: prf_uid
                                        },
                                        functionSuccess: function () {
                                            emailEventPropertiesWindow.close();
                                            PMDesigner.msgFlash('Email Event Edited correctly'.translate(), document.body, 'success', 3000, 5);
                                        },
                                        functionFailure: function (xhr, response) {
                                            PMDesigner.msgWinError(response.error.message);
                                            PMDesigner.msgFlash('There are problems Edited the Email Event, please try again.'.translate(), document.body, 'error', 3000, 5);
                                        }
                                    });
                                    restClient.executeRestClient();
                                }
                            },
                            functionFailure: function (xhr, response) {
                                PMDesigner.msgWinError(response.error.message);
                            }
                        })).executeRestClient();
                    }
                }
            },
            buttonType: 'success'
        });

        emailEventPropertiesWindow = new PMUI.ui.Window({
            id: "emailEventPropertiesWindow",
            title: "Email Event Properties".translate(),
            width: DEFAULT_WINDOW_WIDTH,
            height: DEFAULT_WINDOW_HEIGHT,
            footerItems: [
                buttonCancel,
                buttonSave
            ],
            buttonPanelPosition: "bottom",
            footerAling: "right",
            onBeforeClose: function () {
                PMDesigner.hideAllTinyEditorControls();
                clickedClose = true;
                emailEventPropertiesWindow.isDirtyFormScript();
            }
        });

        emailEventPropertiesWindow.isDirtyFormScript = function () {
            var that = this,
                newValues,
                message_window,
                formData = formEmailEvent.getData();
            formData.filecontent = $(tinyMCE.activeEditor.getContent()).text().trim().length ? tinyMCE.activeEditor.getContent() : "";
            newValues = formData;
            if (JSON.stringify(oldValues) !== JSON.stringify(newValues)) {
                message_window = new PMUI.ui.MessageWindow({
                    id: "cancelMessageTriggers",
                    windowMessageType: 'warning',
                    width: 490,
                    title: "Email Event".translate(),
                    message: 'Are you sure you want to discard your changes?'.translate(),
                    footerItems: [
                        {
                            text: "No".translate(),
                            handler: function () {
                                message_window.close();
                            },
                            buttonType: "error"
                        },
                        {
                            text: "Yes".translate(),
                            handler: function () {
                                message_window.close();
                                that.close();
                            },
                            buttonType: "success"
                        }
                    ]
                });
                message_window.open();
                message_window.showFooter();
            } else {
                that.close();
            }
        };
        /*end window*/

        /*form*/
        emailAcounts = new PMUI.field.DropDownListField({
            id: "emailAcounts",
            name: "emailAcounts",
            label: "From".translate(),
            options: null,
            controlsWidth: 400,
            required: false,
            labelWidth: "15%",
            onChange: function (newValue, prevValue) {
                var uidTri = newValue,
                    oldValue,
                    i;
                for (i = 0; i < triggerSelectedData.length; i += 1) {
                    if (triggerSelectedData[i].tri_uid === uidTri) {
                        formScriptTask.getItems()[1].controls[0].cm.setValue(triggerSelectedData[i].tri_webbot);
                        oldValue = triggerSelectedData[i].tri_webbot;
                    }
                }
            }
        });
        emailContent = new PMUI.field.TextAreaField({
            id: 'filecontent',
            name: 'filecontent',
            label: 'Content'.translate(),
            required: true,
            value: '',
            rows: 210,
            labelWidth: "15%",
            controlsWidth: 720,
            onChange: function (currentValue, previousValue) {
            },
            style: {cssClasses: ['mafe-textarea-resize']}
        });
        getEmailAccounts = function (emailAcounts) {
            var restClient = new PMRestClient({
                endpoint: 'email-event/accounts/emailServer',
                typeRequest: 'get',
                functionSuccess: function (xhr, response) {
                    var i,
                        arrayOptions;
                    triggerSelectedData = response;
                    arrayOptions = [];
                    arrayOptions[0] = {
                        label: "Default email account".translate(),
                        value: "",
                        disabled: false,
                        selected: true
                    };
                    for (i = 0; i < triggerSelectedData.length ; i += 1) {
                        arrayOptions.push({
                            value: triggerSelectedData[i].uid,
                            label: response[i].mess_engine === "MAIL" ?
                                triggerSelectedData[i].mess_from_name && triggerSelectedData[i].mess_from_name !== "" ?
                                triggerSelectedData[i].mess_from_name : defaultServerlabel : triggerSelectedData[i].mess_from_name && triggerSelectedData[i].mess_from_name !== "" ?
                                triggerSelectedData[i].mess_from_name + ' <' + triggerSelectedData[i].mess_account + '>' : ' <' + triggerSelectedData[i].mess_account + '>'
                        });
                        auxFromMail[triggerSelectedData[i].uid] = triggerSelectedData[i].email;
                    }
                    emailAcounts.setOptions(arrayOptions);
                    emailAcounts.setValue(arrayOptions[0].value);
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                },
                messageError: "There are problems getting the Triggers list, please try again.".translate()
            });
            restClient.executeRestClient();
        };
        formEmailEvent = new PMUI.form.Form({
            id: "formEmailEvent",
            border: true,
            visibleHeader: false,
            width: '900px',
            height: "300px",
            name: "formEmailEvent",
            title: '',
            items: [
                {
                    id: "panelDetailsCustom",
                    pmType: "panel",
                    layout: 'vbox',
                    fieldset: false,
                    height: '380px',
                    legend: "DETAILS".translate(),
                    items: [
                        {
                            id: "activityId",
                            pmType: "text",
                            value: activityId,
                            name: "activityId",
                            visible: false,
                            valueType: 'string'
                        },
                        {
                            id: "activityType",
                            pmType: "text",
                            value: activityType,
                            name: "activityType",
                            visible: false,
                            valueType: 'string'
                        },
                        emailAcounts,
                        new CriteriaField({
                            id: "ToEmail",
                            pmType: "text",
                            helper: "The email can be a string or a variable (@@myEmail), comma separated list of emails".translate(),
                            label: "To".translate(),
                            controlsWidth: 400,
                            value: "",
                            name: "ToEmail",
                            required: true,
                            visible: true,
                            labelWidth: "15%",
                            valueType: 'string'
                        }),
                        new CriteriaField({
                            id: 'subjectEmail',
                            pmType: 'text',
                            label: "Subject".translate(),
                            controlsWidth: 400,
                            value: "",
                            name: "subjectEmail",
                            required: false,
                            visible: true,
                            labelWidth: "15%",
                            valueType: 'string'
                        }),
                        emailContent,
                        {
                            id: "emailEventId",
                            pmType: "text",
                            value: emailEventId,
                            name: "emailEventId",
                            visible: false,
                            valueType: 'string'
                        },
                        {
                            id: "prf_uid",
                            pmType: "text",
                            value: prf_uid,
                            name: "prf_uid",
                            visible: false,
                            valueType: 'string'
                        }
                    ]
                }
            ]
        });
        emailWindowTinyMCE = function () {
            initTinyMCE = function () {
                tinyMCE.activeEditor.domainURL = "/sys" + WORKSPACE + "/" + LANG + "/" + SKIN + "/";
                tinyMCE.activeEditor.processID = PMDesigner.project.id;
            };
            formEmailEvent.getField('filecontent').getControls()[0].getHTML().className = 'tmceEditor';
            applyStyleWindowForm(emailEventPropertiesWindow);
            tinyMCE.init({
                editor_selector: 'tmceEditor',
                mode: 'specific_textareas',
                directionality: 'ltr',
                verify_html: false,
                skin: 'o2k7',
                theme: 'advanced',
                skin_variant: 'silver',
                relative_urls : false,
                remove_script_host : false,
                convert_urls: false,
                plugins: 'advhr,advimage,advlink,advlist,autolink,autoresize,contextmenu,directionality,emotions,example,example_dependency,fullpage,fullscreen,iespell,inlinepopups,insertdatetime,layer,legacyoutput,lists,media,nonbreaking,noneditable,pagebreak,paste,preview,print,save,searchreplace,style,tabfocus,table,template,visualblocks,visualchars,wordcount,xhtmlxtras,pmSimpleUploader,pmVariablePicker,style',
                theme_advanced_buttons1: 'pmSimpleUploader,|,pmVariablePicker,|,bold,italic,underline,|,justifyleft,justifycenter,justifyright,justifyfull,|,fontselect,fontsizeselect,|,cut,copy,paste',
                theme_advanced_buttons2: 'bullist,numlist,|,outdent,indent,blockquote,|,tablecontrols,|,undo,redo,|,link,unlink,image,|,forecolor,backcolor,styleprops',
                theme_advanced_buttons3: 'hr,removeformat,visualaid,|,sub,sup,|,ltr,rtl,|,code',
                popup_css: "/js/tinymce/jscripts/tiny_mce/themes/advanced/skins/default/dialogTinyBpmn.css",
                oninit: initTinyMCE,
                onchange_callback: function (inst) {
                    formEmailEvent.getField('filecontent').setValue(tinyMCE.activeEditor.getContent({format: 'raw'}));
                },
                handle_event_callback: function (e) {
                },
                setup: function (ed) {
                    ed.onSetContent.add(function (ed, l) {
                        formEmailEvent.getField('filecontent').setValue(tinyMCE.activeEditor.getContent({format: 'raw'}));
                    });
                }
            });
        };
        getFormData = function ($form) {
            var unindexed_array = $form.serializeArray(),
                indexed_array = {};

            $.map(unindexed_array, function (n, i) {
                indexed_array[n['name']] = n['value'];
            });
            return indexed_array;
        };
        getEmailEventData = function () {
            var restClient = new PMRestClient({
                endpoint: 'email-event/' + activityId,
                typeRequest: 'get',
                functionSuccess: function (xhr, response) {
                    var valFrom;
                    if (typeof response === "object") {
                        emailEventId = response.email_event_uid;
                        if (emailEventId !== "" && typeof emailEventId !== "undefined") {
                            formEmailEvent.getField('emailEventId').setValue(response.email_event_uid);
                            // Set as selected the email server by uid
                            if (response.email_server_uid !== "" && typeof response.email_server_uid !== "undefined") {
                                formEmailEvent.getField('emailAcounts').setValue(response.email_server_uid);
                            }
                            formEmailEvent.getField('subjectEmail').setValue(response.email_event_subject);
                            formEmailEvent.getField('ToEmail').setValue(response.email_event_to);

                            formEmailEvent.getField('prf_uid').setValue(response.prf_uid);

                            (new PMRestClient({
                                endpoint: 'file-manager',
                                typeRequest: 'get',
                                messageError: '',
                                data: {
                                    path: "templates"
                                },
                                functionSuccess: function (xhr, response) {
                                    for (var i = 0; i < response.length; i += 1) {
                                        if (response[i].prf_uid == formEmailEvent.getField('prf_uid').getValue()) {
                                            formEmailEvent.getField('filecontent').setValue(response[i].prf_content);
                                            if (tinyMCE.activeEditor && !$(tinyMCE.activeEditor.getContent()).text().trim().length) {
                                                tinyMCE.activeEditor.setContent(response[i].prf_content);
                                            }
                                            break;
                                        }
                                    }
                                },
                                functionFailure: function (xhr, response) {
                                    PMDesigner.msgWinError(response.error.message);
                                }
                            })).executeRestClient();
                        }
                    }
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                }
            });
            restClient.executeRestClient();
        };

        getEmailAccounts(emailAcounts);

        /*end form*/

        this.deleteEmailEventByEventUid = function () {
            (new PMRestClient({
                endpoint: 'email-event/by-event/' + activityId,
                typeRequest: 'remove',
                messageError: '',
                functionSuccess: function (xhr, response) {
                },
                functionFailure: function (xhr, response) {
                }
            })).executeRestClient();
            return this;
        };

        domSettings = function () {
            emailEventPropertiesWindow.footer.html.style.textAlign = 'right';
            $("#emailAcounts").find("select:eq(0)").css("height", "auto").attr({
                "onmousedown": "if(this.options.length>" + ddSize + "){this.size=" + ddSize + ";}",
                "onchange": "this.size=0;",
                "onblur": "this.size=0;"
            });
        };
        emailEventPropertiesWindow.addItem(formEmailEvent);
        emailEventPropertiesWindow.open();
        emailEventPropertiesWindow.showFooter();
        emailWindowTinyMCE();
        domSettings();
        getEmailEventData();
        oldValues = formEmailEvent.getData();
    };
}());


SaveAsForm = function (settings) {
    Mafe.Form.call(this, settings);
    SaveAsForm.prototype.init.call(this, settings);
};
SaveAsForm.prototype = new Mafe.Form();
SaveAsForm.prototype.init = function () {
    var that = this;
    that.onSave = new Function();
    that.onCancel = new Function();
    that.setID("SaveAsForm");
    that.setTitle("Save as".translate());
    that.setItems([{
        id: "prj_name",
        name: "pro_title",
        pmType: "text",
        label: "Title".translate(),
        placeholder: "Title".translate(),
        maxLength: 100,
        required: true
    }, {
        id: "prj_description",
        pmType: "textarea",
        name: "pro_description",
        label: "Description".translate(),
        placeholder: "Description".translate(),
        rows: 200
    }, {
        id: "pro_category",
        name: "pro_category",
        pmType: "dropdown",
        label: "Category".translate(),
        options: [
            {value: "", label: "No Category".translate()}
        ]
    }
    ]);
    that.setButtons([{
        id: "idCancel",
        text: "Cancel".translate(),
        buttonType: "error",
        handler: function () {
            that.onCancel();
        }
    }, {
        id: "idSave",
        text: "Save".translate(),
        buttonType: "success",
        handler: function () {
            that.onSave();
        }
    }
    ]);
    that.loadCategory();
};
SaveAsForm.prototype.loadCategory = function () {
    var that = this,
        titleProcess;
    var a = new PMRestClient({
        typeRequest: "post",
        multipart: true,
        data: {
            calls: [{
                url: "project/categories",
                method: "GET"
            }, {
                url: "project/" + PMDesigner.project.projectId + "/process",
                method: "GET"
            }
            ]
        },
        functionSuccess: function (xhr, response) {
            var i, dt, category = that.getField("pro_category");
            dt = response[0].response;
            for (i = 0; i < dt.length; i++) {
                category.addOption({
                    value: dt[i].cat_uid,
                    label: dt[i].cat_name
                });
            }
            //load data
            dt = response[1].response;
            titleProcess = "Copy of".translate() + " [" + dt.pro_title + "]";
            that.getField("pro_title").setValue(titleProcess.substring(0, 100));
            that.getField("pro_description").setValue(dt.pro_description);
            that.getField("pro_category").setValue(dt.pro_category);
        },
        functionFailure: function (xhr, response) {
        }
    });
    a.setBaseEndPoint("");
    a.executeRestClient();
};

SaveAs = function (settings) {
    Mafe.Window.call(this, settings);
    SaveAs.prototype.init.call(this, settings);
};
SaveAs.prototype = new Mafe.Window();
SaveAs.prototype.init = function () {
    var that = this;
    that.saveAsForm = new SaveAsForm();
    that.saveAsForm.onYesConfirmCancellation = function () {
        that.close();
    };
    that.saveAsForm.onCancel = function () {
        that.saveAsForm.loseChanges({title: that.title});
    };
    that.saveAsForm.onSave = function () {
        that.saveAsForm.getField("pro_title").setReadOnly(true);
        that.saveAsForm.getField("pro_description").setReadOnly(true);
        PMUI.getPMUIObject($(that.saveAsForm.html).find("#idSave")[0]).setDisabled(true);
        var a = new PMRestClient({
            typeRequest: "post",
            multipart: true,
            data: {
                calls: [{
                    url: "project/save-as",
                    method: "POST",
                    data: {
                        prj_uid: PMDesigner.project.projectId,
                        prj_name: that.saveAsForm.getField("pro_title").getValue(),
                        prj_description: that.saveAsForm.getField("pro_description").getValue(),
                        prj_category: that.saveAsForm.getField("pro_category").getValue()
                    }
                }
                ]
            },
            functionSuccess: function (xhr, response) {
                if (response[0].response.prj_uid) {
                    that.close();
                    window.location.href = "designer?prj_uid=" + response[0].response.prj_uid;
                } else {
                    that.saveAsForm.getField("pro_title").setReadOnly(false);
                    that.saveAsForm.getField("pro_description").setReadOnly(false);
                    PMUI.getPMUIObject($(that.saveAsForm.html).find("#idSave")[0]).setDisabled(false);
                    var field = that.saveAsForm.getField("pro_title");
                    $(field.html).find(".pmui-textlabel").text(response[0].response);
                    field.showMessage();
                }
            },
            functionFailure: function (xhr, response) {
            }
        });
        a.setBaseEndPoint("");
        a.executeRestClient();
    };

    that.setTitle("Save as".translate());
    that.addItem(that.saveAsForm);
};
(function () {
    PMDesigner.granularProcessExport = function () {
        var totalObjects = 0,
            objectValues,
            buttonSave,
            objectsGroup,
            loadObjects,
            domSettings,
            granularProcessExportForm,
            granularProcessExportWindow,
            buttonCancel = new PMUI.ui.Button({
                id: 'cancelButton',
                text: "Cancel".translate(),
                buttonType: 'error',
                handler: function (event) {
                    granularProcessExportWindow.close();
                }
            });

        buttonSave = new PMUI.ui.Button({
            id: 'saveButton',
            text: "Export".translate(),
            handler: function (event) {
                var selectedObjects,
                    locationOrigin,
                    ws = enviromentVariables('WORKSPACE'),
                    formData = granularProcessExportForm.getData();
                selectedObjects = JSON.parse(formData.objectsGroup).map(function (n) {
                    return n;
                });
                if (selectedObjects.length === 0) {
                    PMDesigner.msgFlash('At least one object should be selected in order to execute the action.'.translate(), document.body, 'error', 3000, 5);
                    return;
                }
                if (!window.location.origin) {
                    locationOrigin = window.location.protocol + "//" + window.location.hostname +
                        (window.location.port ? ':' + window.location.port : '');
                } else if (typeof HTTP_SERVER_HOSTNAME !== 'undefined') {
                    locationOrigin = HTTP_SERVER_HOSTNAME;
                } else {
                    locationOrigin = window.location.origin;
                }
                location.href = locationOrigin + "/api/1.0/" + ws + "/project/" + PMDesigner.project.id +
                    "/export-granular?access_token=" + PMDesigner.project.keys.access_token + '&objects=' +
                    encodeURIComponent(JSON.stringify(selectedObjects));
                granularProcessExportWindow.close();
            },
            buttonType: 'success'
        });

        granularProcessExportWindow = new PMUI.ui.Window({
            id: "granularProcessExportWindow",
            title: "Export Process Objects".translate(),
            width: 350,
            height: DEFAULT_WINDOW_HEIGHT,
            footerItems: [
                buttonCancel,
                buttonSave
            ],
            buttonPanelPosition: "bottom",
            footerAling: "right",
            onBeforeClose: function () {
                granularProcessExportWindow.close();
            }
        });

        objectsGroup = new PMUI.field.CheckBoxGroupField({
            labelVisible: false,
            id: 'objectsGroup',
            controlPositioning: 'horizontal',
            maxDirectionOptions: 1,
            required: true,
            options: [],
            onChange: function (newVal, oldVal) {
                if (totalObjects > JSON.parse(newVal).length) {
                    granularProcessExportForm.getField('checkAllId').setValue(0);
                }
            }
        });

        loadObjects = function () {
            var objectsList,
                restClient,
                i;
            restClient = new PMRestClient({
                endpoint: 'export/listObjects',
                typeRequest: 'get',
                functionSuccess: function (xhr, response) {
                    var arrayIds = [],
                        i;
                    objectsList = JSON.parse(response);
                    for (i in objectsList.data) {
                        objectsGroup.addOption({
                            label: objectsList.data[i].OBJECT_NAME,
                            name: objectsList.data[i].OBJECT_NAME,
                            selected: false,
                            value: objectsList.data[i].OBJECT_ID
                        });
                        arrayIds.push(objectsList.data[i].OBJECT_ID.toString());
                    }
                    totalObjects = objectsList.data.length;
                    objectValues = objectsGroup.getValueFromControls();
                    objectValues = JSON.parse(objectValues).length ? objectValues : JSON.stringify(arrayIds);
                },
                functionFailure: function (xhr, response) {
                    PMDesigner.msgWinError(response.error.message);
                    PMDesigner.msgFlash('There are problems loading the process objects.'.translate(), document.body, 'error', 3000, 5);
                }
            });
            restClient.executeRestClient();
        };
        loadObjects();

        granularProcessExportForm = new PMUI.form.Form({
            id: "granularProcessExportForm",
            border: true,
            visibleHeader: false,
            width: '340px',
            name: "granularProcessExportForm",
            title: "",
            items: [
                {
                    id: "panelDetailsCustom",
                    pmType: "panel",
                    layout: 'vbox',
                    fieldset: false,
                    height: '380px',
                    legend: "DETAILS".translate(),
                    items: [
                        {
                            pmType: "checkbox",
                            id: "checkAllId",
                            labelVisible: false,
                            controlPositioning: 'vertical',
                            maxDirectionOptions: 2,
                            value: '',
                            options: [
                                {
                                    label: "Check All".translate(),
                                    disabled: false,
                                    value: '1',
                                    selected: false
                                }
                            ],
                            onChange: function (newVal, oldVal) {
                                if (newVal[2] === "1") {
                                    objectsGroup.setValueToControls(objectValues);
                                } else {
                                    objectsGroup.setValueToControls();
                                }
                            }
                        },
                        objectsGroup
                    ]
                }
            ]
        });

        domSettings = function () {
            $('#objectsGroup').find('label:eq(0)').remove();
        };

        granularProcessExportWindow.addItem(granularProcessExportForm);
        granularProcessExportWindow.open();
        granularProcessExportWindow.showFooter();
        domSettings();
    };
}());



(function(e){var a=/^\s*|\s*$/g,b,d="B".replace(/A(.)|B/,"$1")==="$1";var c={majorVersion:"3",minorVersion:"5.12",releaseDate:"2016-10-31",_init:function(){var s=this,q=document,o=navigator,g=o.userAgent,m,f,l,k,j,r;s.isIE11=g.indexOf("Trident/")!=-1&&(g.indexOf("rv:")!=-1||o.appName.indexOf("Netscape")!=-1);s.isOpera=e.opera&&opera.buildNumber;s.isWebKit=/WebKit/.test(g);s.isIE=!s.isWebKit&&!s.isOpera&&(/MSIE/gi).test(g)&&(/Explorer/gi).test(o.appName)||s.isIE11;s.isIE6=s.isIE&&/MSIE [56]/.test(g);s.isIE7=s.isIE&&/MSIE [7]/.test(g);s.isIE8=s.isIE&&/MSIE [8]/.test(g);s.isIE9=s.isIE&&/MSIE [9]/.test(g);s.isGecko=!s.isWebKit&&!s.isIE11&&/Gecko/.test(g);s.isMac=g.indexOf("Mac")!=-1;s.isAir=/adobeair/i.test(g);s.isIDevice=/(iPad|iPhone)/.test(g);s.isIOS5=s.isIDevice&&g.match(/AppleWebKit\/(\d*)/)[1]>=534;s.isIE12=(document.msElementsFromPoint&&!s.isIE&&!s.isIE11);if(s.isIE12){s.isIE11=true;s.isWebKit=false}if(e.tinyMCEPreInit){s.suffix=tinyMCEPreInit.suffix;s.baseURL=tinyMCEPreInit.base;s.query=tinyMCEPreInit.query;return}s.suffix="";f=q.getElementsByTagName("base");for(m=0;m<f.length;m++){r=f[m].href;if(r){if(/^https?:\/\/[^\/]+$/.test(r)){r+="/"}k=r?r.match(/.*\//)[0]:""}}function h(i){if(i.src&&/tiny_mce(|_gzip|_jquery|_prototype|_full)(_dev|_src)?.js/.test(i.src)){if(/_(src|dev)\.js/g.test(i.src)){s.suffix="_src"}if((j=i.src.indexOf("?"))!=-1){s.query=i.src.substring(j+1)}s.baseURL=i.src.substring(0,i.src.lastIndexOf("/"));if(k&&s.baseURL.indexOf("://")==-1&&s.baseURL.indexOf("/")!==0){s.baseURL=k+s.baseURL}return s.baseURL}return null}f=q.getElementsByTagName("script");for(m=0;m<f.length;m++){if(h(f[m])){return}}l=q.getElementsByTagName("head")[0];if(l){f=l.getElementsByTagName("script");for(m=0;m<f.length;m++){if(h(f[m])){return}}}return},is:function(g,f){if(!f){return g!==b}if(f=="array"&&c.isArray(g)){return true}return typeof(g)==f},isArray:Array.isArray||function(f){return Object.prototype.toString.call(f)==="[object Array]"},makeMap:function(f,j,h){var g;f=f||[];j=j||",";if(typeof(f)=="string"){f=f.split(j)}h=h||{};g=f.length;while(g--){h[f[g]]={}}return h},each:function(i,f,h){var j,g;if(!i){return 0}h=h||i;if(i.length!==b){for(j=0,g=i.length;j<g;j++){if(f.call(h,i[j],j,i)===false){return 0}}}else{for(j in i){if(i.hasOwnProperty(j)){if(f.call(h,i[j],j,i)===false){return 0}}}}return 1},map:function(g,h){var i=[];c.each(g,function(f){i.push(h(f))});return i},grep:function(g,h){var i=[];c.each(g,function(f){if(!h||h(f)){i.push(f)}});return i},inArray:function(g,h){var j,f;if(g){for(j=0,f=g.length;j<f;j++){if(g[j]===h){return j}}}return -1},extend:function(n,k){var j,f,h,g=arguments,m;for(j=1,f=g.length;j<f;j++){k=g[j];for(h in k){if(k.hasOwnProperty(h)){m=k[h];if(m!==b){n[h]=m}}}}return n},trim:function(f){return(f?""+f:"").replace(a,"")},create:function(o,f,j){var n=this,g,i,k,l,h,m=0;o=/^((static) )?([\w.]+)(:([\w.]+))?/.exec(o);k=o[3].match(/(^|\.)(\w+)$/i)[2];i=n.createNS(o[3].replace(/\.\w+$/,""),j);if(i[k]){return}if(o[2]=="static"){i[k]=f;if(this.onCreate){this.onCreate(o[2],o[3],i[k])}return}if(!f[k]){f[k]=function(){};m=1}i[k]=f[k];n.extend(i[k].prototype,f);if(o[5]){g=n.resolve(o[5]).prototype;l=o[5].match(/\.(\w+)$/i)[1];h=i[k];if(m){i[k]=function(){return g[l].apply(this,arguments)}}else{i[k]=function(){this.parent=g[l];return h.apply(this,arguments)}}i[k].prototype[k]=i[k];n.each(g,function(p,q){i[k].prototype[q]=g[q]});n.each(f,function(p,q){if(g[q]){i[k].prototype[q]=function(){this.parent=g[q];return p.apply(this,arguments)}}else{if(q!=k){i[k].prototype[q]=p}}})}n.each(f["static"],function(p,q){i[k][q]=p});if(this.onCreate){this.onCreate(o[2],o[3],i[k].prototype)}},walk:function(i,h,j,g){g=g||this;if(i){if(j){i=i[j]}c.each(i,function(k,f){if(h.call(g,k,f,j)===false){return false}c.walk(k,h,j,g)})}},createNS:function(j,h){var g,f;h=h||e;j=j.split(".");for(g=0;g<j.length;g++){f=j[g];if(!h[f]){h[f]={}}h=h[f]}return h},resolve:function(j,h){var g,f;h=h||e;j=j.split(".");for(g=0,f=j.length;g<f;g++){h=h[j[g]];if(!h){break}}return h},addUnload:function(j,i){var h=this,g;g=function(){var f=h.unloads,l,m;if(f){for(m in f){l=f[m];if(l&&l.func){l.func.call(l.scope,1)}}if(e.detachEvent){e.detachEvent("onbeforeunload",k);e.detachEvent("onunload",g)}else{if(e.removeEventListener){e.removeEventListener("unload",g,false)}}h.unloads=l=f=w=g=0;if(e.CollectGarbage){CollectGarbage()}}};function k(){var l=document;function f(){l.detachEvent("onstop",f);if(g){g()}l=0}if(l.readyState=="interactive"){if(l){l.attachEvent("onstop",f)}e.setTimeout(function(){if(l){l.detachEvent("onstop",f)}},0)}}j={func:j,scope:i||this};if(!h.unloads){if(e.attachEvent){e.attachEvent("onunload",g);e.attachEvent("onbeforeunload",k)}else{if(e.addEventListener){e.addEventListener("unload",g,false)}}h.unloads=[j]}else{h.unloads.push(j)}return j},removeUnload:function(i){var g=this.unloads,h=null;c.each(g,function(j,f){if(j&&j.func==i){g.splice(f,1);h=i;return false}});return h},explode:function(f,g){if(!f||c.is(f,"array")){return f}return c.map(f.split(g||","),c.trim)},_addVer:function(g){var f;if(!this.query){return g}f=(g.indexOf("?")==-1?"?":"&")+this.query;if(g.indexOf("#")==-1){return g+f}return g.replace("#",f+"#")},_replace:function(h,f,g){if(d){return g.replace(h,function(){var l=f,j=arguments,k;for(k=0;k<j.length-2;k++){if(j[k]===b){l=l.replace(new RegExp("\\$"+k,"g"),"")}else{l=l.replace(new RegExp("\\$"+k,"g"),j[k])}}return l})}return g.replace(h,f)}};c._init();e.tinymce=e.tinyMCE=c})(window);tinymce.create("tinymce.util.Dispatcher",{scope:null,listeners:null,inDispatch:false,Dispatcher:function(a){this.scope=a||this;this.listeners=[]},add:function(b,a){this.listeners.push({cb:b,scope:a||this.scope});return b},addToTop:function(d,b){var a=this,c={cb:d,scope:b||a.scope};if(a.inDispatch){a.listeners=[c].concat(a.listeners)}else{a.listeners.unshift(c)}return d},remove:function(c){var b=this.listeners,a=null;tinymce.each(b,function(e,d){if(c==e.cb){a=e;b.splice(d,1);return false}});return a},dispatch:function(){var a=this,e,b=arguments,c,d=a.listeners,f;a.inDispatch=true;for(c=0;c<d.length;c++){f=d[c];e=f.cb.apply(f.scope,b.length>0?b:[f.scope]);if(e===false){break}}a.inDispatch=false;return e}});(function(){var a=tinymce.each;tinymce.create("tinymce.util.URI",{URI:function(e,g){var f=this,i,d,c,h;e=tinymce.trim(e);g=f.settings=g||{};if(/^([\w\-]+):([^\/]{2})/i.test(e)||/^\s*#/.test(e)){f.source=e;return}if(e.indexOf("/")===0&&e.indexOf("//")!==0){e=(g.base_uri?g.base_uri.protocol||"http":"http")+"://mce_host"+e}if(!/^[\w\-]*:?\/\//.test(e)){h=g.base_uri?g.base_uri.path:new tinymce.util.URI(location.href).directory;e=((g.base_uri&&g.base_uri.protocol)||"http")+"://mce_host"+f.toAbsPath(h,e)}e=e.replace(/@@/g,"(mce_at)");e=/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(e);a(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],function(b,j){var k=e[j];if(k){k=k.replace(/\(mce_at\)/g,"@@")}f[b]=k});c=g.base_uri;if(c){if(!f.protocol){f.protocol=c.protocol}if(!f.userInfo){f.userInfo=c.userInfo}if(!f.port&&f.host==="mce_host"){f.port=c.port}if(!f.host||f.host==="mce_host"){f.host=c.host}f.source=""}},setPath:function(c){var b=this;c=/^(.*?)\/?(\w+)?$/.exec(c);b.path=c[0];b.directory=c[1];b.file=c[2];b.source="";b.getURI()},toRelative:function(b){var d=this,f;if(b==="./"){return b}b=new tinymce.util.URI(b,{base_uri:d});if((b.host!="mce_host"&&d.host!=b.host&&b.host)||d.port!=b.port||d.protocol!=b.protocol){return b.getURI()}var c=d.getURI(),e=b.getURI();if(c==e||(c.charAt(c.length-1)=="/"&&c.substr(0,c.length-1)==e)){return c}f=d.toRelPath(d.path,b.path);if(b.query){f+="?"+b.query}if(b.anchor){f+="#"+b.anchor}return f},toAbsolute:function(b,c){b=new tinymce.util.URI(b,{base_uri:this});return b.getURI(this.host==b.host&&this.protocol==b.protocol?c:0)},toRelPath:function(g,h){var c,f=0,d="",e,b;g=g.substring(0,g.lastIndexOf("/"));g=g.split("/");c=h.split("/");if(g.length>=c.length){for(e=0,b=g.length;e<b;e++){if(e>=c.length||g[e]!=c[e]){f=e+1;break}}}if(g.length<c.length){for(e=0,b=c.length;e<b;e++){if(e>=g.length||g[e]!=c[e]){f=e+1;break}}}if(f===1){return h}for(e=0,b=g.length-(f-1);e<b;e++){d+="../"}for(e=f-1,b=c.length;e<b;e++){if(e!=f-1){d+="/"+c[e]}else{d+=c[e]}}return d},toAbsPath:function(e,f){var c,b=0,h=[],d,g;d=/\/$/.test(f)?"/":"";e=e.split("/");f=f.split("/");a(e,function(i){if(i){h.push(i)}});e=h;for(c=f.length-1,h=[];c>=0;c--){if(f[c].length===0||f[c]==="."){continue}if(f[c]===".."){b++;continue}if(b>0){b--;continue}h.push(f[c])}c=e.length-b;if(c<=0){g=h.reverse().join("/")}else{g=e.slice(0,c).join("/")+"/"+h.reverse().join("/")}if(g.indexOf("/")!==0){g="/"+g}if(d&&g.lastIndexOf("/")!==g.length-1){g+=d}return g},getURI:function(d){var c,b=this;if(!b.source||d){c="";if(!d){if(b.protocol){c+=b.protocol+"://"}if(b.userInfo){c+=b.userInfo+"@"}if(b.host){c+=b.host}if(b.port){c+=":"+b.port}}if(b.path){c+=b.path}if(b.query){c+="?"+b.query}if(b.anchor){c+="#"+b.anchor}b.source=c}return b.source}})})();(function(){var a=tinymce.each;tinymce.create("static tinymce.util.Cookie",{getHash:function(d){var b=this.get(d),c;if(b){a(b.split("&"),function(e){e=e.split("=");c=c||{};c[unescape(e[0])]=unescape(e[1])})}return c},setHash:function(j,b,g,f,i,c){var h="";a(b,function(e,d){h+=(!h?"":"&")+escape(d)+"="+escape(e)});this.set(j,h,g,f,i,c)},get:function(i){var h=document.cookie,g,f=i+"=",d;if(!h){return}d=h.indexOf("; "+f);if(d==-1){d=h.indexOf(f);if(d!==0){return null}}else{d+=2}g=h.indexOf(";",d);if(g==-1){g=h.length}return unescape(h.substring(d+f.length,g))},set:function(i,b,g,f,h,c){document.cookie=i+"="+escape(b)+((g)?"; expires="+g.toGMTString():"")+((f)?"; path="+escape(f):"")+((h)?"; domain="+h:"")+((c)?"; secure":"")},remove:function(c,e,d){var b=new Date();b.setTime(b.getTime()-1000);this.set(c,"",b,e,d)}})})();(function(){function serialize(o,quote){var i,v,t,name;quote=quote||'"';if(o==null){return"null"}t=typeof o;if(t=="string"){v="\bb\tt\nn\ff\rr\"\"''\\\\";return quote+o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g,function(a,b){if(quote==='"'&&a==="'"){return a}i=v.indexOf(b);if(i+1){return"\\"+v.charAt(i+1)}a=b.charCodeAt().toString(16);return"\\u"+"0000".substring(a.length)+a})+quote}if(t=="object"){if(o.hasOwnProperty&&Object.prototype.toString.call(o)==="[object Array]"){for(i=0,v="[";i<o.length;i++){v+=(i>0?",":"")+serialize(o[i],quote)}return v+"]"}v="{";for(name in o){if(o.hasOwnProperty(name)){v+=typeof o[name]!="function"?(v.length>1?","+quote:quote)+name+quote+":"+serialize(o[name],quote):""}}return v+"}"}return""+o}tinymce.util.JSON={serialize:serialize,parse:function(s){try{return eval("("+s+")")}catch(ex){}}}})();tinymce.create("static tinymce.util.XHR",{send:function(g){var a,e,b=window,h=0;function f(){if(!g.async||a.readyState==4||h++>10000){if(g.success&&h<10000&&a.status==200){g.success.call(g.success_scope,""+a.responseText,a,g)}else{if(g.error){g.error.call(g.error_scope,h>10000?"TIMED_OUT":"GENERAL",a,g)}}a=null}else{b.setTimeout(f,10)}}g.scope=g.scope||this;g.success_scope=g.success_scope||g.scope;g.error_scope=g.error_scope||g.scope;g.async=g.async===false?false:true;g.data=g.data||"";function d(i){a=0;try{a=new ActiveXObject(i)}catch(c){}return a}a=b.XMLHttpRequest?new XMLHttpRequest():d("Microsoft.XMLHTTP")||d("Msxml2.XMLHTTP");if(a){if(a.overrideMimeType){a.overrideMimeType(g.content_type)}a.open(g.type||(g.data?"POST":"GET"),g.url,g.async);if(g.content_type){a.setRequestHeader("Content-Type",g.content_type)}a.setRequestHeader("X-Requested-With","XMLHttpRequest");a.send(g.data);if(!g.async){return f()}e=b.setTimeout(f,10)}}});(function(){var c=tinymce.extend,b=tinymce.util.JSON,a=tinymce.util.XHR;tinymce.create("tinymce.util.JSONRequest",{JSONRequest:function(d){this.settings=c({},d);this.count=0},send:function(f){var e=f.error,d=f.success;f=c(this.settings,f);f.success=function(h,g){h=b.parse(h);if(typeof(h)=="undefined"){h={error:"JSON Parse error."}}if(h.error){e.call(f.error_scope||f.scope,h.error,g)}else{d.call(f.success_scope||f.scope,h.result)}};f.error=function(h,g){if(e){e.call(f.error_scope||f.scope,h,g)}};f.data=b.serialize({id:f.id||"c"+(this.count++),method:f.method,params:f.params});f.content_type="application/json";a.send(f)},"static":{sendRPC:function(d){return new tinymce.util.JSONRequest().send(d)}}})}());(function(a){a.VK={BACKSPACE:8,DELETE:46,DOWN:40,ENTER:13,LEFT:37,RIGHT:39,SPACEBAR:32,TAB:9,UP:38,modifierPressed:function(b){return b.shiftKey||b.ctrlKey||b.altKey},metaKeyPressed:function(b){return a.isMac?b.metaKey:b.ctrlKey&&!b.altKey}}})(tinymce);tinymce.util.Quirks=function(a){var j=tinymce.VK,f=j.BACKSPACE,l=j.DELETE,e=a.dom,n=a.selection,J=a.settings,y=a.parser,q=a.serializer,G=tinymce.each;function C(P,O){try{a.getDoc().execCommand(P,false,O)}catch(N){}}function p(){var N=a.getDoc().documentMode;return N?N:6}function B(N){return N.isDefaultPrevented()}function L(){function N(T){var P,R,O,U,Q,S,V;function W(){if(Q.nodeType==3){if(T&&S==Q.length){return true}if(!T&&S===0){return true}}}P=n.getRng();var X=[P.startContainer,P.startOffset,P.endContainer,P.endOffset];if(!P.collapsed){T=true}Q=P[(T?"start":"end")+"Container"];S=P[(T?"start":"end")+"Offset"];if(Q.nodeType==3){R=e.getParent(P.startContainer,e.isBlock);if(T){R=e.getNext(R,e.isBlock)}if(R&&(W()||!P.collapsed)){O=e.create("em",{id:"__mceDel"});G(tinymce.grep(R.childNodes),function(Y){O.appendChild(Y)});R.appendChild(O)}}P=e.createRng();P.setStart(X[0],X[1]);P.setEnd(X[2],X[3]);n.setRng(P);a.getDoc().execCommand(T?"ForwardDelete":"Delete",false,null);if(O){U=n.getBookmark();while(V=e.get("__mceDel")){e.remove(V,true)}n.moveToBookmark(U)}}a.onKeyDown.add(function(O,Q){var P;P=Q.keyCode==l;if(!B(Q)&&(P||Q.keyCode==f)&&!j.modifierPressed(Q)){Q.preventDefault();N(P)}});a.addCommand("Delete",function(){N()})}function s(){function N(Q){var P=e.create("body");var R=Q.cloneContents();P.appendChild(R);return n.serializer.serialize(P,{format:"html"})}function O(P){var R=N(P);var S=e.createRng();S.selectNode(a.getBody());var Q=N(S);return R===Q}a.onKeyDown.add(function(Q,S){var R=S.keyCode,P;if(!B(S)&&(R==l||R==f)){P=Q.selection.isCollapsed();if(P&&!e.isEmpty(Q.getBody())){return}if(tinymce.isIE&&!P){return}if(!P&&!O(Q.selection.getRng())){return}Q.setContent("");Q.selection.setCursorLocation(Q.getBody(),0);Q.nodeChanged()}})}function K(){a.onKeyDown.add(function(N,O){if(!B(O)&&O.keyCode==65&&j.metaKeyPressed(O)){O.preventDefault();N.execCommand("SelectAll")}})}function M(){if(!a.settings.content_editable){e.bind(a.getDoc(),"focusin",function(N){n.setRng(n.getRng())});e.bind(a.getDoc(),"mousedown",function(N){if(N.target==a.getDoc().documentElement){a.getWin().focus();n.setRng(n.getRng())}})}}function D(){a.onKeyDown.add(function(N,Q){if(!B(Q)&&Q.keyCode===f){if(n.isCollapsed()&&n.getRng(true).startOffset===0){var P=n.getNode();var O=P.previousSibling;if(O&&O.nodeName&&O.nodeName.toLowerCase()==="hr"){e.remove(O);tinymce.dom.Event.cancel(Q)}}}})}function A(){if(!Range.prototype.getClientRects){a.onMouseDown.add(function(O,P){if(!B(P)&&P.target.nodeName==="HTML"){var N=O.getBody();N.blur();setTimeout(function(){N.focus()},0)}})}}function h(){a.onClick.add(function(N,O){O=O.target;if(/^(IMG|HR)$/.test(O.nodeName)){n.getSel().setBaseAndExtent(O,0,O,1)}if(O.nodeName=="A"&&e.hasClass(O,"mceItemAnchor")){n.select(O)}N.nodeChanged()})}function c(){function O(){var Q=e.getAttribs(n.getStart().cloneNode(false));return function(){var R=n.getStart();if(R!==a.getBody()){e.setAttrib(R,"style",null);G(Q,function(S){R.setAttributeNode(S.cloneNode(true))})}}}function N(){return !n.isCollapsed()&&e.getParent(n.getStart(),e.isBlock)!=e.getParent(n.getEnd(),e.isBlock)}function P(Q,R){R.preventDefault();return false}a.onKeyPress.add(function(Q,S){var R;if(!B(S)&&(S.keyCode==8||S.keyCode==46)&&N()){R=O();Q.getDoc().execCommand("delete",false,null);R();S.preventDefault();return false}});e.bind(a.getDoc(),"cut",function(R){var Q;if(!B(R)&&N()){Q=O();a.onKeyUp.addToTop(P);setTimeout(function(){Q();a.onKeyUp.remove(P)},0)}})}function b(){var O,N;e.bind(a.getDoc(),"selectionchange",function(){if(N){clearTimeout(N);N=0}N=window.setTimeout(function(){var P=n.getRng();if(!O||!tinymce.dom.RangeUtils.compareRanges(P,O)){a.nodeChanged();O=P}},50)})}function z(){document.body.setAttribute("role","application")}function v(){a.onKeyDown.add(function(N,P){if(!B(P)&&P.keyCode===f){if(n.isCollapsed()&&n.getRng(true).startOffset===0){var O=n.getNode().previousSibling;if(O&&O.nodeName&&O.nodeName.toLowerCase()==="table"){return tinymce.dom.Event.cancel(P)}}}})}function E(){if(p()>7){return}C("RespectVisibilityInDesign",true);a.contentStyles.push(".mceHideBrInPre pre br {display: none}");e.addClass(a.getBody(),"mceHideBrInPre");y.addNodeFilter("pre",function(N,P){var Q=N.length,S,O,T,R;while(Q--){S=N[Q].getAll("br");O=S.length;while(O--){T=S[O];R=T.prev;if(R&&R.type===3&&R.value.charAt(R.value-1)!="\n"){R.value+="\n"}else{T.parent.insert(new tinymce.html.Node("#text",3),T,true).value="\n"}}}});q.addNodeFilter("pre",function(N,P){var Q=N.length,S,O,T,R;while(Q--){S=N[Q].getAll("br");O=S.length;while(O--){T=S[O];R=T.prev;if(R&&R.type==3){R.value=R.value.replace(/\r?\n$/,"")}}}})}function g(){e.bind(a.getBody(),"mouseup",function(P){var O,N=n.getNode();if(N.nodeName=="IMG"){if(O=e.getStyle(N,"width")){e.setAttrib(N,"width",O.replace(/[^0-9%]+/g,""));e.setStyle(N,"width","")}if(O=e.getStyle(N,"height")){e.setAttrib(N,"height",O.replace(/[^0-9%]+/g,""));e.setStyle(N,"height","")}}})}function d(){a.onKeyDown.add(function(T,U){var S,N,O,Q,R,V,P;S=U.keyCode==l;if(!B(U)&&(S||U.keyCode==f)&&!j.modifierPressed(U)){N=n.getRng();O=N.startContainer;Q=N.startOffset;P=N.collapsed;if(O.nodeType==3&&O.nodeValue.length>0&&((Q===0&&!P)||(P&&Q===(S?0:1)))){V=O.previousSibling;if(V&&V.nodeName=="IMG"){return}nonEmptyElements=T.schema.getNonEmptyElements();U.preventDefault();R=e.create("br",{id:"__tmp"});O.parentNode.insertBefore(R,O);T.getDoc().execCommand(S?"ForwardDelete":"Delete",false,null);O=n.getRng().startContainer;V=O.previousSibling;if(V&&V.nodeType==1&&!e.isBlock(V)&&e.isEmpty(V)&&!nonEmptyElements[V.nodeName.toLowerCase()]){e.remove(V)}e.remove("__tmp")}}})}function I(){a.onKeyDown.add(function(R,S){var P,O,T,N,Q;if(B(S)||S.keyCode!=j.BACKSPACE){return}P=n.getRng();O=P.startContainer;T=P.startOffset;N=e.getRoot();Q=O;if(!P.collapsed||T!==0){return}while(Q&&Q.parentNode&&Q.parentNode.firstChild==Q&&Q.parentNode!=N){Q=Q.parentNode}if(Q.tagName==="BLOCKQUOTE"){R.formatter.toggle("blockquote",null,Q);P=e.createRng();P.setStart(O,0);P.setEnd(O,0);n.setRng(P)}})}function H(){function N(){a._refreshContentEditable();C("StyleWithCSS",false);C("enableInlineTableEditing",false);if(!J.object_resizing){C("enableObjectResizing",false)}}if(!J.readonly){a.onBeforeExecCommand.add(N);a.onMouseDown.add(N)}}function u(){function N(O,P){G(e.select("a"),function(S){var Q=S.parentNode,R=e.getRoot();if(Q.lastChild===S){while(Q&&!e.isBlock(Q)){if(Q.parentNode.lastChild!==Q||Q===R){return}Q=Q.parentNode}e.add(Q,"br",{"data-mce-bogus":1})}})}a.onExecCommand.add(function(O,P){if(P==="CreateLink"){N(O)}});a.onSetContent.add(n.onSetContent.add(N))}function o(){if(J.forced_root_block){a.onInit.add(function(){C("DefaultParagraphSeparator",J.forced_root_block)})}}function r(){function N(P,O){if(!P||!O.initial){a.execCommand("mceRepaint")}}a.onUndo.add(N);a.onRedo.add(N);a.onSetContent.add(N)}function i(){a.onKeyDown.add(function(O,P){var N;if(!B(P)&&P.keyCode==f){N=O.getDoc().selection.createRange();if(N&&N.item){P.preventDefault();O.undoManager.beforeChange();e.remove(N.item(0));O.undoManager.add()}}})}function t(){var N;if(p()>=10){N="";G("p div h1 h2 h3 h4 h5 h6".split(" "),function(O,P){N+=(P>0?",":"")+O+":empty"});a.contentStyles.push(N+"{padding-right: 1px !important}")}}function x(){var P,O,af,N,aa,ad,ab,ae,Q,R,ac,Y,X,Z=document,V=a.getDoc();if(!J.object_resizing||J.webkit_fake_resize===false){return}C("enableObjectResizing",false);ac={n:[0.5,0,0,-1],e:[1,0.5,1,0],s:[0.5,1,0,1],w:[0,0.5,-1,0],nw:[0,0,-1,-1],ne:[1,0,1,-1],se:[1,1,1,1],sw:[0,1,-1,1]};function T(aj){var ai,ah;ai=aj.screenX-ad;ah=aj.screenY-ab;Y=ai*aa[2]+ae;X=ah*aa[3]+Q;Y=Y<5?5:Y;X=X<5?5:X;if(j.modifierPressed(aj)||(af.nodeName=="IMG"&&aa[2]*aa[3]!==0)){Y=Math.round(X/R);X=Math.round(Y*R)}e.setStyles(N,{width:Y,height:X});if(aa[2]<0&&N.clientWidth<=Y){e.setStyle(N,"left",P+(ae-Y))}if(aa[3]<0&&N.clientHeight<=X){e.setStyle(N,"top",O+(Q-X))}}function ag(){function ah(ai,aj){if(aj){if(af.style[ai]||!a.schema.isValid(af.nodeName.toLowerCase(),ai)){e.setStyle(af,ai,aj)}else{e.setAttrib(af,ai,aj)}}}ah("width",Y);ah("height",X);e.unbind(V,"mousemove",T);e.unbind(V,"mouseup",ag);if(Z!=V){e.unbind(Z,"mousemove",T);e.unbind(Z,"mouseup",ag)}e.remove(N);S(af)}function S(ak){var ai,aj,ah;U();ai=e.getPos(ak);P=ai.x;O=ai.y;aj=ak.offsetWidth;ah=ak.offsetHeight;if(af!=ak){af=ak;Y=X=0}G(ac,function(an,al){var am;am=e.get("mceResizeHandle"+al);if(!am){am=e.add(V.documentElement,"div",{id:"mceResizeHandle"+al,"class":"mceResizeHandle",style:"cursor:"+al+"-resize; margin:0; padding:0"});e.bind(am,"mousedown",function(ao){ao.preventDefault();ag();ad=ao.screenX;ab=ao.screenY;ae=af.clientWidth;Q=af.clientHeight;R=Q/ae;aa=an;N=af.cloneNode(true);e.addClass(N,"mceClonedResizable");e.setStyles(N,{left:P,top:O,margin:0});V.documentElement.appendChild(N);e.bind(V,"mousemove",T);e.bind(V,"mouseup",ag);if(Z!=V){e.bind(Z,"mousemove",T);e.bind(Z,"mouseup",ag)}})}else{e.show(am)}e.setStyles(am,{left:(aj*an[0]+P)-(am.offsetWidth/2),top:(ah*an[1]+O)-(am.offsetHeight/2)})});if(!tinymce.isOpera&&af.nodeName=="IMG"){af.setAttribute("data-mce-selected","1")}}function U(){if(af){af.removeAttribute("data-mce-selected")}for(var ah in ac){e.hide("mceResizeHandle"+ah)}}a.contentStyles.push(".mceResizeHandle {position: absolute;border: 1px solid black;background: #FFF;width: 5px;height: 5px;z-index: 10000}.mceResizeHandle:hover {background: #000}img[data-mce-selected] {outline: 1px solid black}img.mceClonedResizable, table.mceClonedResizable {position: absolute;outline: 1px dashed black;opacity: .5;z-index: 10000}");function W(){var ah=e.getParent(n.getNode(),"table,img");G(e.select("img[data-mce-selected]"),function(ai){ai.removeAttribute("data-mce-selected")});if(ah){S(ah)}else{U()}}a.onNodeChange.add(W);e.bind(V,"selectionchange",W);a.serializer.addAttributeFilter("data-mce-selected",function(ah,ai){var aj=ah.length;while(aj--){ah[aj].attr(ai,null)}})}function F(){if(p()<9){y.addNodeFilter("noscript",function(N){var O=N.length,P,Q;while(O--){P=N[O];Q=P.firstChild;if(Q){P.attr("data-mce-innertext",Q.value)}}});q.addNodeFilter("noscript",function(N){var O=N.length,P,R,Q;while(O--){P=N[O];R=N[O].firstChild;if(R){R.value=tinymce.html.Entities.decode(R.value)}else{Q=P.attributes.map["data-mce-innertext"];if(Q){P.attr("data-mce-innertext",null);R=new tinymce.html.Node("#text",3);R.value=Q;R.raw=true;P.append(R)}}}})}}function m(){a.contentStyles.push("body {min-height: 100px}");a.onClick.add(function(N,O){if(O.target.nodeName=="HTML"){a.execCommand("SelectAll");a.selection.collapse(true);a.nodeChanged()}})}function k(){a.onInit.add(function(){var N;a.getBody().addEventListener("mscontrolselect",function(O){setTimeout(function(){if(a.selection.getNode()!=O.target){N=a.selection.getRng();n.fakeRng=a.dom.createRng();n.fakeRng.setStartBefore(O.target);n.fakeRng.setEndAfter(O.target)}},0)},false);a.getDoc().addEventListener("selectionchange",function(O){if(N&&!tinymce.dom.RangeUtils.compareRanges(a.selection.getRng(),N)){n.fakeRng=N=null}},false)})}v();I();s();if(tinymce.isWebKit){d();L();M();h();o();if(tinymce.isIDevice){b()}else{x();K()}}if(tinymce.isIE&&!tinymce.isIE11){D();z();E();g();i();t();F()}if(tinymce.isIE11){m();k()}if(tinymce.isGecko&&!tinymce.isIE11){D();A();c();H();u();r()}if(tinymce.isOpera){x()}};(function(j){var a,g,d,k=/[&<>\"\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,b=/[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,f=/[<>&\"\']/g,c=/&(#x|#)?([\w]+);/g,i={128:"\u20AC",130:"\u201A",131:"\u0192",132:"\u201E",133:"\u2026",134:"\u2020",135:"\u2021",136:"\u02C6",137:"\u2030",138:"\u0160",139:"\u2039",140:"\u0152",142:"\u017D",145:"\u2018",146:"\u2019",147:"\u201C",148:"\u201D",149:"\u2022",150:"\u2013",151:"\u2014",152:"\u02DC",153:"\u2122",154:"\u0161",155:"\u203A",156:"\u0153",158:"\u017E",159:"\u0178"};g={'"':"&quot;","'":"&#39;","<":"&lt;",">":"&gt;","&":"&amp;"};d={"&lt;":"<","&gt;":">","&amp;":"&","&quot;":'"',"&apos;":"'"};function h(l){var m;m=document.createElement("div");m.innerHTML=l;return m.textContent||m.innerText||l}function e(m,p){var n,o,l,q={};if(m){m=m.split(",");p=p||10;for(n=0;n<m.length;n+=2){o=String.fromCharCode(parseInt(m[n],p));if(!g[o]){l="&"+m[n+1]+";";q[o]=l;q[l]=o}}return q}}a=e("50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,t9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro",32);j.html=j.html||{};j.html.Entities={encodeRaw:function(m,l){return m.replace(l?k:b,function(n){return g[n]||n})},encodeAllRaw:function(l){return(""+l).replace(f,function(m){return g[m]||m})},encodeNumeric:function(m,l){return m.replace(l?k:b,function(n){if(n.length>1){return"&#"+(((n.charCodeAt(0)-55296)*1024)+(n.charCodeAt(1)-56320)+65536)+";"}return g[n]||"&#"+n.charCodeAt(0)+";"})},encodeNamed:function(n,l,m){m=m||a;return n.replace(l?k:b,function(o){return g[o]||m[o]||o})},getEncodeFunc:function(l,o){var p=j.html.Entities;o=e(o)||a;function m(r,q){return r.replace(q?k:b,function(s){return g[s]||o[s]||"&#"+s.charCodeAt(0)+";"||s})}function n(r,q){return p.encodeNamed(r,q,o)}l=j.makeMap(l.replace(/\+/g,","));if(l.named&&l.numeric){return m}if(l.named){if(o){return n}return p.encodeNamed}if(l.numeric){return p.encodeNumeric}return p.encodeRaw},decode:function(l){return l.replace(c,function(n,m,o){if(m){o=parseInt(o,m.length===2?16:10);if(o>65535){o-=65536;return String.fromCharCode(55296+(o>>10),56320+(o&1023))}else{return i[o]||String.fromCharCode(o)}}return d[n]||a[n]||h(n)})}}})(tinymce);tinymce.html.Styles=function(d,f){var k=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,h=/(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,b=/\s*([^:]+):\s*([^;]+);?/g,l=/\s+$/,m=/rgb/,e,g,a={},j;d=d||{};j="\\\" \\' \\; \\: ; : \uFEFF".split(" ");for(g=0;g<j.length;g++){a[j[g]]="\uFEFF"+g;a["\uFEFF"+g]=j[g]}function c(n,q,p,i){function o(r){r=parseInt(r).toString(16);return r.length>1?r:"0"+r}return"#"+o(q)+o(p)+o(i)}return{toHex:function(i){return i.replace(k,c)},parse:function(s){var z={},q,n,x,r,v=d.url_converter,y=d.url_converter_scope||this;function p(D,G){var F,C,B,E;if(z["border-image"]==="none"){delete z["border-image"]}F=z[D+"-top"+G];if(!F){return}C=z[D+"-right"+G];if(F!=C){return}B=z[D+"-bottom"+G];if(C!=B){return}E=z[D+"-left"+G];if(B!=E){return}z[D+G]=E;delete z[D+"-top"+G];delete z[D+"-right"+G];delete z[D+"-bottom"+G];delete z[D+"-left"+G]}function u(C){var D=z[C],B;if(!D||D.indexOf(" ")<0){return}D=D.split(" ");B=D.length;while(B--){if(D[B]!==D[0]){return false}}z[C]=D[0];return true}function A(D,C,B,E){if(!u(C)){return}if(!u(B)){return}if(!u(E)){return}z[D]=z[C]+" "+z[B]+" "+z[E];delete z[C];delete z[B];delete z[E]}function t(B){r=true;return a[B]}function i(C,B){if(r){C=C.replace(/\uFEFF[0-9]/g,function(D){return a[D]})}if(!B){C=C.replace(/\\([\'\";:])/g,"$1")}return C}function o(C,B,F,E,G,D){G=G||D;if(G){G=i(G);return"'"+G.replace(/\'/g,"\\'")+"'"}B=i(B||F||E);if(v){B=v.call(y,B,"style")}return"url('"+B.replace(/\'/g,"\\'")+"')"}if(s){s=s.replace(/\\[\"\';:\uFEFF]/g,t).replace(/\"[^\"]+\"|\'[^\']+\'/g,function(B){return B.replace(/[;:]/g,t)});while(q=b.exec(s)){n=q[1].replace(l,"").toLowerCase();x=q[2].replace(l,"");if(n&&x.length>0){if(n==="font-weight"&&x==="700"){x="bold"}else{if(n==="color"||n==="background-color"){x=x.toLowerCase()}}x=x.replace(k,c);x=x.replace(h,o);z[n]=r?i(x,true):x}b.lastIndex=q.index+q[0].length}p("border","");p("border","-width");p("border","-color");p("border","-style");p("padding","");p("margin","");A("border","border-width","border-style","border-color");if(z.border==="medium none"){delete z.border}}return z},serialize:function(p,r){var o="",n,q;function i(t){var x,u,s,v;x=f.styles[t];if(x){for(u=0,s=x.length;u<s;u++){t=x[u];v=p[t];if(v!==e&&v.length>0){o+=(o.length>0?" ":"")+t+": "+v+";"}}}}if(r&&f&&f.styles){i("*");i(r)}else{for(n in p){q=p[n];if(q!==e&&q.length>0){o+=(o.length>0?" ":"")+n+": "+q+";"}}}return o}}};(function(f){var a={},e=f.makeMap,g=f.each;function d(j,i){return j.split(i||",")}function h(m,l){var j,k={};function i(n){return n.replace(/[A-Z]+/g,function(o){return i(m[o])})}for(j in m){if(m.hasOwnProperty(j)){m[j]=i(m[j])}}i(l).replace(/#/g,"#text").replace(/(\w+)\[([^\]]+)\]\[([^\]]*)\]/g,function(q,o,n,p){n=d(n,"|");k[o]={attributes:e(n),attributesOrder:n,children:e(p,"|",{"#comment":{}})}});return k}function b(){var i=a.html5;if(!i){i=a.html5=h({A:"id|accesskey|class|dir|draggable|item|hidden|itemprop|role|spellcheck|style|subject|title|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup",B:"#|a|abbr|area|audio|b|bdo|br|button|canvas|cite|code|command|datalist|del|dfn|em|embed|i|iframe|img|input|ins|kbd|keygen|label|link|map|mark|meta|meter|noscript|object|output|progress|q|ruby|samp|script|select|small|span|strong|sub|sup|svg|textarea|time|var|video|wbr",C:"#|a|abbr|area|address|article|aside|audio|b|bdo|blockquote|br|button|canvas|cite|code|command|datalist|del|details|dfn|dialog|div|dl|em|embed|fieldset|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hgroup|hr|i|iframe|img|input|ins|kbd|keygen|label|link|map|mark|menu|meta|meter|nav|noscript|ol|object|output|p|pre|progress|q|ruby|samp|script|section|select|small|span|strong|style|sub|sup|svg|table|textarea|time|ul|var|video"},"html[A|manifest][body|head]head[A][base|command|link|meta|noscript|script|style|title]title[A][#]base[A|href|target][]link[A|href|rel|media|type|sizes][]meta[A|http-equiv|name|content|charset][]style[A|type|media|scoped][#]script[A|charset|type|src|defer|async][#]noscript[A][C]body[A][C]section[A][C]nav[A][C]article[A][C]aside[A][C]h1[A][B]h2[A][B]h3[A][B]h4[A][B]h5[A][B]h6[A][B]hgroup[A][h1|h2|h3|h4|h5|h6]header[A][C]footer[A][C]address[A][C]p[A][B]br[A][]pre[A][B]dialog[A][dd|dt]blockquote[A|cite][C]ol[A|start|reversed][li]ul[A][li]li[A|value][C]dl[A][dd|dt]dt[A][B]dd[A][C]a[A|href|target|ping|rel|media|type][B]em[A][B]strong[A][B]small[A][B]cite[A][B]q[A|cite][B]dfn[A][B]abbr[A][B]code[A][B]var[A][B]samp[A][B]kbd[A][B]sub[A][B]sup[A][B]i[A][B]b[A][B]mark[A][B]progress[A|value|max][B]meter[A|value|min|max|low|high|optimum][B]time[A|datetime][B]ruby[A][B|rt|rp]rt[A][B]rp[A][B]bdo[A][B]span[A][B]ins[A|cite|datetime][B]del[A|cite|datetime][B]figure[A][C|legend|figcaption]figcaption[A][C]img[A|alt|src|height|width|usemap|ismap][]iframe[A|name|src|height|width|sandbox|seamless][]embed[A|src|height|width|type][]object[A|data|type|height|width|usemap|name|form|classid][param]param[A|name|value][]details[A|open][C|legend]command[A|type|label|icon|disabled|checked|radiogroup][]menu[A|type|label][C|li]legend[A][C|B]div[A][C]source[A|src|type|media][]audio[A|src|autobuffer|autoplay|loop|controls][source]video[A|src|autobuffer|autoplay|loop|controls|width|height|poster][source]hr[A][]form[A|accept-charset|action|autocomplete|enctype|method|name|novalidate|target][C]fieldset[A|disabled|form|name][C|legend]label[A|form|for][B]input[A|type|accept|alt|autocomplete|autofocus|checked|disabled|form|formaction|formenctype|formmethod|formnovalidate|formtarget|height|list|max|maxlength|min|multiple|pattern|placeholder|readonly|required|size|src|step|width|files|value|name][]button[A|autofocus|disabled|form|formaction|formenctype|formmethod|formnovalidate|formtarget|name|value|type][B]select[A|autofocus|disabled|form|multiple|name|size][option|optgroup]datalist[A][B|option]optgroup[A|disabled|label][option]option[A|disabled|selected|label|value][]textarea[A|autofocus|disabled|form|maxlength|name|placeholder|readonly|required|rows|cols|wrap][]keygen[A|autofocus|challenge|disabled|form|keytype|name][]output[A|for|form|name][B]canvas[A|width|height][]map[A|name][B|C]area[A|shape|coords|href|alt|target|media|rel|ping|type][]mathml[A][]svg[A][]table[A|border][caption|colgroup|thead|tfoot|tbody|tr]caption[A][C]colgroup[A|span][col]col[A|span][]thead[A][tr]tfoot[A][tr]tbody[A][tr]tr[A][th|td]th[A|headers|rowspan|colspan|scope][B]td[A|headers|rowspan|colspan][C]wbr[A][]")}return i}function c(){var i=a.html4;if(!i){i=a.html4=h({Z:"H|K|N|O|P",Y:"X|form|R|Q",ZG:"E|span|width|align|char|charoff|valign",X:"p|T|div|U|W|isindex|fieldset|table",ZF:"E|align|char|charoff|valign",W:"pre|hr|blockquote|address|center|noframes",ZE:"abbr|axis|headers|scope|rowspan|colspan|align|char|charoff|valign|nowrap|bgcolor|width|height",ZD:"[E][S]",U:"ul|ol|dl|menu|dir",ZC:"p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q",T:"h1|h2|h3|h4|h5|h6",ZB:"X|S|Q",S:"R|P",ZA:"a|G|J|M|O|P",R:"a|H|K|N|O",Q:"noscript|P",P:"ins|del|script",O:"input|select|textarea|label|button",N:"M|L",M:"em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym",L:"sub|sup",K:"J|I",J:"tt|i|b|u|s|strike",I:"big|small|font|basefont",H:"G|F",G:"br|span|bdo",F:"object|applet|img|map|iframe",E:"A|B|C",D:"accesskey|tabindex|onfocus|onblur",C:"onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup",B:"lang|xml:lang|dir",A:"id|class|style|title"},"script[id|charset|type|language|src|defer|xml:space][]style[B|id|type|media|title|xml:space][]object[E|declare|classid|codebase|data|type|codetype|archive|standby|width|height|usemap|name|tabindex|align|border|hspace|vspace][#|param|Y]param[id|name|value|valuetype|type][]p[E|align][#|S]a[E|D|charset|type|name|href|hreflang|rel|rev|shape|coords|target][#|Z]br[A|clear][]span[E][#|S]bdo[A|C|B][#|S]applet[A|codebase|archive|code|object|alt|name|width|height|align|hspace|vspace][#|param|Y]h1[E|align][#|S]img[E|src|alt|name|longdesc|width|height|usemap|ismap|align|border|hspace|vspace][]map[B|C|A|name][X|form|Q|area]h2[E|align][#|S]iframe[A|longdesc|name|src|frameborder|marginwidth|marginheight|scrolling|align|width|height][#|Y]h3[E|align][#|S]tt[E][#|S]i[E][#|S]b[E][#|S]u[E][#|S]s[E][#|S]strike[E][#|S]big[E][#|S]small[E][#|S]font[A|B|size|color|face][#|S]basefont[id|size|color|face][]em[E][#|S]strong[E][#|S]dfn[E][#|S]code[E][#|S]q[E|cite][#|S]samp[E][#|S]kbd[E][#|S]var[E][#|S]cite[E][#|S]abbr[E][#|S]acronym[E][#|S]sub[E][#|S]sup[E][#|S]input[E|D|type|name|value|checked|disabled|readonly|size|maxlength|src|alt|usemap|onselect|onchange|accept|align][]select[E|name|size|multiple|disabled|tabindex|onfocus|onblur|onchange][optgroup|option]optgroup[E|disabled|label][option]option[E|selected|disabled|label|value][]textarea[E|D|name|rows|cols|disabled|readonly|onselect|onchange][]label[E|for|accesskey|onfocus|onblur][#|S]button[E|D|name|value|type|disabled][#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]h4[E|align][#|S]ins[E|cite|datetime][#|Y]h5[E|align][#|S]del[E|cite|datetime][#|Y]h6[E|align][#|S]div[E|align][#|Y]ul[E|type|compact][li]li[E|type|value][#|Y]ol[E|type|compact|start][li]dl[E|compact][dt|dd]dt[E][#|S]dd[E][#|Y]menu[E|compact][li]dir[E|compact][li]pre[E|width|xml:space][#|ZA]hr[E|align|noshade|size|width][]blockquote[E|cite][#|Y]address[E][#|S|p]center[E][#|Y]noframes[E][#|Y]isindex[A|B|prompt][]fieldset[E][#|legend|Y]legend[E|accesskey|align][#|S]table[E|summary|width|border|frame|rules|cellspacing|cellpadding|align|bgcolor][caption|col|colgroup|thead|tfoot|tbody|tr]caption[E|align][#|S]col[ZG][]colgroup[ZG][col]thead[ZF][tr]tr[ZF|bgcolor][th|td]th[E|ZE][#|Y]form[E|action|method|name|enctype|onsubmit|onreset|accept|accept-charset|target][#|X|R|Q]noscript[E][#|Y]td[E|ZE][#|Y]tfoot[ZF][tr]tbody[ZF][tr]area[E|D|shape|coords|href|nohref|alt|target][]base[id|href|target][]body[E|onload|onunload|background|bgcolor|text|link|vlink|alink][#|Y]")}return i}f.html.Schema=function(A){var u=this,s={},k={},j=[],D,y;var o,q,z,r,v,n,p={};function m(F,E,H){var G=A[F];if(!G){G=a[F];if(!G){G=e(E," ",e(E.toUpperCase()," "));G=f.extend(G,H);a[F]=G}}else{G=e(G,",",e(G.toUpperCase()," "))}return G}A=A||{};y=A.schema=="html5"?b():c();if(A.verify_html===false){A.valid_elements="*[*]"}if(A.valid_styles){D={};g(A.valid_styles,function(F,E){D[E]=f.explode(F)})}o=m("whitespace_elements","pre script noscript style textarea");q=m("self_closing_elements","colgroup dd dt li option p td tfoot th thead tr");z=m("short_ended_elements","area base basefont br col frame hr img input isindex link meta param embed source wbr");r=m("boolean_attributes","checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected autoplay loop controls");n=m("non_empty_elements","td th iframe video audio object script",z);textBlockElementsMap=m("text_block_elements","h1 h2 h3 h4 h5 h6 p div address pre form blockquote center dir fieldset header footer article section hgroup aside nav figure");v=m("block_elements","hr table tbody thead tfoot th tr td li ol ul caption dl dt dd noscript menu isindex samp option datalist select optgroup",textBlockElementsMap);function i(E){return new RegExp("^"+E.replace(/([?+*])/g,".$1")+"$")}function C(L){var K,G,Z,V,aa,F,I,U,X,Q,Y,ac,O,J,W,E,S,H,ab,ad,P,T,N=/^([#+\-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,R=/^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,M=/[*?+]/;if(L){L=d(L);if(s["@"]){S=s["@"].attributes;H=s["@"].attributesOrder}for(K=0,G=L.length;K<G;K++){F=N.exec(L[K]);if(F){W=F[1];Q=F[2];E=F[3];X=F[4];O={};J=[];I={attributes:O,attributesOrder:J};if(W==="#"){I.paddEmpty=true}if(W==="-"){I.removeEmpty=true}if(S){for(ad in S){O[ad]=S[ad]}J.push.apply(J,H)}if(X){X=d(X,"|");for(Z=0,V=X.length;Z<V;Z++){F=R.exec(X[Z]);if(F){U={};ac=F[1];Y=F[2].replace(/::/g,":");W=F[3];T=F[4];if(ac==="!"){I.attributesRequired=I.attributesRequired||[];I.attributesRequired.push(Y);U.required=true}if(ac==="-"){delete O[Y];J.splice(f.inArray(J,Y),1);continue}if(W){if(W==="="){I.attributesDefault=I.attributesDefault||[];I.attributesDefault.push({name:Y,value:T});U.defaultValue=T}if(W===":"){I.attributesForced=I.attributesForced||[];I.attributesForced.push({name:Y,value:T});U.forcedValue=T}if(W==="<"){U.validValues=e(T,"?")}}if(M.test(Y)){I.attributePatterns=I.attributePatterns||[];U.pattern=i(Y);I.attributePatterns.push(U)}else{if(!O[Y]){J.push(Y)}O[Y]=U}}}}if(!S&&Q=="@"){S=O;H=J}if(E){I.outputName=Q;s[E]=I}if(M.test(Q)){I.pattern=i(Q);j.push(I)}else{s[Q]=I}}}}}function t(E){s={};j=[];C(E);g(y,function(G,F){k[F]=G.children})}function l(F){var E=/^(~)?(.+)$/;if(F){g(d(F),function(J){var H=E.exec(J),I=H[1]==="~",K=I?"span":"div",G=H[2];k[G]=k[K];p[G]=K;if(!I){v[G.toUpperCase()]={};v[G]={}}if(!s[G]){s[G]=s[K]}g(k,function(L,M){if(L[K]){L[G]=L[K]}})})}}function x(F){var E=/^([+\-]?)(\w+)\[([^\]]+)\]$/;if(F){g(d(F),function(J){var I=E.exec(J),G,H;if(I){H=I[1];if(H){G=k[I[2]]}else{G=k[I[2]]={"#comment":{}}}G=k[I[2]];g(d(I[3],"|"),function(K){if(H==="-"){delete G[K]}else{G[K]={}}})}})}}function B(E){var G=s[E],F;if(G){return G}F=j.length;while(F--){G=j[F];if(G.pattern.test(E)){return G}}}if(!A.valid_elements){g(y,function(F,E){s[E]={attributes:F.attributes,attributesOrder:F.attributesOrder};k[E]=F.children});if(A.schema!="html5"){g(d("strong/b,em/i"),function(E){E=d(E,"/");s[E[1]].outputName=E[0]})}s.img.attributesDefault=[{name:"alt",value:""}];g(d("ol,ul,sub,sup,blockquote,span,font,a,table,tbody,tr,strong,em,b,i"),function(E){if(s[E]){s[E].removeEmpty=true}});g(d("p,h1,h2,h3,h4,h5,h6,th,td,pre,div,address,caption"),function(E){s[E].paddEmpty=true})}else{t(A.valid_elements)}l(A.custom_elements);x(A.valid_children);C(A.extended_valid_elements);x("+ol[ul|ol],+ul[ul|ol]");if(A.invalid_elements){f.each(f.explode(A.invalid_elements),function(E){if(s[E]){delete s[E]}})}if(!B("span")){C("span[!data-mce-type|*]")}u.children=k;u.styles=D;u.getBoolAttrs=function(){return r};u.getBlockElements=function(){return v};u.getTextBlockElements=function(){return textBlockElementsMap};u.getShortEndedElements=function(){return z};u.getSelfClosingElements=function(){return q};u.getNonEmptyElements=function(){return n};u.getWhiteSpaceElements=function(){return o};u.isValidChild=function(E,G){var F=k[E];return !!(F&&F[G])};u.isValid=function(F,E){var H,G,I=B(F);if(I){if(E){if(I.attributes[E]){return true}H=I.attributePatterns;if(H){G=H.length;while(G--){if(H[G].pattern.test(F)){return true}}}}else{return true}}return false};u.getElementRule=B;u.getCustomElements=function(){return p};u.addValidElements=C;u.setValidElements=t;u.addCustomElements=l;u.addValidChildren=x;u.elements=s}})(tinymce);(function(a){a.html.SaxParser=function(c,e){var b=this,d=function(){};c=c||{};b.schema=e=e||new a.html.Schema();if(c.fix_self_closing!==false){c.fix_self_closing=true}a.each("comment cdata text start end pi doctype".split(" "),function(f){if(f){b[f]=c[f]||d}});b.parse=function(E){var n=this,g,G=0,I,B,A=[],N,Q,C,r,z,s,M,H,O,v,m,k,t,R,o,P,F,S,L,f,J,l,D,K,h,x=0,j=a.html.Entities.decode,y,q;function u(T){var V,U;V=A.length;while(V--){if(A[V].name===T){break}}if(V>=0){for(U=A.length-1;U>=V;U--){T=A[U];if(T.valid){n.end(T.name)}}A.length=V}}function p(U,T,Y,X,W){var Z,V;T=T.toLowerCase();Y=T in H?T:j(Y||X||W||"");if(v&&!z&&T.indexOf("data-")!==0){Z=P[T];if(!Z&&F){V=F.length;while(V--){Z=F[V];if(Z.pattern.test(T)){break}}if(V===-1){Z=null}}if(!Z){return}if(Z.validValues&&!(Y in Z.validValues)){return}}N.map[T]=Y;N.push({name:T,value:Y})}l=new RegExp("<(?:(?:!--([\\w\\W]*?)-->)|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|(?:!DOCTYPE([\\w\\W]*?)>)|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|(?:\\/([^>]+)>)|(?:([A-Za-z0-9\\-\\:\\.]+)((?:\\s+[^\"'>]+(?:(?:\"[^\"]*\")|(?:'[^']*')|[^>]*))*|\\/|\\s+)>))","g");D=/([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g;K={script:/<\/script[^>]*>/gi,style:/<\/style[^>]*>/gi,noscript:/<\/noscript[^>]*>/gi};M=e.getShortEndedElements();J=c.self_closing_elements||e.getSelfClosingElements();H=e.getBoolAttrs();v=c.validate;s=c.remove_internals;y=c.fix_self_closing;q=a.isIE;o=/^:/;while(g=l.exec(E)){if(G<g.index){n.text(j(E.substr(G,g.index-G)))}if(I=g[6]){I=I.toLowerCase();if(q&&o.test(I)){I=I.substr(1)}u(I)}else{if(I=g[7]){I=I.toLowerCase();if(q&&o.test(I)){I=I.substr(1)}O=I in M;if(y&&J[I]&&A.length>0&&A[A.length-1].name===I){u(I)}if(!v||(m=e.getElementRule(I))){k=true;if(v){P=m.attributes;F=m.attributePatterns}if(R=g[8]){z=R.indexOf("data-mce-type")!==-1;if(z&&s){k=false}N=[];N.map={};R.replace(D,p)}else{N=[];N.map={}}if(v&&!z){S=m.attributesRequired;L=m.attributesDefault;f=m.attributesForced;if(f){Q=f.length;while(Q--){t=f[Q];r=t.name;h=t.value;if(h==="{$uid}"){h="mce_"+x++}N.map[r]=h;N.push({name:r,value:h})}}if(L){Q=L.length;while(Q--){t=L[Q];r=t.name;if(!(r in N.map)){h=t.value;if(h==="{$uid}"){h="mce_"+x++}N.map[r]=h;N.push({name:r,value:h})}}}if(S){Q=S.length;while(Q--){if(S[Q] in N.map){break}}if(Q===-1){k=false}}if(N.map["data-mce-bogus"]){k=false}}if(k){n.start(I,N,O)}}else{k=false}if(B=K[I]){B.lastIndex=G=g.index+g[0].length;if(g=B.exec(E)){if(k){C=E.substr(G,g.index-G)}G=g.index+g[0].length}else{C=E.substr(G);G=E.length}if(k&&C.length>0){n.text(C,true)}if(k){n.end(I)}l.lastIndex=G;continue}if(!O){if(!R||R.indexOf("/")!=R.length-1){A.push({name:I,valid:k})}else{if(k){n.end(I)}}}}else{if(I=g[1]){n.comment(I)}else{if(I=g[2]){n.cdata(I)}else{if(I=g[3]){n.doctype(I)}else{if(I=g[4]){n.pi(I,g[5])}}}}}}G=g.index+g[0].length}if(G<E.length){n.text(j(E.substr(G)))}for(Q=A.length-1;Q>=0;Q--){I=A[Q];if(I.valid){n.end(I.name)}}}}})(tinymce);(function(d){var c=/^[ \t\r\n]*$/,e={"#text":3,"#comment":8,"#cdata":4,"#pi":7,"#doctype":10,"#document-fragment":11};function a(k,l,j){var i,h,f=j?"lastChild":"firstChild",g=j?"prev":"next";if(k[f]){return k[f]}if(k!==l){i=k[g];if(i){return i}for(h=k.parent;h&&h!==l;h=h.parent){i=h[g];if(i){return i}}}}function b(f,g){this.name=f;this.type=g;if(g===1){this.attributes=[];this.attributes.map={}}}d.extend(b.prototype,{replace:function(g){var f=this;if(g.parent){g.remove()}f.insert(g,f);f.remove();return f},attr:function(h,l){var f=this,g,j,k;if(typeof h!=="string"){for(j in h){f.attr(j,h[j])}return f}if(g=f.attributes){if(l!==k){if(l===null){if(h in g.map){delete g.map[h];j=g.length;while(j--){if(g[j].name===h){g=g.splice(j,1);return f}}}return f}if(h in g.map){j=g.length;while(j--){if(g[j].name===h){g[j].value=l;break}}}else{g.push({name:h,value:l})}g.map[h]=l;return f}else{return g.map[h]}}},clone:function(){var g=this,n=new b(g.name,g.type),h,f,m,j,k;if(m=g.attributes){k=[];k.map={};for(h=0,f=m.length;h<f;h++){j=m[h];if(j.name!=="id"){k[k.length]={name:j.name,value:j.value};k.map[j.name]=j.value}}n.attributes=k}n.value=g.value;n.shortEnded=g.shortEnded;return n},wrap:function(g){var f=this;f.parent.insert(g,f);g.append(f);return f},unwrap:function(){var f=this,h,g;for(h=f.firstChild;h;){g=h.next;f.insert(h,f,true);h=g}f.remove()},remove:function(){var f=this,h=f.parent,g=f.next,i=f.prev;if(h){if(h.firstChild===f){h.firstChild=g;if(g){g.prev=null}}else{i.next=g}if(h.lastChild===f){h.lastChild=i;if(i){i.next=null}}else{g.prev=i}f.parent=f.next=f.prev=null}return f},append:function(h){var f=this,g;if(h.parent){h.remove()}g=f.lastChild;if(g){g.next=h;h.prev=g;f.lastChild=h}else{f.lastChild=f.firstChild=h}h.parent=f;return h},insert:function(h,f,i){var g;if(h.parent){h.remove()}g=f.parent||this;if(i){if(f===g.firstChild){g.firstChild=h}else{f.prev.next=h}h.prev=f.prev;h.next=f;f.prev=h}else{if(f===g.lastChild){g.lastChild=h}else{f.next.prev=h}h.next=f.next;h.prev=f;f.next=h}h.parent=g;return h},getAll:function(g){var f=this,h,i=[];for(h=f.firstChild;h;h=a(h,f)){if(h.name===g){i.push(h)}}return i},empty:function(){var g=this,f,h,j;if(g.firstChild){f=[];for(j=g.firstChild;j;j=a(j,g)){f.push(j)}h=f.length;while(h--){j=f[h];j.parent=j.firstChild=j.lastChild=j.next=j.prev=null}}g.firstChild=g.lastChild=null;return g},isEmpty:function(k){var f=this,j=f.firstChild,h,g;if(j){do{if(j.type===1){if(j.attributes.map["data-mce-bogus"]){continue}if(k[j.name]){return false}h=j.attributes.length;while(h--){g=j.attributes[h].name;if(g==="name"||g.indexOf("data-mce-")===0){return false}}}if(j.type===8){return false}if((j.type===3&&!c.test(j.value))){return false}}while(j=a(j,f))}return true},walk:function(f){return a(this,null,f)}});d.extend(b,{create:function(g,f){var i,h;i=new b(g,e[g]||1);if(f){for(h in f){i.attr(h,f[h])}}return i}});d.html.Node=b})(tinymce);(function(b){var a=b.html.Node;b.html.DomParser=function(g,h){var f=this,e={},d=[],i={},c={};g=g||{};g.validate="validate" in g?g.validate:true;g.root_name=g.root_name||"body";f.schema=h=h||new b.html.Schema();function j(n){var p,q,y,x,A,o,r,l,u,v,k,t,m,z,s;t=b.makeMap("tr,td,th,tbody,thead,tfoot,table");k=h.getNonEmptyElements();m=h.getTextBlockElements();for(p=0;p<n.length;p++){q=n[p];if(!q.parent||q.fixed){continue}if(m[q.name]&&q.parent.name=="li"){z=q.next;while(z){if(m[z.name]){z.name="li";z.fixed=true;q.parent.insert(z,q.parent)}else{break}z=z.next}q.unwrap(q);continue}x=[q];for(y=q.parent;y&&!h.isValidChild(y.name,q.name)&&!t[y.name];y=y.parent){x.push(y)}if(y&&x.length>1){x.reverse();A=o=f.filterNode(x[0].clone());for(u=0;u<x.length-1;u++){if(h.isValidChild(o.name,x[u].name)){r=f.filterNode(x[u].clone());o.append(r)}else{r=o}for(l=x[u].firstChild;l&&l!=x[u+1];){s=l.next;r.append(l);l=s}o=r}if(!A.isEmpty(k)){y.insert(A,x[0],true);y.insert(q,A)}else{y.insert(q,x[0],true)}y=x[0];if(y.isEmpty(k)||y.firstChild===y.lastChild&&y.firstChild.name==="br"){y.empty().remove()}}else{if(q.parent){if(q.name==="li"){z=q.prev;if(z&&(z.name==="ul"||z.name==="ul")){z.append(q);continue}z=q.next;if(z&&(z.name==="ul"||z.name==="ul")){z.insert(q,z.firstChild,true);continue}q.wrap(f.filterNode(new a("ul",1)));continue}if(h.isValidChild(q.parent.name,"div")&&h.isValidChild("div",q.name)){q.wrap(f.filterNode(new a("div",1)))}else{if(q.name==="style"||q.name==="script"){q.empty().remove()}else{q.unwrap()}}}}}}f.filterNode=function(m){var l,k,n;if(k in e){n=i[k];if(n){n.push(m)}else{i[k]=[m]}}l=d.length;while(l--){k=d[l].name;if(k in m.attributes.map){n=c[k];if(n){n.push(m)}else{c[k]=[m]}}}return m};f.addNodeFilter=function(k,l){b.each(b.explode(k),function(m){var n=e[m];if(!n){e[m]=n=[]}n.push(l)})};f.addAttributeFilter=function(k,l){b.each(b.explode(k),function(m){var n;for(n=0;n<d.length;n++){if(d[n].name===m){d[n].callbacks.push(l);return}}d.push({name:m,callbacks:[l]})})};f.parse=function(v,m){var n,J,B,A,D,C,x,r,F,N,z,o,E,M=[],L,t,k,y,s,p,u,q;m=m||{};i={};c={};o=b.extend(b.makeMap("script,style,head,html,body,title,meta,param"),h.getBlockElements());u=h.getNonEmptyElements();p=h.children;z=g.validate;q="forced_root_block" in m?m.forced_root_block:g.forced_root_block;s=h.getWhiteSpaceElements();E=/^[ \t\r\n]+/;t=/[ \t\r\n]+$/;k=/[ \t\r\n]+/g;y=/^[ \t\r\n]+$/;function G(){var O=J.firstChild,l,P;while(O){l=O.next;if(O.type==3||(O.type==1&&O.name!=="p"&&!o[O.name]&&!O.attr("data-mce-type"))){if(!P){P=K(q,1);J.insert(P,O);P.append(O)}else{P.append(O)}}else{P=null}O=l}}function K(l,O){var P=new a(l,O),Q;if(l in e){Q=i[l];if(Q){Q.push(P)}else{i[l]=[P]}}return P}function I(P){var Q,l,O;for(Q=P.prev;Q&&Q.type===3;){l=Q.value.replace(t,"");if(l.length>0){Q.value=l;Q=Q.prev}else{O=Q.prev;Q.remove();Q=O}}}function H(O){var P,l={};for(P in O){if(P!=="li"&&P!="p"){l[P]=O[P]}}return l}n=new b.html.SaxParser({validate:z,self_closing_elements:H(h.getSelfClosingElements()),cdata:function(l){B.append(K("#cdata",4)).value=l},text:function(P,l){var O;if(!L){P=P.replace(k," ");if(B.lastChild&&o[B.lastChild.name]){P=P.replace(E,"")}}if(P.length!==0){O=K("#text",3);O.raw=!!l;B.append(O).value=P}},comment:function(l){B.append(K("#comment",8)).value=l},pi:function(l,O){B.append(K(l,7)).value=O;I(B)},doctype:function(O){var l;l=B.append(K("#doctype",10));l.value=O;I(B)},start:function(l,W,P){var U,R,Q,O,S,X,V,T;Q=z?h.getElementRule(l):{};if(Q){U=K(Q.outputName||l,1);U.attributes=W;U.shortEnded=P;B.append(U);T=p[B.name];if(T&&p[U.name]&&!T[U.name]){M.push(U)}R=d.length;while(R--){S=d[R].name;if(S in W.map){F=c[S];if(F){F.push(U)}else{c[S]=[U]}}}if(o[l]){I(U)}if(!P){B=U}if(!L&&s[l]){L=true}}},end:function(l){var S,P,R,O,Q;P=z?h.getElementRule(l):{};if(P){if(o[l]){if(!L){S=B.firstChild;if(S&&S.type===3){R=S.value.replace(E,"");if(R.length>0){S.value=R;S=S.next}else{O=S.next;S.remove();S=O;while(S&&S.type===3){R=S.value;O=S.next;if(R.length===0||y.test(R)){S.remove();S=O}S=O}}}S=B.lastChild;if(S&&S.type===3){R=S.value.replace(t,"");if(R.length>0){S.value=R;S=S.prev}else{O=S.prev;S.remove();S=O;while(S&&S.type===3){R=S.value;O=S.prev;if(R.length===0||y.test(R)){S.remove();S=O}S=O}}}}}if(L&&s[l]){L=false}if(P.removeEmpty||P.paddEmpty){if(B.isEmpty(u)){if(P.paddEmpty){B.empty().append(new a("#text","3")).value="\u00a0"}else{if(!B.attributes.map.name&&!B.attributes.map.id){Q=B.parent;if(o[B.name]){B.empty().remove()}else{B.unwrap()}B=Q;return}}}}B=B.parent}}},h);J=B=new a(m.context||g.root_name,11);n.parse(v);if(z&&M.length){if(!m.context){j(M)}else{m.invalid=true}}if(q&&J.name=="body"){G()}if(!m.invalid){for(N in i){F=e[N];A=i[N];x=A.length;while(x--){if(!A[x].parent){A.splice(x,1)}}for(D=0,C=F.length;D<C;D++){F[D](A,N,m)}}for(D=0,C=d.length;D<C;D++){F=d[D];if(F.name in c){A=c[F.name];x=A.length;while(x--){if(!A[x].parent){A.splice(x,1)}}for(x=0,r=F.callbacks.length;x<r;x++){F.callbacks[x](A,F.name,m)}}}}return J};if(g.remove_trailing_brs){f.addNodeFilter("br",function(n,m){var r,q=n.length,o,v=b.extend({},h.getBlockElements()),k=h.getNonEmptyElements(),t,s,p,u;v.body=1;for(r=0;r<q;r++){o=n[r];t=o.parent;if(v[o.parent.name]&&o===t.lastChild){p=o.prev;while(p){u=p.name;if(u!=="span"||p.attr("data-mce-type")!=="bookmark"){if(u!=="br"){break}if(u==="br"){o=null;break}}p=p.prev}if(o){o.remove();if(t.isEmpty(k)){elementRule=h.getElementRule(t.name);if(elementRule){if(elementRule.removeEmpty){t.remove()}else{if(elementRule.paddEmpty){t.empty().append(new b.html.Node("#text",3)).value="\u00a0"}}}}}}else{s=o;while(t.firstChild===s&&t.lastChild===s){s=t;if(v[t.name]){break}t=t.parent}if(s===t){textNode=new b.html.Node("#text",3);textNode.value="\u00a0";o.replace(textNode)}}}})}if(!g.allow_html_in_named_anchor){f.addAttributeFilter("id,name",function(k,l){var n=k.length,p,m,o,q;while(n--){q=k[n];if(q.name==="a"&&q.firstChild&&!q.attr("href")){o=q.parent;p=q.lastChild;do{m=p.prev;o.insert(p,q);p=m}while(p)}}})}}})(tinymce);tinymce.html.Writer=function(e){var c=[],a,b,d,f,g;e=e||{};a=e.indent;b=tinymce.makeMap(e.indent_before||"");d=tinymce.makeMap(e.indent_after||"");f=tinymce.html.Entities.getEncodeFunc(e.entity_encoding||"raw",e.entities);g=e.element_format=="html";return{start:function(m,k,p){var n,j,h,o;if(a&&b[m]&&c.length>0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}c.push("<",m);if(k){for(n=0,j=k.length;n<j;n++){h=k[n];c.push(" ",h.name,'="',f(h.value,true),'"')}}if(!p||g){c[c.length]=">"}else{c[c.length]=" />"}if(p&&a&&d[m]&&c.length>0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}},end:function(h){var i;c.push("</",h,">");if(a&&d[h]&&c.length>0){i=c[c.length-1];if(i.length>0&&i!=="\n"){c.push("\n")}}},text:function(i,h){if(i.length>0){c[c.length]=h?i:f(i)}},cdata:function(h){c.push("<![CDATA[",h,"]]>")},comment:function(h){c.push("<!--",h,"-->")},pi:function(h,i){if(i){c.push("<?",h," ",i,"?>")}else{c.push("<?",h,"?>")}if(a){c.push("\n")}},doctype:function(h){c.push("<!DOCTYPE",h,">",a?"\n":"")},reset:function(){c.length=0},getContent:function(){return c.join("").replace(/\n$/,"")}}};(function(a){a.html.Serializer=function(c,d){var b=this,e=new a.html.Writer(c);c=c||{};c.validate="validate" in c?c.validate:true;b.schema=d=d||new a.html.Schema();b.writer=e;b.serialize=function(h){var g,i;i=c.validate;g={3:function(k,j){e.text(k.value,k.raw)},8:function(j){e.comment(j.value)},7:function(j){e.pi(j.name,j.value)},10:function(j){e.doctype(j.value)},4:function(j){e.cdata(j.value)},11:function(j){if((j=j.firstChild)){do{f(j)}while(j=j.next)}}};e.reset();function f(k){var t=g[k.type],j,o,s,r,p,u,n,m,q;if(!t){j=k.name;o=k.shortEnded;s=k.attributes;if(i&&s&&s.length>1){u=[];u.map={};q=d.getElementRule(k.name);for(n=0,m=q.attributesOrder.length;n<m;n++){r=q.attributesOrder[n];if(r in s.map){p=s.map[r];u.map[r]=p;u.push({name:r,value:p})}}for(n=0,m=s.length;n<m;n++){r=s[n].name;if(!(r in u.map)){p=s.map[r];u.map[r]=p;u.push({name:r,value:p})}}s=u}e.start(k.name,s,o);if(!o){if((k=k.firstChild)){do{f(k)}while(k=k.next)}e.end(j)}}else{t(k)}}if(h.type==1&&!c.inner){f(h)}else{g[11](h)}return e.getContent()}}})(tinymce);tinymce.dom={};(function(b,h){var g=!!document.addEventListener;function c(k,j,l,i){if(k.addEventListener){k.addEventListener(j,l,i||false)}else{if(k.attachEvent){k.attachEvent("on"+j,l)}}}function e(k,j,l,i){if(k.removeEventListener){k.removeEventListener(j,l,i||false)}else{if(k.detachEvent){k.detachEvent("on"+j,l)}}}function a(n,l){var i,k=l||{};function j(){return false}function m(){return true}for(i in n){if(i!=="layerX"&&i!=="layerY"){k[i]=n[i]}}if(!k.target){k.target=k.srcElement||document}k.preventDefault=function(){k.isDefaultPrevented=m;if(n){if(n.preventDefault){n.preventDefault()}else{n.returnValue=false}}};k.stopPropagation=function(){k.isPropagationStopped=m;if(n){if(n.stopPropagation){n.stopPropagation()}else{n.cancelBubble=true}}};k.stopImmediatePropagation=function(){k.isImmediatePropagationStopped=m;k.stopPropagation()};if(!k.isDefaultPrevented){k.isDefaultPrevented=j;k.isPropagationStopped=j;k.isImmediatePropagationStopped=j}return k}function d(m,n,l){var k=m.document,j={type:"ready"};function i(){if(!l.domLoaded){l.domLoaded=true;n(j)}}if(k.readyState=="complete"){i();return}if(g){c(m,"DOMContentLoaded",i)}else{c(k,"readystatechange",function(){if(k.readyState==="complete"){e(k,"readystatechange",arguments.callee);i()}});if(k.documentElement.doScroll&&m===m.top){(function(){try{k.documentElement.doScroll("left")}catch(o){setTimeout(arguments.callee,0);return}i()})()}}c(m,"load",i)}function f(k){var q=this,p={},i,o,n,m,l;m="onmouseenter" in document.documentElement;n="onfocusin" in document.documentElement;l={mouseenter:"mouseover",mouseleave:"mouseout"};i=1;q.domLoaded=false;q.events=p;function j(t,x){var s,u,r,v;s=p[x][t.type];if(s){for(u=0,r=s.length;u<r;u++){v=s[u];if(v&&v.func.call(v.scope,t)===false){t.preventDefault()}if(t.isImmediatePropagationStopped()){return}}}}q.bind=function(x,A,D,E){var s,t,u,r,B,z,C,v=window;function y(F){j(a(F||v.event),s)}if(!x||x.nodeType===3||x.nodeType===8){return}if(!x[h]){s=i++;x[h]=s;p[s]={}}else{s=x[h];if(!p[s]){p[s]={}}}E=E||x;A=A.split(" ");u=A.length;while(u--){r=A[u];z=y;B=C=false;if(r==="DOMContentLoaded"){r="ready"}if((q.domLoaded||x.readyState=="complete")&&r==="ready"){q.domLoaded=true;D.call(E,a({type:r}));continue}if(!m){B=l[r];if(B){z=function(F){var H,G;H=F.currentTarget;G=F.relatedTarget;if(G&&H.contains){G=H.contains(G)}else{while(G&&G!==H){G=G.parentNode}}if(!G){F=a(F||v.event);F.type=F.type==="mouseout"?"mouseleave":"mouseenter";F.target=H;j(F,s)}}}}if(!n&&(r==="focusin"||r==="focusout")){C=true;B=r==="focusin"?"focus":"blur";z=function(F){F=a(F||v.event);F.type=F.type==="focus"?"focusin":"focusout";j(F,s)}}t=p[s][r];if(!t){p[s][r]=t=[{func:D,scope:E}];t.fakeName=B;t.capture=C;t.nativeHandler=z;if(!g){t.proxyHandler=k(s)}if(r==="ready"){d(x,z,q)}else{c(x,B||r,g?z:t.proxyHandler,C)}}else{t.push({func:D,scope:E})}}x=t=0;return D};q.unbind=function(x,z,A){var s,u,v,B,r,t;if(!x||x.nodeType===3||x.nodeType===8){return q}s=x[h];if(s){t=p[s];if(z){z=z.split(" ");v=z.length;while(v--){r=z[v];u=t[r];if(u){if(A){B=u.length;while(B--){if(u[B].func===A){u.splice(B,1)}}}if(!A||u.length===0){delete t[r];e(x,u.fakeName||r,g?u.nativeHandler:u.proxyHandler,u.capture)}}}}else{for(r in t){u=t[r];e(x,u.fakeName||r,g?u.nativeHandler:u.proxyHandler,u.capture)}t={}}for(r in t){return q}delete p[s];try{delete x[h]}catch(y){x[h]=null}}return q};q.fire=function(u,s,r){var v,t;if(!u||u.nodeType===3||u.nodeType===8){return q}t=a(null,r);t.type=s;do{v=u[h];if(v){j(t,v)}u=u.parentNode||u.ownerDocument||u.defaultView||u.parentWindow}while(u&&!t.isPropagationStopped());return q};q.clean=function(u){var s,r,t=q.unbind;if(!u||u.nodeType===3||u.nodeType===8){return q}if(u[h]){t(u)}if(!u.getElementsByTagName){u=u.document}if(u&&u.getElementsByTagName){t(u);r=u.getElementsByTagName("*");s=r.length;while(s--){u=r[s];if(u[h]){t(u)}}}return q};q.callNativeHandler=function(s,r){if(p){p[s][r.type].nativeHandler(r)}};q.destory=function(){p={}};q.add=function(v,s,u,t){if(typeof(v)==="string"){v=document.getElementById(v)}if(v&&v instanceof Array){var r=v.length;while(r--){q.add(v[r],s,u,t)}return}if(s==="init"){s="ready"}return q.bind(v,s instanceof Array?s.join(" "):s,u,t)};q.remove=function(v,s,u,t){if(!v){return q}if(typeof(v)==="string"){v=document.getElementById(v)}if(v instanceof Array){var r=v.length;while(r--){q.remove(v[r],s,u,t)}return q}return q.unbind(v,s instanceof Array?s.join(" "):s,u)};q.clear=function(r){if(typeof(r)==="string"){r=document.getElementById(r)}return q.clean(r)};q.cancel=function(r){if(r){q.prevent(r);q.stop(r)}return false};q.prevent=function(r){if(!r.preventDefault){r=a(r)}r.preventDefault();return false};q.stop=function(r){if(!r.stopPropagation){r=a(r)}r.stopPropagation();return false}}b.EventUtils=f;b.Event=new f(function(i){return function(j){tinymce.dom.Event.callNativeHandler(i,j)}});b.Event.bind(window,"ready",function(){});b=0})(tinymce.dom,"data-mce-expando");tinymce.dom.TreeWalker=function(a,c){var b=a;function d(i,f,e,j){var h,g;if(i){if(!j&&i[f]){return i[f]}if(i!=c){h=i[e];if(h){return h}for(g=i.parentNode;g&&g!=c;g=g.parentNode){h=g[e];if(h){return h}}}}}this.current=function(){return b};this.next=function(e){return(b=d(b,"firstChild","nextSibling",e))};this.prev=function(e){return(b=d(b,"lastChild","previousSibling",e))}};(function(e){var g=e.each,d=e.is,f=e.isWebKit,b=e.isIE,h=e.html.Entities,c=/^([a-z0-9],?)+$/i,a=/^[ \t\r\n]*$/;e.create("tinymce.dom.DOMUtils",{doc:null,root:null,files:null,pixelStyles:/^(top|left|bottom|right|width|height|borderWidth)$/,props:{"for":"htmlFor","class":"className",className:"className",checked:"checked",disabled:"disabled",maxlength:"maxLength",readonly:"readOnly",selected:"selected",value:"value",id:"id",name:"name",type:"type"},DOMUtils:function(o,l){var k=this,i,j,n;k.doc=o;k.win=window;k.files={};k.cssFlicker=false;k.counter=0;k.stdMode=!e.isIE||o.documentMode>=8;k.boxModel=!e.isIE||o.compatMode=="CSS1Compat"||k.stdMode;k.hasOuterHTML="outerHTML" in o.createElement("a");k.settings=l=e.extend({keep_values:false,hex_colors:1},l);k.schema=l.schema;k.styles=new e.html.Styles({url_converter:l.url_converter,url_converter_scope:l.url_converter_scope},l.schema);if(e.isIE6){try{o.execCommand("BackgroundImageCache",false,true)}catch(m){k.cssFlicker=true}}k.fixDoc(o);k.events=l.ownEvents?new e.dom.EventUtils(l.proxy):e.dom.Event;e.addUnload(k.destroy,k);n=l.schema?l.schema.getBlockElements():{};k.isBlock=function(q){if(!q){return false}var p=q.nodeType;if(p){return !!(p===1&&n[q.nodeName])}return !!n[q]}},fixDoc:function(k){var j=this.settings,i;if(b&&!e.isIE11&&j.schema){("abbr article aside audio canvas details figcaption figure footer header hgroup mark menu meter nav output progress section summary time video").replace(/\w+/g,function(l){k.createElement(l)});for(i in j.schema.getCustomElements()){k.createElement(i)}}},clone:function(k,i){var j=this,m,l;if(!b||e.isIE11||k.nodeType!==1||i){return k.cloneNode(i)}l=j.doc;if(!i){m=l.createElement(k.nodeName);g(j.getAttribs(k),function(n){j.setAttrib(m,n.nodeName,j.getAttrib(k,n.nodeName))});return m}return m.firstChild},getRoot:function(){var i=this,j=i.settings;return(j&&i.get(j.root_element))||i.doc.body},getViewPort:function(j){var k,i;j=!j?this.win:j;k=j.document;i=this.boxModel?k.documentElement:k.body;return{x:j.pageXOffset||i.scrollLeft,y:j.pageYOffset||i.scrollTop,w:j.innerWidth||i.clientWidth,h:j.innerHeight||i.clientHeight}},getRect:function(l){var k,i=this,j;l=i.get(l);k=i.getPos(l);j=i.getSize(l);return{x:k.x,y:k.y,w:j.w,h:j.h}},getSize:function(l){var j=this,i,k;l=j.get(l);i=j.getStyle(l,"width");k=j.getStyle(l,"height");if(i.indexOf("px")===-1){i=0}if(k.indexOf("px")===-1){k=0}return{w:parseInt(i,10)||l.offsetWidth||l.clientWidth,h:parseInt(k,10)||l.offsetHeight||l.clientHeight}},getParent:function(k,j,i){return this.getParents(k,j,i,false)},getParents:function(s,m,k,q){var j=this,i,l=j.settings,p=[];s=j.get(s);q=q===undefined;if(l.strict_root){k=k||j.getRoot()}if(d(m,"string")){i=m;if(m==="*"){m=function(o){return o.nodeType==1}}else{m=function(o){return j.is(o,i)}}}while(s){if(s==k||!s.nodeType||s.nodeType===9){break}if(!m||m(s)){if(q){p.push(s)}else{return s}}s=s.parentNode}return q?p:null},get:function(i){var j;if(i&&this.doc&&typeof(i)=="string"){j=i;i=this.doc.getElementById(i);if(i&&i.id!==j){return this.doc.getElementsByName(j)[1]}}return i},getNext:function(j,i){return this._findSib(j,i,"nextSibling")},getPrev:function(j,i){return this._findSib(j,i,"previousSibling")},select:function(k,j){var i=this;return e.dom.Sizzle(k,i.get(j)||i.get(i.settings.root_element)||i.doc,[])},is:function(l,j){var k;if(l.length===undefined){if(j==="*"){return l.nodeType==1}if(c.test(j)){j=j.toLowerCase().split(/,/);l=l.nodeName.toLowerCase();for(k=j.length-1;k>=0;k--){if(j[k]==l){return true}}return false}}return e.dom.Sizzle.matches(j,l.nodeType?[l]:l).length>0},add:function(l,o,i,k,m){var j=this;return this.run(l,function(r){var q,n;q=d(o,"string")?j.doc.createElement(o):o;j.setAttribs(q,i);if(k){if(k.nodeType){q.appendChild(k)}else{j.setHTML(q,k)}}return !m?r.appendChild(q):q})},create:function(k,i,j){return this.add(this.doc.createElement(k),k,i,j,1)},createHTML:function(q,i,m){var p="",l=this,j;p+="<"+q;for(j in i){if(i.hasOwnProperty(j)){p+=" "+j+'="'+l.encode(i[j])+'"'}}if(typeof(m)!="undefined"){return p+">"+m+"</"+q+">"}return p+" />"},remove:function(i,j){return this.run(i,function(l){var m,k=l.parentNode;if(!k){return null}if(j){while(m=l.firstChild){if(!e.isIE||m.nodeType!==3||m.nodeValue){k.insertBefore(m,l)}else{l.removeChild(m)}}}return k.removeChild(l)})},setStyle:function(l,i,j){var k=this;return k.run(l,function(o){var n,m;n=o.style;i=i.replace(/-(\D)/g,function(q,p){return p.toUpperCase()});if(k.pixelStyles.test(i)&&(e.is(j,"number")||/^[\-0-9\.]+$/.test(j))){j+="px"}switch(i){case"opacity":if(b&&!e.isIE11){n.filter=j===""?"":"alpha(opacity="+(j*100)+")";if(!l.currentStyle||!l.currentStyle.hasLayout){n.display="inline-block"}}n[i]=n["-moz-opacity"]=n["-khtml-opacity"]=j||"";break;case"float":(b&&!e.isIE11)?n.styleFloat=j:n.cssFloat=j;break;default:n[i]=j||""}if(k.settings.update_styles){k.setAttrib(o,"data-mce-style")}})},getStyle:function(l,i,k){l=this.get(l);if(!l){return}if(this.doc.defaultView&&k){i=i.replace(/[A-Z]/g,function(m){return"-"+m});try{return this.doc.defaultView.getComputedStyle(l,null).getPropertyValue(i)}catch(j){return null}}i=i.replace(/-(\D)/g,function(n,m){return m.toUpperCase()});if(i=="float"){i=b?"styleFloat":"cssFloat"}if(l.currentStyle&&k){return l.currentStyle[i]}return l.style?l.style[i]:undefined},setStyles:function(l,m){var j=this,k=j.settings,i;i=k.update_styles;k.update_styles=0;g(m,function(o,p){j.setStyle(l,p,o)});k.update_styles=i;if(k.update_styles){j.setAttrib(l,k.cssText)}},removeAllAttribs:function(i){return this.run(i,function(l){var k,j=l.attributes;for(k=j.length-1;k>=0;k--){l.removeAttributeNode(j.item(k))}})},setAttrib:function(k,l,i){var j=this;if(!k||!l){return}if(j.settings.strict){l=l.toLowerCase()}return this.run(k,function(p){var o=j.settings;var m=p.getAttribute(l);if(i!==null){switch(l){case"style":if(!d(i,"string")){g(i,function(q,r){j.setStyle(p,r,q)});return}if(o.keep_values){if(i&&!j._isRes(i)){p.setAttribute("data-mce-style",i,2)}else{p.removeAttribute("data-mce-style",2)}}p.style.cssText=i;break;case"class":p.className=i||"";break;case"src":case"href":if(o.keep_values){if(o.url_converter){i=o.url_converter.call(o.url_converter_scope||j,i,l,p)}j.setAttrib(p,"data-mce-"+l,i,2)}break;case"shape":p.setAttribute("data-mce-style",i);break}}if(d(i)&&i!==null&&i.length!==0){p.setAttribute(l,""+i,2)}else{p.removeAttribute(l,2)}if(tinyMCE.activeEditor&&m!=i){var n=tinyMCE.activeEditor;n.onSetAttrib.dispatch(n,p,l,i)}})},setAttribs:function(j,k){var i=this;return this.run(j,function(l){g(k,function(m,o){i.setAttrib(l,o,m)})})},getAttrib:function(m,o,k){var i,j=this,l;m=j.get(m);if(!m||m.nodeType!==1){return k===l?false:k}if(!d(k)){k=""}if(/^(src|href|style|coords|shape)$/.test(o)){i=m.getAttribute("data-mce-"+o);if(i){return i}}if(b&&j.props[o]){i=m[j.props[o]];i=i&&i.nodeValue?i.nodeValue:i}if(!i){i=m.getAttribute(o,2)}if(/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(o)){if(m[j.props[o]]===true&&i===""){return o}return i?o:""}if(m.nodeName==="FORM"&&m.getAttributeNode(o)){return m.getAttributeNode(o).nodeValue}if(o==="style"){i=i||m.style.cssText;if(i){i=j.serializeStyle(j.parseStyle(i),m.nodeName);if(j.settings.keep_values&&!j._isRes(i)){m.setAttribute("data-mce-style",i)}}}if(f&&o==="class"&&i){i=i.replace(/(apple|webkit)\-[a-z\-]+/gi,"")}if(b){switch(o){case"rowspan":case"colspan":if(i===1){i=""}break;case"size":if(i==="+0"||i===20||i===0){i=""}break;case"width":case"height":case"vspace":case"checked":case"disabled":case"readonly":if(i===0){i=""}break;case"hspace":if(i===-1){i=""}break;case"maxlength":case"tabindex":if(i===32768||i===2147483647||i==="32768"){i=""}break;case"multiple":case"compact":case"noshade":case"nowrap":if(i===65535){return o}return k;case"shape":i=i.toLowerCase();break;default:if(o.indexOf("on")===0&&i){i=e._replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/,"$1",""+i)}}}return(i!==l&&i!==null&&i!=="")?""+i:k},getPos:function(q,l){var j=this,i=0,p=0,m,o=j.doc,k;q=j.get(q);l=l||o.body;if(q){if(q.getBoundingClientRect){q=q.getBoundingClientRect();m=j.boxModel?o.documentElement:o.body;i=q.left+(o.documentElement.scrollLeft||o.body.scrollLeft)-m.clientTop;p=q.top+(o.documentElement.scrollTop||o.body.scrollTop)-m.clientLeft;return{x:i,y:p}}k=q;while(k&&k!=l&&k.nodeType){i+=k.offsetLeft||0;p+=k.offsetTop||0;k=k.offsetParent}k=q.parentNode;while(k&&k!=l&&k.nodeType){i-=k.scrollLeft||0;p-=k.scrollTop||0;k=k.parentNode}}return{x:i,y:p}},parseStyle:function(i){return this.styles.parse(i)},serializeStyle:function(j,i){return this.styles.serialize(j,i)},addStyle:function(j){var k=this.doc,i;styleElm=k.getElementById("mceDefaultStyles");if(!styleElm){styleElm=k.createElement("style"),styleElm.id="mceDefaultStyles";styleElm.type="text/css";i=k.getElementsByTagName("head")[0];if(i.firstChild){i.insertBefore(styleElm,i.firstChild)}else{i.appendChild(styleElm)}}if(styleElm.styleSheet){styleElm.styleSheet.cssText+=j}else{styleElm.appendChild(k.createTextNode(j))}},loadCSS:function(i){var k=this,l=k.doc,j;if(!i){i=""}j=l.getElementsByTagName("head")[0];g(i.split(","),function(m){var n;if(k.files[m]){return}k.files[m]=true;n=k.create("link",{rel:"stylesheet",href:e._addVer(m)});if(b&&!e.isIE11&&l.documentMode&&l.recalc){n.onload=function(){if(l.recalc){l.recalc()}n.onload=null}}j.appendChild(n)})},addClass:function(i,j){return this.run(i,function(k){var l;if(!j){return 0}if(this.hasClass(k,j)){return k.className}l=this.removeClass(k,j);return k.className=(l!=""?(l+" "):"")+j})},removeClass:function(k,l){var i=this,j;return i.run(k,function(n){var m;if(i.hasClass(n,l)){if(!j){j=new RegExp("(^|\\s+)"+l+"(\\s+|$)","g")}m=n.className.replace(j," ");m=e.trim(m!=" "?m:"");n.className=m;if(!m){n.removeAttribute("class");n.removeAttribute("className")}return m}return n.className})},hasClass:function(j,i){j=this.get(j);if(!j||!i){return false}return(" "+j.className+" ").indexOf(" "+i+" ")!==-1},show:function(i){return this.setStyle(i,"display","block")},hide:function(i){return this.setStyle(i,"display","none")},isHidden:function(i){i=this.get(i);return !i||i.style.display=="none"||this.getStyle(i,"display")=="none"},uniqueId:function(i){return(!i?"mce_":i)+(this.counter++)},setHTML:function(k,j){var i=this;return i.run(k,function(m){if(b){while(m.firstChild){m.removeChild(m.firstChild)}try{m.innerHTML="<br />"+j;m.removeChild(m.firstChild)}catch(l){var n=i.create("div");n.innerHTML="<br />"+j;g(e.grep(n.childNodes),function(p,o){if(o&&m.canHaveHTML){m.appendChild(p)}})}}else{m.innerHTML=j}return j})},getOuterHTML:function(k){var j,i=this;k=i.get(k);if(!k){return null}if(k.nodeType===1&&i.hasOuterHTML){return k.outerHTML}j=(k.ownerDocument||i.doc).createElement("body");j.appendChild(k.cloneNode(true));return j.innerHTML},setOuterHTML:function(l,j,m){var i=this;function k(p,o,r){var s,q;q=r.createElement("body");q.innerHTML=o;s=q.lastChild;while(s){i.insertAfter(s.cloneNode(true),p);s=s.previousSibling}i.remove(p)}return this.run(l,function(o){o=i.get(o);if(o.nodeType==1){m=m||o.ownerDocument||i.doc;if(b){try{if(b&&o.nodeType==1){o.outerHTML=j}else{k(o,j,m)}}catch(n){k(o,j,m)}}else{k(o,j,m)}}})},decode:h.decode,encode:h.encodeAllRaw,insertAfter:function(i,j){j=this.get(j);return this.run(i,function(l){var k,m;k=j.parentNode;m=j.nextSibling;if(m){k.insertBefore(l,m)}else{k.appendChild(l)}return l})},replace:function(m,l,i){var j=this;if(d(l,"array")){m=m.cloneNode(true)}return j.run(l,function(k){if(i){g(e.grep(k.childNodes),function(n){m.appendChild(n)})}return k.parentNode.replaceChild(m,k)})},rename:function(l,i){var k=this,j;if(l.nodeName!=i.toUpperCase()){j=k.create(i);g(k.getAttribs(l),function(m){k.setAttrib(j,m.nodeName,k.getAttrib(l,m.nodeName))});k.replace(j,l,1)}return j||l},findCommonAncestor:function(k,i){var l=k,j;while(l){j=i;while(j&&l!=j){j=j.parentNode}if(l==j){break}l=l.parentNode}if(!l&&k.ownerDocument){return k.ownerDocument.documentElement}return l},toHex:function(i){var k=/^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(i);function j(l){l=parseInt(l,10).toString(16);return l.length>1?l:"0"+l}if(k){i="#"+j(k[1])+j(k[2])+j(k[3]);return i}return i},getClasses:function(){var n=this,j=[],m,o={},p=n.settings.class_filter,l;if(n.classes){return n.classes}function q(i){g(i.imports,function(s){q(s)});g(i.cssRules||i.rules,function(t){switch(t.type||1){case 1:if(t.selectorText){g(t.selectorText.split(","),function(r){r=r.replace(/^\s*|\s*$|^\s\./g,"");if(/\.mce/.test(r)||!/\.[\w\-]+$/.test(r)){return}l=r;r=e._replace(/.*\.([a-z0-9_\-]+).*/i,"$1",r);if(p&&!(r=p(r,l))){return}if(!o[r]){j.push({"class":r});o[r]=1}})}break;case 3:try{q(t.styleSheet)}catch(s){}break}})}try{g(n.doc.styleSheets,q)}catch(k){}if(j.length>0){n.classes=j}return j},run:function(l,k,j){var i=this,m;if(i.doc&&typeof(l)==="string"){l=i.get(l)}if(!l){return false}j=j||this;if(!l.nodeType&&(l.length||l.length===0)){m=[];g(l,function(o,n){if(o){if(typeof(o)=="string"){o=i.doc.getElementById(o)}m.push(k.call(j,o,n))}});return m}return k.call(j,l)},getAttribs:function(j){var i;j=this.get(j);if(!j){return[]}if(b){i=[];if(j.nodeName=="OBJECT"){return j.attributes}if(j.nodeName==="OPTION"&&this.getAttrib(j,"selected")){i.push({specified:1,nodeName:"selected"})}j.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi,"").replace(/[\w:\-]+/gi,function(k){i.push({specified:1,nodeName:k})});return i}return j.attributes},isEmpty:function(m,k){var r=this,o,n,q,j,l,p=0;m=m.firstChild;if(m){j=new e.dom.TreeWalker(m,m.parentNode);k=k||r.schema?r.schema.getNonEmptyElements():null;do{q=m.nodeType;if(q===1){if(m.getAttribute("data-mce-bogus")){continue}l=m.nodeName.toLowerCase();if(k&&k[l]){if(l==="br"){p++;continue}return false}n=r.getAttribs(m);o=m.attributes.length;while(o--){l=m.attributes[o].nodeName;if(l==="name"||l==="data-mce-bookmark"){return false}}}if(q==8){return false}if((q===3&&!a.test(m.nodeValue))){return false}}while(m=j.next())}return p<=1},destroy:function(j){var i=this;i.win=i.doc=i.root=i.events=i.frag=null;if(!j){e.removeUnload(i.destroy)}},createRng:function(){var i=this.doc;return i.createRange?i.createRange():new e.dom.Range(this)},nodeIndex:function(m,n){var i=0,k,l,j;if(m){for(k=m.nodeType,m=m.previousSibling,l=m;m;m=m.previousSibling){j=m.nodeType;if(n&&j==3){if(j==k||!m.nodeValue.length){continue}}i++;k=j}}return i},split:function(m,l,p){var q=this,i=q.createRng(),n,k,o;function j(v){var t,s=v.childNodes,u=v.nodeType;function x(A){var z=A.previousSibling&&A.previousSibling.nodeName=="SPAN";var y=A.nextSibling&&A.nextSibling.nodeName=="SPAN";return z&&y}if(u==1&&v.getAttribute("data-mce-type")=="bookmark"){return}for(t=s.length-1;t>=0;t--){j(s[t])}if(u!=9){if(u==3&&v.nodeValue.length>0){var r=e.trim(v.nodeValue).length;if(!q.isBlock(v.parentNode)||r>0||r===0&&x(v)){return}}else{if(u==1){s=v.childNodes;if(s.length==1&&s[0]&&s[0].nodeType==1&&s[0].getAttribute("data-mce-type")=="bookmark"){v.parentNode.insertBefore(s[0],v)}if(s.length||/^(br|hr|input|img)$/i.test(v.nodeName)){return}}}q.remove(v)}return v}if(m&&l){i.setStart(m.parentNode,q.nodeIndex(m));i.setEnd(l.parentNode,q.nodeIndex(l));n=i.extractContents();i=q.createRng();i.setStart(l.parentNode,q.nodeIndex(l)+1);i.setEnd(m.parentNode,q.nodeIndex(m)+1);k=i.extractContents();o=m.parentNode;o.insertBefore(j(n),m);if(p){o.replaceChild(p,l)}else{o.insertBefore(l,m)}o.insertBefore(j(k),m);q.remove(m);return p||l}},bind:function(l,i,k,j){return this.events.add(l,i,k,j||this)},unbind:function(k,i,j){return this.events.remove(k,i,j)},fire:function(k,j,i){return this.events.fire(k,j,i)},getContentEditable:function(j){var i;if(j.nodeType!=1){return null}i=j.getAttribute("data-mce-contenteditable");if(i&&i!=="inherit"){return i}return j.contentEditable!=="inherit"?j.contentEditable:null},_findSib:function(l,i,j){var k=this,m=i;if(l){if(d(m,"string")){m=function(n){return k.is(n,i)}}for(l=l[j];l;l=l[j]){if(m(l)){return l}}}return null},_isRes:function(i){return/^(top|left|bottom|right|width|height)/i.test(i)||/;\s*(top|left|bottom|right|width|height)/i.test(i)}});e.DOM=new e.dom.DOMUtils(document,{process_html:0})})(tinymce);(function(a){function b(c){var O=this,e=c.doc,U=0,F=1,j=2,E=true,S=false,W="startOffset",h="startContainer",Q="endContainer",A="endOffset",k=tinymce.extend,n=c.nodeIndex;k(O,{startContainer:e,startOffset:0,endContainer:e,endOffset:0,collapsed:E,commonAncestorContainer:e,START_TO_START:0,START_TO_END:1,END_TO_END:2,END_TO_START:3,setStart:q,setEnd:s,setStartBefore:g,setStartAfter:J,setEndBefore:K,setEndAfter:u,collapse:B,selectNode:y,selectNodeContents:G,compareBoundaryPoints:v,deleteContents:p,extractContents:I,cloneContents:d,insertNode:D,surroundContents:N,cloneRange:L,toStringIE:T});function x(){return e.createDocumentFragment()}function q(X,t){C(E,X,t)}function s(X,t){C(S,X,t)}function g(t){q(t.parentNode,n(t))}function J(t){q(t.parentNode,n(t)+1)}function K(t){s(t.parentNode,n(t))}function u(t){s(t.parentNode,n(t)+1)}function B(t){if(t){O[Q]=O[h];O[A]=O[W]}else{O[h]=O[Q];O[W]=O[A]}O.collapsed=E}function y(t){g(t);u(t)}function G(t){q(t,0);s(t,t.nodeType===1?t.childNodes.length:t.nodeValue.length)}function v(aa,t){var ad=O[h],Y=O[W],ac=O[Q],X=O[A],ab=t.startContainer,af=t.startOffset,Z=t.endContainer,ae=t.endOffset;if(aa===0){return H(ad,Y,ab,af)}if(aa===1){return H(ac,X,ab,af)}if(aa===2){return H(ac,X,Z,ae)}if(aa===3){return H(ad,Y,Z,ae)}}function p(){l(j)}function I(){return l(U)}function d(){return l(F)}function D(aa){var X=this[h],t=this[W],Z,Y;if((X.nodeType===3||X.nodeType===4)&&X.nodeValue){if(!t){X.parentNode.insertBefore(aa,X)}else{if(t>=X.nodeValue.length){c.insertAfter(aa,X)}else{Z=X.splitText(t);X.parentNode.insertBefore(aa,Z)}}}else{if(X.childNodes.length>0){Y=X.childNodes[t]}if(Y){X.insertBefore(aa,Y)}else{X.appendChild(aa)}}}function N(X){var t=O.extractContents();O.insertNode(X);X.appendChild(t);O.selectNode(X)}function L(){return k(new b(c),{startContainer:O[h],startOffset:O[W],endContainer:O[Q],endOffset:O[A],collapsed:O.collapsed,commonAncestorContainer:O.commonAncestorContainer})}function P(t,X){var Y;if(t.nodeType==3){return t}if(X<0){return t}Y=t.firstChild;while(Y&&X>0){--X;Y=Y.nextSibling}if(Y){return Y}return t}function m(){return(O[h]==O[Q]&&O[W]==O[A])}function H(Z,ab,X,aa){var ac,Y,t,ad,af,ae;if(Z==X){if(ab==aa){return 0}if(ab<aa){return -1}return 1}ac=X;while(ac&&ac.parentNode!=Z){ac=ac.parentNode}if(ac){Y=0;t=Z.firstChild;while(t!=ac&&Y<ab){Y++;t=t.nextSibling}if(ab<=Y){return -1}return 1}ac=Z;while(ac&&ac.parentNode!=X){ac=ac.parentNode}if(ac){Y=0;t=X.firstChild;while(t!=ac&&Y<aa){Y++;t=t.nextSibling}if(Y<aa){return -1}return 1}ad=c.findCommonAncestor(Z,X);af=Z;while(af&&af.parentNode!=ad){af=af.parentNode}if(!af){af=ad}ae=X;while(ae&&ae.parentNode!=ad){ae=ae.parentNode}if(!ae){ae=ad}if(af==ae){return 0}t=ad.firstChild;while(t){if(t==af){return -1}if(t==ae){return 1}t=t.nextSibling}}function C(X,aa,Z){var t,Y;if(X){O[h]=aa;O[W]=Z}else{O[Q]=aa;O[A]=Z}t=O[Q];while(t.parentNode){t=t.parentNode}Y=O[h];while(Y.parentNode){Y=Y.parentNode}if(Y==t){if(H(O[h],O[W],O[Q],O[A])>0){O.collapse(X)}}else{O.collapse(X)}O.collapsed=m();O.commonAncestorContainer=c.findCommonAncestor(O[h],O[Q])}function l(ad){var ac,Z=0,af=0,X,ab,Y,aa,t,ae;if(O[h]==O[Q]){return f(ad)}for(ac=O[Q],X=ac.parentNode;X;ac=X,X=X.parentNode){if(X==O[h]){return r(ac,ad)}++Z}for(ac=O[h],X=ac.parentNode;X;ac=X,X=X.parentNode){if(X==O[Q]){return V(ac,ad)}++af}ab=af-Z;Y=O[h];while(ab>0){Y=Y.parentNode;ab--}aa=O[Q];while(ab<0){aa=aa.parentNode;ab++}for(t=Y.parentNode,ae=aa.parentNode;t!=ae;t=t.parentNode,ae=ae.parentNode){Y=t;aa=ae}return o(Y,aa,ad)}function f(ac){var ae,af,t,Y,Z,ad,aa,X,ab;if(ac!=j){ae=x()}if(O[W]==O[A]){return ae}if(O[h].nodeType==3){af=O[h].nodeValue;t=af.substring(O[W],O[A]);if(ac!=F){Y=O[h];X=O[W];ab=O[A]-O[W];if(X===0&&ab>=Y.nodeValue.length-1){Y.parentNode.removeChild(Y)}else{Y.deleteData(X,ab)}O.collapse(E)}if(ac==j){return}if(t.length>0){ae.appendChild(e.createTextNode(t))}return ae}Y=P(O[h],O[W]);Z=O[A]-O[W];while(Y&&Z>0){ad=Y.nextSibling;aa=z(Y,ac);if(ae){ae.appendChild(aa)}--Z;Y=ad}if(ac!=F){O.collapse(E)}return ae}function r(ad,aa){var ac,ab,X,t,Z,Y;if(aa!=j){ac=x()}ab=i(ad,aa);if(ac){ac.appendChild(ab)}X=n(ad);t=X-O[W];if(t<=0){if(aa!=F){O.setEndBefore(ad);O.collapse(S)}return ac}ab=ad.previousSibling;while(t>0){Z=ab.previousSibling;Y=z(ab,aa);if(ac){ac.insertBefore(Y,ac.firstChild)}--t;ab=Z}if(aa!=F){O.setEndBefore(ad);O.collapse(S)}return ac}function V(ab,aa){var ad,X,ac,t,Z,Y;if(aa!=j){ad=x()}ac=R(ab,aa);if(ad){ad.appendChild(ac)}X=n(ab);++X;t=O[A]-X;ac=ab.nextSibling;while(ac&&t>0){Z=ac.nextSibling;Y=z(ac,aa);if(ad){ad.appendChild(Y)}--t;ac=Z}if(aa!=F){O.setStartAfter(ab);O.collapse(E)}return ad}function o(ab,t,ae){var Y,ag,aa,ac,ad,X,af,Z;if(ae!=j){ag=x()}Y=R(ab,ae);if(ag){ag.appendChild(Y)}aa=ab.parentNode;ac=n(ab);ad=n(t);++ac;X=ad-ac;af=ab.nextSibling;while(X>0){Z=af.nextSibling;Y=z(af,ae);if(ag){ag.appendChild(Y)}af=Z;--X}Y=i(t,ae);if(ag){ag.appendChild(Y)}if(ae!=F){O.setStartAfter(ab);O.collapse(E)}return ag}function i(ac,ad){var Y=P(O[Q],O[A]-1),ae,ab,aa,t,X,Z=Y!=O[Q];if(Y==ac){return M(Y,Z,S,ad)}ae=Y.parentNode;ab=M(ae,S,S,ad);while(ae){while(Y){aa=Y.previousSibling;t=M(Y,Z,S,ad);if(ad!=j){ab.insertBefore(t,ab.firstChild)}Z=E;Y=aa}if(ae==ac){return ab}Y=ae.previousSibling;ae=ae.parentNode;X=M(ae,S,S,ad);if(ad!=j){X.appendChild(ab)}ab=X}}function R(ac,ad){var Z=P(O[h],O[W]),aa=Z!=O[h],ae,ab,Y,t,X;if(Z==ac){return M(Z,aa,E,ad)}ae=Z.parentNode;ab=M(ae,S,E,ad);while(ae){while(Z){Y=Z.nextSibling;t=M(Z,aa,E,ad);if(ad!=j){ab.appendChild(t)}aa=E;Z=Y}if(ae==ac){return ab}Z=ae.nextSibling;ae=ae.parentNode;X=M(ae,S,E,ad);if(ad!=j){X.appendChild(ab)}ab=X}}function M(t,aa,ad,ae){var Z,Y,ab,X,ac;if(aa){return z(t,ae)}if(t.nodeType==3){Z=t.nodeValue;if(ad){X=O[W];Y=Z.substring(X);ab=Z.substring(0,X)}else{X=O[A];Y=Z.substring(0,X);ab=Z.substring(X)}if(ae!=F){t.nodeValue=ab}if(ae==j){return}ac=c.clone(t,S);ac.nodeValue=Y;return ac}if(ae==j){return}return c.clone(t,S)}function z(X,t){if(t!=j){return t==F?c.clone(X,E):X}X.parentNode.removeChild(X)}function T(){return c.create("body",null,d()).outerText}return O}a.Range=b;b.prototype.toString=function(){return this.toStringIE()}})(tinymce.dom);(function(){function a(d){var b=this,h=d.dom,c=true,f=false;function e(i,j){var k,t=0,q,n,m,l,o,r,p=-1,s;k=i.duplicate();k.collapse(j);s=k.parentElement();if(s.ownerDocument!==d.dom.doc){return}while(s.contentEditable==="false"){s=s.parentNode}if(!s.hasChildNodes()){return{node:s,inside:1}}m=s.children;q=m.length-1;while(t<=q){r=Math.floor((t+q)/2);l=m[r];k.moveToElementText(l);p=k.compareEndPoints(j?"StartToStart":"EndToEnd",i);if(p>0){q=r-1}else{if(p<0){t=r+1}else{return{node:l}}}}if(p<0){if(!l){k.moveToElementText(s);k.collapse(true);l=s;n=true}else{k.collapse(false)}o=0;while(k.compareEndPoints(j?"StartToStart":"StartToEnd",i)!==0){if(k.move("character",1)===0||s!=k.parentElement()){break}o++}}else{k.collapse(true);o=0;while(k.compareEndPoints(j?"StartToStart":"StartToEnd",i)!==0){if(k.move("character",-1)===0||s!=k.parentElement()){break}o++}}return{node:l,position:p,offset:o,inside:n}}function g(){var i=d.getRng(),r=h.createRng(),l,k,p,q,m,j;l=i.item?i.item(0):i.parentElement();if(l.ownerDocument!=h.doc){return r}k=d.isCollapsed();if(i.item){r.setStart(l.parentNode,h.nodeIndex(l));r.setEnd(r.startContainer,r.startOffset+1);return r}function o(A){var u=e(i,A),s,y,z=0,x,v,t;s=u.node;y=u.offset;if(u.inside&&!s.hasChildNodes()){r[A?"setStart":"setEnd"](s,0);return}if(y===v){r[A?"setStartBefore":"setEndAfter"](s);return}if(u.position<0){x=u.inside?s.firstChild:s.nextSibling;if(!x){r[A?"setStartAfter":"setEndAfter"](s);return}if(!y){if(x.nodeType==3){r[A?"setStart":"setEnd"](x,0)}else{r[A?"setStartBefore":"setEndBefore"](x)}return}while(x){t=x.nodeValue;z+=t.length;if(z>=y){s=x;z-=y;z=t.length-z;break}x=x.nextSibling}}else{x=s.previousSibling;if(!x){return r[A?"setStartBefore":"setEndBefore"](s)}if(!y){if(s.nodeType==3){r[A?"setStart":"setEnd"](x,s.nodeValue.length)}else{r[A?"setStartAfter":"setEndAfter"](x)}return}while(x){z+=x.nodeValue.length;if(z>=y){s=x;z-=y;break}x=x.previousSibling}}r[A?"setStart":"setEnd"](s,z)}try{o(true);if(!k){o()}}catch(n){if(n.number==-2147024809){m=b.getBookmark(2);p=i.duplicate();p.collapse(true);l=p.parentElement();if(!k){p=i.duplicate();p.collapse(false);q=p.parentElement();q.innerHTML=q.innerHTML}l.innerHTML=l.innerHTML;b.moveToBookmark(m);i=d.getRng();o(true);if(!k){o()}}else{throw n}}return r}this.getBookmark=function(m){var j=d.getRng(),o,i,l={};function n(u){var t,p,s,r,q=[];t=u.parentNode;p=h.getRoot().parentNode;while(t!=p&&t.nodeType!==9){s=t.children;r=s.length;while(r--){if(u===s[r]){q.push(r);break}}u=t;t=t.parentNode}return q}function k(q){var p;p=e(j,q);if(p){return{position:p.position,offset:p.offset,indexes:n(p.node),inside:p.inside}}}if(m===2){if(!j.item){l.start=k(true);if(!d.isCollapsed()){l.end=k()}}else{l.start={ctrl:true,indexes:n(j.item(0))}}}return l};this.moveToBookmark=function(k){var j,i=h.doc.body;function m(o){var r,q,n,p;r=h.getRoot();for(q=o.length-1;q>=0;q--){p=r.children;n=o[q];if(n<=p.length-1){r=p[n]}}return r}function l(r){var n=k[r?"start":"end"],q,p,o;if(n){q=n.position>0;p=i.createTextRange();p.moveToElementText(m(n.indexes));offset=n.offset;if(offset!==o){p.collapse(n.inside||q);p.moveStart("character",q?-offset:offset)}else{p.collapse(r)}j.setEndPoint(r?"StartToStart":"EndToStart",p);if(r){j.collapse(true)}}}if(k.start){if(k.start.ctrl){j=i.createControlRange();j.addElement(m(k.start.indexes));j.select()}else{j=i.createTextRange();l(true);l();j.select()}}};this.addRange=function(i){var n,l,k,p,v,q,t,s=d.dom.doc,m=s.body,r,u;function j(C){var y,B,x,A,z;x=h.create("a");y=C?k:v;B=C?p:q;A=n.duplicate();if(y==s||y==s.documentElement){y=m;B=0}if(y.nodeType==3){y.parentNode.insertBefore(x,y);A.moveToElementText(x);A.moveStart("character",B);h.remove(x);n.setEndPoint(C?"StartToStart":"EndToEnd",A)}else{z=y.childNodes;if(z.length){if(B>=z.length){h.insertAfter(x,z[z.length-1])}else{y.insertBefore(x,z[B])}A.moveToElementText(x)}else{if(y.canHaveHTML){y.innerHTML="<span>\uFEFF</span>";x=y.firstChild;A.moveToElementText(x);A.collapse(f)}}n.setEndPoint(C?"StartToStart":"EndToEnd",A);h.remove(x)}}k=i.startContainer;p=i.startOffset;v=i.endContainer;q=i.endOffset;n=m.createTextRange();if(k==v&&k.nodeType==1){if(p==q&&!k.hasChildNodes()){if(k.canHaveHTML){t=k.previousSibling;if(t&&!t.hasChildNodes()&&h.isBlock(t)){t.innerHTML="\uFEFF"}else{t=null}k.innerHTML="<span>\uFEFF</span><span>\uFEFF</span>";n.moveToElementText(k.lastChild);n.select();h.doc.selection.clear();k.innerHTML="";if(t){t.innerHTML=""}return}else{p=h.nodeIndex(k);k=k.parentNode}}if(p==q-1){try{u=k.childNodes[p];l=m.createControlRange();l.addElement(u);l.select();r=d.getRng();if(r.item&&u===r.item(0)){return}}catch(o){}}}j(true);j();n.select()};this.getRangeAt=g}tinymce.dom.TridentSelection=a})();(function(){var n=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,i="sizcache",o=0,r=Object.prototype.toString,h=false,g=true,q=/\\/g,u=/\r\n/g,x=/\W/;[0,0].sort(function(){g=false;return 0});var d=function(C,e,F,G){F=F||[];e=e||document;var I=e;if(e.nodeType!==1&&e.nodeType!==9){return[]}if(!C||typeof C!=="string"){return F}var z,K,N,y,J,M,L,E,B=true,A=d.isXML(e),D=[],H=C;do{n.exec("");z=n.exec(H);if(z){H=z[3];D.push(z[1]);if(z[2]){y=z[3];break}}}while(z);if(D.length>1&&j.exec(C)){if(D.length===2&&k.relative[D[0]]){K=s(D[0]+D[1],e,G)}else{K=k.relative[D[0]]?[e]:d(D.shift(),e);while(D.length){C=D.shift();if(k.relative[C]){C+=D.shift()}K=s(C,K,G)}}}else{if(!G&&D.length>1&&e.nodeType===9&&!A&&k.match.ID.test(D[0])&&!k.match.ID.test(D[D.length-1])){J=d.find(D.shift(),e,A);e=J.expr?d.filter(J.expr,J.set)[0]:J.set[0]}if(e){J=G?{expr:D.pop(),set:l(G)}:d.find(D.pop(),D.length===1&&(D[0]==="~"||D[0]==="+")&&e.parentNode?e.parentNode:e,A);K=J.expr?d.filter(J.expr,J.set):J.set;if(D.length>0){N=l(K)}else{B=false}while(D.length){M=D.pop();L=M;if(!k.relative[M]){M=""}else{L=D.pop()}if(L==null){L=e}k.relative[M](N,L,A)}}else{N=D=[]}}if(!N){N=K}if(!N){d.error(M||C)}if(r.call(N)==="[object Array]"){if(!B){F.push.apply(F,N)}else{if(e&&e.nodeType===1){for(E=0;N[E]!=null;E++){if(N[E]&&(N[E]===true||N[E].nodeType===1&&d.contains(e,N[E]))){F.push(K[E])}}}else{for(E=0;N[E]!=null;E++){if(N[E]&&N[E].nodeType===1){F.push(K[E])}}}}}else{l(N,F)}if(y){d(y,I,F,G);d.uniqueSort(F)}return F};d.uniqueSort=function(y){if(p){h=g;y.sort(p);if(h){for(var e=1;e<y.length;e++){if(y[e]===y[e-1]){y.splice(e--,1)}}}}return y};d.matches=function(e,y){return d(e,null,null,y)};d.matchesSelector=function(e,y){return d(y,null,null,[e]).length>0};d.find=function(E,e,F){var D,z,B,A,C,y;if(!E){return[]}for(z=0,B=k.order.length;z<B;z++){C=k.order[z];if((A=k.leftMatch[C].exec(E))){y=A[1];A.splice(1,1);if(y.substr(y.length-1)!=="\\"){A[1]=(A[1]||"").replace(q,"");D=k.find[C](A,e,F);if(D!=null){E=E.replace(k.match[C],"");break}}}}if(!D){D=typeof e.getElementsByTagName!=="undefined"?e.getElementsByTagName("*"):[]}return{set:D,expr:E}};d.filter=function(I,H,L,B){var D,e,G,N,K,y,A,C,J,z=I,M=[],F=H,E=H&&H[0]&&d.isXML(H[0]);while(I&&H.length){for(G in k.filter){if((D=k.leftMatch[G].exec(I))!=null&&D[2]){y=k.filter[G];A=D[1];e=false;D.splice(1,1);if(A.substr(A.length-1)==="\\"){continue}if(F===M){M=[]}if(k.preFilter[G]){D=k.preFilter[G](D,F,L,M,B,E);if(!D){e=N=true}else{if(D===true){continue}}}if(D){for(C=0;(K=F[C])!=null;C++){if(K){N=y(K,D,C,F);J=B^N;if(L&&N!=null){if(J){e=true}else{F[C]=false}}else{if(J){M.push(K);e=true}}}}}if(N!==undefined){if(!L){F=M}I=I.replace(k.match[G],"");if(!e){return[]}break}}}if(I===z){if(e==null){d.error(I)}else{break}}z=I}return F};d.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)};var b=d.getText=function(B){var z,A,e=B.nodeType,y="";if(e){if(e===1||e===9||e===11){if(typeof B.textContent==="string"){return B.textContent}else{if(typeof B.innerText==="string"){return B.innerText.replace(u,"")}else{for(B=B.firstChild;B;B=B.nextSibling){y+=b(B)}}}}else{if(e===3||e===4){return B.nodeValue}}}else{for(z=0;(A=B[z]);z++){if(A.nodeType!==8){y+=b(A)}}}return y};var k=d.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(e){return e.getAttribute("href")},type:function(e){return e.getAttribute("type")}},relative:{"+":function(D,y){var A=typeof y==="string",C=A&&!x.test(y),E=A&&!C;if(C){y=y.toLowerCase()}for(var z=0,e=D.length,B;z<e;z++){if((B=D[z])){while((B=B.previousSibling)&&B.nodeType!==1){}D[z]=E||B&&B.nodeName.toLowerCase()===y?B||false:B===y}}if(E){d.filter(y,D,true)}},">":function(D,y){var C,B=typeof y==="string",z=0,e=D.length;if(B&&!x.test(y)){y=y.toLowerCase();for(;z<e;z++){C=D[z];if(C){var A=C.parentNode;D[z]=A.nodeName.toLowerCase()===y?A:false}}}else{for(;z<e;z++){C=D[z];if(C){D[z]=B?C.parentNode:C.parentNode===y}}if(B){d.filter(y,D,true)}}},"":function(A,y,C){var B,z=o++,e=t;if(typeof y==="string"&&!x.test(y)){y=y.toLowerCase();B=y;e=a}e("parentNode",y,z,A,B,C)},"~":function(A,y,C){var B,z=o++,e=t;if(typeof y==="string"&&!x.test(y)){y=y.toLowerCase();B=y;e=a}e("previousSibling",y,z,A,B,C)}},find:{ID:function(y,z,A){if(typeof z.getElementById!=="undefined"&&!A){var e=z.getElementById(y[1]);return e&&e.parentNode?[e]:[]}},NAME:function(z,C){if(typeof C.getElementsByName!=="undefined"){var y=[],B=C.getElementsByName(z[1]);for(var A=0,e=B.length;A<e;A++){if(B[A].getAttribute("name")===z[1]){y.push(B[A])}}return y.length===0?null:y}},TAG:function(e,y){if(typeof y.getElementsByTagName!=="undefined"){return y.getElementsByTagName(e[1])}}},preFilter:{CLASS:function(A,y,z,e,D,E){A=" "+A[1].replace(q,"")+" ";if(E){return A}for(var B=0,C;(C=y[B])!=null;B++){if(C){if(D^(C.className&&(" "+C.className+" ").replace(/[\t\n\r]/g," ").indexOf(A)>=0)){if(!z){e.push(C)}}else{if(z){y[B]=false}}}}return false},ID:function(e){return e[1].replace(q,"")},TAG:function(y,e){return y[1].replace(q,"").toLowerCase()},CHILD:function(e){if(e[1]==="nth"){if(!e[2]){d.error(e[0])}e[2]=e[2].replace(/^\+|\s*/g,"");var y=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(e[2]==="even"&&"2n"||e[2]==="odd"&&"2n+1"||!/\D/.test(e[2])&&"0n+"+e[2]||e[2]);e[2]=(y[1]+(y[2]||1))-0;e[3]=y[3]-0}else{if(e[2]){d.error(e[0])}}e[0]=o++;return e},ATTR:function(B,y,z,e,C,D){var A=B[1]=B[1].replace(q,"");if(!D&&k.attrMap[A]){B[1]=k.attrMap[A]}B[4]=(B[4]||B[5]||"").replace(q,"");if(B[2]==="~="){B[4]=" "+B[4]+" "}return B},PSEUDO:function(B,y,z,e,C){if(B[1]==="not"){if((n.exec(B[3])||"").length>1||/^\w/.test(B[3])){B[3]=d(B[3],null,null,y)}else{var A=d.filter(B[3],y,z,true^C);if(!z){e.push.apply(e,A)}return false}}else{if(k.match.POS.test(B[0])||k.match.CHILD.test(B[0])){return true}}return B},POS:function(e){e.unshift(true);return e}},filters:{enabled:function(e){return e.disabled===false&&e.type!=="hidden"},disabled:function(e){return e.disabled===true},checked:function(e){return e.checked===true},selected:function(e){if(e.parentNode){e.parentNode.selectedIndex}return e.selected===true},parent:function(e){return !!e.firstChild},empty:function(e){return !e.firstChild},has:function(z,y,e){return !!d(e[3],z).length},header:function(e){return(/h\d/i).test(e.nodeName)},text:function(z){var e=z.getAttribute("type"),y=z.type;return z.nodeName.toLowerCase()==="input"&&"text"===y&&(e===y||e===null)},radio:function(e){return e.nodeName.toLowerCase()==="input"&&"radio"===e.type},checkbox:function(e){return e.nodeName.toLowerCase()==="input"&&"checkbox"===e.type},file:function(e){return e.nodeName.toLowerCase()==="input"&&"file"===e.type},password:function(e){return e.nodeName.toLowerCase()==="input"&&"password"===e.type},submit:function(y){var e=y.nodeName.toLowerCase();return(e==="input"||e==="button")&&"submit"===y.type},image:function(e){return e.nodeName.toLowerCase()==="input"&&"image"===e.type},reset:function(y){var e=y.nodeName.toLowerCase();return(e==="input"||e==="button")&&"reset"===y.type},button:function(y){var e=y.nodeName.toLowerCase();return e==="input"&&"button"===y.type||e==="button"},input:function(e){return(/input|select|textarea|button/i).test(e.nodeName)},focus:function(e){return e===e.ownerDocument.activeElement}},setFilters:{first:function(y,e){return e===0},last:function(z,y,e,A){return y===A.length-1},even:function(y,e){return e%2===0},odd:function(y,e){return e%2===1},lt:function(z,y,e){return y<e[3]-0},gt:function(z,y,e){return y>e[3]-0},nth:function(z,y,e){return e[3]-0===y},eq:function(z,y,e){return e[3]-0===y}},filter:{PSEUDO:function(z,E,D,F){var e=E[1],y=k.filters[e];if(y){return y(z,D,E,F)}else{if(e==="contains"){return(z.textContent||z.innerText||b([z])||"").indexOf(E[3])>=0}else{if(e==="not"){var A=E[3];for(var C=0,B=A.length;C<B;C++){if(A[C]===z){return false}}return true}else{d.error(e)}}}},CHILD:function(z,B){var A,H,D,G,e,C,F,E=B[1],y=z;switch(E){case"only":case"first":while((y=y.previousSibling)){if(y.nodeType===1){return false}}if(E==="first"){return true}y=z;case"last":while((y=y.nextSibling)){if(y.nodeType===1){return false}}return true;case"nth":A=B[2];H=B[3];if(A===1&&H===0){return true}D=B[0];G=z.parentNode;if(G&&(G[i]!==D||!z.nodeIndex)){C=0;for(y=G.firstChild;y;y=y.nextSibling){if(y.nodeType===1){y.nodeIndex=++C}}G[i]=D}F=z.nodeIndex-H;if(A===0){return F===0}else{return(F%A===0&&F/A>=0)}}},ID:function(y,e){return y.nodeType===1&&y.getAttribute("id")===e},TAG:function(y,e){return(e==="*"&&y.nodeType===1)||!!y.nodeName&&y.nodeName.toLowerCase()===e},CLASS:function(y,e){return(" "+(y.className||y.getAttribute("class"))+" ").indexOf(e)>-1},ATTR:function(C,A){var z=A[1],e=d.attr?d.attr(C,z):k.attrHandle[z]?k.attrHandle[z](C):C[z]!=null?C[z]:C.getAttribute(z),D=e+"",B=A[2],y=A[4];return e==null?B==="!=":!B&&d.attr?e!=null:B==="="?D===y:B==="*="?D.indexOf(y)>=0:B==="~="?(" "+D+" ").indexOf(y)>=0:!y?D&&e!==false:B==="!="?D!==y:B==="^="?D.indexOf(y)===0:B==="$="?D.substr(D.length-y.length)===y:B==="|="?D===y||D.substr(0,y.length+1)===y+"-":false},POS:function(B,y,z,C){var e=y[2],A=k.setFilters[e];if(A){return A(B,z,y,C)}}}};var j=k.match.POS,c=function(y,e){return"\\"+(e-0+1)};for(var f in k.match){k.match[f]=new RegExp(k.match[f].source+(/(?![^\[]*\])(?![^\(]*\))/.source));k.leftMatch[f]=new RegExp(/(^(?:.|\r|\n)*?)/.source+k.match[f].source.replace(/\\(\d+)/g,c))}k.match.globalPOS=j;var l=function(y,e){y=Array.prototype.slice.call(y,0);if(e){e.push.apply(e,y);return e}return y};try{Array.prototype.slice.call(document.documentElement.childNodes,0)[0].nodeType}catch(v){l=function(B,A){var z=0,y=A||[];if(r.call(B)==="[object Array]"){Array.prototype.push.apply(y,B)}else{if(typeof B.length==="number"){for(var e=B.length;z<e;z++){y.push(B[z])}}else{for(;B[z];z++){y.push(B[z])}}}return y}}var p,m;if(document.documentElement.compareDocumentPosition){p=function(y,e){if(y===e){h=true;return 0}if(!y.compareDocumentPosition||!e.compareDocumentPosition){return y.compareDocumentPosition?-1:1}return y.compareDocumentPosition(e)&4?-1:1}}else{p=function(F,E){if(F===E){h=true;return 0}else{if(F.sourceIndex&&E.sourceIndex){return F.sourceIndex-E.sourceIndex}}var C,y,z=[],e=[],B=F.parentNode,D=E.parentNode,G=B;if(B===D){return m(F,E)}else{if(!B){return -1}else{if(!D){return 1}}}while(G){z.unshift(G);G=G.parentNode}G=D;while(G){e.unshift(G);G=G.parentNode}C=z.length;y=e.length;for(var A=0;A<C&&A<y;A++){if(z[A]!==e[A]){return m(z[A],e[A])}}return A===C?m(F,e[A],-1):m(z[A],E,1)};m=function(y,e,z){if(y===e){return z}var A=y.nextSibling;while(A){if(A===e){return -1}A=A.nextSibling}return 1}}(function(){var y=document.createElement("div"),z="script"+(new Date()).getTime(),e=document.documentElement;y.innerHTML="<a name='"+z+"'/>";e.insertBefore(y,e.firstChild);if(document.getElementById(z)){k.find.ID=function(B,C,D){if(typeof C.getElementById!=="undefined"&&!D){var A=C.getElementById(B[1]);return A?A.id===B[1]||typeof A.getAttributeNode!=="undefined"&&A.getAttributeNode("id").nodeValue===B[1]?[A]:undefined:[]}};k.filter.ID=function(C,A){var B=typeof C.getAttributeNode!=="undefined"&&C.getAttributeNode("id");return C.nodeType===1&&B&&B.nodeValue===A}}e.removeChild(y);e=y=null})();(function(){var e=document.createElement("div");e.appendChild(document.createComment(""));if(e.getElementsByTagName("*").length>0){k.find.TAG=function(y,C){var B=C.getElementsByTagName(y[1]);if(y[1]==="*"){var A=[];for(var z=0;B[z];z++){if(B[z].nodeType===1){A.push(B[z])}}B=A}return B}}e.innerHTML="<a href='#'></a>";if(e.firstChild&&typeof e.firstChild.getAttribute!=="undefined"&&e.firstChild.getAttribute("href")!=="#"){k.attrHandle.href=function(y){return y.getAttribute("href",2)}}e=null})();if(document.querySelectorAll){(function(){var e=d,A=document.createElement("div"),z="__sizzle__";A.innerHTML="<p class='TEST'></p>";if(A.querySelectorAll&&A.querySelectorAll(".TEST").length===0){return}d=function(L,C,G,K){C=C||document;if(!K&&!d.isXML(C)){var J=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(L);if(J&&(C.nodeType===1||C.nodeType===9)){if(J[1]){return l(C.getElementsByTagName(L),G)}else{if(J[2]&&k.find.CLASS&&C.getElementsByClassName){return l(C.getElementsByClassName(J[2]),G)}}}if(C.nodeType===9){if(L==="body"&&C.body){return l([C.body],G)}else{if(J&&J[3]){var F=C.getElementById(J[3]);if(F&&F.parentNode){if(F.id===J[3]){return l([F],G)}}else{return l([],G)}}}try{return l(C.querySelectorAll(L),G)}catch(H){}}else{if(C.nodeType===1&&C.nodeName.toLowerCase()!=="object"){var D=C,E=C.getAttribute("id"),B=E||z,N=C.parentNode,M=/^\s*[+~]/.test(L);if(!E){C.setAttribute("id",B)}else{B=B.replace(/'/g,"\\$&")}if(M&&N){C=C.parentNode}try{if(!M||N){return l(C.querySelectorAll("[id='"+B+"'] "+L),G)}}catch(I){}finally{if(!E){D.removeAttribute("id")}}}}}return e(L,C,G,K)};for(var y in e){d[y]=e[y]}A=null})()}(function(){var e=document.documentElement,z=e.matchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.msMatchesSelector;if(z){var B=!z.call(document.createElement("div"),"div"),y=false;try{z.call(document.documentElement,"[test!='']:sizzle")}catch(A){y=true}d.matchesSelector=function(D,F){F=F.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!d.isXML(D)){try{if(y||!k.match.PSEUDO.test(F)&&!/!=/.test(F)){var C=z.call(D,F);if(C||!B||D.document&&D.document.nodeType!==11){return C}}}catch(E){}}return d(F,null,null,[D]).length>0}}})();(function(){var e=document.createElement("div");e.innerHTML="<div class='test e'></div><div class='test'></div>";if(!e.getElementsByClassName||e.getElementsByClassName("e").length===0){return}e.lastChild.className="e";if(e.getElementsByClassName("e").length===1){return}k.order.splice(1,0,"CLASS");k.find.CLASS=function(y,z,A){if(typeof z.getElementsByClassName!=="undefined"&&!A){return z.getElementsByClassName(y[1])}};e=null})();function a(y,D,C,G,E,F){for(var A=0,z=G.length;A<z;A++){var e=G[A];if(e){var B=false;e=e[y];while(e){if(e[i]===C){B=G[e.sizset];break}if(e.nodeType===1&&!F){e[i]=C;e.sizset=A}if(e.nodeName.toLowerCase()===D){B=e;break}e=e[y]}G[A]=B}}}function t(y,D,C,G,E,F){for(var A=0,z=G.length;A<z;A++){var e=G[A];if(e){var B=false;e=e[y];while(e){if(e[i]===C){B=G[e.sizset];break}if(e.nodeType===1){if(!F){e[i]=C;e.sizset=A}if(typeof D!=="string"){if(e===D){B=true;break}}else{if(d.filter(D,[e]).length>0){B=e;break}}}e=e[y]}G[A]=B}}}if(document.documentElement.contains){d.contains=function(y,e){return y!==e&&(y.contains?y.contains(e):true)}}else{if(document.documentElement.compareDocumentPosition){d.contains=function(y,e){return !!(y.compareDocumentPosition(e)&16)}}else{d.contains=function(){return false}}}d.isXML=function(e){var y=(e?e.ownerDocument||e:0).documentElement;return y?y.nodeName!=="HTML":false};var s=function(z,e,D){var C,E=[],B="",F=e.nodeType?[e]:e;while((C=k.match.PSEUDO.exec(z))){B+=C[0];z=z.replace(k.match.PSEUDO,"")}z=k.relative[z]?z+"*":z;for(var A=0,y=F.length;A<y;A++){d(z,F[A],E,D)}return d.filter(B,E)};window.tinymce.dom.Sizzle=d})();(function(a){a.dom.Element=function(f,d){var b=this,e,c;b.settings=d=d||{};b.id=f;b.dom=e=d.dom||a.DOM;if(!a.isIE){c=e.get(b.id)}a.each(("getPos,getRect,getParent,add,setStyle,getStyle,setStyles,setAttrib,setAttribs,getAttrib,addClass,removeClass,hasClass,getOuterHTML,setOuterHTML,remove,show,hide,isHidden,setHTML,get").split(/,/),function(g){b[g]=function(){var h=[f],j;for(j=0;j<arguments.length;j++){h.push(arguments[j])}h=e[g].apply(e,h);b.update(g);return h}});a.extend(b,{on:function(i,h,g){return a.dom.Event.add(b.id,i,h,g)},getXY:function(){return{x:parseInt(b.getStyle("left")),y:parseInt(b.getStyle("top"))}},getSize:function(){var g=e.get(b.id);return{w:parseInt(b.getStyle("width")||g.clientWidth),h:parseInt(b.getStyle("height")||g.clientHeight)}},moveTo:function(g,h){b.setStyles({left:g,top:h})},moveBy:function(g,i){var h=b.getXY();b.moveTo(h.x+g,h.y+i)},resizeTo:function(g,i){b.setStyles({width:g,height:i})},resizeBy:function(g,j){var i=b.getSize();b.resizeTo(i.w+g,i.h+j)},update:function(h){var g;if(a.isIE6&&d.blocker){h=h||"";if(h.indexOf("get")===0||h.indexOf("has")===0||h.indexOf("is")===0){return}if(h=="remove"){e.remove(b.blocker);return}if(!b.blocker){b.blocker=e.uniqueId();g=e.add(d.container||e.getRoot(),"iframe",{id:b.blocker,style:"position:absolute;",frameBorder:0,src:'javascript:""'});e.setStyle(g,"opacity",0)}else{g=e.get(b.blocker)}e.setStyles(g,{left:b.getStyle("left",1),top:b.getStyle("top",1),width:b.getStyle("width",1),height:b.getStyle("height",1),display:b.getStyle("display",1),zIndex:parseInt(b.getStyle("zIndex",1)||0)-1})}}})}})(tinymce);(function(d){function f(g){return g.replace(/[\n\r]+/g,"")}var c=d.is,b=d.isIE,e=d.each,a=d.dom.TreeWalker;d.create("tinymce.dom.Selection",{Selection:function(k,j,i,h){var g=this;g.dom=k;g.win=j;g.serializer=i;g.editor=h;e(["onBeforeSetContent","onBeforeGetContent","onSetContent","onGetContent"],function(l){g[l]=new d.util.Dispatcher(g)});if(!g.win.getSelection){g.tridentSel=new d.dom.TridentSelection(g)}if(d.isIE&&!d.isIE11&&k.boxModel){this._fixIESelection()}d.addUnload(g.destroy,g)},setCursorLocation:function(i,j){var g=this;var h=g.dom.createRng();h.setStart(i,j);h.setEnd(i,j);g.setRng(h);g.collapse(false)},getContent:function(h){var g=this,i=g.getRng(),m=g.dom.create("body"),k=g.getSel(),j,l,o;h=h||{};j=l="";h.get=true;h.format=h.format||"html";h.forced_root_block="";g.onBeforeGetContent.dispatch(g,h);if(h.format=="text"){return g.isCollapsed()?"":(i.text||(k.toString?k.toString():""))}if(i.cloneContents){o=i.cloneContents();if(o){m.appendChild(o)}}else{if(c(i.item)||c(i.htmlText)){m.innerHTML="<br>"+(i.item?i.item(0).outerHTML:i.htmlText);m.removeChild(m.firstChild)}else{m.innerHTML=i.toString()}}if(/^\s/.test(m.innerHTML)){j=" "}if(/\s+$/.test(m.innerHTML)){l=" "}h.getInner=true;h.content=g.isCollapsed()?"":j+g.serializer.serialize(m,h)+l;g.onGetContent.dispatch(g,h);return h.content},setContent:function(h,j){var o=this,g=o.getRng(),k,l=o.win.document,n,m;j=j||{format:"html"};j.set=true;h=j.content=h;if(!j.no_events){o.onBeforeSetContent.dispatch(o,j)}h=j.content;if(g.insertNode){h+='<span id="__caret">_</span>';if(g.startContainer==l&&g.endContainer==l){l.body.innerHTML=h}else{g.deleteContents();if(l.body.childNodes.length===0){l.body.innerHTML=h}else{if(g.createContextualFragment){g.insertNode(g.createContextualFragment(h))}else{n=l.createDocumentFragment();m=l.createElement("div");n.appendChild(m);m.outerHTML=h;g.insertNode(n)}}}k=o.dom.get("__caret");g=l.createRange();g.setStartBefore(k);g.setEndBefore(k);o.setRng(g);o.dom.remove("__caret");try{o.setRng(g)}catch(i){}}else{if(g.item){l.execCommand("Delete",false,null);g=o.getRng()}if(/^\s+/.test(h)){g.pasteHTML('<span id="__mce_tmp">_</span>'+h);o.dom.remove("__mce_tmp")}else{g.pasteHTML(h)}}if(!j.no_events){o.onSetContent.dispatch(o,j)}},getStart:function(){var i=this,h=i.getRng(),j,g,l,k;if(h.duplicate||h.item){if(h.item){return h.item(0)}l=h.duplicate();l.collapse(1);j=l.parentElement();if(j.ownerDocument!==i.dom.doc){j=i.dom.getRoot()}g=k=h.parentElement();while(k=k.parentNode){if(k==j){j=g;break}}return j}else{j=h.startContainer;if(j.nodeType==1&&j.hasChildNodes()){j=j.childNodes[Math.min(j.childNodes.length-1,h.startOffset)]}if(j&&j.nodeType==3){return j.parentNode}return j}},getEnd:function(){var h=this,g=h.getRng(),j,i;if(g.duplicate||g.item){if(g.item){return g.item(0)}g=g.duplicate();g.collapse(0);j=g.parentElement();if(j.ownerDocument!==h.dom.doc){j=h.dom.getRoot()}if(j&&j.nodeName=="BODY"){return j.lastChild||j}return j}else{j=g.endContainer;i=g.endOffset;if(j.nodeType==1&&j.hasChildNodes()){j=j.childNodes[i>0?i-1:i]}if(j&&j.nodeType==3){return j.parentNode}return j}},getBookmark:function(s,v){var y=this,n=y.dom,h,k,j,o,i,p,q,m="\uFEFF",x;function g(z,A){var t=0;e(n.select(z),function(C,B){if(C==A){t=B}});return t}function u(t){function z(E){var A,D,C,B=E?"start":"end";A=t[B+"Container"];D=t[B+"Offset"];if(A.nodeType==1&&A.nodeName=="TR"){C=A.childNodes;A=C[Math.min(E?D:D-1,C.length-1)];if(A){D=E?0:A.childNodes.length;t["set"+(E?"Start":"End")](A,D)}}}z(true);z();return t}function l(){var z=y.getRng(true),t=n.getRoot(),A={};function B(E,J){var D=E[J?"startContainer":"endContainer"],I=E[J?"startOffset":"endOffset"],C=[],F,H,G=0;if(D.nodeType==3){if(v){for(F=D.previousSibling;F&&F.nodeType==3;F=F.previousSibling){I+=F.nodeValue.length}}C.push(I)}else{H=D.childNodes;if(I>=H.length&&H.length){G=1;I=Math.max(0,H.length-1)}C.push(y.dom.nodeIndex(H[I],v)+G)}for(;D&&D!=t;D=D.parentNode){C.push(y.dom.nodeIndex(D,v))}return C}A.start=B(z,true);if(!y.isCollapsed()){A.end=B(z)}return A}if(s==2){if(y.tridentSel){return y.tridentSel.getBookmark(s)}return l()}if(s){h=y.getRng();if(h.setStart){h={startContainer:h.startContainer,startOffset:h.startOffset,endContainer:h.endContainer,endOffset:h.endOffset}}return{rng:h}}h=y.getRng();j=n.uniqueId();o=tinyMCE.activeEditor.selection.isCollapsed();x="overflow:hidden;line-height:0px";if(h.duplicate||h.item){if(!h.item){k=h.duplicate();try{h.collapse();h.pasteHTML('<span data-mce-type="bookmark" id="'+j+'_start" style="'+x+'">'+m+"</span>");if(!o){k.collapse(false);h.moveToElementText(k.parentElement());if(h.compareEndPoints("StartToEnd",k)===0){k.move("character",-1)}k.pasteHTML('<span data-mce-type="bookmark" id="'+j+'_end" style="'+x+'">'+m+"</span>")}}catch(r){return null}}else{p=h.item(0);i=p.nodeName;return{name:i,index:g(i,p)}}}else{p=y.getNode();i=p.nodeName;if(i=="IMG"){return{name:i,index:g(i,p)}}k=u(h.cloneRange());if(!o){k.collapse(false);k.insertNode(n.create("span",{"data-mce-type":"bookmark",id:j+"_end",style:x},m))}h=u(h);h.collapse(true);h.insertNode(n.create("span",{"data-mce-type":"bookmark",id:j+"_start",style:x},m))}y.moveToBookmark({id:j,keep:1});return{id:j}},moveToBookmark:function(q){var v=this,n=v.dom,l,j,g,i,u,k,x,r,s;function h(C){var t=q[C?"start":"end"],z,A,B,y;if(t){B=t[0];for(A=u,z=t.length-1;z>=1;z--){y=A.childNodes;if(t[z]>y.length-1){return}A=y[t[z]]}if(A.nodeType===3){B=Math.min(t[0],A.nodeValue.length)}if(A.nodeType===1){B=Math.min(t[0],A.childNodes.length)}if(C){g.setStart(A,B)}else{g.setEnd(A,B)}}return true}function m(D){var y=n.get(q.id+"_"+D),C,t,A,B,z=q.keep;if(y){C=y.parentNode;if(D=="start"){if(!z){t=n.nodeIndex(y)}else{C=y.firstChild;t=1}k=x=C;r=s=t}else{if(!z){t=n.nodeIndex(y)}else{C=y.firstChild;t=1}x=C;s=t}if(!z){B=y.previousSibling;A=y.nextSibling;e(d.grep(y.childNodes),function(E){if(E.nodeType==3){E.nodeValue=E.nodeValue.replace(/\uFEFF/g,"")}});while(y=n.get(q.id+"_"+D)){n.remove(y,1)}if(B&&A&&B.nodeType==A.nodeType&&B.nodeType==3&&!d.isOpera){t=B.nodeValue.length;B.appendData(A.nodeValue);n.remove(A);if(D=="start"){k=x=B;r=s=t}else{x=B;s=t}}}}}function o(t){if(n.isBlock(t)&&!t.innerHTML&&!b){t.innerHTML='<br data-mce-bogus="1" />'}return t}if(q){if(q.start){g=n.createRng();u=n.getRoot();if(v.tridentSel){return v.tridentSel.moveToBookmark(q)}if(h(true)&&h()){v.setRng(g)}}else{if(q.id){m("start");m("end");if(k){g=n.createRng();g.setStart(o(k),r);g.setEnd(o(x),s);v.setRng(g)}}else{if(q.name){v.select(n.select(q.name)[q.index])}else{if(q.rng){g=q.rng;if(g.startContainer){i=v.dom.createRng();try{i.setStart(g.startContainer,g.startOffset);i.setEnd(g.endContainer,g.endOffset)}catch(p){}g=i}v.setRng(g)}}}}}},select:function(l,k){var j=this,m=j.dom,h=m.createRng(),g;function i(n,p){var o=new a(n,n);do{if(n.nodeType==3&&d.trim(n.nodeValue).length!==0){if(p){h.setStart(n,0)}else{h.setEnd(n,n.nodeValue.length)}return}if(n.nodeName=="BR"){if(p){h.setStartBefore(n)}else{h.setEndBefore(n)}return}}while(n=(p?o.next():o.prev()))}if(l){g=m.nodeIndex(l);h.setStart(l.parentNode,g);h.setEnd(l.parentNode,g+1);if(k){i(l,1);i(l)}j.setRng(h)}return l},isCollapsed:function(){var g=this,i=g.getRng(),h=g.getSel();if(!i||i.item){return false}if(i.compareEndPoints){return i.compareEndPoints("StartToEnd",i)===0}return !h||i.collapsed},collapse:function(g){var i=this,h=i.getRng(),j;if(h.item){j=h.item(0);h=i.win.document.body.createTextRange();h.moveToElementText(j)}h.collapse(!!g);i.setRng(h)},getSel:function(){var h=this,g=this.win;return g.getSelection?g.getSelection():g.document.selection},getRng:function(m){var h=this,j,g,l,k=h.win.document;if(h.fakeRng){return h.fakeRng}if(m&&h.tridentSel){return h.tridentSel.getRangeAt(0)}try{if(j=h.getSel()){g=j.rangeCount>0?j.getRangeAt(0):(j.createRange?j.createRange():k.createRange())}}catch(i){}if(d.isIE&&!d.isIE11&&g&&g.setStart&&k.selection.createRange().item){l=k.selection.createRange().item(0);g=k.createRange();g.setStartBefore(l);g.setEndAfter(l)}if(!g){g=k.createRange?k.createRange():k.body.createTextRange()}if(g.setStart&&g.startContainer.nodeType===9&&g.collapsed){l=h.dom.getRoot();g.setStart(l,0);g.setEnd(l,0)}if(h.selectedRange&&h.explicitRange){if(g.compareBoundaryPoints(g.START_TO_START,h.selectedRange)===0&&g.compareBoundaryPoints(g.END_TO_END,h.selectedRange)===0){g=h.explicitRange}else{h.selectedRange=null;h.explicitRange=null}}return g},setRng:function(k,g){var j,i=this;if(!i.tridentSel){j=i.getSel();if(j){i.explicitRange=k;try{j.removeAllRanges()}catch(h){}j.addRange(k);if(g===false&&j.extend){j.collapse(k.endContainer,k.endOffset);j.extend(k.startContainer,k.startOffset)}i.selectedRange=j.rangeCount>0?j.getRangeAt(0):null}}else{if(k.cloneRange){try{i.tridentSel.addRange(k);return}catch(h){}}try{k.select()}catch(h){}}},setNode:function(h){var g=this;g.setContent(g.dom.getOuterHTML(h));return h},getNode:function(){var i=this,h=i.getRng(),j=i.getSel(),m,l=h.startContainer,g=h.endContainer;function k(q,o){var p=q;while(q&&q.nodeType===3&&q.length===0){q=o?q.nextSibling:q.previousSibling}return q||p}if(!h){return i.dom.getRoot()}if(h.setStart){m=h.commonAncestorContainer;if(!h.collapsed){if(h.startContainer==h.endContainer){if(h.endOffset-h.startOffset<2){if(h.startContainer.hasChildNodes()){m=h.startContainer.childNodes[h.startOffset]}}}if(l.nodeType===3&&g.nodeType===3){if(l.length===h.startOffset){l=k(l.nextSibling,true)}else{l=l.parentNode}if(h.endOffset===0){g=k(g.previousSibling,false)}else{g=g.parentNode}if(l&&l===g){return l}}}if(m&&m.nodeType==3){return m.parentNode}return m}return h.item?h.item(0):h.parentElement()},getSelectedBlocks:function(p,h){var o=this,k=o.dom,m,l,i,j=[];m=k.getParent(p||o.getStart(),k.isBlock);l=k.getParent(h||o.getEnd(),k.isBlock);if(m){j.push(m)}if(m&&l&&m!=l){i=m;var g=new a(m,k.getRoot());while((i=g.next())&&i!=l){if(k.isBlock(i)){j.push(i)}}}if(l&&m!=l){j.push(l)}return j},isForward:function(){var i=this.dom,g=this.getSel(),j,h;if(!g||g.anchorNode==null||g.focusNode==null){return true}j=i.createRng();j.setStart(g.anchorNode,g.anchorOffset);j.collapse(true);h=i.createRng();h.setStart(g.focusNode,g.focusOffset);h.collapse(true);return j.compareBoundaryPoints(j.START_TO_START,h)<=0},normalize:function(){var h=this,g,m,l,j,i;function k(p){var o,r,n,s=h.dom,u=s.getRoot(),q,t,v;function y(z,A){var B=new a(z,s.getParent(z.parentNode,s.isBlock)||u);while(z=B[A?"prev":"next"]()){if(z.nodeName==="BR"){return true}}}function x(B,z){var C,A;z=z||o;C=new a(z,s.getParent(z.parentNode,s.isBlock)||u);while(q=C[B?"prev":"next"]()){if(q.nodeType===3&&q.nodeValue.length>0){o=q;r=B?q.nodeValue.length:0;m=true;return}if(s.isBlock(q)||t[q.nodeName.toLowerCase()]){return}A=q}if(l&&A){o=A;m=true;r=0}}o=g[(p?"start":"end")+"Container"];r=g[(p?"start":"end")+"Offset"];t=s.schema.getNonEmptyElements();if(o.nodeType===9){o=s.getRoot();r=0}if(o===u){if(p){q=o.childNodes[r>0?r-1:0];if(q){v=q.nodeName.toLowerCase();if(t[q.nodeName]||q.nodeName=="TABLE"){return}}}if(o.hasChildNodes()){o=o.childNodes[Math.min(!p&&r>0?r-1:r,o.childNodes.length-1)];r=0;if(o.hasChildNodes()&&!/TABLE/.test(o.nodeName)){q=o;n=new a(o,u);do{if(q.nodeType===3&&q.nodeValue.length>0){r=p?0:q.nodeValue.length;o=q;m=true;break}if(t[q.nodeName.toLowerCase()]){r=s.nodeIndex(q);o=q.parentNode;if(q.nodeName=="IMG"&&!p){r++}m=true;break}}while(q=(p?n.next():n.prev()))}}}if(l){if(o.nodeType===3&&r===0){x(true)}if(o.nodeType===1){q=o.childNodes[r];if(q&&q.nodeName==="BR"&&!y(q)&&!y(q,true)){x(true,o.childNodes[r])}}}if(p&&!l&&o.nodeType===3&&r===o.nodeValue.length){x(false)}if(m){g["set"+(p?"Start":"End")](o,r)}}if(d.isIE){return}g=h.getRng();l=g.collapsed;k(true);if(!l){k()}if(m){if(l){g.collapse(true)}h.setRng(g,h.isForward())}},selectorChanged:function(g,j){var h=this,i;if(!h.selectorChangedData){h.selectorChangedData={};i={};h.editor.onNodeChange.addToTop(function(l,k,o){var p=h.dom,m=p.getParents(o,null,p.getRoot()),n={};e(h.selectorChangedData,function(r,q){e(m,function(s){if(p.is(s,q)){if(!i[q]){e(r,function(t){t(true,{node:s,selector:q,parents:m})});i[q]=r}n[q]=r;return false}})});e(i,function(r,q){if(!n[q]){delete i[q];e(r,function(s){s(false,{node:o,selector:q,parents:m})})}})})}if(!h.selectorChangedData[g]){h.selectorChangedData[g]=[]}h.selectorChangedData[g].push(j);return h},scrollIntoView:function(k){var j,h,g=this,i=g.dom;h=i.getViewPort(g.editor.getWin());j=i.getPos(k).y;if(j<h.y||j+25>h.y+h.h){g.editor.getWin().scrollTo(0,j<h.y?j:j-h.h+25)}},destroy:function(h){var g=this;g.win=null;if(!h){d.removeUnload(g.destroy)}},_fixIESelection:function(){var h=this.dom,n=h.doc,i=n.body,k,o,g;function j(p,s){var q=i.createTextRange();try{q.moveToPoint(p,s)}catch(r){q=null}return q}function m(q){var p;if(q.button){p=j(q.x,q.y);if(p){if(p.compareEndPoints("StartToStart",o)>0){p.setEndPoint("StartToStart",o)}else{p.setEndPoint("EndToEnd",o)}p.select()}}else{l()}}function l(){var p=n.selection.createRange();if(o&&!p.item&&p.compareEndPoints("StartToEnd",p)===0){o.select()}h.unbind(n,"mouseup",l);h.unbind(n,"mousemove",m);o=k=0}n.documentElement.unselectable=true;h.bind(n,["mousedown","contextmenu"],function(p){if(p.target.nodeName==="HTML"){if(k){l()}g=n.documentElement;if(g.scrollHeight>g.clientHeight){return}k=1;o=j(p.x,p.y);if(o){h.bind(n,"mouseup",l);h.bind(n,"mousemove",m);h.win.focus();o.select()}}})}})})(tinymce);(function(a){a.dom.Serializer=function(e,i,f){var h,b,d=a.isIE,g=a.each,c;if(!e.apply_source_formatting){e.indent=false}i=i||a.DOM;f=f||new a.html.Schema(e);e.entity_encoding=e.entity_encoding||"named";e.remove_trailing_brs="remove_trailing_brs" in e?e.remove_trailing_brs:true;h=new a.util.Dispatcher(self);b=new a.util.Dispatcher(self);c=new a.html.DomParser(e,f);c.addAttributeFilter("src,href,style",function(k,j){var o=k.length,l,q,n="data-mce-"+j,p=e.url_converter,r=e.url_converter_scope,m;while(o--){l=k[o];q=l.attributes.map[n];if(q!==m){l.attr(j,q.length>0?q:null);l.attr(n,null)}else{q=l.attributes.map[j];if(j==="style"){q=i.serializeStyle(i.parseStyle(q),l.name)}else{if(p){q=p.call(r,q,j,l.name)}}l.attr(j,q.length>0?q:null)}}});c.addAttributeFilter("class",function(j,k){var l=j.length,m,n;while(l--){m=j[l];n=m.attr("class").replace(/(?:^|\s)mce(Item\w+|Selected)(?!\S)/g,"");m.attr("class",n.length>0?n:null)}});c.addAttributeFilter("data-mce-type",function(j,l,k){var m=j.length,n;while(m--){n=j[m];if(n.attributes.map["data-mce-type"]==="bookmark"&&!k.cleanup){n.remove()}}});c.addAttributeFilter("data-mce-expando",function(j,l,k){var m=j.length;while(m--){j[m].attr(l,null)}});c.addNodeFilter("noscript",function(j){var k=j.length,l;while(k--){l=j[k].firstChild;if(l){l.value=a.html.Entities.decode(l.value)}}});c.addNodeFilter("script,style",function(k,l){var m=k.length,n,o;function j(p){return p.replace(/(<!--\[CDATA\[|\]\]-->)/g,"\n").replace(/^[\r\n]*|[\r\n]*$/g,"").replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi,"").replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g,"")}while(m--){n=k[m];o=n.firstChild?n.firstChild.value:"";if(l==="script"){n.attr("type",(n.attr("type")||"text/javascript").replace(/^mce\-/,""));if(o.length>0){n.firstChild.value="// <![CDATA[\n"+j(o)+"\n// ]]>"}}else{if(o.length>0){n.firstChild.value="<!--\n"+j(o)+"\n-->"}}}});c.addNodeFilter("#comment",function(j,k){var l=j.length,m;while(l--){m=j[l];if(m.value.indexOf("[CDATA[")===0){m.name="#cdata";m.type=4;m.value=m.value.replace(/^\[CDATA\[|\]\]$/g,"")}else{if(m.value.indexOf("mce:protected ")===0){m.name="#text";m.type=3;m.raw=true;m.value=unescape(m.value).substr(14)}}}});c.addNodeFilter("xml:namespace,input",function(j,k){var l=j.length,m;while(l--){m=j[l];if(m.type===7){m.remove()}else{if(m.type===1){if(k==="input"&&!("type" in m.attributes.map)){m.attr("type","text")}}}}});if(e.fix_list_elements){c.addNodeFilter("ul,ol",function(k,l){var m=k.length,n,j;while(m--){n=k[m];j=n.parent;if(j.name==="ul"||j.name==="ol"){if(n.prev&&n.prev.name==="li"){n.prev.append(n)}}}})}c.addAttributeFilter("data-mce-src,data-mce-href,data-mce-style",function(j,k){var l=j.length;while(l--){j[l].attr(k,null)}});return{schema:f,addNodeFilter:c.addNodeFilter,addAttributeFilter:c.addAttributeFilter,onPreProcess:h,onPostProcess:b,serialize:function(o,m){var l,p,k,j,n;if(d&&i.select("script,style,select,map").length>0){n=o.innerHTML;o=o.cloneNode(false);i.setHTML(o,n)}else{o=o.cloneNode(true)}l=o.ownerDocument.implementation;if(l.createHTMLDocument){p=l.createHTMLDocument("");g(o.nodeName=="BODY"?o.childNodes:[o],function(q){p.body.appendChild(p.importNode(q,true))});if(o.nodeName!="BODY"){o=p.body.firstChild}else{o=p.body}k=i.doc;i.doc=p}m=m||{};m.format=m.format||"html";if(!m.no_events){m.node=o;h.dispatch(self,m)}j=new a.html.Serializer(e,f);m.content=j.serialize(c.parse(a.trim(m.getInner?o.innerHTML:i.getOuterHTML(o)),m));if(!m.cleanup){m.content=m.content.replace(/\uFEFF/g,"")}if(!m.no_events){b.dispatch(self,m)}if(k){i.doc=k}m.node=null;return m.content},addRules:function(j){f.addValidElements(j)},setRules:function(j){f.setValidElements(j)}}}})(tinymce);(function(a){a.dom.ScriptLoader=function(h){var c=0,k=1,i=2,l={},j=[],e={},d=[],g=0,f;function b(m,v){var x=this,q=a.DOM,s,o,r,n;function p(){q.remove(n);if(s){s.onreadystatechange=s.onload=s=null}v()}function u(){if(typeof(console)!=="undefined"&&console.log){console.log("Failed to load: "+m)}}n=q.uniqueId();if(a.isIE6){o=new a.util.URI(m);r=location;if(o.host==r.hostname&&o.port==r.port&&(o.protocol+":")==r.protocol&&o.protocol.toLowerCase()!="file"){a.util.XHR.send({url:a._addVer(o.getURI()),success:function(y){var t=q.create("script",{type:"text/javascript"});t.text=y;document.getElementsByTagName("head")[0].appendChild(t);q.remove(t);p()},error:u});return}}s=document.createElement("script");s.id=n;s.type="text/javascript";s.src=a._addVer(m);if(!a.isIE||a.isIE11){s.onload=p}s.onerror=u;if(!a.isOpera){s.onreadystatechange=function(){var t=s.readyState;if(t=="complete"||t=="loaded"){p()}}}(document.getElementsByTagName("head")[0]||document.body).appendChild(s)}this.isDone=function(m){return l[m]==i};this.markDone=function(m){l[m]=i};this.add=this.load=function(m,q,n){var o,p=l[m];if(p==f){j.push(m);l[m]=c}if(q){if(!e[m]){e[m]=[]}e[m].push({func:q,scope:n||this})}};this.loadQueue=function(n,m){this.loadScripts(j,n,m)};this.loadScripts=function(m,q,p){var o;function n(r){a.each(e[r],function(s){s.func.call(s.scope)});e[r]=f}d.push({func:q,scope:p||this});o=function(){var r=a.grep(m);m.length=0;a.each(r,function(s){if(l[s]==i){n(s);return}if(l[s]!=k){l[s]=k;g++;b(s,function(){l[s]=i;g--;n(s);o()})}});if(!g){a.each(d,function(s){s.func.call(s.scope)});d.length=0}};o()}};a.ScriptLoader=new a.dom.ScriptLoader()})(tinymce);(function(a){a.dom.RangeUtils=function(c){var b="\uFEFF";this.walk=function(d,s){var i=d.startContainer,l=d.startOffset,t=d.endContainer,m=d.endOffset,j,g,o,h,r,q,e;e=c.select("td.mceSelected,th.mceSelected");if(e.length>0){a.each(e,function(u){s([u])});return}function f(u){var v;v=u[0];if(v.nodeType===3&&v===i&&l>=v.nodeValue.length){u.splice(0,1)}v=u[u.length-1];if(m===0&&u.length>0&&v===t&&v.nodeType===3){u.splice(u.length-1,1)}return u}function p(x,v,u){var y=[];for(;x&&x!=u;x=x[v]){y.push(x)}return y}function n(v,u){do{if(v.parentNode==u){return v}v=v.parentNode}while(v)}function k(x,v,y){var u=y?"nextSibling":"previousSibling";for(h=x,r=h.parentNode;h&&h!=v;h=r){r=h.parentNode;q=p(h==x?h:h[u],u);if(q.length){if(!y){q.reverse()}s(f(q))}}}if(i.nodeType==1&&i.hasChildNodes()){i=i.childNodes[l]}if(t.nodeType==1&&t.hasChildNodes()){t=t.childNodes[Math.min(m-1,t.childNodes.length-1)]}if(i==t){return s(f([i]))}j=c.findCommonAncestor(i,t);for(h=i;h;h=h.parentNode){if(h===t){return k(i,j,true)}if(h===j){break}}for(h=t;h;h=h.parentNode){if(h===i){return k(t,j)}if(h===j){break}}g=n(i,j)||i;o=n(t,j)||t;k(i,g,true);q=p(g==i?g:g.nextSibling,"nextSibling",o==t?o.nextSibling:o);if(q.length){s(f(q))}k(t,o)};this.split=function(e){var h=e.startContainer,d=e.startOffset,i=e.endContainer,g=e.endOffset;function f(j,k){return j.splitText(k)}if(h==i&&h.nodeType==3){if(d>0&&d<h.nodeValue.length){i=f(h,d);h=i.previousSibling;if(g>d){g=g-d;h=i=f(i,g).previousSibling;g=i.nodeValue.length;d=0}else{g=0}}}else{if(h.nodeType==3&&d>0&&d<h.nodeValue.length){h=f(h,d);d=0}if(i.nodeType==3&&g>0&&g<i.nodeValue.length){i=f(i,g).previousSibling;g=i.nodeValue.length}}return{startContainer:h,startOffset:d,endContainer:i,endOffset:g}}};a.dom.RangeUtils.compareRanges=function(c,b){if(c&&b){if(c.item||c.duplicate){if(c.item&&b.item&&c.item(0)===b.item(0)){return true}if(c.isEqual&&b.isEqual&&b.isEqual(c)){return true}}else{return c.startContainer==b.startContainer&&c.startOffset==b.startOffset}}return false}})(tinymce);(function(b){var a=b.dom.Event,c=b.each;b.create("tinymce.ui.KeyboardNavigation",{KeyboardNavigation:function(e,f){var q=this,n=e.root,m=e.items,o=e.enableUpDown,i=e.enableLeftRight||!e.enableUpDown,l=e.excludeFromTabOrder,k,h,p,d,g;f=f||b.DOM;k=function(r){g=r.target.id};h=function(r){f.setAttrib(r.target.id,"tabindex","-1")};d=function(r){var s=f.get(g);f.setAttrib(s,"tabindex","0");s.focus()};q.focus=function(){f.get(g).focus()};q.destroy=function(){c(m,function(s){var t=f.get(s.id);f.unbind(t,"focus",k);f.unbind(t,"blur",h)});var r=f.get(n);f.unbind(r,"focus",d);f.unbind(r,"keydown",p);m=f=n=q.focus=k=h=p=d=null;q.destroy=function(){}};q.moveFocus=function(v,s){var r=-1,u=q.controls,t;if(!g){return}c(m,function(y,x){if(y.id===g){r=x;return false}});r+=v;if(r<0){r=m.length-1}else{if(r>=m.length){r=0}}t=m[r];f.setAttrib(g,"tabindex","-1");f.setAttrib(t.id,"tabindex","0");f.get(t.id).focus();if(e.actOnFocus){e.onAction(t.id)}if(s){a.cancel(s)}};p=function(z){var v=37,u=39,y=38,A=40,r=27,t=14,s=13,x=32;switch(z.keyCode){case v:if(i){q.moveFocus(-1)}a.cancel(z);break;case u:if(i){q.moveFocus(1)}a.cancel(z);break;case y:if(o){q.moveFocus(-1)}a.cancel(z);break;case A:if(o){q.moveFocus(1)}a.cancel(z);break;case r:if(e.onCancel){e.onCancel();a.cancel(z)}break;case t:case s:case x:if(e.onAction){e.onAction(g);a.cancel(z)}break}};c(m,function(t,r){var s,u;if(!t.id){t.id=f.uniqueId("_mce_item_")}u=f.get(t.id);if(l){f.bind(u,"blur",h);s="-1"}else{s=(r===0?"0":"-1")}u.setAttribute("tabindex",s);f.bind(u,"focus",k)});if(m[0]){g=m[0].id}f.setAttrib(n,"tabindex","-1");var j=f.get(n);f.bind(j,"focus",d);f.bind(j,"keydown",p)}})})(tinymce);(function(c){var b=c.DOM,a=c.is;c.create("tinymce.ui.Control",{Control:function(f,e,d){this.id=f;this.settings=e=e||{};this.rendered=false;this.onRender=new c.util.Dispatcher(this);this.classPrefix="";this.scope=e.scope||this;this.disabled=0;this.active=0;this.editor=d},setAriaProperty:function(f,e){var d=b.get(this.id+"_aria")||b.get(this.id);if(d){b.setAttrib(d,"aria-"+f,!!e)}},focus:function(){b.get(this.id).focus()},setDisabled:function(d){if(d!=this.disabled){this.setAriaProperty("disabled",d);this.setState("Disabled",d);this.setState("Enabled",!d);this.disabled=d}},isDisabled:function(){return this.disabled},setActive:function(d){if(d!=this.active){this.setState("Active",d);this.active=d;this.setAriaProperty("pressed",d)}},isActive:function(){return this.active},setState:function(f,d){var e=b.get(this.id);f=this.classPrefix+f;if(d){b.addClass(e,f)}else{b.removeClass(e,f)}},isRendered:function(){return this.rendered},renderHTML:function(){},renderTo:function(d){b.setHTML(d,this.renderHTML())},postRender:function(){var e=this,d;if(a(e.disabled)){d=e.disabled;e.disabled=-1;e.setDisabled(d)}if(a(e.active)){d=e.active;e.active=-1;e.setActive(d)}},remove:function(){b.remove(this.id);this.destroy()},destroy:function(){c.dom.Event.clear(this.id)}})})(tinymce);tinymce.create("tinymce.ui.Container:tinymce.ui.Control",{Container:function(c,b,a){this.parent(c,b,a);this.controls=[];this.lookup={}},add:function(a){this.lookup[a.id]=a;this.controls.push(a);return a},get:function(a){return this.lookup[a]}});tinymce.create("tinymce.ui.Separator:tinymce.ui.Control",{Separator:function(b,a){this.parent(b,a);this.classPrefix="mceSeparator";this.setDisabled(true)},renderHTML:function(){return tinymce.DOM.createHTML("span",{"class":this.classPrefix,role:"separator","aria-orientation":"vertical",tabindex:"-1"})}});(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.MenuItem:tinymce.ui.Control",{MenuItem:function(g,f){this.parent(g,f);this.classPrefix="mceMenuItem"},setSelected:function(f){this.setState("Selected",f);this.setAriaProperty("checked",!!f);this.selected=f},isSelected:function(){return this.selected},postRender:function(){var f=this;f.parent();if(c(f.selected)){f.setSelected(f.selected)}}})})(tinymce);(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.Menu:tinymce.ui.MenuItem",{Menu:function(h,g){var f=this;f.parent(h,g);f.items={};f.collapsed=false;f.menuCount=0;f.onAddItem=new d.util.Dispatcher(this)},expand:function(g){var f=this;if(g){a(f,function(h){if(h.expand){h.expand()}},"items",f)}f.collapsed=false},collapse:function(g){var f=this;if(g){a(f,function(h){if(h.collapse){h.collapse()}},"items",f)}f.collapsed=true},isCollapsed:function(){return this.collapsed},add:function(f){if(!f.settings){f=new d.ui.MenuItem(f.id||b.uniqueId(),f)}this.onAddItem.dispatch(this,f);return this.items[f.id]=f},addSeparator:function(){return this.add({separator:true})},addMenu:function(f){if(!f.collapse){f=this.createMenu(f)}this.menuCount++;return this.add(f)},hasMenus:function(){return this.menuCount!==0},remove:function(f){delete this.items[f.id]},removeAll:function(){var f=this;a(f,function(g){if(g.removeAll){g.removeAll()}else{g.remove()}g.destroy()},"items",f);f.items={}},createMenu:function(g){var f=new d.ui.Menu(g.id||b.uniqueId(),g);f.onAddItem.add(this.onAddItem.dispatch,this.onAddItem);return f}})})(tinymce);(function(e){var d=e.is,c=e.DOM,f=e.each,a=e.dom.Event,b=e.dom.Element;e.create("tinymce.ui.DropMenu:tinymce.ui.Menu",{DropMenu:function(h,g){g=g||{};g.container=g.container||c.doc.body;g.offset_x=g.offset_x||0;g.offset_y=g.offset_y||0;g.vp_offset_x=g.vp_offset_x||0;g.vp_offset_y=g.vp_offset_y||0;if(d(g.icons)&&!g.icons){g["class"]+=" mceNoIcons"}this.parent(h,g);this.onShowMenu=new e.util.Dispatcher(this);this.onHideMenu=new e.util.Dispatcher(this);this.classPrefix="mceMenu"},createMenu:function(j){var h=this,i=h.settings,g;j.container=j.container||i.container;j.parent=h;j.constrain=j.constrain||i.constrain;j["class"]=j["class"]||i["class"];j.vp_offset_x=j.vp_offset_x||i.vp_offset_x;j.vp_offset_y=j.vp_offset_y||i.vp_offset_y;j.keyboard_focus=i.keyboard_focus;g=new e.ui.DropMenu(j.id||c.uniqueId(),j);g.onAddItem.add(h.onAddItem.dispatch,h.onAddItem);return g},focus:function(){var g=this;if(g.keyboardNav){g.keyboardNav.focus()}},update:function(){var i=this,j=i.settings,g=c.get("menu_"+i.id+"_tbl"),l=c.get("menu_"+i.id+"_co"),h,k;h=j.max_width?Math.min(g.offsetWidth,j.max_width):g.offsetWidth;k=j.max_height?Math.min(g.offsetHeight,j.max_height):g.offsetHeight;if(!c.boxModel){i.element.setStyles({width:h+2,height:k+2})}else{i.element.setStyles({width:h,height:k})}if(j.max_width){c.setStyle(l,"width",h)}if(j.max_height){c.setStyle(l,"height",k);if(g.clientHeight<j.max_height){c.setStyle(l,"overflow","hidden")}}},showMenu:function(p,n,r){var z=this,A=z.settings,o,g=c.getViewPort(),u,l,v,q,i=2,k,j,m=z.classPrefix;z.collapse(1);if(z.isMenuVisible){return}if(!z.rendered){o=c.add(z.settings.container,z.renderNode());f(z.items,function(h){h.postRender()});z.element=new b("menu_"+z.id,{blocker:1,container:A.container})}else{o=c.get("menu_"+z.id)}if(!e.isOpera){c.setStyles(o,{left:-65535,top:-65535})}c.show(o);z.update();p+=A.offset_x||0;n+=A.offset_y||0;g.w-=4;g.h-=4;if(A.constrain){u=o.clientWidth-i;l=o.clientHeight-i;v=g.x+g.w;q=g.y+g.h;if((p+A.vp_offset_x+u)>v){p=r?r-u:Math.max(0,(v-A.vp_offset_x)-u)}if((n+A.vp_offset_y+l)>q){n=Math.max(0,(q-A.vp_offset_y)-l)}}c.setStyles(o,{left:p,top:n});z.element.update();z.isMenuVisible=1;z.mouseClickFunc=a.add(o,"click",function(s){var h;s=s.target;if(s&&(s=c.getParent(s,"tr"))&&!c.hasClass(s,m+"ItemSub")){h=z.items[s.id];if(h.isDisabled()){return}k=z;while(k){if(k.hideMenu){k.hideMenu()}k=k.settings.parent}if(h.settings.onclick){h.settings.onclick(s)}return false}});if(z.hasMenus()){z.mouseOverFunc=a.add(o,"mouseover",function(x){var h,t,s;x=x.target;if(x&&(x=c.getParent(x,"tr"))){h=z.items[x.id];if(z.lastMenu){z.lastMenu.collapse(1)}if(h.isDisabled()){return}if(x&&c.hasClass(x,m+"ItemSub")){t=c.getRect(x);h.showMenu((t.x+t.w-i),t.y-i,t.x);z.lastMenu=h;c.addClass(c.get(h.id).firstChild,m+"ItemActive")}}})}a.add(o,"keydown",z._keyHandler,z);z.onShowMenu.dispatch(z);if(A.keyboard_focus){z._setupKeyboardNav()}},hideMenu:function(j){var g=this,i=c.get("menu_"+g.id),h;if(!g.isMenuVisible){return}if(g.keyboardNav){g.keyboardNav.destroy()}a.remove(i,"mouseover",g.mouseOverFunc);a.remove(i,"click",g.mouseClickFunc);a.remove(i,"keydown",g._keyHandler);c.hide(i);g.isMenuVisible=0;if(!j){g.collapse(1)}if(g.element){g.element.hide()}if(h=c.get(g.id)){c.removeClass(h.firstChild,g.classPrefix+"ItemActive")}g.onHideMenu.dispatch(g)},add:function(i){var g=this,h;i=g.parent(i);if(g.isRendered&&(h=c.get("menu_"+g.id))){g._add(c.select("tbody",h)[0],i)}return i},collapse:function(g){this.parent(g);this.hideMenu(1)},remove:function(g){c.remove(g.id);this.destroy();return this.parent(g)},destroy:function(){var g=this,h=c.get("menu_"+g.id);if(g.keyboardNav){g.keyboardNav.destroy()}a.remove(h,"mouseover",g.mouseOverFunc);a.remove(c.select("a",h),"focus",g.mouseOverFunc);a.remove(h,"click",g.mouseClickFunc);a.remove(h,"keydown",g._keyHandler);if(g.element){g.element.remove()}c.remove(h)},renderNode:function(){var i=this,j=i.settings,l,h,k,g;g=c.create("div",{role:"listbox",id:"menu_"+i.id,"class":j["class"],style:"position:absolute;left:0;top:0;z-index:200000;outline:0"});if(i.settings.parent){c.setAttrib(g,"aria-parent","menu_"+i.settings.parent.id)}k=c.add(g,"div",{role:"presentation",id:"menu_"+i.id+"_co","class":i.classPrefix+(j["class"]?" "+j["class"]:"")});i.element=new b("menu_"+i.id,{blocker:1,container:j.container});if(j.menu_line){c.add(k,"span",{"class":i.classPrefix+"Line"})}l=c.add(k,"table",{role:"presentation",id:"menu_"+i.id+"_tbl",border:0,cellPadding:0,cellSpacing:0});h=c.add(l,"tbody");f(i.items,function(m){i._add(h,m)});i.rendered=true;return g},_setupKeyboardNav:function(){var i,h,g=this;i=c.get("menu_"+g.id);h=c.select("a[role=option]","menu_"+g.id);h.splice(0,0,i);g.keyboardNav=new e.ui.KeyboardNavigation({root:"menu_"+g.id,items:h,onCancel:function(){g.hideMenu()},enableUpDown:true});i.focus()},_keyHandler:function(g){var h=this,i;switch(g.keyCode){case 37:if(h.settings.parent){h.hideMenu();h.settings.parent.focus();a.cancel(g)}break;case 39:if(h.mouseOverFunc){h.mouseOverFunc(g)}break}},_add:function(j,h){var i,q=h.settings,p,l,k,m=this.classPrefix,g;if(q.separator){l=c.add(j,"tr",{id:h.id,"class":m+"ItemSeparator"});c.add(l,"td",{"class":m+"ItemSeparator"});if(i=l.previousSibling){c.addClass(i,"mceLast")}return}i=l=c.add(j,"tr",{id:h.id,"class":m+"Item "+m+"ItemEnabled"});i=k=c.add(i,q.titleItem?"th":"td");i=p=c.add(i,"a",{id:h.id+"_aria",role:q.titleItem?"presentation":"option",href:"javascript:;",onclick:"return false;",onmousedown:"return false;"});if(q.parent){c.setAttrib(p,"aria-haspopup","true");c.setAttrib(p,"aria-owns","menu_"+h.id)}c.addClass(k,q["class"]);g=c.add(i,"span",{"class":"mceIcon"+(q.icon?" mce_"+q.icon:"")});if(q.icon_src){c.add(g,"img",{src:q.icon_src})}i=c.add(i,q.element||"span",{"class":"mceText",title:h.settings.title},h.settings.title);if(h.settings.style){if(typeof h.settings.style=="function"){h.settings.style=h.settings.style()}c.setAttrib(i,"style",h.settings.style)}if(j.childNodes.length==1){c.addClass(l,"mceFirst")}if((i=l.previousSibling)&&c.hasClass(i,m+"ItemSeparator")){c.addClass(l,"mceFirst")}if(h.collapse){c.addClass(l,m+"ItemSub")}if(i=l.previousSibling){c.removeClass(i,"mceLast")}c.addClass(l,"mceLast")}})})(tinymce);(function(b){var a=b.DOM;b.create("tinymce.ui.Button:tinymce.ui.Control",{Button:function(e,d,c){this.parent(e,d,c);this.classPrefix="mceButton"},renderHTML:function(){var f=this.classPrefix,e=this.settings,d,c;c=a.encode(e.label||"");d='<a role="button" id="'+this.id+'" href="javascript:;" class="'+f+" "+f+"Enabled "+e["class"]+(c?" "+f+"Labeled":"")+'" onmousedown="return false;" onclick="return false;" aria-labelledby="'+this.id+'_voice" title="'+a.encode(e.title)+'">';if(e.image&&!(this.editor&&this.editor.forcedHighContrastMode)){d+='<span class="mceIcon '+e["class"]+'"><img class="mceIcon" src="'+e.image+'" alt="'+a.encode(e.title)+'" /></span>'+(c?'<span class="'+f+'Label">'+c+"</span>":"")}else{d+='<span class="mceIcon '+e["class"]+'"></span>'+(c?'<span class="'+f+'Label">'+c+"</span>":"")}d+='<span class="mceVoiceLabel mceIconOnly" style="display: none;" id="'+this.id+'_voice">'+e.title+"</span>";d+="</a>";return d},postRender:function(){var d=this,e=d.settings,c;if(b.isIE&&d.editor){b.dom.Event.add(d.id,"mousedown",function(f){var g=d.editor.selection.getNode().nodeName;c=g==="IMG"?d.editor.selection.getBookmark():null})}b.dom.Event.add(d.id,"click",function(f){if(!d.isDisabled()){if(b.isIE&&d.editor&&c!==null){d.editor.selection.moveToBookmark(c)}return e.onclick.call(e.scope,f)}});b.dom.Event.add(d.id,"keydown",function(f){if(!d.isDisabled()&&f.keyCode==b.VK.SPACEBAR){b.dom.Event.cancel(f);return e.onclick.call(e.scope,f)}})}})})(tinymce);(function(e){var d=e.DOM,b=e.dom.Event,f=e.each,a=e.util.Dispatcher,c;e.create("tinymce.ui.ListBox:tinymce.ui.Control",{ListBox:function(j,i,g){var h=this;h.parent(j,i,g);h.items=[];h.onChange=new a(h);h.onPostRender=new a(h);h.onAdd=new a(h);h.onRenderMenu=new e.util.Dispatcher(this);h.classPrefix="mceListBox";h.marked={}},select:function(h){var g=this,j,i;g.marked={};if(h==c){return g.selectByIndex(-1)}if(h&&typeof(h)=="function"){i=h}else{i=function(k){return k==h}}if(h!=g.selectedValue){f(g.items,function(l,k){if(i(l.value)){j=1;g.selectByIndex(k);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(g){var i=this,j,k,h;i.marked={};if(g!=i.selectedIndex){j=d.get(i.id+"_text");h=d.get(i.id+"_voiceDesc");k=i.items[g];if(k){i.selectedValue=k.value;i.selectedIndex=g;d.setHTML(j,d.encode(k.title));d.setHTML(h,i.settings.title+" - "+k.title);d.removeClass(j,"mceTitle");d.setAttrib(i.id,"aria-valuenow",k.title)}else{d.setHTML(j,d.encode(i.settings.title));d.setHTML(h,d.encode(i.settings.title));d.addClass(j,"mceTitle");i.selectedValue=i.selectedIndex=null;d.setAttrib(i.id,"aria-valuenow",i.settings.title)}j=0}},mark:function(g){this.marked[g]=true},add:function(j,g,i){var h=this;i=i||{};i=e.extend(i,{title:j,value:g});h.items.push(i);h.onAdd.dispatch(h,i)},getLength:function(){return this.items.length},renderHTML:function(){var j="",g=this,i=g.settings,k=g.classPrefix;j='<span role="listbox" aria-haspopup="true" aria-labelledby="'+g.id+'_voiceDesc" aria-describedby="'+g.id+'_voiceDesc"><table role="presentation" tabindex="0" id="'+g.id+'" cellpadding="0" cellspacing="0" class="'+k+" "+k+"Enabled"+(i["class"]?(" "+i["class"]):"")+'"><tbody><tr>';j+="<td>"+d.createHTML("span",{id:g.id+"_voiceDesc","class":"voiceLabel",style:"display:none;"},g.settings.title);j+=d.createHTML("a",{id:g.id+"_text",tabindex:-1,href:"javascript:;","class":"mceText",onclick:"return false;",onmousedown:"return false;"},d.encode(g.settings.title))+"</td>";j+="<td>"+d.createHTML("a",{id:g.id+"_open",tabindex:-1,href:"javascript:;","class":"mceOpen",onclick:"return false;",onmousedown:"return false;"},'<span><span style="display:none;" class="mceIconOnly" aria-hidden="true">\u25BC</span></span>')+"</td>";j+="</tr></tbody></table></span>";return j},showMenu:function(){var h=this,j,i=d.get(this.id),g;if(h.isDisabled()||h.items.length===0){return}if(h.menu&&h.menu.isMenuVisible){return h.hideMenu()}if(!h.isMenuRendered){h.renderMenu();h.isMenuRendered=true}j=d.getPos(i);g=h.menu;g.settings.offset_x=j.x;g.settings.offset_y=j.y;g.settings.keyboard_focus=!e.isOpera;f(h.items,function(k){if(g.items[k.id]){g.items[k.id].setSelected(0)}});f(h.items,function(k){if(g.items[k.id]&&h.marked[k.value]){g.items[k.id].setSelected(1)}if(k.value===h.selectedValue){g.items[k.id].setSelected(1)}});g.showMenu(0,i.clientHeight);b.add(d.doc,"mousedown",h.hideMenu,h);d.addClass(h.id,h.classPrefix+"Selected")},hideMenu:function(h){var g=this;if(g.menu&&g.menu.isMenuVisible){d.removeClass(g.id,g.classPrefix+"Selected");if(h&&h.type=="mousedown"&&(h.target.id==g.id+"_text"||h.target.id==g.id+"_open")){return}if(!h||!d.getParent(h.target,".mceMenu")){d.removeClass(g.id,g.classPrefix+"Selected");b.remove(d.doc,"mousedown",g.hideMenu,g);g.menu.hideMenu()}}},renderMenu:function(){var h=this,g;g=h.settings.control_manager.createDropMenu(h.id+"_menu",{menu_line:1,"class":h.classPrefix+"Menu mceNoIcons",max_width:250,max_height:150});g.onHideMenu.add(function(){h.hideMenu();h.focus()});g.add({title:h.settings.title,"class":"mceMenuItemTitle",onclick:function(){if(h.settings.onselect("")!==false){h.select("")}}});f(h.items,function(i){if(i.value===c){g.add({title:i.title,role:"option","class":"mceMenuItemTitle",onclick:function(){if(h.settings.onselect("")!==false){h.select("")}}})}else{i.id=d.uniqueId();i.role="option";i.onclick=function(){if(h.settings.onselect(i.value)!==false){h.select(i.value)}};g.add(i)}});h.onRenderMenu.dispatch(h,g);h.menu=g},postRender:function(){var g=this,h=g.classPrefix;b.add(g.id,"click",g.showMenu,g);b.add(g.id,"keydown",function(i){if(i.keyCode==32){g.showMenu(i);b.cancel(i)}});b.add(g.id,"focus",function(){if(!g._focused){g.keyDownHandler=b.add(g.id,"keydown",function(i){if(i.keyCode==40){g.showMenu();b.cancel(i)}});g.keyPressHandler=b.add(g.id,"keypress",function(j){var i;if(j.keyCode==13){i=g.selectedValue;g.selectedValue=null;b.cancel(j);g.settings.onselect(i)}})}g._focused=1});b.add(g.id,"blur",function(){b.remove(g.id,"keydown",g.keyDownHandler);b.remove(g.id,"keypress",g.keyPressHandler);g._focused=0});if(e.isIE6||!d.boxModel){b.add(g.id,"mouseover",function(){if(!d.hasClass(g.id,h+"Disabled")){d.addClass(g.id,h+"Hover")}});b.add(g.id,"mouseout",function(){if(!d.hasClass(g.id,h+"Disabled")){d.removeClass(g.id,h+"Hover")}})}g.onPostRender.dispatch(g,d.get(g.id))},destroy:function(){this.parent();b.clear(this.id+"_text");b.clear(this.id+"_open")}})})(tinymce);(function(e){var d=e.DOM,b=e.dom.Event,f=e.each,a=e.util.Dispatcher,c;e.create("tinymce.ui.NativeListBox:tinymce.ui.ListBox",{NativeListBox:function(h,g){this.parent(h,g);this.classPrefix="mceNativeListBox"},setDisabled:function(g){d.get(this.id).disabled=g;this.setAriaProperty("disabled",g)},isDisabled:function(){return d.get(this.id).disabled},select:function(h){var g=this,j,i;if(h==c){return g.selectByIndex(-1)}if(h&&typeof(h)=="function"){i=h}else{i=function(k){return k==h}}if(h!=g.selectedValue){f(g.items,function(l,k){if(i(l.value)){j=1;g.selectByIndex(k);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(g){d.get(this.id).selectedIndex=g+1;this.selectedValue=this.items[g]?this.items[g].value:null},add:function(k,h,g){var j,i=this;g=g||{};g.value=h;if(i.isRendered()){d.add(d.get(this.id),"option",g,k)}j={title:k,value:h,attribs:g};i.items.push(j);i.onAdd.dispatch(i,j)},getLength:function(){return this.items.length},renderHTML:function(){var i,g=this;i=d.createHTML("option",{value:""},"-- "+g.settings.title+" --");f(g.items,function(h){i+=d.createHTML("option",{value:h.value},h.title)});i=d.createHTML("select",{id:g.id,"class":"mceNativeListBox","aria-labelledby":g.id+"_aria"},i);i+=d.createHTML("span",{id:g.id+"_aria",style:"display: none"},g.settings.title);return i},postRender:function(){var h=this,i,j=true;h.rendered=true;function g(l){var k=h.items[l.target.selectedIndex-1];if(k&&(k=k.value)){h.onChange.dispatch(h,k);if(h.settings.onselect){h.settings.onselect(k)}}}b.add(h.id,"change",g);b.add(h.id,"keydown",function(q){var n,p=37,m=39,l=38,r=40,k=13,o=32;b.remove(h.id,"change",i);j=false;n=b.add(h.id,"blur",function(){if(j){return}j=true;b.add(h.id,"change",g);b.remove(h.id,"blur",n)});if(q.keyCode==k||q.keyCode==o){g(q);return b.cancel(q)}else{if(q.keyCode==r||q.keyCode==l){q.stopImmediatePropagation()}}});h.onPostRender.dispatch(h,d.get(h.id))}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.MenuButton:tinymce.ui.Button",{MenuButton:function(g,f,e){this.parent(g,f,e);this.onRenderMenu=new c.util.Dispatcher(this);f.menu_container=f.menu_container||b.doc.body},showMenu:function(){var g=this,j,i,h=b.get(g.id),f;if(g.isDisabled()){return}if(!g.isMenuRendered){g.renderMenu();g.isMenuRendered=true}if(g.isMenuVisible){return g.hideMenu()}j=b.getPos(g.settings.menu_container);i=b.getPos(h);f=g.menu;f.settings.offset_x=i.x;f.settings.offset_y=i.y;f.settings.vp_offset_x=i.x;f.settings.vp_offset_y=i.y;f.settings.keyboard_focus=g._focused;f.showMenu(0,h.firstChild.clientHeight);a.add(b.doc,"mousedown",g.hideMenu,g);g.setState("Selected",1);g.isMenuVisible=1},renderMenu:function(){var f=this,e;e=f.settings.control_manager.createDropMenu(f.id+"_menu",{menu_line:1,"class":this.classPrefix+"Menu",icons:f.settings.icons});e.onHideMenu.add(function(){f.hideMenu();f.focus()});f.onRenderMenu.dispatch(f,e);f.menu=e},hideMenu:function(g){var f=this;if(g&&g.type=="mousedown"&&b.getParent(g.target,function(h){return h.id===f.id||h.id===f.id+"_open"})){return}if(!g||!b.getParent(g.target,".mceMenu")){f.setState("Selected",0);a.remove(b.doc,"mousedown",f.hideMenu,f);if(f.menu){f.menu.hideMenu()}}f.isMenuVisible=0},postRender:function(){var e=this,f=e.settings;a.add(e.id,"click",function(){if(!e.isDisabled()){if(f.onclick){f.onclick(e.value)}e.showMenu()}})}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.SplitButton:tinymce.ui.MenuButton",{SplitButton:function(g,f,e){this.parent(g,f,e);this.classPrefix="mceSplitButton"},renderHTML:function(){var i,f=this,g=f.settings,e;i="<tbody><tr>";if(g.image){e=b.createHTML("img ",{src:g.image,role:"presentation","class":"mceAction "+g["class"]})}else{e=b.createHTML("span",{"class":"mceAction "+g["class"]},"")}e+=b.createHTML("span",{"class":"mceVoiceLabel mceIconOnly",id:f.id+"_voice",style:"display:none;"},g.title);i+="<td >"+b.createHTML("a",{role:"button",id:f.id+"_action",tabindex:"-1",href:"javascript:;","class":"mceAction "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"</td>";e=b.createHTML("span",{"class":"mceOpen "+g["class"]},'<span style="display:none;" class="mceIconOnly" aria-hidden="true">\u25BC</span>');i+="<td >"+b.createHTML("a",{role:"button",id:f.id+"_open",tabindex:"-1",href:"javascript:;","class":"mceOpen "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"</td>";i+="</tr></tbody>";i=b.createHTML("table",{role:"presentation","class":"mceSplitButton mceSplitButtonEnabled "+g["class"],cellpadding:"0",cellspacing:"0",title:g.title},i);return b.createHTML("div",{id:f.id,role:"button",tabindex:"0","aria-labelledby":f.id+"_voice","aria-haspopup":"true"},i)},postRender:function(){var e=this,g=e.settings,f;if(g.onclick){f=function(h){if(!e.isDisabled()){g.onclick(e.value);a.cancel(h)}};a.add(e.id+"_action","click",f);a.add(e.id,["click","keydown"],function(h){var k=32,m=14,i=13,j=38,l=40;if((h.keyCode===32||h.keyCode===13||h.keyCode===14)&&!h.altKey&&!h.ctrlKey&&!h.metaKey){f();a.cancel(h)}else{if(h.type==="click"||h.keyCode===l){e.showMenu();a.cancel(h)}}})}a.add(e.id+"_open","click",function(h){e.showMenu();a.cancel(h)});a.add([e.id,e.id+"_open"],"focus",function(){e._focused=1});a.add([e.id,e.id+"_open"],"blur",function(){e._focused=0});if(c.isIE6||!b.boxModel){a.add(e.id,"mouseover",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.addClass(e.id,"mceSplitButtonHover")}});a.add(e.id,"mouseout",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.removeClass(e.id,"mceSplitButtonHover")}})}},destroy:function(){this.parent();a.clear(this.id+"_action");a.clear(this.id+"_open");a.clear(this.id)}})})(tinymce);(function(d){var c=d.DOM,a=d.dom.Event,b=d.is,e=d.each;d.create("tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton",{ColorSplitButton:function(i,h,f){var g=this;g.parent(i,h,f);g.settings=h=d.extend({colors:"000000,993300,333300,003300,003366,000080,333399,333333,800000,FF6600,808000,008000,008080,0000FF,666699,808080,FF0000,FF9900,99CC00,339966,33CCCC,3366FF,800080,999999,FF00FF,FFCC00,FFFF00,00FF00,00FFFF,00CCFF,993366,C0C0C0,FF99CC,FFCC99,FFFF99,CCFFCC,CCFFFF,99CCFF,CC99FF,FFFFFF",grid_width:8,default_color:"#888888"},g.settings);g.onShowMenu=new d.util.Dispatcher(g);g.onHideMenu=new d.util.Dispatcher(g);g.value=h.default_color},showMenu:function(){var f=this,g,j,i,h;if(f.isDisabled()){return}if(!f.isMenuRendered){f.renderMenu();f.isMenuRendered=true}if(f.isMenuVisible){return f.hideMenu()}i=c.get(f.id);c.show(f.id+"_menu");c.addClass(i,"mceSplitButtonSelected");h=c.getPos(i);c.setStyles(f.id+"_menu",{left:h.x,top:h.y+i.firstChild.clientHeight,zIndex:200000});i=0;a.add(c.doc,"mousedown",f.hideMenu,f);f.onShowMenu.dispatch(f);if(f._focused){f._keyHandler=a.add(f.id+"_menu","keydown",function(k){if(k.keyCode==27){f.hideMenu()}});c.select("a",f.id+"_menu")[0].focus()}f.keyboardNav=new d.ui.KeyboardNavigation({root:f.id+"_menu",items:c.select("a",f.id+"_menu"),onCancel:function(){f.hideMenu();f.focus()}});f.keyboardNav.focus();f.isMenuVisible=1},hideMenu:function(g){var f=this;if(f.isMenuVisible){if(g&&g.type=="mousedown"&&c.getParent(g.target,function(h){return h.id===f.id+"_open"})){return}if(!g||!c.getParent(g.target,".mceSplitButtonMenu")){c.removeClass(f.id,"mceSplitButtonSelected");a.remove(c.doc,"mousedown",f.hideMenu,f);a.remove(f.id+"_menu","keydown",f._keyHandler);c.hide(f.id+"_menu")}f.isMenuVisible=0;f.onHideMenu.dispatch();f.keyboardNav.destroy()}},renderMenu:function(){var p=this,h,k=0,q=p.settings,g,j,l,o,f;o=c.add(q.menu_container,"div",{role:"listbox",id:p.id+"_menu","class":q.menu_class+" "+q["class"],style:"position:absolute;left:0;top:-1000px;"});h=c.add(o,"div",{"class":q["class"]+" mceSplitButtonMenu"});c.add(h,"span",{"class":"mceMenuLine"});g=c.add(h,"table",{role:"presentation","class":"mceColorSplitMenu"});j=c.add(g,"tbody");k=0;e(b(q.colors,"array")?q.colors:q.colors.split(","),function(m){m=m.replace(/^#/,"");if(!k--){l=c.add(j,"tr");k=q.grid_width-1}g=c.add(l,"td");var i={href:"javascript:;",style:{backgroundColor:"#"+m},title:p.editor.getLang("colors."+m,m),"data-mce-color":"#"+m};if(!d.isIE){i.role="option"}g=c.add(g,"a",i);if(p.editor.forcedHighContrastMode){g=c.add(g,"canvas",{width:16,height:16,"aria-hidden":"true"});if(g.getContext&&(f=g.getContext("2d"))){f.fillStyle="#"+m;f.fillRect(0,0,16,16)}else{c.remove(g)}}});if(q.more_colors_func){g=c.add(j,"tr");g=c.add(g,"td",{colspan:q.grid_width,"class":"mceMoreColors"});g=c.add(g,"a",{role:"option",id:p.id+"_more",href:"javascript:;",onclick:"return false;","class":"mceMoreColors"},q.more_colors_title);a.add(g,"click",function(i){q.more_colors_func.call(q.more_colors_scope||this);return a.cancel(i)})}c.addClass(h,"mceColorSplitMenu");a.add(p.id+"_menu","mousedown",function(i){return a.cancel(i)});a.add(p.id+"_menu","click",function(i){var m;i=c.getParent(i.target,"a",j);if(i&&i.nodeName.toLowerCase()=="a"&&(m=i.getAttribute("data-mce-color"))){p.setColor(m)}return false});return o},setColor:function(f){this.displayColor(f);this.hideMenu();this.settings.onselect(f)},displayColor:function(g){var f=this;c.setStyle(f.id+"_preview","backgroundColor",g);f.value=g},postRender:function(){var f=this,g=f.id;f.parent();c.add(g+"_action","div",{id:g+"_preview","class":"mceColorPreview"});c.setStyle(f.id+"_preview","backgroundColor",f.value)},destroy:function(){var f=this;f.parent();a.clear(f.id+"_menu");a.clear(f.id+"_more");c.remove(f.id+"_menu");if(f.keyboardNav){f.keyboardNav.destroy()}}})})(tinymce);(function(b){var d=b.DOM,c=b.each,a=b.dom.Event;b.create("tinymce.ui.ToolbarGroup:tinymce.ui.Container",{renderHTML:function(){var f=this,i=[],e=f.controls,j=b.each,g=f.settings;i.push('<div id="'+f.id+'" role="group" aria-labelledby="'+f.id+'_voice">');i.push("<span role='application'>");i.push('<span id="'+f.id+'_voice" class="mceVoiceLabel" style="display:none;">'+d.encode(g.name)+"</span>");j(e,function(h){i.push(h.renderHTML())});i.push("</span>");i.push("</div>");return i.join("")},focus:function(){var e=this;d.get(e.id).focus()},postRender:function(){var f=this,e=[];c(f.controls,function(g){c(g.controls,function(h){if(h.id){e.push(h)}})});f.keyNav=new b.ui.KeyboardNavigation({root:f.id,items:e,onCancel:function(){if(b.isWebKit){d.get(f.editor.id+"_ifr").focus()}f.editor.focus()},excludeFromTabOrder:!f.settings.tab_focus_toolbar})},destroy:function(){var e=this;e.parent();e.keyNav.destroy();a.clear(e.id)}})})(tinymce);(function(a){var c=a.DOM,b=a.each;a.create("tinymce.ui.Toolbar:tinymce.ui.Container",{renderHTML:function(){var m=this,f="",j,k,n=m.settings,e,d,g,l;l=m.controls;for(e=0;e<l.length;e++){k=l[e];d=l[e-1];g=l[e+1];if(e===0){j="mceToolbarStart";if(k.Button){j+=" mceToolbarStartButton"}else{if(k.SplitButton){j+=" mceToolbarStartSplitButton"}else{if(k.ListBox){j+=" mceToolbarStartListBox"}}}f+=c.createHTML("td",{"class":j},c.createHTML("span",null,"<!-- IE -->"))}if(d&&k.ListBox){if(d.Button||d.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarEnd"},c.createHTML("span",null,"<!-- IE -->"))}}if(c.stdMode){f+='<td style="position: relative">'+k.renderHTML()+"</td>"}else{f+="<td>"+k.renderHTML()+"</td>"}if(g&&k.ListBox){if(g.Button||g.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarStart"},c.createHTML("span",null,"<!-- IE -->"))}}}j="mceToolbarEnd";if(k.Button){j+=" mceToolbarEndButton"}else{if(k.SplitButton){j+=" mceToolbarEndSplitButton"}else{if(k.ListBox){j+=" mceToolbarEndListBox"}}}f+=c.createHTML("td",{"class":j},c.createHTML("span",null,"<!-- IE -->"));return c.createHTML("table",{id:m.id,"class":"mceToolbar"+(n["class"]?" "+n["class"]:""),cellpadding:"0",cellspacing:"0",align:m.settings.align||"",role:"presentation",tabindex:"-1"},"<tbody><tr>"+f+"</tr></tbody>")}})})(tinymce);(function(b){var a=b.util.Dispatcher,c=b.each;b.create("tinymce.AddOnManager",{AddOnManager:function(){var d=this;d.items=[];d.urls={};d.lookup={};d.onAdd=new a(d)},get:function(d){if(this.lookup[d]){return this.lookup[d].instance}else{return undefined}},dependencies:function(e){var d;if(this.lookup[e]){d=this.lookup[e].dependencies}return d||[]},requireLangPack:function(e){var d=b.settings;if(d&&d.language&&d.language_load!==false){b.ScriptLoader.add(this.urls[e]+"/langs/"+d.language+".js")}},add:function(f,e,d){this.items.push(e);this.lookup[f]={instance:e,dependencies:d};this.onAdd.dispatch(this,f,e);return e},createUrl:function(d,e){if(typeof e==="object"){return e}else{return{prefix:d.prefix,resource:e,suffix:d.suffix}}},addComponents:function(f,d){var e=this.urls[f];b.each(d,function(g){b.ScriptLoader.add(e+"/"+g)})},load:function(j,f,d,h){var g=this,e=f;function i(){var k=g.dependencies(j);b.each(k,function(m){var l=g.createUrl(f,m);g.load(l.resource,l,undefined,undefined)});if(d){if(h){d.call(h)}else{d.call(b.ScriptLoader)}}}if(g.urls[j]){return}if(typeof f==="object"){e=f.prefix+f.resource+f.suffix}if(e.indexOf("/")!==0&&e.indexOf("://")==-1){e=b.baseURL+"/"+e}g.urls[j]=e.substring(0,e.lastIndexOf("/"));if(g.lookup[j]){i()}else{b.ScriptLoader.add(e,i,h)}}});b.PluginManager=new b.AddOnManager();b.ThemeManager=new b.AddOnManager()}(tinymce));(function(j){var g=j.each,d=j.extend,k=j.DOM,i=j.dom.Event,f=j.ThemeManager,b=j.PluginManager,e=j.explode,h=j.util.Dispatcher,a,c=0;j.documentBaseURL=window.location.href.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,"");if(!/[\/\\]$/.test(j.documentBaseURL)){j.documentBaseURL+="/"}j.baseURL=new j.util.URI(j.documentBaseURL).toAbsolute(j.baseURL);j.baseURI=new j.util.URI(j.baseURL);j.onBeforeUnload=new h(j);i.add(window,"beforeunload",function(l){j.onBeforeUnload.dispatch(j,l)});j.onAddEditor=new h(j);j.onRemoveEditor=new h(j);j.EditorManager=d(j,{editors:[],i18n:{},activeEditor:null,init:function(x){var v=this,o,n=j.ScriptLoader,u,l=[],r;function q(t){var s=t.id;if(!s){s=t.name;if(s&&!k.get(s)){s=t.name}else{s=k.uniqueId()}t.setAttribute("id",s)}return s}function m(z,A,t){var y=z[A];if(!y){return}if(j.is(y,"string")){t=y.replace(/\.\w+$/,"");t=t?j.resolve(t):0;y=j.resolve(y)}return y.apply(t||this,Array.prototype.slice.call(arguments,2))}function p(t,s){return s.constructor===RegExp?s.test(t.className):k.hasClass(t,s)}v.settings=x;i.bind(window,"ready",function(){var s,t;m(x,"onpageload");switch(x.mode){case"exact":s=x.elements||"";if(s.length>0){g(e(s),function(y){if(k.get(y)){r=new j.Editor(y,x);l.push(r);r.render(1)}else{g(document.forms,function(z){g(z.elements,function(A){if(A.name===y){y="mce_editor_"+c++;k.setAttrib(A,"id",y);r=new j.Editor(y,x);l.push(r);r.render(1)}})})}})}break;case"textareas":case"specific_textareas":g(k.select("textarea"),function(y){if(x.editor_deselector&&p(y,x.editor_deselector)){return}if(!x.editor_selector||p(y,x.editor_selector)){r=new j.Editor(q(y),x);l.push(r);r.render(1)}});break;default:if(x.types){g(x.types,function(y){g(k.select(y.selector),function(A){var z=new j.Editor(q(A),j.extend({},x,y));l.push(z);z.render(1)})})}else{if(x.selector){g(k.select(x.selector),function(z){var y=new j.Editor(q(z),x);l.push(y);y.render(1)})}}}if(x.oninit){s=t=0;g(l,function(y){t++;if(!y.initialized){y.onInit.add(function(){s++;if(s==t){m(x,"oninit")}})}else{s++}if(s==t){m(x,"oninit")}})}})},get:function(l){if(l===a){return this.editors}if(!this.editors.hasOwnProperty(l)){return a}return this.editors[l]},getInstanceById:function(l){return this.get(l)},add:function(m){var l=this,n=l.editors;n[m.id]=m;n.push(m);l._setActive(m);l.onAddEditor.dispatch(l,m);return m},remove:function(n){var m=this,l,o=m.editors;if(!o[n.id]){return null}delete o[n.id];for(l=0;l<o.length;l++){if(o[l]==n){o.splice(l,1);break}}if(m.activeEditor==n){m._setActive(o[0])}n.destroy();m.onRemoveEditor.dispatch(m,n);return n},execCommand:function(r,p,o){var q=this,n=q.get(o),l;function m(){n.destroy();l.detachEvent("onunload",m);l=l.tinyMCE=l.tinymce=null}switch(r){case"mceFocus":n.focus();return true;case"mceAddEditor":case"mceAddControl":if(!q.get(o)){new j.Editor(o,q.settings).render()}return true;case"mceAddFrameControl":l=o.window;l.tinyMCE=tinyMCE;l.tinymce=j;j.DOM.doc=l.document;j.DOM.win=l;n=new j.Editor(o.element_id,o);n.render();if(j.isIE&&!j.isIE11){l.attachEvent("onunload",m)}o.page_window=null;return true;case"mceRemoveEditor":case"mceRemoveControl":if(n){n.remove()}return true;case"mceToggleEditor":if(!n){q.execCommand("mceAddControl",0,o);return true}if(n.isHidden()){n.show()}else{n.hide()}return true}if(q.activeEditor){return q.activeEditor.execCommand(r,p,o)}return false},execInstanceCommand:function(p,o,n,m){var l=this.get(p);if(l){return l.execCommand(o,n,m)}return false},triggerSave:function(){g(this.editors,function(l){l.save()})},addI18n:function(n,q){var l,m=this.i18n;if(!j.is(n,"string")){g(n,function(r,p){g(r,function(t,s){g(t,function(v,u){if(s==="common"){m[p+"."+u]=v}else{m[p+"."+s+"."+u]=v}})})})}else{g(q,function(r,p){m[n+"."+p]=r})}},_setActive:function(l){this.selectedInstance=this.activeEditor=l}})})(tinymce);(function(k){var l=k.DOM,j=k.dom.Event,f=k.extend,i=k.each,a=k.isGecko,b=k.isIE,e=k.isWebKit,d=k.is,h=k.ThemeManager,c=k.PluginManager,g=k.explode;k.create("tinymce.Editor",{Editor:function(p,o){var m=this,n=true;m.settings=o=f({id:p,language:"en",theme:"advanced",skin:"default",delta_width:0,delta_height:0,popup_css:"",plugins:"",document_base_url:k.documentBaseURL,add_form_submit_trigger:n,submit_patch:n,add_unload_trigger:n,convert_urls:n,relative_urls:n,remove_script_host:n,table_inline_editing:false,object_resizing:n,accessibility_focus:n,doctype:k.isIE6?'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">':"<!DOCTYPE>",visual:n,font_size_style_values:"xx-small,x-small,small,medium,large,x-large,xx-large",font_size_legacy_values:"xx-small,small,medium,large,x-large,xx-large,300%",apply_source_formatting:n,directionality:"ltr",forced_root_block:"p",hidden_input:n,padd_empty_editor:n,render_ui:n,indentation:"30px",fix_table_elements:n,inline_styles:n,convert_fonts_to_spans:n,indent:"simple",indent_before:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist",indent_after:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist",validate:n,entity_encoding:"named",url_converter:m.convertURL,url_converter_scope:m,ie7_compat:n},o);m.id=m.editorId=p;m.isNotDirty=false;m.plugins={};m.documentBaseURI=new k.util.URI(o.document_base_url||k.documentBaseURL,{base_uri:tinyMCE.baseURI});m.baseURI=k.baseURI;m.contentCSS=[];m.contentStyles=[];m.setupEvents();m.execCommands={};m.queryStateCommands={};m.queryValueCommands={};m.execCallback("setup",m)},render:function(o){var p=this,q=p.settings,r=p.id,m=k.ScriptLoader;if(!j.domLoaded){j.add(window,"ready",function(){p.render()});return}tinyMCE.settings=q;if(!p.getElement()){return}if(k.isIDevice&&!k.isIOS5){return}if(!/TEXTAREA|INPUT/i.test(p.getElement().nodeName)&&q.hidden_input&&l.getParent(r,"form")){l.insertAfter(l.create("input",{type:"hidden",name:r}),r)}if(!q.content_editable){p.orgVisibility=p.getElement().style.visibility;p.getElement().style.visibility="hidden"}if(k.WindowManager){p.windowManager=new k.WindowManager(p)}if(q.encoding=="xml"){p.onGetContent.add(function(s,t){if(t.save){t.content=l.encode(t.content)}})}if(q.add_form_submit_trigger){p.onSubmit.addToTop(function(){if(p.initialized){p.save();p.isNotDirty=1}})}if(q.add_unload_trigger){p._beforeUnload=tinyMCE.onBeforeUnload.add(function(){if(p.initialized&&!p.destroyed&&!p.isHidden()){p.save({format:"raw",no_events:true})}})}k.addUnload(p.destroy,p);if(q.submit_patch){p.onBeforeRenderUI.add(function(){var s=p.getElement().form;if(!s){return}if(s._mceOldSubmit){return}if(!s.submit.nodeType&&!s.submit.length){p.formElement=s;s._mceOldSubmit=s.submit;s.submit=function(){k.triggerSave();p.isNotDirty=1;return p.formElement._mceOldSubmit(p.formElement)}}s=null})}function n(){if(q.language&&q.language_load!==false){m.add(k.baseURL+"/langs/"+q.language+".js")}if(q.theme&&typeof q.theme!="function"&&q.theme.charAt(0)!="-"&&!h.urls[q.theme]){h.load(q.theme,"themes/"+q.theme+"/editor_template"+k.suffix+".js")}i(g(q.plugins),function(t){if(t&&!c.urls[t]){if(t.charAt(0)=="-"){t=t.substr(1,t.length);var s=c.dependencies(t);i(s,function(v){var u={prefix:"plugins/",resource:v,suffix:"/editor_plugin"+k.suffix+".js"};v=c.createUrl(u,v);c.load(v.resource,v)})}else{if(t=="safari"){return}c.load(t,{prefix:"plugins/",resource:t,suffix:"/editor_plugin"+k.suffix+".js"})}}});m.loadQueue(function(){if(!p.removed){p.init()}})}n()},init:function(){var q,G=this,H=G.settings,D,y,z,C=G.getElement(),p,m,E,v,B,F,x,r=[];k.add(G);H.aria_label=H.aria_label||l.getAttrib(C,"aria-label",G.getLang("aria.rich_text_area"));if(H.theme){if(typeof H.theme!="function"){H.theme=H.theme.replace(/-/,"");p=h.get(H.theme);G.theme=new p();if(G.theme.init){G.theme.init(G,h.urls[H.theme]||k.documentBaseURL.replace(/\/$/,""))}}else{G.theme=H.theme}}function A(s){var t=c.get(s),o=c.urls[s]||k.documentBaseURL.replace(/\/$/,""),n;if(t&&k.inArray(r,s)===-1){i(c.dependencies(s),function(u){A(u)});n=new t(G,o);G.plugins[s]=n;if(n.init){n.init(G,o);r.push(s)}}}i(g(H.plugins.replace(/\-/g,"")),A);if(H.popup_css!==false){if(H.popup_css){H.popup_css=G.documentBaseURI.toAbsolute(H.popup_css)}else{H.popup_css=G.baseURI.toAbsolute("themes/"+H.theme+"/skins/"+H.skin+"/dialog.css")}}if(H.popup_css_add){H.popup_css+=","+G.documentBaseURI.toAbsolute(H.popup_css_add)}G.controlManager=new k.ControlManager(G);G.onBeforeRenderUI.dispatch(G,G.controlManager);if(H.render_ui&&G.theme){G.orgDisplay=C.style.display;if(typeof H.theme!="function"){D=H.width||C.style.width||C.offsetWidth;y=H.height||C.style.height||C.offsetHeight;z=H.min_height||100;F=/^[0-9\.]+(|px)$/i;if(F.test(""+D)){D=Math.max(parseInt(D,10)+(p.deltaWidth||0),100)}if(F.test(""+y)){y=Math.max(parseInt(y,10)+(p.deltaHeight||0),z)}p=G.theme.renderUI({targetNode:C,width:D,height:y,deltaWidth:H.delta_width,deltaHeight:H.delta_height});l.setStyles(p.sizeContainer||p.editorContainer,{width:D,height:y});y=(p.iframeHeight||y)+(typeof(y)=="number"?(p.deltaHeight||0):"");if(y<z){y=z}}else{p=H.theme(G,C);if(p.editorContainer.nodeType){p.editorContainer=p.editorContainer.id=p.editorContainer.id||G.id+"_parent"}if(p.iframeContainer.nodeType){p.iframeContainer=p.iframeContainer.id=p.iframeContainer.id||G.id+"_iframecontainer"}y=p.iframeHeight||C.offsetHeight;if(b){G.onInit.add(function(n){n.dom.bind(n.getBody(),"beforedeactivate keydown keyup",function(){n.bookmark=n.selection.getBookmark(1)})});G.onNodeChange.add(function(n){if(document.activeElement.id==n.id+"_ifr"){n.bookmark=n.selection.getBookmark(1)}})}}G.editorContainer=p.editorContainer}if(H.content_css){i(g(H.content_css),function(n){G.contentCSS.push(G.documentBaseURI.toAbsolute(n))})}if(H.content_style){G.contentStyles.push(H.content_style)}if(H.content_editable){C=q=p=null;return G.initContentBody()}if(document.domain&&location.hostname!=document.domain){k.relaxedDomain=document.domain}G.iframeHTML=H.doctype+'<html><head xmlns="http://www.w3.org/1999/xhtml">';if(H.document_base_url!=k.documentBaseURL){G.iframeHTML+='<base href="'+G.documentBaseURI.getURI()+'" />'}if(k.isIE8){if(H.ie7_compat){G.iframeHTML+='<meta http-equiv="X-UA-Compatible" content="IE=7" />'}else{G.iframeHTML+='<meta http-equiv="X-UA-Compatible" content="IE=edge" />'}}G.iframeHTML+='<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';for(x=0;x<G.contentCSS.length;x++){G.iframeHTML+='<link type="text/css" rel="stylesheet" href="'+G.contentCSS[x]+'" />'}G.contentCSS=[];v=H.body_id||"tinymce";if(v.indexOf("=")!=-1){v=G.getParam("body_id","","hash");v=v[G.id]||v}B=H.body_class||"";if(B.indexOf("=")!=-1){B=G.getParam("body_class","","hash");B=B[G.id]||""}G.iframeHTML+='</head><body id="'+v+'" class="mceContentBody '+B+'" onload="window.parent.tinyMCE.get(\''+G.id+"').onLoad.dispatch();\"><br></body></html>";if(k.relaxedDomain&&(b||(k.isOpera&&parseFloat(opera.version())<11))){E='javascript:(function(){document.open();document.domain="'+document.domain+'";var ed = window.parent.tinyMCE.get("'+G.id+'");document.write(ed.iframeHTML);document.close();ed.initContentBody();})()'}q=l.add(p.iframeContainer,"iframe",{id:G.id+"_ifr",src:E||'javascript:""',frameBorder:"0",allowTransparency:"true",title:H.aria_label,style:{width:"100%",height:y,display:"block"}});G.contentAreaContainer=p.iframeContainer;if(p.editorContainer){l.get(p.editorContainer).style.display=G.orgDisplay}C.style.visibility=G.orgVisibility;l.get(G.id).style.display="none";l.setAttrib(G.id,"aria-hidden",true);if(!k.relaxedDomain||!E){G.initContentBody()}C=q=p=null},initContentBody:function(){var n=this,p=n.settings,q=l.get(n.id),r=n.getDoc(),o,m,s;if((!b||!k.relaxedDomain)&&!p.content_editable){r.open();r.write(n.iframeHTML);r.close();if(k.relaxedDomain){r.domain=k.relaxedDomain}}if(p.content_editable){l.addClass(q,"mceContentBody");n.contentDocument=r=p.content_document||document;n.contentWindow=p.content_window||window;n.bodyElement=q;p.content_document=p.content_window=null}m=n.getBody();m.disabled=true;if(!p.readonly){m.contentEditable=n.getParam("content_editable_state",true)}m.disabled=false;n.schema=new k.html.Schema(p);n.dom=new k.dom.DOMUtils(r,{keep_values:true,url_converter:n.convertURL,url_converter_scope:n,hex_colors:p.force_hex_style_colors,class_filter:p.class_filter,update_styles:true,root_element:p.content_editable?n.id:null,schema:n.schema});n.parser=new k.html.DomParser(p,n.schema);n.parser.addAttributeFilter("src,href,style",function(t,u){var v=t.length,y,A=n.dom,z,x;while(v--){y=t[v];z=y.attr(u);x="data-mce-"+u;if(!y.attributes.map[x]){if(u==="style"){y.attr(x,A.serializeStyle(A.parseStyle(z),y.name))}else{y.attr(x,n.convertURL(z,u,y.name))}}}});n.parser.addNodeFilter("script",function(t,u){var v=t.length,x;while(v--){x=t[v];x.attr("type","mce-"+(x.attr("type")||"text/javascript"))}});n.parser.addNodeFilter("#cdata",function(t,u){var v=t.length,x;while(v--){x=t[v];x.type=8;x.name="#comment";x.value="[CDATA["+x.value+"]]"}});n.parser.addNodeFilter("p,h1,h2,h3,h4,h5,h6,div",function(u,v){var x=u.length,y,t=n.schema.getNonEmptyElements();while(x--){y=u[x];if(y.isEmpty(t)){y.empty().append(new k.html.Node("br",1)).shortEnded=true}}});n.serializer=new k.dom.Serializer(p,n.dom,n.schema);n.selection=new k.dom.Selection(n.dom,n.getWin(),n.serializer,n);n.formatter=new k.Formatter(n);n.undoManager=new k.UndoManager(n);n.forceBlocks=new k.ForceBlocks(n);n.enterKey=new k.EnterKey(n);n.editorCommands=new k.EditorCommands(n);n.onExecCommand.add(function(t,u){if(!/^(FontName|FontSize)$/.test(u)){n.nodeChanged()}});n.serializer.onPreProcess.add(function(t,u){return n.onPreProcess.dispatch(n,u,t)});n.serializer.onPostProcess.add(function(t,u){return n.onPostProcess.dispatch(n,u,t)});n.onPreInit.dispatch(n);if(!p.browser_spellcheck&&!p.gecko_spellcheck){r.body.spellcheck=false}if(!p.readonly){n.bindNativeEvents()}n.controlManager.onPostRender.dispatch(n,n.controlManager);n.onPostRender.dispatch(n);n.quirks=k.util.Quirks(n);if(p.directionality){m.dir=p.directionality}if(p.nowrap){m.style.whiteSpace="nowrap"}if(p.protect){n.onBeforeSetContent.add(function(t,u){i(p.protect,function(v){u.content=u.content.replace(v,function(x){return"<!--mce:protected "+escape(x)+"-->"})})})}n.onSetContent.add(function(){n.addVisual(n.getBody())});if(p.padd_empty_editor){n.onPostProcess.add(function(t,u){u.content=u.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/,"")})}n.load({initial:true,format:"html"});n.startContent=n.getContent({format:"raw"});n.initialized=true;n.onInit.dispatch(n);n.execCallback("setupcontent_callback",n.id,m,r);n.execCallback("init_instance_callback",n);n.focus(true);n.nodeChanged({initial:true});if(n.contentStyles.length>0){s="";i(n.contentStyles,function(t){s+=t+"\r\n"});n.dom.addStyle(s)}i(n.contentCSS,function(t){n.dom.loadCSS(t)});if(p.auto_focus){setTimeout(function(){var t=k.get(p.auto_focus);t.selection.select(t.getBody(),1);t.selection.collapse(1);t.getBody().focus();t.getWin().focus()},100)}q=r=m=null},focus:function(p){var o,u=this,t=u.selection,q=u.settings.content_editable,n,r,s=u.getDoc(),m;if(!p){if(u.bookmark){t.moveToBookmark(u.bookmark);u.bookmark=null}n=t.getRng();if(n.item){r=n.item(0)}u._refreshContentEditable();if(!q){u.getWin().focus()}if(k.isGecko||q){m=u.getBody();if(m.setActive&&!k.isIE11){m.setActive()}else{m.focus()}if(q){t.normalize()}}if(r&&r.ownerDocument==s){n=s.body.createControlRange();n.addElement(r);n.select()}}if(k.activeEditor!=u){if((o=k.activeEditor)!=null){o.onDeactivate.dispatch(o,u)}u.onActivate.dispatch(u,o)}k._setActive(u)},execCallback:function(q){var m=this,p=m.settings[q],o;if(!p){return}if(m.callbackLookup&&(o=m.callbackLookup[q])){p=o.func;o=o.scope}if(d(p,"string")){o=p.replace(/\.\w+$/,"");o=o?k.resolve(o):0;p=k.resolve(p);m.callbackLookup=m.callbackLookup||{};m.callbackLookup[q]={func:p,scope:o}}return p.apply(o||m,Array.prototype.slice.call(arguments,1))},translate:function(m){var o=this.settings.language||"en",n=k.i18n;if(!m){return""}return n[o+"."+m]||m.replace(/\{\#([^\}]+)\}/g,function(q,p){return n[o+"."+p]||"{#"+p+"}"})},getLang:function(o,m){return k.i18n[(this.settings.language||"en")+"."+o]||(d(m)?m:"{#"+o+"}")},getParam:function(t,q,m){var r=k.trim,p=d(this.settings[t])?this.settings[t]:q,s;if(m==="hash"){s={};if(d(p,"string")){i(p.indexOf("=")>0?p.split(/[;,](?![^=;,]*(?:[;,]|$))/):p.split(","),function(n){n=n.split("=");if(n.length>1){s[r(n[0])]=r(n[1])}else{s[r(n[0])]=r(n)}})}else{s=p}return s}return p},nodeChanged:function(q){var m=this,n=m.selection,p;if(!m.initialized){return}q=q||{};p=n.getStart()||m.getBody();p=b&&p.ownerDocument!=m.getDoc()?m.getBody():p;q.parents=[];m.dom.getParent(p,function(o){if(o.nodeName=="BODY"){return true}q.parents.push(o)});m.onNodeChange.dispatch(m,q?q.controlManager||m.controlManager:m.controlManager,p,n.isCollapsed(),q)},addButton:function(n,o){var m=this;m.buttons=m.buttons||{};m.buttons[n]=o},addCommand:function(m,o,n){this.execCommands[m]={func:o,scope:n||this}},addQueryStateHandler:function(m,o,n){this.queryStateCommands[m]={func:o,scope:n||this}},addQueryValueHandler:function(m,o,n){this.queryValueCommands[m]={func:o,scope:n||this}},addShortcut:function(o,q,m,p){var n=this,r;if(n.settings.custom_shortcuts===false){return false}n.shortcuts=n.shortcuts||{};if(d(m,"string")){r=m;m=function(){n.execCommand(r,false,null)}}if(d(m,"object")){r=m;m=function(){n.execCommand(r[0],r[1],r[2])}}i(g(o),function(s){var t={func:m,scope:p||this,desc:n.translate(q),alt:false,ctrl:false,shift:false};i(g(s,"+"),function(u){switch(u){case"alt":case"ctrl":case"shift":t[u]=true;break;default:t.charCode=u.charCodeAt(0);t.keyCode=u.toUpperCase().charCodeAt(0)}});n.shortcuts[(t.ctrl?"ctrl":"")+","+(t.alt?"alt":"")+","+(t.shift?"shift":"")+","+t.keyCode]=t});return true},execCommand:function(u,r,x,m){var p=this,q=0,v,n;if(!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(u)&&(!m||!m.skip_focus)){p.focus()}m=f({},m);p.onBeforeExecCommand.dispatch(p,u,r,x,m);if(m.terminate){return false}if(p.execCallback("execcommand_callback",p.id,p.selection.getNode(),u,r,x)){p.onExecCommand.dispatch(p,u,r,x,m);return true}if(v=p.execCommands[u]){n=v.func.call(v.scope,r,x);if(n!==true){p.onExecCommand.dispatch(p,u,r,x,m);return n}}i(p.plugins,function(o){if(o.execCommand&&o.execCommand(u,r,x)){p.onExecCommand.dispatch(p,u,r,x,m);q=1;return false}});if(q){return true}if(p.theme&&p.theme.execCommand&&p.theme.execCommand(u,r,x)){p.onExecCommand.dispatch(p,u,r,x,m);return true}if(p.editorCommands.execCommand(u,r,x)){p.onExecCommand.dispatch(p,u,r,x,m);return true}p.getDoc().execCommand(u,r,x);p.onExecCommand.dispatch(p,u,r,x,m)},queryCommandState:function(q){var n=this,r,p;if(n._isHidden()){return}if(r=n.queryStateCommands[q]){p=r.func.call(r.scope);if(p!==true){return p}}r=n.editorCommands.queryCommandState(q);if(r!==-1){return r}try{return this.getDoc().queryCommandState(q)}catch(m){}},queryCommandValue:function(r){var n=this,q,p;if(n._isHidden()){return}if(q=n.queryValueCommands[r]){p=q.func.call(q.scope);if(p!==true){return p}}q=n.editorCommands.queryCommandValue(r);if(d(q)){return q}try{return this.getDoc().queryCommandValue(r)}catch(m){}},show:function(){var m=this;l.show(m.getContainer());l.hide(m.id);m.load()},hide:function(){var m=this,n=m.getDoc();if(b&&n){n.execCommand("SelectAll")}m.save();l.hide(m.getContainer());l.setStyle(m.id,"display",m.orgDisplay)},isHidden:function(){return !l.isHidden(this.id)},setProgressState:function(m,n,p){this.onSetProgressState.dispatch(this,m,n,p);return m},load:function(q){var m=this,p=m.getElement(),n;if(p){q=q||{};q.load=true;n=m.setContent(d(p.value)?p.value:p.innerHTML,q);q.element=p;if(!q.no_events){m.onLoadContent.dispatch(m,q)}q.element=p=null;return n}},save:function(r){var m=this,q=m.getElement(),n,p;if(!q||!m.initialized){return}r=r||{};r.save=true;r.element=q;n=r.content=m.getContent(r);if(!r.no_events){m.onSaveContent.dispatch(m,r)}n=r.content;if(!/TEXTAREA|INPUT/i.test(q.nodeName)){q.innerHTML=n;if(p=l.getParent(m.id,"form")){i(p.elements,function(o){if(o.name==m.id){o.value=n;return false}})}}else{q.value=n}r.element=q=null;return n},setContent:function(q,o){var n=this,m=n.getBody(),p;o=o||{};o.format=o.format||"html";o.set=true;o.content=q;if(!o.no_events){n.onBeforeSetContent.dispatch(n,o)}q=o.content;if(q.length===0||/^\s+$/.test(q)){p=n.settings.forced_root_block;if(p&&n.schema.isValidChild(m.nodeName.toLowerCase(),p.toLowerCase())){if(b){q="<"+p+"></"+p+">"}else{q="<"+p+'><br data-mce-bogus="1"></'+p+">"}}else{if(!b){q='<br data-mce-bogus="1">'}}m.innerHTML=q;n.selection.select(m,true);n.selection.collapse(true);return}if(o.format!=="raw"){q=new k.html.Serializer({},n.schema).serialize(n.parser.parse(q))}o.content=k.trim(q);n.dom.setHTML(m,o.content);if(!o.no_events){n.onSetContent.dispatch(n,o)}if(!n.settings.content_editable||document.activeElement===n.getBody()){n.selection.normalize()}return o.content},getContent:function(o){var n=this,p,m=n.getBody();o=o||{};o.format=o.format||"html";o.get=true;o.getInner=true;if(!o.no_events){n.onBeforeGetContent.dispatch(n,o)}if(o.format=="raw"){p=m.innerHTML}else{if(o.format=="text"){p=m.innerText||m.textContent}else{p=n.serializer.serialize(m,o)}}if(o.format!="text"){o.content=k.trim(p)}else{o.content=p}if(!o.no_events){n.onGetContent.dispatch(n,o)}return o.content},isDirty:function(){var m=this;return k.trim(m.startContent)!==k.trim(m.getContent({format:"raw"}))&&!m.isNotDirty},getContainer:function(){var m=this;if(!m.container){m.container=l.get(m.editorContainer||m.id+"_parent")}return m.container},getContentAreaContainer:function(){return this.contentAreaContainer},getElement:function(){return l.get(this.settings.content_element||this.id)},getWin:function(){var m=this,n;if(!m.contentWindow){n=l.get(m.id+"_ifr");if(n){m.contentWindow=n.contentWindow}}return m.contentWindow},getDoc:function(){var m=this,n;if(!m.contentDocument){n=m.getWin();if(n){m.contentDocument=n.document}}return m.contentDocument},getBody:function(){return this.bodyElement||this.getDoc().body},convertURL:function(o,n,q){var m=this,p=m.settings;if(p.urlconverter_callback){return m.execCallback("urlconverter_callback",o,q,true,n)}if(!p.convert_urls||(q&&q.nodeName=="LINK")||o.indexOf("file:")===0){return o}if(p.relative_urls){return m.documentBaseURI.toRelative(o)}o=m.documentBaseURI.toAbsolute(o,p.remove_script_host);return o},addVisual:function(q){var n=this,o=n.settings,p=n.dom,m;q=q||n.getBody();if(!d(n.hasVisual)){n.hasVisual=o.visual}i(p.select("table,a",q),function(s){var r;switch(s.nodeName){case"TABLE":m=o.visual_table_class||"mceItemTable";r=p.getAttrib(s,"border");if(!r||r=="0"){if(n.hasVisual){p.addClass(s,m)}else{p.removeClass(s,m)}}return;case"A":if(!p.getAttrib(s,"href",false)){r=p.getAttrib(s,"name")||s.id;m="mceItemAnchor";if(r){if(n.hasVisual){p.addClass(s,m)}else{p.removeClass(s,m)}}}return}});n.onVisualAid.dispatch(n,q,n.hasVisual)},remove:function(){var m=this,o=m.getContainer(),n=m.getDoc();if(!m.removed){m.removed=1;if(b&&n){n.execCommand("SelectAll")}m.save();l.setStyle(m.id,"display",m.orgDisplay);if(!m.settings.content_editable){j.unbind(m.getWin());j.unbind(m.getDoc())}j.unbind(m.getBody());j.clear(o);m.execCallback("remove_instance_callback",m);m.onRemove.dispatch(m);m.onExecCommand.listeners=[];k.remove(m);l.remove(o)}},destroy:function(n){var m=this;if(m.destroyed){return}if(a){j.unbind(m.getDoc());j.unbind(m.getWin());j.unbind(m.getBody())}if(!n){k.removeUnload(m.destroy);tinyMCE.onBeforeUnload.remove(m._beforeUnload);if(m.theme&&m.theme.destroy){m.theme.destroy()}m.controlManager.destroy();m.selection.destroy();m.dom.destroy()}if(m.formElement){m.formElement.submit=m.formElement._mceOldSubmit;m.formElement._mceOldSubmit=null}m.contentAreaContainer=m.formElement=m.container=m.settings.content_element=m.bodyElement=m.contentDocument=m.contentWindow=null;if(m.selection){m.selection=m.selection.win=m.selection.dom=m.selection.dom.doc=null}m.destroyed=1},_refreshContentEditable:function(){var n=this,m,o;if(n._isHidden()){m=n.getBody();o=m.parentNode;o.removeChild(m);o.appendChild(m);m.focus()}},_isHidden:function(){var m;if(!a){return 0}m=this.selection.getSel();return(!m||!m.rangeCount||m.rangeCount===0)}})})(tinymce);(function(a){var b=a.each;a.Editor.prototype.setupEvents=function(){var c=this,d=c.settings;b(["onPreInit","onBeforeRenderUI","onPostRender","onLoad","onInit","onRemove","onActivate","onDeactivate","onClick","onEvent","onMouseUp","onMouseDown","onDblClick","onKeyDown","onKeyUp","onKeyPress","onContextMenu","onSubmit","onReset","onPaste","onPreProcess","onPostProcess","onBeforeSetContent","onBeforeGetContent","onSetContent","onGetContent","onLoadContent","onSaveContent","onNodeChange","onChange","onBeforeExecCommand","onExecCommand","onUndo","onRedo","onVisualAid","onSetProgressState","onSetAttrib"],function(e){c[e]=new a.util.Dispatcher(c)});if(d.cleanup_callback){c.onBeforeSetContent.add(function(e,f){f.content=e.execCallback("cleanup_callback","insert_to_editor",f.content,f)});c.onPreProcess.add(function(e,f){if(f.set){e.execCallback("cleanup_callback","insert_to_editor_dom",f.node,f)}if(f.get){e.execCallback("cleanup_callback","get_from_editor_dom",f.node,f)}});c.onPostProcess.add(function(e,f){if(f.set){f.content=e.execCallback("cleanup_callback","insert_to_editor",f.content,f)}if(f.get){f.content=e.execCallback("cleanup_callback","get_from_editor",f.content,f)}})}if(d.save_callback){c.onGetContent.add(function(e,f){if(f.save){f.content=e.execCallback("save_callback",e.id,f.content,e.getBody())}})}if(d.handle_event_callback){c.onEvent.add(function(f,g,h){if(c.execCallback("handle_event_callback",g,f,h)===false){g.preventDefault();g.stopPropagation()}})}if(d.handle_node_change_callback){c.onNodeChange.add(function(f,e,g){f.execCallback("handle_node_change_callback",f.id,g,-1,-1,true,f.selection.isCollapsed())})}if(d.save_callback){c.onSaveContent.add(function(e,g){var f=e.execCallback("save_callback",e.id,g.content,e.getBody());if(f){g.content=f}})}if(d.onchange_callback){c.onChange.add(function(f,e){f.execCallback("onchange_callback",f,e)})}};a.Editor.prototype.bindNativeEvents=function(){var l=this,f,d=l.settings,e=l.dom,h;h={mouseup:"onMouseUp",mousedown:"onMouseDown",click:"onClick",keyup:"onKeyUp",keydown:"onKeyDown",keypress:"onKeyPress",submit:"onSubmit",reset:"onReset",contextmenu:"onContextMenu",dblclick:"onDblClick",paste:"onPaste"};function c(i,m){var n=i.type;if(l.removed){return}if(l.onEvent.dispatch(l,i,m)!==false){l[h[i.fakeType||i.type]].dispatch(l,i,m)}}function j(i){l.focus(true)}function k(i,m){if(m.keyCode!=65||!a.VK.metaKeyPressed(m)){l.selection.normalize()}l.nodeChanged()}b(h,function(m,n){var i=d.content_editable?l.getBody():l.getDoc();switch(n){case"contextmenu":e.bind(i,n,c);break;case"paste":e.bind(l.getBody(),n,c);break;case"submit":case"reset":e.bind(l.getElement().form||a.DOM.getParent(l.id,"form"),n,c);break;default:e.bind(i,n,c)}});e.bind(d.content_editable?l.getBody():(a.isGecko?l.getDoc():l.getWin()),"focus",function(i){l.focus(true)});if(d.content_editable&&a.isOpera){e.bind(l.getBody(),"click",j);e.bind(l.getBody(),"keydown",j)}l.onMouseUp.add(k);l.onKeyUp.add(function(i,n){var m=n.keyCode;if((m>=33&&m<=36)||(m>=37&&m<=40)||m==13||m==45||m==46||m==8||(a.isMac&&(m==91||m==93))||n.ctrlKey){k(i,n)}});l.onReset.add(function(){l.setContent(l.startContent,{format:"raw"})});function g(m,i){if(m.altKey||m.ctrlKey||m.metaKey){b(l.shortcuts,function(n){var o=a.isMac?m.metaKey:m.ctrlKey;if(n.ctrl!=o||n.alt!=m.altKey||n.shift!=m.shiftKey){return}if(m.keyCode==n.keyCode||(m.charCode&&m.charCode==n.charCode)){m.preventDefault();if(i){n.func.call(n.scope)}return true}})}}l.onKeyUp.add(function(i,m){g(m)});l.onKeyPress.add(function(i,m){g(m)});l.onKeyDown.add(function(i,m){g(m,true)});if(a.isOpera){l.onClick.add(function(i,m){m.preventDefault()})}}})(tinymce);(function(d){var e=d.each,b,a=true,c=false;d.EditorCommands=function(n){var m=n.dom,p=n.selection,j={state:{},exec:{},value:{}},k=n.settings,q=n.formatter,o;function r(z,y,x){var v;z=z.toLowerCase();if(v=j.exec[z]){v(z,y,x);return a}return c}function l(x){var v;x=x.toLowerCase();if(v=j.state[x]){return v(x)}return -1}function h(x){var v;x=x.toLowerCase();if(v=j.value[x]){return v(x)}return c}function u(v,x){x=x||"exec";e(v,function(z,y){e(y.toLowerCase().split(","),function(A){j[x][A]=z})})}d.extend(this,{execCommand:r,queryCommandState:l,queryCommandValue:h,addCommands:u});function f(y,x,v){if(x===b){x=c}if(v===b){v=null}return n.getDoc().execCommand(y,x,v)}function t(v){return q.match(v)}function s(v,x){q.toggle(v,x?{value:x}:b)}function i(v){o=p.getBookmark(v)}function g(){p.moveToBookmark(o)}u({"mceResetDesignMode,mceBeginUndoLevel":function(){},"mceEndUndoLevel,mceAddUndoLevel":function(){n.undoManager.add()},"Cut,Copy,Paste":function(z){var y=n.getDoc(),v;try{f(z)}catch(x){v=a}if(v||!y.queryCommandSupported(z)){if(d.isGecko){n.windowManager.confirm(n.getLang("clipboard_msg"),function(A){if(A){open("http://www.mozilla.org/editor/midasdemo/securityprefs.html","_blank")}})}else{n.windowManager.alert(n.getLang("clipboard_no_support"))}}},unlink:function(v){if(p.isCollapsed()){p.select(p.getNode())}f(v);p.collapse(c)},"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(v){var x=v.substring(7);e("left,center,right,full".split(","),function(y){if(x!=y){q.remove("align"+y)}});s("align"+x);r("mceRepaint")},"InsertUnorderedList,InsertOrderedList":function(y){var v,x;f(y);v=m.getParent(p.getNode(),"ol,ul");if(v){x=v.parentNode;if(/^(H[1-6]|P|ADDRESS|PRE)$/.test(x.nodeName)){i();m.split(x,v);g()}}},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(v){s(v)},"ForeColor,HiliteColor,FontName":function(y,x,v){s(y,v)},FontSize:function(z,y,x){var v,A;if(x>=1&&x<=7){A=d.explode(k.font_size_style_values);v=d.explode(k.font_size_classes);if(v){x=v[x-1]||x}else{x=A[x-1]||x}}s(z,x)},RemoveFormat:function(v){q.remove(v)},mceBlockQuote:function(v){s("blockquote")},FormatBlock:function(y,x,v){return s(v||"p")},mceCleanup:function(){var v=p.getBookmark();n.setContent(n.getContent({cleanup:a}),{cleanup:a});p.moveToBookmark(v)},mceRemoveNode:function(z,y,x){var v=x||p.getNode();if(v!=n.getBody()){i();n.dom.remove(v,a);g()}},mceSelectNodeDepth:function(z,y,x){var v=0;m.getParent(p.getNode(),function(A){if(A.nodeType==1&&v++==x){p.select(A);return c}},n.getBody())},mceSelectNode:function(y,x,v){p.select(v)},mceInsertContent:function(B,I,K){var y,J,E,z,F,G,D,C,L,x,A,M,v,H;y=n.parser;J=new d.html.Serializer({},n.schema);v='<span id="mce_marker" data-mce-type="bookmark">\uFEFF</span>';G={content:K,format:"html"};p.onBeforeSetContent.dispatch(p,G);K=G.content;if(K.indexOf("{$caret}")==-1){K+="{$caret}"}K=K.replace(/\{\$caret\}/,v);if(!p.isCollapsed()){n.getDoc().execCommand("Delete",false,null)}E=p.getNode();G={context:E.nodeName.toLowerCase()};F=y.parse(K,G);A=F.lastChild;if(A.attr("id")=="mce_marker"){D=A;for(A=A.prev;A;A=A.walk(true)){if(A.type==3||!m.isBlock(A.name)){A.parent.insert(D,A,A.name==="br");break}}}if(!G.invalid){K=J.serialize(F);A=E.firstChild;M=E.lastChild;if(!A||(A===M&&A.nodeName==="BR")){m.setHTML(E,K)}else{p.setContent(K)}}else{p.setContent(v);E=p.getNode();z=n.getBody();if(E.nodeType==9){E=A=z}else{A=E}while(A!==z){E=A;A=A.parentNode}K=E==z?z.innerHTML:m.getOuterHTML(E);K=J.serialize(y.parse(K.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i,function(){return J.serialize(F)})));if(E==z){m.setHTML(z,K)}else{m.setOuterHTML(E,K)}}D=m.get("mce_marker");C=m.getRect(D);L=m.getViewPort(n.getWin());if((C.y+C.h>L.y+L.h||C.y<L.y)||(C.x>L.x+L.w||C.x<L.x)){H=d.isIE?n.getDoc().documentElement:n.getBody();H.scrollLeft=C.x;H.scrollTop=C.y-L.h+25}x=m.createRng();A=D.previousSibling;if(A&&A.nodeType==3){x.setStart(A,A.nodeValue.length)}else{x.setStartBefore(D);x.setEndBefore(D)}m.remove(D);p.setRng(x);p.onSetContent.dispatch(p,G);n.addVisual()},mceInsertRawHTML:function(y,x,v){p.setContent("tiny_mce_marker");n.setContent(n.getContent().replace(/tiny_mce_marker/g,function(){return v}))},mceToggleFormat:function(y,x,v){s(v)},mceSetContent:function(y,x,v){n.setContent(v)},"Indent,Outdent":function(z){var x,v,y;x=k.indentation;v=/[a-z%]+$/i.exec(x);x=parseInt(x);if(!l("InsertUnorderedList")&&!l("InsertOrderedList")){if(!k.forced_root_block&&!m.getParent(p.getNode(),m.isBlock)){q.apply("div")}e(p.getSelectedBlocks(),function(A){if(z=="outdent"){y=Math.max(0,parseInt(A.style.paddingLeft||0)-x);m.setStyle(A,"paddingLeft",y?y+v:"")}else{m.setStyle(A,"paddingLeft",(parseInt(A.style.paddingLeft||0)+x)+v)}})}else{f(z)}},mceRepaint:function(){var x;if(d.isGecko){try{i(a);if(p.getSel()){p.getSel().selectAllChildren(n.getBody())}p.collapse(a);g()}catch(v){}}},mceToggleFormat:function(y,x,v){q.toggle(v)},InsertHorizontalRule:function(){n.execCommand("mceInsertContent",false,"<hr />")},mceToggleVisualAid:function(){n.hasVisual=!n.hasVisual;n.addVisual()},mceReplaceContent:function(y,x,v){n.execCommand("mceInsertContent",false,v.replace(/\{\$selection\}/g,p.getContent({format:"text"})))},mceInsertLink:function(z,y,x){var v;if(typeof(x)=="string"){x={href:x}}v=m.getParent(p.getNode(),"a");x.href=x.href.replace(" ","%20");if(!v||!x.href){q.remove("link")}if(x.href){q.apply("link",x,v)}},selectAll:function(){var x=m.getRoot(),v=m.createRng();if(p.getRng().setStart){v.setStart(x,0);v.setEnd(x,x.childNodes.length);p.setRng(v)}else{f("SelectAll")}}});u({"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(z){var x="align"+z.substring(7);var v=p.isCollapsed()?[m.getParent(p.getNode(),m.isBlock)]:p.getSelectedBlocks();var y=d.map(v,function(A){return !!q.matchNode(A,x)});return d.inArray(y,a)!==-1},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(v){return t(v)},mceBlockQuote:function(){return t("blockquote")},Outdent:function(){var v;if(k.inline_styles){if((v=m.getParent(p.getStart(),m.isBlock))&&parseInt(v.style.paddingLeft)>0){return a}if((v=m.getParent(p.getEnd(),m.isBlock))&&parseInt(v.style.paddingLeft)>0){return a}}return l("InsertUnorderedList")||l("InsertOrderedList")||(!k.inline_styles&&!!m.getParent(p.getNode(),"BLOCKQUOTE"))},"InsertUnorderedList,InsertOrderedList":function(x){var v=m.getParent(p.getNode(),"ul,ol");return v&&(x==="insertunorderedlist"&&v.tagName==="UL"||x==="insertorderedlist"&&v.tagName==="OL")}},"state");u({"FontSize,FontName":function(y){var x=0,v;if(v=m.getParent(p.getNode(),"span")){if(y=="fontsize"){x=v.style.fontSize}else{x=v.style.fontFamily.replace(/, /g,",").replace(/[\'\"]/g,"").toLowerCase()}}return x}},"value");u({Undo:function(){n.undoManager.undo()},Redo:function(){n.undoManager.redo()}})}})(tinymce);(function(b){var a=b.util.Dispatcher;b.UndoManager=function(h){var l,i=0,e=[],g,k,j,f;function c(){return b.trim(h.getContent({format:"raw",no_events:1}).replace(/<span[^>]+data-mce-bogus[^>]+>[\u200B\uFEFF]+<\/span>/g,""))}function d(){l.typing=false;l.add()}onBeforeAdd=new a(l);k=new a(l);j=new a(l);f=new a(l);k.add(function(m,n){if(m.hasUndo()){return h.onChange.dispatch(h,n,m)}});j.add(function(m,n){return h.onUndo.dispatch(h,n,m)});f.add(function(m,n){return h.onRedo.dispatch(h,n,m)});h.onInit.add(function(){l.add()});h.onBeforeExecCommand.add(function(m,p,o,q,n){if(p!="Undo"&&p!="Redo"&&p!="mceRepaint"&&(!n||!n.skip_undo)){l.beforeChange()}});h.onExecCommand.add(function(m,p,o,q,n){if(p!="Undo"&&p!="Redo"&&p!="mceRepaint"&&(!n||!n.skip_undo)){l.add()}});h.onSaveContent.add(d);h.dom.bind(h.dom.getRoot(),"dragend",d);h.dom.bind(h.getBody(),"focusout",function(m){if(!h.removed&&l.typing){d()}});h.onKeyUp.add(function(m,o){var n=o.keyCode;if((n>=33&&n<=36)||(n>=37&&n<=40)||n==45||n==13||o.ctrlKey){d()}});h.onKeyDown.add(function(m,o){var n=o.keyCode;if((n>=33&&n<=36)||(n>=37&&n<=40)||n==45){if(l.typing){d()}return}if((n<16||n>20)&&n!=224&&n!=91&&!l.typing){l.beforeChange();l.typing=true;l.add()}});h.onMouseDown.add(function(m,n){if(l.typing){d()}});h.addShortcut("ctrl+z","undo_desc","Undo");h.addShortcut("ctrl+y","redo_desc","Redo");l={data:e,typing:false,onBeforeAdd:onBeforeAdd,onAdd:k,onUndo:j,onRedo:f,beforeChange:function(){g=h.selection.getBookmark(2,true)},add:function(p){var m,n=h.settings,o;p=p||{};p.content=c();l.onBeforeAdd.dispatch(l,p);o=e[i];if(o&&o.content==p.content){return null}if(e[i]){e[i].beforeBookmark=g}if(n.custom_undo_redo_levels){if(e.length>n.custom_undo_redo_levels){for(m=0;m<e.length-1;m++){e[m]=e[m+1]}e.length--;i=e.length}}p.bookmark=h.selection.getBookmark(2,true);if(i<e.length-1){e.length=i+1}e.push(p);i=e.length-1;l.onAdd.dispatch(l,p);h.isNotDirty=0;return p},undo:function(){var n,m;if(l.typing){l.add();l.typing=false}if(i>0){n=e[--i];h.setContent(n.content,{format:"raw"});h.selection.moveToBookmark(n.beforeBookmark);l.onUndo.dispatch(l,n)}return n},redo:function(){var m;if(i<e.length-1){m=e[++i];h.setContent(m.content,{format:"raw"});h.selection.moveToBookmark(m.bookmark);l.onRedo.dispatch(l,m)}return m},clear:function(){e=[];i=0;l.typing=false},hasUndo:function(){return i>0||this.typing},hasRedo:function(){return i<e.length-1&&!this.typing}};return l}})(tinymce);tinymce.ForceBlocks=function(c){var b=c.settings,e=c.dom,a=c.selection,d=c.schema.getBlockElements();function f(){var j=a.getStart(),h=c.getBody(),g,k,o,s,q,i,l,m=-16777215,p,r;if(!j||j.nodeType!==1||!b.forced_root_block){return}while(j&&j!=h){if(d[j.nodeName]){return}j=j.parentNode}g=a.getRng();if(g.setStart){k=g.startContainer;o=g.startOffset;s=g.endContainer;q=g.endOffset}else{if(g.item){j=g.item(0);g=c.getDoc().body.createTextRange();g.moveToElementText(j)}r=g.parentElement().ownerDocument===c.getDoc();tmpRng=g.duplicate();tmpRng.collapse(true);o=tmpRng.move("character",m)*-1;if(!tmpRng.collapsed){tmpRng=g.duplicate();tmpRng.collapse(false);q=(tmpRng.move("character",m)*-1)-o}}j=h.firstChild;while(j){if(j.nodeType===3||(j.nodeType==1&&!d[j.nodeName])){if(j.nodeType===3&&j.nodeValue.length==0){l=j;j=j.nextSibling;e.remove(l);continue}if(!i){i=e.create(b.forced_root_block);j.parentNode.insertBefore(i,j);p=true}l=j;j=j.nextSibling;i.appendChild(l)}else{i=null;j=j.nextSibling}}if(p){if(g.setStart){g.setStart(k,o);g.setEnd(s,q);a.setRng(g)}else{if(r){try{g=c.getDoc().body.createTextRange();g.moveToElementText(h);g.collapse(true);g.moveStart("character",o);if(q>0){g.moveEnd("character",q)}g.select()}catch(n){}}}c.nodeChanged()}}if(b.forced_root_block){c.onKeyUp.add(f);c.onNodeChange.add(f)}};(function(c){var b=c.DOM,a=c.dom.Event,d=c.each,e=c.extend;c.create("tinymce.ControlManager",{ControlManager:function(f,j){var h=this,g;j=j||{};h.editor=f;h.controls={};h.onAdd=new c.util.Dispatcher(h);h.onPostRender=new c.util.Dispatcher(h);h.prefix=j.prefix||f.id+"_";h._cls={};h.onPostRender.add(function(){d(h.controls,function(i){i.postRender()})})},get:function(f){return this.controls[this.prefix+f]||this.controls[f]},setActive:function(h,f){var g=null;if(g=this.get(h)){g.setActive(f)}return g},setDisabled:function(h,f){var g=null;if(g=this.get(h)){g.setDisabled(f)}return g},add:function(g){var f=this;if(g){f.controls[g.id]=g;f.onAdd.dispatch(g,f)}return g},createControl:function(j){var o,k,g,h=this,m=h.editor,n,f;if(!h.controlFactories){h.controlFactories=[];d(m.plugins,function(i){if(i.createControl){h.controlFactories.push(i)}})}n=h.controlFactories;for(k=0,g=n.length;k<g;k++){o=n[k].createControl(j,h);if(o){return h.add(o)}}if(j==="|"||j==="separator"){return h.createSeparator()}if(m.buttons&&(o=m.buttons[j])){return h.createButton(j,o)}return h.add(o)},createDropMenu:function(f,n,h){var m=this,i=m.editor,j,g,k,l;n=e({"class":"mceDropDown",constrain:i.settings.constrain_menus},n);n["class"]=n["class"]+" "+i.getParam("skin")+"Skin";if(k=i.getParam("skin_variant")){n["class"]+=" "+i.getParam("skin")+"Skin"+k.substring(0,1).toUpperCase()+k.substring(1)}n["class"]+=i.settings.directionality=="rtl"?" mceRtl":"";f=m.prefix+f;l=h||m._cls.dropmenu||c.ui.DropMenu;j=m.controls[f]=new l(f,n);j.onAddItem.add(function(r,q){var p=q.settings;p.title=i.getLang(p.title,p.title);if(!p.onclick){p.onclick=function(o){if(p.cmd){i.execCommand(p.cmd,p.ui||false,p.value)}}}});i.onRemove.add(function(){j.destroy()});if(c.isIE){j.onShowMenu.add(function(){i.focus();g=i.selection.getBookmark(1)});j.onHideMenu.add(function(){if(g){i.selection.moveToBookmark(g);g=0}})}return m.add(j)},createListBox:function(f,n,h){var l=this,j=l.editor,i,k,m;if(l.get(f)){return null}n.title=j.translate(n.title);n.scope=n.scope||j;if(!n.onselect){n.onselect=function(o){j.execCommand(n.cmd,n.ui||false,o||n.value)}}n=e({title:n.title,"class":"mce_"+f,scope:n.scope,control_manager:l},n);f=l.prefix+f;function g(o){return o.settings.use_accessible_selects&&!c.isGecko}if(j.settings.use_native_selects||g(j)){k=new c.ui.NativeListBox(f,n)}else{m=h||l._cls.listbox||c.ui.ListBox;k=new m(f,n,j)}l.controls[f]=k;if(c.isWebKit){k.onPostRender.add(function(p,o){a.add(o,"mousedown",function(){j.bookmark=j.selection.getBookmark(1)});a.add(o,"focus",function(){j.selection.moveToBookmark(j.bookmark);j.bookmark=null})})}if(k.hideMenu){j.onMouseDown.add(k.hideMenu,k)}return l.add(k)},createButton:function(m,i,l){var h=this,g=h.editor,j,k,f;if(h.get(m)){return null}i.title=g.translate(i.title);i.label=g.translate(i.label);i.scope=i.scope||g;if(!i.onclick&&!i.menu_button){i.onclick=function(){g.execCommand(i.cmd,i.ui||false,i.value)}}i=e({title:i.title,"class":"mce_"+m,unavailable_prefix:g.getLang("unavailable",""),scope:i.scope,control_manager:h},i);m=h.prefix+m;if(i.menu_button){f=l||h._cls.menubutton||c.ui.MenuButton;k=new f(m,i,g);g.onMouseDown.add(k.hideMenu,k)}else{f=h._cls.button||c.ui.Button;k=new f(m,i,g)}return h.add(k)},createMenuButton:function(h,f,g){f=f||{};f.menu_button=1;return this.createButton(h,f,g)},createSplitButton:function(m,i,l){var h=this,g=h.editor,j,k,f;if(h.get(m)){return null}i.title=g.translate(i.title);i.scope=i.scope||g;if(!i.onclick){i.onclick=function(n){g.execCommand(i.cmd,i.ui||false,n||i.value)}}if(!i.onselect){i.onselect=function(n){g.execCommand(i.cmd,i.ui||false,n||i.value)}}i=e({title:i.title,"class":"mce_"+m,scope:i.scope,control_manager:h},i);m=h.prefix+m;f=l||h._cls.splitbutton||c.ui.SplitButton;k=h.add(new f(m,i,g));g.onMouseDown.add(k.hideMenu,k);return k},createColorSplitButton:function(f,n,h){var l=this,j=l.editor,i,k,m,g;if(l.get(f)){return null}n.title=j.translate(n.title);n.scope=n.scope||j;if(!n.onclick){n.onclick=function(o){if(c.isIE){g=j.selection.getBookmark(1)}j.execCommand(n.cmd,n.ui||false,o||n.value)}}if(!n.onselect){n.onselect=function(o){j.execCommand(n.cmd,n.ui||false,o||n.value)}}n=e({title:n.title,"class":"mce_"+f,menu_class:j.getParam("skin")+"Skin",scope:n.scope,more_colors_title:j.getLang("more_colors")},n);f=l.prefix+f;m=h||l._cls.colorsplitbutton||c.ui.ColorSplitButton;k=new m(f,n,j);j.onMouseDown.add(k.hideMenu,k);j.onRemove.add(function(){k.destroy()});if(c.isIE){k.onShowMenu.add(function(){j.focus();g=j.selection.getBookmark(1)});k.onHideMenu.add(function(){if(g){j.selection.moveToBookmark(g);g=0}})}return l.add(k)},createToolbar:function(k,h,j){var i,g=this,f;k=g.prefix+k;f=j||g._cls.toolbar||c.ui.Toolbar;i=new f(k,h,g.editor);if(g.get(k)){return null}return g.add(i)},createToolbarGroup:function(k,h,j){var i,g=this,f;k=g.prefix+k;f=j||this._cls.toolbarGroup||c.ui.ToolbarGroup;i=new f(k,h,g.editor);if(g.get(k)){return null}return g.add(i)},createSeparator:function(g){var f=g||this._cls.separator||c.ui.Separator;return new f()},setControlType:function(g,f){return this._cls[g.toLowerCase()]=f},destroy:function(){d(this.controls,function(f){f.destroy()});this.controls=null}})})(tinymce);(function(d){var a=d.util.Dispatcher,e=d.each,c=d.isIE,b=d.isOpera;d.create("tinymce.WindowManager",{WindowManager:function(f){var g=this;g.editor=f;g.onOpen=new a(g);g.onClose=new a(g);g.params={};g.features={}},open:function(z,h){var v=this,k="",n,m,i=v.editor.settings.dialog_type=="modal",q,o,j,g=d.DOM.getViewPort(),r;z=z||{};h=h||{};o=b?g.w:screen.width;j=b?g.h:screen.height;z.name=z.name||"mc_"+new Date().getTime();z.width=parseInt(z.width||320);z.height=parseInt(z.height||240);z.resizable=true;z.left=z.left||parseInt(o/2)-(z.width/2);z.top=z.top||parseInt(j/2)-(z.height/2);h.inline=false;h.mce_width=z.width;h.mce_height=z.height;h.mce_auto_focus=z.auto_focus;if(i){if(c){z.center=true;z.help=false;z.dialogWidth=z.width+"px";z.dialogHeight=z.height+"px";z.scroll=z.scrollbars||false}}e(z,function(p,f){if(d.is(p,"boolean")){p=p?"yes":"no"}if(!/^(name|url)$/.test(f)){if(c&&i){k+=(k?";":"")+f+":"+p}else{k+=(k?",":"")+f+"="+p}}});v.features=z;v.params=h;v.onOpen.dispatch(v,z,h);r=z.url||z.file;r=d._addVer(r);try{if(c&&i){q=1;window.showModalDialog(r,window,k)}else{q=window.open(r,z.name,k)}}catch(l){}if(!q){alert(v.editor.getLang("popup_blocked"))}},close:function(f){f.close();this.onClose.dispatch(this)},createInstance:function(i,h,g,m,l,k){var j=d.resolve(i);return new j(h,g,m,l,k)},confirm:function(h,f,i,g){g=g||window;f.call(i||this,g.confirm(this._decode(this.editor.getLang(h,h))))},alert:function(h,f,j,g){var i=this;g=g||window;g.alert(i._decode(i.editor.getLang(h,h)));if(f){f.call(j||i)}},resizeBy:function(f,g,h){h.resizeBy(f,g)},_decode:function(f){return d.DOM.decode(f).replace(/\\n/g,"\n")}})}(tinymce));(function(a){a.Formatter=function(Z){var P={},S=a.each,c=Z.dom,r=Z.selection,t=a.dom.TreeWalker,M=new a.dom.RangeUtils(c),k=Z.schema.isValidChild,G=c.isBlock,m=Z.settings.forced_root_block,s=c.nodeIndex,F="\uFEFF",d=/^(src|href|style)$/,W=false,B=true,O,C,x=c.getContentEditable;function H(aa){if(aa.nodeType){aa=aa.nodeName}return !!Z.schema.getTextBlockElements()[aa.toLowerCase()]}function n(ab,aa){return c.getParents(ab,aa,c.getRoot())}function b(aa){return aa.nodeType===1&&aa.id==="_mce_caret"}function i(){l({alignleft:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"left"},defaultBlock:"div"},{selector:"img,table",collapsed:false,styles:{"float":"left"}}],aligncenter:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"center"},defaultBlock:"div"},{selector:"img",collapsed:false,styles:{display:"block",marginLeft:"auto",marginRight:"auto"}},{selector:"table",collapsed:false,styles:{marginLeft:"auto",marginRight:"auto"}}],alignright:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"right"},defaultBlock:"div"},{selector:"img,table",collapsed:false,styles:{"float":"right"}}],alignfull:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"justify"},defaultBlock:"div"}],bold:[{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}},{inline:"b",remove:"all"}],italic:[{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}},{inline:"i",remove:"all"}],underline:[{inline:"span",styles:{textDecoration:"underline"},exact:true},{inline:"u",remove:"all"}],strikethrough:[{inline:"span",styles:{textDecoration:"line-through"},exact:true},{inline:"strike",remove:"all"}],forecolor:{inline:"span",styles:{color:"%value"},wrap_links:false},hilitecolor:{inline:"span",styles:{backgroundColor:"%value"},wrap_links:false},fontname:{inline:"span",styles:{fontFamily:"%value"}},fontsize:{inline:"span",styles:{fontSize:"%value"}},fontsize_class:{inline:"span",attributes:{"class":"%value"}},blockquote:{block:"blockquote",wrapper:1,remove:"all"},subscript:{inline:"sub"},superscript:{inline:"sup"},link:{inline:"a",selector:"a",remove:"all",split:true,deep:true,onmatch:function(){return true},onformat:function(ac,aa,ab){S(ab,function(ae,ad){c.setAttrib(ac,ad,ae)})}},removeformat:[{selector:"b,strong,em,i,font,u,strike",remove:"all",split:true,expand:false,block_expand:true,deep:true},{selector:"span",attributes:["style","class"],remove:"empty",split:true,expand:false,deep:true},{selector:"*",attributes:["style","class"],split:false,expand:false,deep:true}]});S("p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp".split(/\s/),function(aa){l(aa,{block:aa,remove:"all"})});l(Z.settings.formats)}function V(){Z.addShortcut("ctrl+b","bold_desc","Bold");Z.addShortcut("ctrl+i","italic_desc","Italic");Z.addShortcut("ctrl+u","underline_desc","Underline");for(var aa=1;aa<=6;aa++){Z.addShortcut("ctrl+"+aa,"",["FormatBlock",false,"h"+aa])}Z.addShortcut("ctrl+7","",["FormatBlock",false,"p"]);Z.addShortcut("ctrl+8","",["FormatBlock",false,"div"]);Z.addShortcut("ctrl+9","",["FormatBlock",false,"address"])}function U(aa){return aa?P[aa]:P}function l(aa,ab){if(aa){if(typeof(aa)!=="string"){S(aa,function(ad,ac){l(ac,ad)})}else{ab=ab.length?ab:[ab];S(ab,function(ac){if(ac.deep===C){ac.deep=!ac.selector}if(ac.split===C){ac.split=!ac.selector||ac.inline}if(ac.remove===C&&ac.selector&&!ac.inline){ac.remove="none"}if(ac.selector&&ac.inline){ac.mixed=true;ac.block_expand=true}if(typeof(ac.classes)==="string"){ac.classes=ac.classes.split(/\s+/)}});P[aa]=ab}}}var h=function(ab){var aa;Z.dom.getParent(ab,function(ac){aa=Z.dom.getStyle(ac,"text-decoration");return aa&&aa!=="none"});return aa};var K=function(aa){var ab;if(aa.nodeType===1&&aa.parentNode&&aa.parentNode.nodeType===1){ab=h(aa.parentNode);if(Z.dom.getStyle(aa,"color")&&ab){Z.dom.setStyle(aa,"text-decoration",ab)}else{if(Z.dom.getStyle(aa,"textdecoration")===ab){Z.dom.setStyle(aa,"text-decoration",null)}}}};function X(ac,aj,ae){var af=U(ac),ak=af[0],ai,ab,ag=r.isCollapsed();function aa(ap,ao){ao=ao||ak;if(ap){if(ao.onformat){ao.onformat(ap,ao,aj,ae)}S(ao.styles,function(ar,aq){c.setStyle(ap,aq,q(ar,aj))});S(ao.attributes,function(ar,aq){c.setAttrib(ap,aq,q(ar,aj))});S(ao.classes,function(aq){aq=q(aq,aj);if(!c.hasClass(ap,aq)){c.addClass(ap,aq)}})}}function ad(){function aq(ax,av){var aw=new t(av);for(ae=aw.current();ae;ae=aw.prev()){if(ae.childNodes.length>1||ae==ax||ae.tagName=="BR"){return ae}}}var ap=Z.selection.getRng();var au=ap.startContainer;var ao=ap.endContainer;if(au!=ao&&ap.endOffset===0){var at=aq(au,ao);var ar=at.nodeType==3?at.length:at.childNodes.length;ap.setEnd(at,ar)}return ap}function ah(ao){var aq=-1;var ap;S(ao.childNodes,function(at,ar){if(at.nodeName==="UL"||at.nodeName==="OL"){aq=ar;ap=at;return false}});return{listIndex:aq,list:ap}}function al(ap,ao){var ar=-1;var aq=-1;S(ap.childNodes,function(au,at){if(au.nodeName==="SPAN"&&c.getAttrib(au,"data-mce-type")=="bookmark"){if(au.id==ao.id+"_start"){ar=at}else{if(au.id==ao.id+"_end"){aq=at}}}});return{startIndex:ar,endIndex:aq}}function am(ap,ar,av){var ao=[],au,aq,at=true;au=ak.inline||ak.block;aq=c.create(au);aa(aq);M.walk(ap,function(aw){var ax;function ay(aA){var aF,aD,aB,aC,aE;aE=at;aF=aA.nodeName.toLowerCase();aD=aA.parentNode.nodeName.toLowerCase();if(aA.nodeType===1&&x(aA)){aE=at;at=x(aA)==="true";aC=true}if(f(aF,"br")){ax=0;if(ak.block){c.remove(aA)}return}if(ak.wrapper&&y(aA,ac,aj)){ax=0;return}if(at&&!aC&&ak.block&&!ak.wrapper&&H(aF)){aA=c.rename(aA,au);aa(aA);ao.push(aA);ax=0;return}if(ak.selector){S(af,function(aG){if("collapsed" in aG&&aG.collapsed!==ag){return}if(c.is(aA,aG.selector)&&!b(aA)){aa(aA,aG);aB=true}});if(!ak.inline||aB){ax=0;return}}function az(aG){return aG.nodeType===3&&aG.nodeValue.length===1&&aG.nodeValue.charCodeAt(0)===65279}if(at&&!aC&&k(au,aF)&&k(aD,au)&&!(!av&&az(aA))&&!b(aA)&&(!ak.inline||!G(aA))){if(!ax){ax=c.clone(aq,W);aA.parentNode.insertBefore(ax,aA);ao.push(ax)}ax.appendChild(aA)}else{ax=0;S(a.grep(aA.childNodes),ay);if(aC){at=aE}ax=0}}S(aw,ay)});if(ak.wrap_links===false){S(ao,function(aw){function ax(aB){var aA,az,ay;if(aB.nodeName==="A"){az=c.clone(aq,W);ao.push(az);ay=a.grep(aB.childNodes);for(aA=0;aA<ay.length;aA++){az.appendChild(ay[aA])}aB.appendChild(az)}S(a.grep(aB.childNodes),ax)}ax(aw)})}S(ao,function(ay){var aw;function az(aB){var aA=0;S(aB.childNodes,function(aC){if(!e(aC)&&!J(aC)){aA++}});return aA}function ax(aA){var aC,aB;S(aA.childNodes,function(aD){if(aD.nodeType==1&&!J(aD)&&!b(aD)){aC=aD;return W}});if(aC&&g(aC,ak)){aB=c.clone(aC,W);aa(aB);c.replace(aB,aA,B);c.remove(aC,1)}return aB||aA}aw=az(ay);if((ao.length>1||!G(ay))&&aw===0){c.remove(ay,1);return}if(ak.inline||ak.wrapper){if(!ak.exact&&aw===1){ay=ax(ay)}S(af,function(aA){S(c.select(aA.inline,ay),function(aC){var aB;if(aA.wrap_links===false){aB=aC.parentNode;do{if(aB.nodeName==="A"){return}aB=aB.parentNode}while(aB)}Y(aA,aj,aC,aA.exact?aC:null)})});if(y(ay.parentNode,ac,aj)){c.remove(ay,1);ay=0;return B}if(ak.merge_with_parents){c.getParent(ay.parentNode,function(aA){if(y(aA,ac,aj)){c.remove(ay,1);ay=0;return B}})}if(ay&&ak.merge_siblings!==false){ay=u(D(ay),ay);ay=u(ay,D(ay,B))}}})}if(ak){if(ae){if(ae.nodeType){ab=c.createRng();ab.setStartBefore(ae);ab.setEndAfter(ae);am(p(ab,af),null,true)}else{am(ae,null,true)}}else{if(!ag||!ak.inline||c.select("td.mceSelected,th.mceSelected").length){var an=Z.selection.getNode();if(!m&&af[0].defaultBlock&&!c.getParent(an,c.isBlock)){X(af[0].defaultBlock)}Z.selection.setRng(ad());ai=r.getBookmark();am(p(r.getRng(B),af),ai);if(ak.styles&&(ak.styles.color||ak.styles.textDecoration)){a.walk(an,K,"childNodes");K(an)}r.moveToBookmark(ai);Q(r.getRng(B));Z.nodeChanged()}else{T("apply",ac,aj)}}}}function A(ac,aj,ae){var af=U(ac),am=af[0],ai,ab,ak=true;function ad(ar){var aq,ap,ao,au,at;if(ar.nodeType===3){return}if(ar.nodeType===1&&x(ar)){au=ak;ak=x(ar)==="true";at=true}aq=a.grep(ar.childNodes);if(ak&&!at){for(ap=0,ao=af.length;ap<ao;ap++){if(Y(af[ap],aj,ar,ar)){break}}}if(am.deep){if(aq.length){for(ap=0,ao=aq.length;ap<ao;ap++){ad(aq[ap])}if(at){ak=au}}}}function ag(ao){var ap;S(n(ao.parentNode).reverse(),function(aq){var ar;if(!ap&&aq.id!="_start"&&aq.id!="_end"){ar=y(aq,ac,aj);if(ar&&ar.split!==false){ap=aq}}});return ap}function aa(ar,ao,au,ax){var ay,aw,av,aq,at,ap;if(ar){ap=ar.parentNode;for(ay=ao.parentNode;ay&&ay!=ap;ay=ay.parentNode){aw=c.clone(ay,W);for(at=0;at<af.length;at++){if(Y(af[at],aj,aw,aw)){aw=0;break}}if(aw){if(av){aw.appendChild(av)}if(!aq){aq=aw}av=aw}}if(ax&&(!am.mixed||!G(ar))){ao=c.split(ar,ao)}if(av){au.parentNode.insertBefore(av,au);aq.appendChild(au)}}return ao}function al(ao){return aa(ag(ao),ao,ao,true)}function ah(aq){var ap=c.get(aq?"_start":"_end"),ao=ap[aq?"firstChild":"lastChild"];if(J(ao)){ao=ao[aq?"firstChild":"lastChild"]}c.remove(ap,true);return ao}function an(ao){var ap,aq;ao=p(ao,af,B);if(am.split){ap=L(ao,B);aq=L(ao);if(ap!=aq){if(/^(TR|TD)$/.test(ap.nodeName)&&ap.firstChild){ap=(ap.nodeName=="TD"?ap.firstChild:ap.firstChild.firstChild)||ap}ap=R(ap,"span",{id:"_start","data-mce-type":"bookmark"});aq=R(aq,"span",{id:"_end","data-mce-type":"bookmark"});al(ap);al(aq);ap=ah(B);aq=ah()}else{ap=aq=al(ap)}ao.startContainer=ap.parentNode;ao.startOffset=s(ap);ao.endContainer=aq.parentNode;ao.endOffset=s(aq)+1}M.walk(ao,function(ar){S(ar,function(at){ad(at);if(at.nodeType===1&&Z.dom.getStyle(at,"text-decoration")==="underline"&&at.parentNode&&h(at.parentNode)==="underline"){Y({deep:false,exact:true,inline:"span",styles:{textDecoration:"underline"}},null,at)}})})}if(ae){if(ae.nodeType){ab=c.createRng();ab.setStartBefore(ae);ab.setEndAfter(ae);an(ab)}else{an(ae)}return}if(!r.isCollapsed()||!am.inline||c.select("td.mceSelected,th.mceSelected").length){ai=r.getBookmark();an(r.getRng(B));r.moveToBookmark(ai);if(am.inline&&j(ac,aj,r.getStart())){Q(r.getRng(true))}Z.nodeChanged()}else{T("remove",ac,aj)}}function E(ab,ad,ac){var aa=U(ab);if(j(ab,ad,ac)&&(!("toggle" in aa[0])||aa[0].toggle)){A(ab,ad,ac)}else{X(ab,ad,ac)}}function y(ab,aa,ag,ae){var ac=U(aa),ah,af,ad;function ai(am,ao,ap){var al,an,aj=ao[ap],ak;if(ao.onmatch){return ao.onmatch(am,ao,ap)}if(aj){if(aj.length===C){for(al in aj){if(aj.hasOwnProperty(al)){if(ap==="attributes"){an=c.getAttrib(am,al)}else{an=N(am,al)}if(ae&&!an&&!ao.exact){return}if((!ae||ao.exact)&&!f(an,q(aj[al],ag))){return}}}}else{for(ak=0;ak<aj.length;ak++){if(ap==="attributes"?c.getAttrib(am,aj[ak]):N(am,aj[ak])){return ao}}}}return ao}if(ac&&ab){for(af=0;af<ac.length;af++){ah=ac[af];if(g(ab,ah)&&ai(ab,ah,"attributes")&&ai(ab,ah,"styles")){ad=ah.classes;if(ad){for(af=0;af<ad.length;af++){if(!c.hasClass(ab,ad[af])){return}}}return ah}}}}function j(ac,ae,ad){var ab;function aa(af){af=c.getParent(af,function(ag){return !!y(ag,ac,ae,true)});return y(af,ac,ae)}if(ad){return aa(ad)}ad=r.getNode();if(aa(ad)){return B}ab=r.getStart();if(ab!=ad){if(aa(ab)){return B}}return W}function v(ae,ad){var ab,ac=[],aa={};ab=r.getStart();c.getParent(ab,function(ah){var ag,af;for(ag=0;ag<ae.length;ag++){af=ae[ag];if(!aa[af]&&y(ah,af,ad)){aa[af]=true;ac.push(af)}}},c.getRoot());return ac}function z(ae){var ag=U(ae),ad,ac,af,ab,aa;if(ag){ad=r.getStart();ac=n(ad);for(ab=ag.length-1;ab>=0;ab--){aa=ag[ab].selector;if(!aa){return B}for(af=ac.length-1;af>=0;af--){if(c.is(ac[af],aa)){return B}}}}return W}function I(aa,ad,ab){var ac;if(!O){O={};ac={};Z.onNodeChange.addToTop(function(af,ae,ah){var ag=n(ah),ai={};S(O,function(aj,ak){S(ag,function(al){if(y(al,ak,{},aj.similar)){if(!ac[ak]){S(aj,function(am){am(true,{node:al,format:ak,parents:ag})});ac[ak]=aj}ai[ak]=aj;return false}})});S(ac,function(aj,ak){if(!ai[ak]){delete ac[ak];S(aj,function(al){al(false,{node:ah,format:ak,parents:ag})})}})})}S(aa.split(","),function(ae){if(!O[ae]){O[ae]=[];O[ae].similar=ab}O[ae].push(ad)});return this}a.extend(this,{get:U,register:l,apply:X,remove:A,toggle:E,match:j,matchAll:v,matchNode:y,canApply:z,formatChanged:I});i();V();function g(aa,ab){if(f(aa,ab.inline)){return B}if(f(aa,ab.block)){return B}if(ab.selector){return c.is(aa,ab.selector)}}function f(ab,aa){ab=ab||"";aa=aa||"";ab=""+(ab.nodeName||ab);aa=""+(aa.nodeName||aa);return ab.toLowerCase()==aa.toLowerCase()}function N(ab,aa){var ac=c.getStyle(ab,aa);if(aa=="color"||aa=="backgroundColor"){ac=c.toHex(ac)}if(aa=="fontWeight"&&ac==700){ac="bold"}return""+ac}function q(aa,ab){if(typeof(aa)!="string"){aa=aa(ab)}else{if(ab){aa=aa.replace(/%(\w+)/g,function(ad,ac){return ab[ac]||ad})}}return aa}function e(aa){return aa&&aa.nodeType===3&&/^([\t \r\n]+|)$/.test(aa.nodeValue)}function R(ac,ab,aa){var ad=c.create(ab,aa);ac.parentNode.insertBefore(ad,ac);ad.appendChild(ac);return ad}function p(aa,al,ad){var am,ag,ak,ac=aa.startContainer,ah=aa.startOffset,ap=aa.endContainer,aj=aa.endOffset;function an(ax){var ar,av,au,at,aq;ar=av=ax?ac:ap;at=ax?"previousSibling":"nextSibling";aq=c.getRoot();function aw(ay){return ay.nodeName=="BR"&&ay.getAttribute("data-mce-bogus")&&!ay.nextSibling}if(ar.nodeType==3&&!e(ar)){if(ax?ah>0:aj<ar.nodeValue.length){return ar}}for(;;){if(!al[0].block_expand&&G(av)){return av}for(au=av[at];au;au=au[at]){if(!J(au)&&!e(au)&&!aw(au)){return av}}if(av.parentNode==aq){ar=av;break}av=av.parentNode}return ar}function af(aq,ar){if(ar===C){ar=aq.nodeType===3?aq.length:aq.childNodes.length}while(aq&&aq.hasChildNodes()){aq=aq.childNodes[ar];if(aq){ar=aq.nodeType===3?aq.length:aq.childNodes.length}}return{node:aq,offset:ar}}if(ac.nodeType==1&&ac.hasChildNodes()){am=ac.childNodes.length-1;ac=ac.childNodes[ah>am?am:ah];if(ac&&ac.nodeType==3){ah=0}}if(ap.nodeType==1&&ap.hasChildNodes()){am=ap.childNodes.length-1;ap=ap.childNodes[aj>am?am:aj-1];if(ap&&ap.nodeType==3){aj=ap.nodeValue.length}}function ao(ar){var aq=ar;while(aq){if(aq.nodeType===1&&x(aq)){return x(aq)==="false"?aq:ar}aq=aq.parentNode}return ar}function ai(ar,aw,ay){var av,at,ax,aq;function au(aA,aC){var aD,az,aB=aA.nodeValue;if(typeof(aC)=="undefined"){aC=ay?aB.length:0}if(ay){aD=aB.lastIndexOf(" ",aC);az=aB.lastIndexOf("\u00a0",aC);aD=aD>az?aD:az;if(aD!==-1&&!ad){aD++}}else{aD=aB.indexOf(" ",aC);az=aB.indexOf("\u00a0",aC);aD=aD!==-1&&(az===-1||aD<az)?aD:az}return aD}if(ar.nodeType===3){ax=au(ar,aw);if(ax!==-1){return{container:ar,offset:ax}}aq=ar}av=new t(ar,c.getParent(ar,G)||Z.getBody());while(at=av[ay?"prev":"next"]()){if(at.nodeType===3){aq=at;ax=au(at);if(ax!==-1){return{container:at,offset:ax}}}else{if(G(at)){break}}}if(aq){if(ay){aw=0}else{aw=aq.length}return{container:aq,offset:aw}}}function ae(ar,aq){var at,au,aw,av;if(ar.nodeType==3&&ar.nodeValue.length===0&&ar[aq]){ar=ar[aq]}at=n(ar);for(au=0;au<at.length;au++){for(aw=0;aw<al.length;aw++){av=al[aw];if("collapsed" in av&&av.collapsed!==aa.collapsed){continue}if(c.is(at[au],av.selector)){return at[au]}}}return ar}function ab(ar,aq){var at;if(!al[0].wrapper){at=c.getParent(ar,al[0].block)}if(!at){at=c.getParent(ar.nodeType==3?ar.parentNode:ar,H)}if(at&&al[0].wrapper){at=n(at,"ul,ol").reverse()[0]||at}if(!at){at=ar;while(at[aq]&&!G(at[aq])){at=at[aq];if(f(at,"br")){break}}}return at||ar}ac=ao(ac);ap=ao(ap);if(J(ac.parentNode)||J(ac)){ac=J(ac)?ac:ac.parentNode;ac=ac.nextSibling||ac;if(ac.nodeType==3){ah=0}}if(J(ap.parentNode)||J(ap)){ap=J(ap)?ap:ap.parentNode;ap=ap.previousSibling||ap;if(ap.nodeType==3){aj=ap.length}}if(al[0].inline){if(aa.collapsed){ak=ai(ac,ah,true);if(ak){ac=ak.container;ah=ak.offset}ak=ai(ap,aj);if(ak){ap=ak.container;aj=ak.offset}}ag=af(ap,aj);if(ag.node){while(ag.node&&ag.offset===0&&ag.node.previousSibling){ag=af(ag.node.previousSibling)}if(ag.node&&ag.offset>0&&ag.node.nodeType===3&&ag.node.nodeValue.charAt(ag.offset-1)===" "){if(ag.offset>1){ap=ag.node;ap.splitText(ag.offset-1)}}}}if(al[0].inline||al[0].block_expand){if(!al[0].inline||(ac.nodeType!=3||ah===0)){ac=an(true)}if(!al[0].inline||(ap.nodeType!=3||aj===ap.nodeValue.length)){ap=an()}}if(al[0].selector&&al[0].expand!==W&&!al[0].inline){ac=ae(ac,"previousSibling");ap=ae(ap,"nextSibling")}if(al[0].block||al[0].selector){ac=ab(ac,"previousSibling");ap=ab(ap,"nextSibling");if(al[0].block){if(!G(ac)){ac=an(true)}if(!G(ap)){ap=an()}}}if(ac.nodeType==1){ah=s(ac);ac=ac.parentNode}if(ap.nodeType==1){aj=s(ap)+1;ap=ap.parentNode}return{startContainer:ac,startOffset:ah,endContainer:ap,endOffset:aj}}function Y(ag,af,ad,aa){var ac,ab,ae;if(!g(ad,ag)){return W}if(ag.remove!="all"){S(ag.styles,function(ai,ah){ai=q(ai,af);if(typeof(ah)==="number"){ah=ai;aa=0}if(!aa||f(N(aa,ah),ai)){c.setStyle(ad,ah,"")}ae=1});if(ae&&c.getAttrib(ad,"style")===""){ad.removeAttribute("style");ad.removeAttribute("data-mce-style")}S(ag.attributes,function(aj,ah){var ai;aj=q(aj,af);if(typeof(ah)==="number"){ah=aj;aa=0}if(!aa||f(c.getAttrib(aa,ah),aj)){if(ah=="class"){aj=c.getAttrib(ad,ah);if(aj){ai="";S(aj.split(/\s+/),function(ak){if(/mce\w+/.test(ak)){ai+=(ai?" ":"")+ak}});if(ai){c.setAttrib(ad,ah,ai);return}}}if(ah=="class"){ad.removeAttribute("className")}if(d.test(ah)){ad.removeAttribute("data-mce-"+ah)}ad.removeAttribute(ah)}});S(ag.classes,function(ah){ah=q(ah,af);if(!aa||c.hasClass(aa,ah)){c.removeClass(ad,ah)}});ab=c.getAttribs(ad);for(ac=0;ac<ab.length;ac++){if(ab[ac].nodeName.indexOf("_")!==0){return W}}}if(ag.remove!="none"){o(ad,ag);return B}}function o(ac,ad){var aa=ac.parentNode,ab;function ae(ag,af,ah){ag=D(ag,af,ah);return !ag||(ag.nodeName=="BR"||G(ag))}if(ad.block){if(!m){if(G(ac)&&!G(aa)){if(!ae(ac,W)&&!ae(ac.firstChild,B,1)){ac.insertBefore(c.create("br"),ac.firstChild)}if(!ae(ac,B)&&!ae(ac.lastChild,W,1)){ac.appendChild(c.create("br"))}}}else{if(aa==c.getRoot()){if(!ad.list_block||!f(ac,ad.list_block)){S(a.grep(ac.childNodes),function(af){if(k(m,af.nodeName.toLowerCase())){if(!ab){ab=R(af,m)}else{ab.appendChild(af)}}else{ab=0}})}}}}if(ad.selector&&ad.inline&&!f(ad.inline,ac)){return}c.remove(ac,1)}function D(ab,aa,ac){if(ab){aa=aa?"nextSibling":"previousSibling";for(ab=ac?ab:ab[aa];ab;ab=ab[aa]){if(ab.nodeType==1||!e(ab)){return ab}}}}function J(aa){return aa&&aa.nodeType==1&&aa.getAttribute("data-mce-type")=="bookmark"}function u(ad,ac){var ab,aa;function af(ai,ah){if(ai.nodeName!=ah.nodeName){return W}function ag(ak){var al={};S(c.getAttribs(ak),function(am){var an=am.nodeName.toLowerCase();if(an.indexOf("_")!==0&&an!=="style"){al[an]=c.getAttrib(ak,an)}});return al}function aj(an,am){var al,ak;for(ak in an){if(an.hasOwnProperty(ak)){al=am[ak];if(al===C){return W}if(an[ak]!=al){return W}delete am[ak]}}for(ak in am){if(am.hasOwnProperty(ak)){return W}}return B}if(!aj(ag(ai),ag(ah))){return W}if(!aj(c.parseStyle(c.getAttrib(ai,"style")),c.parseStyle(c.getAttrib(ah,"style")))){return W}return B}function ae(ah,ag){for(ab=ah;ab;ab=ab[ag]){if(ab.nodeType==3&&ab.nodeValue.length!==0){return ah}if(ab.nodeType==1&&!J(ab)){return ab}}return ah}if(ad&&ac){ad=ae(ad,"previousSibling");ac=ae(ac,"nextSibling");if(af(ad,ac)){for(ab=ad.nextSibling;ab&&ab!=ac;){aa=ab;ab=ab.nextSibling;ad.appendChild(aa)}c.remove(ac);S(a.grep(ac.childNodes),function(ag){ad.appendChild(ag)});return ad}}return ac}function L(ab,ae){var aa,ad,ac;aa=ab[ae?"startContainer":"endContainer"];ad=ab[ae?"startOffset":"endOffset"];if(aa.nodeType==1){ac=aa.childNodes.length-1;if(!ae&&ad){ad--}aa=aa.childNodes[ad>ac?ac:ad]}if(aa.nodeType===3&&ae&&ad>=aa.nodeValue.length){aa=new t(aa,Z.getBody()).next()||aa}if(aa.nodeType===3&&!ae&&ad===0){aa=new t(aa,Z.getBody()).prev()||aa}return aa}function T(ak,aa,ai){var am="_mce_caret",ab=Z.settings.caret_debug;function ac(aq){var ap=c.create("span",{id:am,"data-mce-bogus":true,style:ab?"color:red":""});if(aq){ap.appendChild(Z.getDoc().createTextNode(F))}return ap}function aj(aq,ap){while(aq){if((aq.nodeType===3&&aq.nodeValue!==F)||aq.childNodes.length>1){return false}if(ap&&aq.nodeType===1){ap.push(aq)}aq=aq.firstChild}return true}function af(ap){while(ap){if(ap.id===am){return ap}ap=ap.parentNode}}function ae(ap){var aq;if(ap){aq=new t(ap,ap);for(ap=aq.current();ap;ap=aq.next()){if(ap.nodeType===3){return ap}}}}function ad(ar,aq){var at,ap;if(!ar){ar=af(r.getStart());if(!ar){while(ar=c.get(am)){ad(ar,false)}}}else{ap=r.getRng(true);if(aj(ar)){if(aq!==false){ap.setStartBefore(ar);ap.setEndBefore(ar)}c.remove(ar)}else{at=ae(ar);if(at.nodeValue.charAt(0)===F){at=at.deleteData(0,1)}c.remove(ar,1)}r.setRng(ap)}}function al(aq){var ap=aq.nodeName.toLowerCase();switch(ap){case"html","#document":return false;case"body":return true;default:return al(aq.parentNode)}}function ag(ap){return al(ap.startContainer)||al(ap.endContainer)}function ah(){var ar,ap,aw,av,at,aq,au;ar=r.getRng(true);av=ar.startOffset;aq=ar.startContainer;au=aq.nodeValue;ap=af(r.getStart());if(ap){aw=ae(ap)}if(au&&av>0&&av<au.length&&/\w/.test(au.charAt(av))&&/\w/.test(au.charAt(av-1))){at=r.getBookmark();ar.collapse(true);ar=p(ar,U(aa));ar=M.split(ar);X(aa,ai,ar);r.moveToBookmark(at)}else{if(!ap||aw.nodeValue!==F){ap=ac(true);aw=ap.firstChild;if(ag(ar)){ar.insertNode(ap)}else{ar.startContainer.ownerDocument.body.appendChild(ap)}av=1;X(aa,ai,ap)}else{X(aa,ai,ap)}r.setCursorLocation(aw,av)}}function an(){var ap=r.getRng(true),aq,au,ax,aw,ar,aA,az=[],av,ay;aq=ap.startContainer;au=ap.startOffset;ar=aq;if(aq.nodeType==3){if(au!=aq.nodeValue.length||aq.nodeValue===F){aw=true}ar=ar.parentNode}while(ar){if(y(ar,aa,ai)){aA=ar;break}if(ar.nextSibling){aw=true}az.push(ar);ar=ar.parentNode}if(!aA){return}if(aw){ax=r.getBookmark();ap.collapse(true);ap=p(ap,U(aa),true);ap=M.split(ap);A(aa,ai,ap);r.moveToBookmark(ax)}else{ay=ac();ar=ay;for(av=az.length-1;av>=0;av--){ar.appendChild(c.clone(az[av],false));ar=ar.firstChild}ar.appendChild(c.doc.createTextNode(F));ar=ar.firstChild;var at=c.getParent(aA,H);if(at&&c.isEmpty(at)){aA.parentNode.replaceChild(ay,aA)}else{c.insertAfter(ay,aA)}r.setCursorLocation(ar,1);if(c.isEmpty(aA)){c.remove(aA)}}}function ao(){var ap;ap=af(r.getStart());if(ap&&!c.isEmpty(ap)){a.walk(ap,function(aq){if(aq.nodeType==1&&aq.id!==am&&!c.isEmpty(aq)){c.setAttrib(aq,"data-mce-bogus",null)}},"childNodes")}}if(!Z._hasCaretEvents){Z.onBeforeGetContent.addToTop(function(){var ap=[],aq;if(aj(af(r.getStart()),ap)){aq=ap.length;while(aq--){c.setAttrib(ap[aq],"data-mce-bogus","1")}}});a.each("onMouseUp onKeyUp".split(" "),function(ap){Z[ap].addToTop(function(){ad();ao()})});Z.onKeyDown.addToTop(function(ap,ar){var aq=ar.keyCode;if(aq==8||aq==37||aq==39){ad(af(r.getStart()))}ao()});r.onSetContent.add(ao);Z._hasCaretEvents=true}if(ak=="apply"){ah()}else{an()}}function Q(ab){var aa=ab.startContainer,ah=ab.startOffset,ad,ag,af,ac,ae;if(aa.nodeType==3&&ah>=aa.nodeValue.length){ah=s(aa);aa=aa.parentNode;ad=true}if(aa.nodeType==1){ac=aa.childNodes;aa=ac[Math.min(ah,ac.length-1)];ag=new t(aa,c.getParent(aa,c.isBlock));if(ah>ac.length-1||ad){ag.next()}for(af=ag.current();af;af=ag.next()){if(af.nodeType==3&&!e(af)){ae=c.create("a",null,F);af.parentNode.insertBefore(ae,af);ab.setStart(af,0);r.setRng(ab);c.remove(ae);return}}}}}})(tinymce);tinymce.onAddEditor.add(function(e,a){var d,h,g,c=a.settings;function b(j,i){e.each(i,function(l,k){if(l){g.setStyle(j,k,l)}});g.rename(j,"span")}function f(i,j){g=i.dom;if(c.convert_fonts_to_spans){e.each(g.select("font,u,strike",j.node),function(k){d[k.nodeName.toLowerCase()](a.dom,k)})}}if(c.inline_styles){h=e.explode(c.font_size_legacy_values);d={font:function(j,i){b(i,{backgroundColor:i.style.backgroundColor,color:i.color,fontFamily:i.face,fontSize:h[parseInt(i.size,10)-1]})},u:function(j,i){b(i,{textDecoration:"underline"})},strike:function(j,i){b(i,{textDecoration:"line-through"})}};a.onPreProcess.add(f);a.onSetContent.add(f);a.onInit.add(function(){a.selection.onSetContent.add(f)})}});(function(b){var a=b.dom.TreeWalker;b.EnterKey=function(f){var i=f.dom,e=f.selection,d=f.settings,h=f.undoManager,c=f.schema.getNonEmptyElements();function g(B){var v=e.getRng(true),G,j,A,u,p,M,C,o,k,n,t,J,x,D;function E(N){return N&&i.isBlock(N)&&!/^(TD|TH|CAPTION|FORM)$/.test(N.nodeName)&&!/^(fixed|absolute)/i.test(N.style.position)&&i.getContentEditable(N)!=="true"}function F(O){var N;if(b.isIE&&!b.isIE11&&i.isBlock(O)){N=e.getRng();O.appendChild(i.create("span",null,"\u00a0"));e.select(O);O.lastChild.outerHTML="";e.setRng(N)}}function z(P){var O=P,Q=[],N;while(O=O.firstChild){if(i.isBlock(O)){return}if(O.nodeType==1&&!c[O.nodeName.toLowerCase()]){Q.push(O)}}N=Q.length;while(N--){O=Q[N];if(!O.hasChildNodes()||(O.firstChild==O.lastChild&&O.firstChild.nodeValue==="")){i.remove(O)}else{if(O.nodeName=="A"&&(O.innerText||O.textContent)===" "){i.remove(O)}}}}function m(O){var T,R,N,U,S,Q=O,P;N=i.createRng();if(O.hasChildNodes()){T=new a(O,O);while(R=T.current()){if(R.nodeType==3){N.setStart(R,0);N.setEnd(R,0);break}if(c[R.nodeName.toLowerCase()]){N.setStartBefore(R);N.setEndBefore(R);break}Q=R;R=T.next()}if(!R){N.setStart(Q,0);N.setEnd(Q,0)}}else{if(O.nodeName=="BR"){if(O.nextSibling&&i.isBlock(O.nextSibling)){if(!M||M<9){P=i.create("br");O.parentNode.insertBefore(P,O)}N.setStartBefore(O);N.setEndBefore(O)}else{N.setStartAfter(O);N.setEndAfter(O)}}else{N.setStart(O,0);N.setEnd(O,0)}}e.setRng(N);i.remove(P);S=i.getViewPort(f.getWin());U=i.getPos(O).y;if(U<S.y||U+25>S.y+S.h){f.getWin().scrollTo(0,U<S.y?U:U-S.h+25)}}function r(O){var P=A,R,Q,N;R=O||t=="TABLE"?i.create(O||x):p.cloneNode(false);N=R;if(d.keep_styles!==false){do{if(/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(P.nodeName)){if(P.id=="_mce_caret"){continue}Q=P.cloneNode(false);i.setAttrib(Q,"id","");if(R.hasChildNodes()){Q.appendChild(R.firstChild);R.appendChild(Q)}else{N=Q;R.appendChild(Q)}}}while(P=P.parentNode)}if(!b.isIE||b.isIE11){N.innerHTML='<br data-mce-bogus="1">'}return R}function q(Q){var P,O,N;if(A.nodeType==3&&(Q?u>0:u<A.nodeValue.length)){return false}if(A.parentNode==p&&D&&!Q){return true}if(Q&&A.nodeType==1&&A==p.firstChild){return true}if(A.nodeName==="TABLE"||(A.previousSibling&&A.previousSibling.nodeName=="TABLE")){return(D&&!Q)||(!D&&Q)}P=new a(A,p);if(A.nodeType==3){if(Q&&u==0){P.prev()}else{if(!Q&&u==A.nodeValue.length){P.next()}}}while(O=P.current()){if(O.nodeType===1){if(!O.getAttribute("data-mce-bogus")){N=O.nodeName.toLowerCase();if(c[N]&&N!=="br"){return false}}}else{if(O.nodeType===3&&!/^[ \t\r\n]*$/.test(O.nodeValue)){return false}}if(Q){P.prev()}else{P.next()}}return true}function l(N,T){var U,S,P,R,Q,O=x||"P";S=i.getParent(N,i.isBlock);if(!S||!E(S)){S=S||j;if(!S.hasChildNodes()){U=i.create(O);S.appendChild(U);v.setStart(U,0);v.setEnd(U,0);return U}R=N;while(R.parentNode!=S){R=R.parentNode}while(R&&!i.isBlock(R)){P=R;R=R.previousSibling}if(P){U=i.create(O);P.parentNode.insertBefore(U,P);R=P;while(R&&!i.isBlock(R)){Q=R.nextSibling;U.appendChild(R);R=Q}v.setStart(N,T);v.setEnd(N,T)}}return N}function H(){function N(P){var O=n[P?"firstChild":"lastChild"];while(O){if(O.nodeType==1){break}O=O[P?"nextSibling":"previousSibling"]}return O===p}o=x?r(x):i.create("BR");if(N(true)&&N()){i.replace(o,n)}else{if(N(true)){n.parentNode.insertBefore(o,n)}else{if(N()){i.insertAfter(o,n);F(o)}else{G=v.cloneRange();G.setStartAfter(p);G.setEndAfter(n);k=G.extractContents();i.insertAfter(k,n);i.insertAfter(o,n)}}}i.remove(p);m(o);h.add()}function y(){var O=new a(A,p),N;while(N=O.next()){if(c[N.nodeName.toLowerCase()]||N.length>0){return true}}}function L(){var P,O,N;if(A&&A.nodeType==3&&u>=A.nodeValue.length){if((!b.isIE||b.isIE11)&&!y()){P=i.create("br");v.insertNode(P);v.setStartAfter(P);v.setEndAfter(P);O=true}}P=i.create("br");v.insertNode(P);if((b.isIE&&!b.isIE11)&&t=="PRE"&&(!M||M<8)){P.parentNode.insertBefore(i.doc.createTextNode("\r"),P)}N=i.create("span",{},"&nbsp;");P.parentNode.insertBefore(N,P);e.scrollIntoView(N);i.remove(N);if(!O){v.setStartAfter(P);v.setEndAfter(P)}else{v.setStartBefore(P);v.setEndBefore(P)}e.setRng(v);h.add()}function s(N){do{if(N.nodeType===3){N.nodeValue=N.nodeValue.replace(/^[\r\n]+/,"")}N=N.firstChild}while(N)}function K(P){var N=i.getRoot(),O,Q;O=P;while(O!==N&&i.getContentEditable(O)!=="false"){if(i.getContentEditable(O)==="true"){Q=O}O=O.parentNode}return O!==N?Q:N}function I(O){var N;if(!b.isIE||b.isIE11){O.normalize();N=O.lastChild;if(!N||(/^(left|right)$/gi.test(i.getStyle(N,"float",true)))){i.add(O,"br")}}}if(!v.collapsed){f.execCommand("Delete");return}if(B.isDefaultPrevented()){return}A=v.startContainer;u=v.startOffset;x=(d.force_p_newlines?"p":"")||d.forced_root_block;x=x?x.toUpperCase():"";M=i.doc.documentMode;C=B.shiftKey;if(A.nodeType==1&&A.hasChildNodes()){D=u>A.childNodes.length-1;A=A.childNodes[Math.min(u,A.childNodes.length-1)]||A;if(D&&A.nodeType==3){u=A.nodeValue.length}else{u=0}}j=K(A);if(!j){return}h.beforeChange();if(!i.isBlock(j)&&j!=i.getRoot()){if(!x||C){L()}return}if((x&&!C)||(!x&&C)){A=l(A,u)}p=i.getParent(A,i.isBlock);n=p?i.getParent(p.parentNode,i.isBlock):null;t=p?p.nodeName.toUpperCase():"";J=n?n.nodeName.toUpperCase():"";if(J=="LI"&&!B.ctrlKey){p=n;t=J}if(t=="LI"){if(!x&&C){L();return}if(i.isEmpty(p)){if(/^(UL|OL|LI)$/.test(n.parentNode.nodeName)){return false}H();return}}if(t=="PRE"&&d.br_in_pre!==false){if(!C){L();return}}else{if((!x&&!C&&t!="LI")||(x&&C)){L();return}}x=x||"P";if(q()){if(/^(H[1-6]|PRE)$/.test(t)&&J!="HGROUP"){o=r(x)}else{o=r()}if(d.end_container_on_empty_block&&E(n)&&i.isEmpty(p)){o=i.split(n,p)}else{i.insertAfter(o,p)}m(o)}else{if(q(true)){o=p.parentNode.insertBefore(r(),p);F(o)}else{G=v.cloneRange();G.setEndAfter(p);k=G.extractContents();s(k);o=k.firstChild;i.insertAfter(k,p);z(o);I(p);m(o)}}i.setAttrib(o,"id","");h.add()}f.onKeyDown.add(function(k,j){if(j.keyCode==13){if(g(j)!==false){j.preventDefault()}}})}})(tinymce);
/**
* Name: editor_plugin_src.js (for pmGrids tinyMCE plugin)
**/

(function(){
    var strPluginURL;
    tinymce.create('tinymce.plugins.pmGridsPlugin', {
    	init: function(ed, url) 
        {   
            strPluginURL = url;                                         // store the URL for future use..
            ed.addCommand('mcepmGrids', function() {
                pmGrids();              
            });
            ed.addButton('pmGrids', {
                title: 'pmGrids',
                label : '  @#',
                cmd: 'mcepmGrids',
                image: url + '/img/grids.png'
            }); 
        },
        createControl: function(n, cm) {
            return null;
        },
        getPluginURL: function() {
            return strPluginURL;
        }
    });
    tinymce.PluginManager.add('pmGrids', tinymce.plugins.pmGridsPlugin);
})();

/**
 * @function pmGrids
 * @description The function intializes the plugin and also creates the popup
 *              window
 * @param field_name deprecated
 * @param win deprecated
 */
function pmGrids(field_name, win) {    
    //tinyMCE.activeEditor.anyVariable='path/to/ProcessMaker' 
    var strPluginPath  = tinyMCE.activeEditor.plugins.pmGrids.getPluginURL(); // get the path to the uploader plugin    
    var strScriptURL   = strPluginPath + "/pmGrids.html"; // loading the form
    
    tinyMCE.activeEditor.windowManager.open({                                                                       // open the plugin popup
        file            : strScriptURL,
        title           : 'ProcessMaker Grid Wizard',
        width           : '600px',
        height          : '230px',
        resizable       : "yes",
        scrollbars      : "no",
        overflow        : false,
        inline          : 1,        // This parameter only has an effect if you use the inlinepopups plugin!
        close_previous  : "no"
    }, {
        window : win,
        input : field_name
    });

    return false;
}

/**
 * @function closePluginPopup
 * @description closes the plugin popup
 */
function closePluginPopup(){    
    tinyMCEPopup.close();  // close popup window
}


/**
 * @function updateEditorContent
 * @description insert the editor content with a html code string
 * @params serializedHTML String html code
 */
function updateEditorContent(serializedHTML){
    tinyMCE.activeEditor.execCommand('mceInsertRawHTML', false, serializedHTML);
    closePluginPopup();
}


/**
* Name: editor_plugin_src.js (for pmSimpleUploader tinyMCE plugin)
**/

(function(){
    var strPluginURL;
    tinymce.create('tinymce.plugins.pmSimpleUploaderPlugin', {
        init: function(ed, url) 
        {
            strPluginURL = url;                                         // store the URL for future use..
            ed.addCommand('mcepmSimpleUploader', function() {
                pmSimpleUploader();              
            });
            ed.addButton('pmSimpleUploader', {
                title: 'pmSimpleUploader',
                label : 'Upload File',
                cmd: 'mcepmSimpleUploader',
                image: url + '/img/pmSimpleUploader.png'
            }); 
        },
        createControl: function(n, cm) {
            return null;
        },
        getPluginURL: function() {
            return strPluginURL;
        }
    });
    tinymce.PluginManager.add('pmSimpleUploader', tinymce.plugins.pmSimpleUploaderPlugin);
})();

/**
 * this function can get called from the plugin inint (above) or from the callback on advlink/advimg plugins..
 * in the latter case, win and type will be set.. In the rist case, we will just update the main editor window
 * with the path of the uploaded file
 */
function pmSimpleUploader(field_name, url, type, win) {    
    var strPluginPath  = tinyMCE.activeEditor.plugins.pmSimpleUploader.getPluginURL();                               // get the path to the uploader plugin    
    var strUploaderURL = strPluginPath + "/uploader.php";                                                           // generate the path to the uploader script    
    var strUploadPath  = tinyMCE.activeEditor.getParam('plugin_pmSimpleUploader_upload_path');                       // get the relative upload path
    var strSubstitutePath = tinyMCE.activeEditor.getParam('plugin_pmSimpleUploader_upload_substitute_path');        // get the path we'll substitute for the for the upload path (i.e. fully qualified)

    if (strUploaderURL.indexOf("?") < 0){                                                                            // if we were called without any GET params
        strUploaderURL = strUploaderURL + "?type=" + type + "&d=" + strUploadPath + "&subs=" + strSubstitutePath;   // add our own params 
    } else {
        strUploaderURL = strUploaderURL + "&type=" + type + "&d=" + strUploadPath + "&subs=" + strSubstitutePath;
    }
    tinyMCE.activeEditor.windowManager.open({                                                                       // open the plugin popup
        file            : strUploaderURL,
        title           : 'Upload from file',
        width           : 500,  
        height          : 100,
        resizable       : "yes", 
        inline          : 1,        // This parameter only has an effect if you use the inlinepopups plugin!
        close_previous  : "no"
    }, {
        window : win,
        input : field_name
    });
  
    return false;
}

/**
 * This function will get called when the uploader is done uploading the file and ready to update
 * calling dialog and close the upload popup
 * strReturnURL should be the string with the path to the uploaded file
 */

function closePluginPopup(){    
    tinyMCEPopup.close();	       // close popup window
}

/**
 * This function update the content editor with the content template file
 */

function updateEditorContent(serializedHTML){
    tinyMCE.activeEditor.execCommand('mceSetContent', false, serializedHTML);
}
/**
* Name: editor_plugin_src.js (for pmVariablePicker tinyMCE plugin)
**/

(function(){
    // set the base url setting
    tinyMCE.baseURL = "/js/tinymce/jscripts/tiny_mce";
    var strPluginURL;
    // the plugin init settings
    tinymce.create('tinymce.plugins.pmVariablePickerPlugin', {
    	init: function(ed, url) 
        {   
            strPluginURL = url;                                         // store the URL for future use..
            ed.addCommand('mcepmVariablePicker', function() {
                pmVariablePicker();              
            });
            ed.addButton('pmVariablePicker', {
                title: 'pmVariablePicker',
                label : '  @#',
                cmd: 'mcepmVariablePicker',
                image: url + '/img/picker.png'
            }); 
        },
        createControl: function(n, cm) {
            return null;
        },
        getPluginURL: function() {
            return strPluginURL;
        }
    });
    tinymce.PluginManager.add('pmVariablePicker', tinymce.plugins.pmVariablePickerPlugin);
})();

// this function can get called from the plugin inint (above) or from the callback on advlink/advimg plugins..
// in the latter case, win and type will be set.. 
/**
 * @function pmVariablePicker
 * @description Opens the plugin popup, loading the form inside it.
 * @param field_name deprecated
 * @param type deprecated
 * @param win deprecated
 * 
 */
function pmVariablePicker(field_name, type, win) {    
    
    var uloc=String(location);
    var new_text = uloc.split('/');
    var loc='/'+new_text[3]+'/'+new_text[4]+'/'+new_text[5]+'/controls/varsAjax?displayOption=tinyMCE&sSymbol=@@&&sProcess='+tinyMCE.activeEditor.processID;
    var strPluginPath  = tinyMCE.activeEditor.plugins.pmVariablePicker.getPluginURL();                               // get the path to the uploader plugin    
    var strUploaderURL = strPluginPath + "/uploader.php";                                                           // generate the path to the uploader script    
    var strUploadPath  = tinyMCE.activeEditor.getParam('plugin_pmVariablePicker_upload_path');                       // get the relative upload path
    var strSubstitutePath = tinyMCE.activeEditor.getParam('plugin_pmVariablePicker_upload_substitute_path');        // get the path we'll substitute for the for the upload path (i.e. fully qualified)
    
    if (strUploaderURL.indexOf("?") < 0){                                                                            // if we were called without any GET params
        strUploaderURL = strUploaderURL + "?type=" + type + "&d=" + strUploadPath + "&subs=" + strSubstitutePath;   // add our own params 
    } else {
        strUploaderURL = strUploaderURL + "&type=" + type + "&d=" + strUploadPath + "&subs=" + strSubstitutePath;
    }
    //tinyMCE.activeEditor.anyVariable='path/to/ProcessMaker' 
    tinyMCE.activeEditor.windowManager.open({                                                                       // open the plugin popup
        file            : loc,
        title           : 'Upload Variable',
        width           : '600px',
        height          : '330px',
        resizable       : "yes",
        scrollbars      : "no",
        overflow        : false,
        inline          : 1,        // This parameter only has an effect if you use the inlinepopups plugin!
        close_previous  : "no"
    }, {
        window : win,
        input : field_name
    });

    return false;
}

/**
 * @function closePluginPopup
 * @description closes the tinyMCE popup window
 */
function closePluginPopup(){
    tinyMCEPopup.close();	                                                                    // close popup window
}

/**
 * @function updateEditorContent
 * @description insert the editor content with a html code string
 * @params serializedHTML String html code
 */
function updateEditorContent(serializedHTML){
    tinyMCE.activeEditor.execCommand('mceInsertContent', false, serializedHTML);
}

/**
 * @function insertFormVar
 * @description alternate version of updateEditorContent this function is 
 *              compatible with the variable picker calls, also closes the popup
 *              window
 * @param fieldName String deprecated
 * @param serializedHTML String the html code to be added. 
 */
function insertFormVar(fieldName,serializedHTML){
    tinyMCEPopup.execCommand('mceInsertContent', false, serializedHTML);
    closePluginPopup();
}

(function(){tinymce.create("tinymce.plugins.VisualChars",{init:function(a,b){var c=this;c.editor=a;a.addCommand("mceVisualChars",c._toggleVisualChars,c);a.addButton("visualchars",{title:"visualchars.desc",cmd:"mceVisualChars"});a.onBeforeGetContent.add(function(d,e){if(c.state&&e.format!="raw"&&!e.draft){c.state=true;c._toggleVisualChars(false)}})},getInfo:function(){return{longname:"Visual characters",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/visualchars",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_toggleVisualChars:function(m){var p=this,k=p.editor,a,g,j,n=k.getDoc(),o=k.getBody(),l,q=k.selection,e,c,f;p.state=!p.state;k.controlManager.setActive("visualchars",p.state);if(m){f=q.getBookmark()}if(p.state){a=[];tinymce.walk(o,function(b){if(b.nodeType==3&&b.nodeValue&&b.nodeValue.indexOf("\u00a0")!=-1){a.push(b)}},"childNodes");for(g=0;g<a.length;g++){l=a[g].nodeValue;l=l.replace(/(\u00a0)/g,'<span data-mce-bogus="1" class="mceItemHidden mceItemNbsp">$1</span>');c=k.dom.create("div",null,l);while(node=c.lastChild){k.dom.insertAfter(node,a[g])}k.dom.remove(a[g])}}else{a=k.dom.select("span.mceItemNbsp",o);for(g=a.length-1;g>=0;g--){k.dom.remove(a[g],1)}}q.moveToBookmark(f)}});tinymce.PluginManager.add("visualchars",tinymce.plugins.VisualChars)})();
(function(){tinymce.create("tinymce.plugins.XHTMLXtrasPlugin",{init:function(a,b){a.addCommand("mceCite",function(){a.windowManager.open({file:b+"/cite.htm",width:350+parseInt(a.getLang("xhtmlxtras.cite_delta_width",0)),height:250+parseInt(a.getLang("xhtmlxtras.cite_delta_height",0)),inline:1},{plugin_url:b})});a.addCommand("mceAcronym",function(){a.windowManager.open({file:b+"/acronym.htm",width:350+parseInt(a.getLang("xhtmlxtras.acronym_delta_width",0)),height:250+parseInt(a.getLang("xhtmlxtras.acronym_delta_height",0)),inline:1},{plugin_url:b})});a.addCommand("mceAbbr",function(){a.windowManager.open({file:b+"/abbr.htm",width:350+parseInt(a.getLang("xhtmlxtras.abbr_delta_width",0)),height:250+parseInt(a.getLang("xhtmlxtras.abbr_delta_height",0)),inline:1},{plugin_url:b})});a.addCommand("mceDel",function(){a.windowManager.open({file:b+"/del.htm",width:340+parseInt(a.getLang("xhtmlxtras.del_delta_width",0)),height:310+parseInt(a.getLang("xhtmlxtras.del_delta_height",0)),inline:1},{plugin_url:b})});a.addCommand("mceIns",function(){a.windowManager.open({file:b+"/ins.htm",width:340+parseInt(a.getLang("xhtmlxtras.ins_delta_width",0)),height:310+parseInt(a.getLang("xhtmlxtras.ins_delta_height",0)),inline:1},{plugin_url:b})});a.addCommand("mceAttributes",function(){a.windowManager.open({file:b+"/attributes.htm",width:380+parseInt(a.getLang("xhtmlxtras.attr_delta_width",0)),height:370+parseInt(a.getLang("xhtmlxtras.attr_delta_height",0)),inline:1},{plugin_url:b})});a.addButton("cite",{title:"xhtmlxtras.cite_desc",cmd:"mceCite"});a.addButton("acronym",{title:"xhtmlxtras.acronym_desc",cmd:"mceAcronym"});a.addButton("abbr",{title:"xhtmlxtras.abbr_desc",cmd:"mceAbbr"});a.addButton("del",{title:"xhtmlxtras.del_desc",cmd:"mceDel"});a.addButton("ins",{title:"xhtmlxtras.ins_desc",cmd:"mceIns"});a.addButton("attribs",{title:"xhtmlxtras.attribs_desc",cmd:"mceAttributes"});a.onNodeChange.add(function(d,c,f,e){f=d.dom.getParent(f,"CITE,ACRONYM,ABBR,DEL,INS");c.setDisabled("cite",e);c.setDisabled("acronym",e);c.setDisabled("abbr",e);c.setDisabled("del",e);c.setDisabled("ins",e);c.setDisabled("attribs",f&&f.nodeName=="BODY");c.setActive("cite",0);c.setActive("acronym",0);c.setActive("abbr",0);c.setActive("del",0);c.setActive("ins",0);if(f){do{c.setDisabled(f.nodeName.toLowerCase(),0);c.setActive(f.nodeName.toLowerCase(),1)}while(f=f.parentNode)}});a.onPreInit.add(function(){a.dom.create("abbr")})},getInfo:function(){return{longname:"XHTML Xtras Plugin",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/xhtmlxtras",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("xhtmlxtras",tinymce.plugins.XHTMLXtrasPlugin)})();
(function(){tinymce.create("tinymce.plugins.WordCount",{block:0,id:null,countre:null,cleanre:null,init:function(c,d){var e=this,f=0,g=tinymce.VK;e.countre=c.getParam("wordcount_countregex",/[\w\u2019\u00co-\u00ff^\uc397^u00f7\'-]+/g);e.cleanre=c.getParam("wordcount_cleanregex",/[0-9.(),;:!?%#$?\'\"_+=\\\/-]*/g);e.update_rate=c.getParam("wordcount_update_rate",2000);e.update_on_delete=c.getParam("wordcount_update_on_delete",false);e.id=c.id+"-word-count";c.onPostRender.add(function(i,h){var j,k;k=i.getParam("wordcount_target_id");if(!k){j=tinymce.DOM.get(i.id+"_path_row");if(j){tinymce.DOM.add(j.parentNode,"div",{style:"float: right"},i.getLang("wordcount.words","Words: ")+'<span id="'+e.id+'">0</span>')}}else{tinymce.DOM.add(k,"span",{},'<span id="'+e.id+'">0</span>')}});c.onInit.add(function(h){h.selection.onSetContent.add(function(){e._count(h)});e._count(h)});c.onSetContent.add(function(h){e._count(h)});function b(h){return h!==f&&(h===g.ENTER||f===g.SPACEBAR||a(f))}function a(h){return h===g.DELETE||h===g.BACKSPACE}c.onKeyUp.add(function(h,i){if(b(i.keyCode)||e.update_on_delete&&a(i.keyCode)){e._count(h)}f=i.keyCode})},_getCount:function(c){var a=0;var b=c.getContent({format:"raw"});if(b){b=b.replace(/\.\.\./g," ");b=b.replace(/<.[^<>]*?>/g," ").replace(/&nbsp;|&#160;/gi," ");b=b.replace(/(\w+)(&.+?;)+(\w+)/,"$1$3").replace(/&.+?;/g," ");b=b.replace(this.cleanre,"");var d=b.match(this.countre);if(d){a=d.length}}return a},_count:function(a){var b=this;if(b.block){return}b.block=1;setTimeout(function(){if(!a.destroyed){var c=b._getCount(a);tinymce.DOM.setHTML(b.id,c.toString());setTimeout(function(){b.block=0},b.update_rate)}},1)},getInfo:function(){return{longname:"Word Count plugin",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/wordcount",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("wordcount",tinymce.plugins.WordCount)})();
(function(d){var e=d.each;function c(g,h){var i=h.ownerDocument,f=i.createRange(),j;f.setStartBefore(h);f.setEnd(g.endContainer,g.endOffset);j=i.createElement("body");j.appendChild(f.cloneContents());return j.innerHTML.replace(/<(br|img|object|embed|input|textarea)[^>]*>/gi,"-").replace(/<[^>]+>/g,"").length===0}function a(g,f){return parseInt(g.getAttribute(f)||1,10)}function b(E,D,H){var g,I,A,n;function x(K,J){K=K.cloneNode(J);K.removeAttribute("id");return K}function s(){var J=0;g=[];e(["thead","tbody","tfoot"],function(K){var L=D.select("> "+K+" tr",E);e(L,function(M,N){N+=J;e(D.select("> td, > th",M),function(T,O){var P,Q,R,S;if(g[N]){while(g[N][O]){O++}}R=a(T,"rowspan");S=a(T,"colspan");for(Q=N;Q<N+R;Q++){if(!g[Q]){g[Q]=[]}for(P=O;P<O+S;P++){g[Q][P]={part:K,real:Q==N&&P==O,elm:T,rowspan:R,colspan:S}}}})});J+=L.length})}function w(J,L){var K;K=g[L];if(K){return K[J]}}function r(L,J,K){if(L){K=parseInt(K,10);if(K===1){L.removeAttribute(J,1)}else{L.setAttribute(J,K,1)}}}function i(J){return J&&(D.hasClass(J.elm,"mceSelected")||J==n)}function j(){var J=[];e(E.rows,function(K){e(K.cells,function(L){if(D.hasClass(L,"mceSelected")||L==n.elm){J.push(K);return false}})});return J}function q(){var J=D.createRng();J.setStartAfter(E);J.setEndAfter(E);H.setRng(J);D.remove(E)}function f(J){var K;d.walk(J,function(M){var L;if(M.nodeType==3){e(D.getParents(M.parentNode,null,J).reverse(),function(N){N=x(N,false);if(!K){K=L=N}else{if(L){L.appendChild(N)}}L=N});if(L){L.innerHTML=d.isIE&&!d.isIE11?"&nbsp;":'<br data-mce-bogus="1" />'}return false}},"childNodes");J=x(J,false);r(J,"rowSpan",1);r(J,"colSpan",1);if(K){J.appendChild(K)}else{if(!d.isIE||d.isIE11){J.innerHTML='<br data-mce-bogus="1" />'}}return J}function p(){var J=D.createRng(),K;e(D.select("tr",E),function(L){if(L.cells.length===0){D.remove(L)}});if(D.select("tr",E).length===0){J.setStartAfter(E);J.setEndAfter(E);H.setRng(J);D.remove(E);return}e(D.select("thead,tbody,tfoot",E),function(L){if(L.rows.length===0){D.remove(L)}});s();K=g[Math.min(g.length-1,I.y)];if(K){H.select(K[Math.min(K.length-1,I.x)].elm,true);H.collapse(true)}}function t(P,N,R,O){var M,K,J,L,Q;M=g[N][P].elm.parentNode;for(J=1;J<=R;J++){M=D.getNext(M,"tr");if(M){for(K=P;K>=0;K--){Q=g[N+J][K].elm;if(Q.parentNode==M){for(L=1;L<=O;L++){D.insertAfter(f(Q),Q)}break}}if(K==-1){for(L=1;L<=O;L++){M.insertBefore(f(M.cells[0]),M.cells[0])}}}}}function z(){e(g,function(J,K){e(J,function(M,L){var P,O,N;if(i(M)){M=M.elm;P=a(M,"colspan");O=a(M,"rowspan");if(P>1||O>1){r(M,"rowSpan",1);r(M,"colSpan",1);for(N=0;N<P-1;N++){D.insertAfter(f(M),M)}t(L,K,O-1,P)}}})})}function o(T,Q,W){var M,L,V,U,S,P,R,J,K,N,O;if(T){O=C(T);M=O.x;L=O.y;V=M+(Q-1);U=L+(W-1)}else{I=A=null;e(g,function(X,Y){e(X,function(aa,Z){if(i(aa)){if(!I){I={x:Z,y:Y}}A={x:Z,y:Y}}})});M=I.x;L=I.y;V=A.x;U=A.y}R=w(M,L);J=w(V,U);if(R&&J&&R.part==J.part){z();s();R=w(M,L).elm;r(R,"colSpan",(V-M)+1);r(R,"rowSpan",(U-L)+1);for(P=L;P<=U;P++){for(S=M;S<=V;S++){if(!g[P]||!g[P][S]){continue}T=g[P][S].elm;if(T!=R){K=d.grep(T.childNodes);e(K,function(X){R.appendChild(X)});if(K.length){K=d.grep(R.childNodes);N=0;e(K,function(X){if(X.nodeName=="BR"&&D.getAttrib(X,"data-mce-bogus")&&N++<K.length-1){R.removeChild(X)}})}D.remove(T)}}}p()}}function k(N){var J,P,M,O,Q,R,K,S,L;e(g,function(T,U){e(T,function(V){if(i(V)){V=V.elm;Q=V.parentNode;R=x(Q,false);J=U;if(N){return false}}});if(N){return !J}});for(O=0;O<g[0].length;O++){if(!g[J][O]){continue}P=g[J][O].elm;if(P!=M){if(!N){L=a(P,"rowspan");if(L>1){r(P,"rowSpan",L+1);continue}}else{if(J>0&&g[J-1][O]){S=g[J-1][O].elm;L=a(S,"rowSpan");if(L>1){r(S,"rowSpan",L+1);continue}}}K=f(P);r(K,"colSpan",P.colSpan);R.appendChild(K);M=P}}if(R.hasChildNodes()){if(!N){D.insertAfter(R,Q)}else{Q.parentNode.insertBefore(R,Q)}}}function h(K){var L,J;e(g,function(M){e(M,function(O,N){if(i(O)){L=N;if(K){return false}}});if(K){return !L}});e(g,function(P,Q){var M,N,O;if(!P[L]){return}M=P[L].elm;if(M!=J){O=a(M,"colspan");N=a(M,"rowspan");if(O==1){if(!K){D.insertAfter(f(M),M);t(L,Q,N-1,O)}else{M.parentNode.insertBefore(f(M),M);t(L,Q,N-1,O)}}else{r(M,"colSpan",M.colSpan+1)}J=M}})}function m(){var J=[];e(g,function(K){e(K,function(M,L){if(i(M)&&d.inArray(J,L)===-1){e(g,function(P){var N=P[L].elm,O;O=a(N,"colSpan");if(O>1){r(N,"colSpan",O-1)}else{D.remove(N)}});J.push(L)}})});p()}function l(){var K;function J(M){var N,L;e(M.cells,function(O){var P=a(O,"rowSpan");if(P>1){r(O,"rowSpan",P-1);N=C(O);t(N.x,N.y,1,1)}});N=C(M.cells[0]);e(g[N.y],function(O){var P;O=O.elm;if(O!=L){P=a(O,"rowSpan");if(P<=1){D.remove(O)}else{r(O,"rowSpan",P-1)}L=O}})}K=j();e(K.reverse(),function(L){J(L)});p()}function B(){var J=j();D.remove(J);p();return J}function G(){var J=j();e(J,function(L,K){J[K]=x(L,true)});return J}function y(L,K){if(!L){return}var M=j(),J=M[K?0:M.length-1],N=J.cells.length;e(g,function(P){var O;N=0;e(P,function(Q){if(Q.real){N+=Q.colspan}if(Q.elm.parentNode==J){O=1}});if(O){return false}});if(!K){L.reverse()}e(L,function(R){var Q=R.cells.length,O,P;for(P=0;P<Q;P++){O=R.cells[P];r(O,"colSpan",1);r(O,"rowSpan",1)}for(P=Q;P<N;P++){R.appendChild(f(R.cells[Q-1]))}for(P=N;P<Q;P++){D.remove(R.cells[P])}if(K){J.parentNode.insertBefore(R,J)}else{D.insertAfter(R,J)}});D.removeClass(D.select("td.mceSelected,th.mceSelected"),"mceSelected")}function C(J){var K;e(g,function(L,M){e(L,function(O,N){if(O.elm==J){K={x:N,y:M};return false}});return !K});return K}function v(J){I=C(J)}function F(){var K,J;K=J=0;e(g,function(L,M){e(L,function(O,N){var Q,P;if(i(O)){O=g[M][N];if(N>K){K=N}if(M>J){J=M}if(O.real){Q=O.colspan-1;P=O.rowspan-1;if(Q){if(N+Q>K){K=N+Q}}if(P){if(M+P>J){J=M+P}}}}})});return{x:K,y:J}}function u(R){var M,L,T,S,K,J,N,O,P,Q;A=C(R);if(I&&A){M=Math.min(I.x,A.x);L=Math.min(I.y,A.y);T=Math.max(I.x,A.x);S=Math.max(I.y,A.y);K=T;J=S;for(P=L;P<=J;P++){R=g[P][M];if(!R.real){if(M-(R.colspan-1)<M){M-=R.colspan-1}}}for(Q=M;Q<=K;Q++){R=g[L][Q];if(!R.real){if(L-(R.rowspan-1)<L){L-=R.rowspan-1}}}for(P=L;P<=S;P++){for(Q=M;Q<=T;Q++){R=g[P][Q];if(R.real){N=R.colspan-1;O=R.rowspan-1;if(N){if(Q+N>K){K=Q+N}}if(O){if(P+O>J){J=P+O}}}}}D.removeClass(D.select("td.mceSelected,th.mceSelected"),"mceSelected");for(P=L;P<=J;P++){for(Q=M;Q<=K;Q++){if(g[P][Q]){D.addClass(g[P][Q].elm,"mceSelected")}}}}}s();n=D.getParent(H.getStart(),"th,td");if(n){I=C(n);A=F();n=w(I.x,I.y)}d.extend(this,{deleteTable:q,split:z,merge:o,insertRow:k,insertCol:h,deleteCols:m,deleteRows:l,cutRows:B,copyRows:G,pasteRows:y,getPos:C,setStartCell:v,setEndCell:u})}d.create("tinymce.plugins.TablePlugin",{init:function(g,h){var f,l,i=true;function k(o){var n=g.selection,m=g.dom.getParent(o||n.getNode(),"table");if(m){return new b(m,g.dom,n)}}function j(){g.getBody().style.webkitUserSelect="";if(i){g.dom.removeClass(g.dom.select("td.mceSelected,th.mceSelected"),"mceSelected");i=false}}e([["table","table.desc","mceInsertTable",true],["delete_table","table.del","mceTableDelete"],["delete_col","table.delete_col_desc","mceTableDeleteCol"],["delete_row","table.delete_row_desc","mceTableDeleteRow"],["col_after","table.col_after_desc","mceTableInsertColAfter"],["col_before","table.col_before_desc","mceTableInsertColBefore"],["row_after","table.row_after_desc","mceTableInsertRowAfter"],["row_before","table.row_before_desc","mceTableInsertRowBefore"],["row_props","table.row_desc","mceTableRowProps",true],["cell_props","table.cell_desc","mceTableCellProps",true],["split_cells","table.split_cells_desc","mceTableSplitCells",true],["merge_cells","table.merge_cells_desc","mceTableMergeCells",true]],function(m){g.addButton(m[0],{title:m[1],cmd:m[2],ui:m[3]})});if(!d.isIE){g.onClick.add(function(m,n){n=n.target;if(n.nodeName==="TABLE"){m.selection.select(n);m.nodeChanged()}})}g.onPreProcess.add(function(n,o){var m,p,q,s=n.dom,r;m=s.select("table",o.node);p=m.length;while(p--){q=m[p];s.setAttrib(q,"data-mce-style","");if((r=s.getAttrib(q,"width"))){s.setStyle(q,"width",r);s.setAttrib(q,"width","")}if((r=s.getAttrib(q,"height"))){s.setStyle(q,"height",r);s.setAttrib(q,"height","")}}});g.onNodeChange.add(function(o,m,r){var q;r=o.selection.getStart();q=o.dom.getParent(r,"td,th,caption");m.setActive("table",r.nodeName==="TABLE"||!!q);if(q&&q.nodeName==="CAPTION"){q=0}m.setDisabled("delete_table",!q);m.setDisabled("delete_col",!q);m.setDisabled("delete_table",!q);m.setDisabled("delete_row",!q);m.setDisabled("col_after",!q);m.setDisabled("col_before",!q);m.setDisabled("row_after",!q);m.setDisabled("row_before",!q);m.setDisabled("row_props",!q);m.setDisabled("cell_props",!q);m.setDisabled("split_cells",!q);m.setDisabled("merge_cells",!q)});g.onInit.add(function(q){var o,s,p=q.dom,t;f=q.windowManager;q.onMouseDown.add(function(v,w){if(w.button!=2){j();s=p.getParent(w.target,"td,th");o=p.getParent(s,"table")}});p.bind(q.getDoc(),"mouseover",function(z){var x,w,y=z.target;if(s&&(t||y!=s)&&(y.nodeName=="TD"||y.nodeName=="TH")){w=p.getParent(y,"table");if(w==o){if(!t){t=k(w);t.setStartCell(s);q.getBody().style.webkitUserSelect="none"}t.setEndCell(y);i=true}x=q.selection.getSel();try{if(x.removeAllRanges){x.removeAllRanges()}else{x.empty()}}catch(v){}z.preventDefault()}});q.onMouseUp.add(function(y){var w,B=y.selection,v,C,A,z;function x(D,F){var E=new d.dom.TreeWalker(D,D);do{if(D.nodeType==3&&d.trim(D.nodeValue).length!==0){if(F){w.setStart(D,0)}else{w.setEnd(D,D.nodeValue.length)}return}if(D.nodeName=="BR"){if(F){w.setStartBefore(D)}else{w.setEndBefore(D)}return}D=(F?E.next():E.prev())}while(D)}if(s){if(t){y.getBody().style.webkitUserSelect=""}v=p.select("td.mceSelected,th.mceSelected");if(v.length>0){w=p.createRng();A=v[0];w.setStartBefore(A);w.setEndAfter(A);x(A,1);C=new d.dom.TreeWalker(A,p.getParent(v[0],"table"));do{if(A.nodeName=="TD"||A.nodeName=="TH"){if(!p.hasClass(A,"mceSelected")){break}z=A}A=C.next()}while(A);x(z);B.setRng(w)}y.nodeChanged();s=t=o=null}});q.onKeyUp.add(function(){j()});q.onKeyDown.add(function(v){m(v)});q.onMouseDown.add(function(v,w){if(w.button!=2){m(v)}});function n(A,w,x,C){var y=3,D=A.dom.getParent(w.startContainer,"TABLE"),z,v,B;if(D){z=D.parentNode}v=w.startContainer.nodeType==y&&w.startOffset===0&&w.endOffset===0&&C&&(x.nodeName==="TR"||x===z);B=(x.nodeName==="TD"||x.nodeName==="TH")&&!C;return v||B}function m(x){if(!d.isWebKit){return}var w=x.selection.getRng();var z=x.selection.getNode();var y=x.dom.getParent(w.startContainer,"TD,TH");if(!n(x,w,z,y)){return}if(!y){y=z}var v=y.lastChild;while(v.lastChild){v=v.lastChild}w.setEnd(v,v.nodeValue.length);x.selection.setRng(w)}q.plugins.table.fixTableCellSelection=m;if(q&&q.plugins.contextmenu){q.plugins.contextmenu.onContextMenu.add(function(x,v,z){var A,y=q.selection,w=y.getNode()||q.getBody();if(q.dom.getParent(z,"td")||q.dom.getParent(z,"th")||q.dom.select("td.mceSelected,th.mceSelected").length){v.removeAll();if(w.nodeName=="A"&&!q.dom.getAttrib(w,"name")){v.add({title:"advanced.link_desc",icon:"link",cmd:q.plugins.advlink?"mceAdvLink":"mceLink",ui:true});v.add({title:"advanced.unlink_desc",icon:"unlink",cmd:"UnLink"});v.addSeparator()}if(w.nodeName=="IMG"&&w.className.indexOf("mceItem")==-1){v.add({title:"advanced.image_desc",icon:"image",cmd:q.plugins.advimage?"mceAdvImage":"mceImage",ui:true});v.addSeparator()}v.add({title:"table.desc",icon:"table",cmd:"mceInsertTable",value:{action:"insert"}});v.add({title:"table.props_desc",icon:"table_props",cmd:"mceInsertTable"});v.add({title:"table.del",icon:"delete_table",cmd:"mceTableDelete"});v.addSeparator();A=v.addMenu({title:"table.cell"});A.add({title:"table.cell_desc",icon:"cell_props",cmd:"mceTableCellProps"});A.add({title:"table.split_cells_desc",icon:"split_cells",cmd:"mceTableSplitCells"});A.add({title:"table.merge_cells_desc",icon:"merge_cells",cmd:"mceTableMergeCells"});A=v.addMenu({title:"table.row"});A.add({title:"table.row_desc",icon:"row_props",cmd:"mceTableRowProps"});A.add({title:"table.row_before_desc",icon:"row_before",cmd:"mceTableInsertRowBefore"});A.add({title:"table.row_after_desc",icon:"row_after",cmd:"mceTableInsertRowAfter"});A.add({title:"table.delete_row_desc",icon:"delete_row",cmd:"mceTableDeleteRow"});A.addSeparator();A.add({title:"table.cut_row_desc",icon:"cut",cmd:"mceTableCutRow"});A.add({title:"table.copy_row_desc",icon:"copy",cmd:"mceTableCopyRow"});A.add({title:"table.paste_row_before_desc",icon:"paste",cmd:"mceTablePasteRowBefore"}).setDisabled(!l);A.add({title:"table.paste_row_after_desc",icon:"paste",cmd:"mceTablePasteRowAfter"}).setDisabled(!l);A=v.addMenu({title:"table.col"});A.add({title:"table.col_before_desc",icon:"col_before",cmd:"mceTableInsertColBefore"});A.add({title:"table.col_after_desc",icon:"col_after",cmd:"mceTableInsertColAfter"});A.add({title:"table.delete_col_desc",icon:"delete_col",cmd:"mceTableDeleteCol"})}else{v.add({title:"table.desc",icon:"table",cmd:"mceInsertTable"})}})}function u(z,K){var I=d.VK;var N=K.keyCode;function L(V,R,P){var Q=V?"previousSibling":"nextSibling";var W=z.dom.getParent(R,"tr");var U=W[Q];if(U){w(z,R,U,V);d.dom.Event.cancel(P);return true}else{var X=z.dom.getParent(W,"table");var T=W.parentNode;var O=T.nodeName.toLowerCase();if(O==="tbody"||O===(V?"tfoot":"thead")){var S=v(V,X,T,"tbody");if(S!==null){return H(V,S,R,P)}}return J(V,W,Q,X,P)}}function v(S,Q,R,U){var P=z.dom.select(">"+U,Q);var O=P.indexOf(R);if(S&&O===0||!S&&O===P.length-1){return y(S,Q)}else{if(O===-1){var T=R.tagName.toLowerCase()==="thead"?0:P.length-1;return P[T]}else{return P[O+(S?-1:1)]}}}function y(R,Q){var P=R?"thead":"tfoot";var O=z.dom.select(">"+P,Q);return O.length!==0?O[0]:null}function H(S,Q,P,R){var O=G(Q,S);if(O){w(z,P,O,S)}d.dom.Event.cancel(R);return true}function J(V,R,O,U,T){var P=U[O];if(P){C(P);return true}else{var S=z.dom.getParent(U,"td,th");if(S){return L(V,S,T)}else{var Q=G(R,!V);C(Q);return d.dom.Event.cancel(T)}}}function G(P,O){var Q=P&&P[O?"lastChild":"firstChild"];return Q&&Q.nodeName==="BR"?z.dom.getParent(Q,"td,th"):Q}function C(O){z.selection.setCursorLocation(O,0)}function x(){return N==I.UP||N==I.DOWN}function A(O){var Q=O.selection.getNode();var P=O.dom.getParent(Q,"tr");return P!==null}function M(P){var O=0;var Q=P;while(Q.previousSibling){Q=Q.previousSibling;O=O+a(Q,"colspan")}return O}function B(Q,O){var R=0;var P=0;e(Q.children,function(S,T){R=R+a(S,"colspan");P=T;if(R>O){return false}});return P}function w(Q,T,V,S){var U=M(Q.dom.getParent(T,"td,th"));var P=B(V,U);var O=V.childNodes[P];var R=G(O,S);C(R||O)}function E(O){var Q=z.selection.getNode();var R=z.dom.getParent(Q,"td,th");var P=z.dom.getParent(O,"td,th");return R&&R!==P&&F(R,P)}function F(P,O){return z.dom.getParent(P,"TABLE")===z.dom.getParent(O,"TABLE")}if(x()&&A(z)){var D=z.selection.getNode();setTimeout(function(){if(E(D)){L(!K.shiftKey&&N===I.UP,D,K)}},0)}}if(d.isWebKit){q.onKeyDown.add(u)}function r(){var v;v=q.getBody().lastChild;while(v&&v.nodeType==3&&!v.nodeValue.length){v=v.previousSibling}if(v&&v.nodeName==="TABLE"){if(q.settings.forced_root_block){q.dom.add(q.getBody(),q.settings.forced_root_block,null,d.isIE&&!d.isIE11?"&nbsp;":'<br data-mce-bogus="1" />')}else{q.dom.add(q.getBody(),"br",{"data-mce-bogus":"1"})}}}if(d.isGecko){q.onKeyDown.add(function(w,y){var v,x,z=w.dom;if(y.keyCode==37||y.keyCode==38){v=w.selection.getRng();x=z.getParent(v.startContainer,"table");if(x&&w.getBody().firstChild==x){if(c(v,x)){v=z.createRng();v.setStartBefore(x);v.setEndBefore(x);w.selection.setRng(v);y.preventDefault()}}}})}q.onKeyUp.add(r);q.onSetContent.add(r);q.onVisualAid.add(r);q.onPreProcess.add(function(v,x){var w=x.node.lastChild;if(w&&(w.nodeName=="BR"||(w.childNodes.length==1&&(w.firstChild.nodeName=="BR"||w.firstChild.nodeValue=="\u00a0")))&&w.previousSibling&&w.previousSibling.nodeName=="TABLE"){v.dom.remove(w)}});r();q.startContent=q.getContent({format:"raw"})});e({mceTableSplitCells:function(m){m.split()},mceTableMergeCells:function(n){var o,p,m;m=g.dom.getParent(g.selection.getNode(),"th,td");if(m){o=m.rowSpan;p=m.colSpan}if(!g.dom.select("td.mceSelected,th.mceSelected").length){f.open({url:h+"/merge_cells.htm",width:240+parseInt(g.getLang("table.merge_cells_delta_width",0),10),height:110+parseInt(g.getLang("table.merge_cells_delta_height",0),10),inline:1},{rows:o,cols:p,onaction:function(q){n.merge(m,q.cols,q.rows)},plugin_url:h})}else{n.merge()}},mceTableInsertRowBefore:function(m){m.insertRow(true)},mceTableInsertRowAfter:function(m){m.insertRow()},mceTableInsertColBefore:function(m){m.insertCol(true)},mceTableInsertColAfter:function(m){m.insertCol()},mceTableDeleteCol:function(m){m.deleteCols()},mceTableDeleteRow:function(m){m.deleteRows()},mceTableCutRow:function(m){l=m.cutRows()},mceTableCopyRow:function(m){l=m.copyRows()},mceTablePasteRowBefore:function(m){m.pasteRows(l,true)},mceTablePasteRowAfter:function(m){m.pasteRows(l)},mceTableDelete:function(m){m.deleteTable()}},function(n,m){g.addCommand(m,function(){var o=k();if(o){n(o);g.execCommand("mceRepaint");j()}})});e({mceInsertTable:function(m){f.open({url:h+"/table.htm",width:400+parseInt(g.getLang("table.table_delta_width",0),10),height:320+parseInt(g.getLang("table.table_delta_height",0),10),inline:1},{plugin_url:h,action:m?m.action:0})},mceTableRowProps:function(){f.open({url:h+"/row.htm",width:400+parseInt(g.getLang("table.rowprops_delta_width",0),10),height:295+parseInt(g.getLang("table.rowprops_delta_height",0),10),inline:1},{plugin_url:h})},mceTableCellProps:function(){f.open({url:h+"/cell.htm",width:400+parseInt(g.getLang("table.cellprops_delta_width",0),10),height:295+parseInt(g.getLang("table.cellprops_delta_height",0),10),inline:1},{plugin_url:h})}},function(n,m){g.addCommand(m,function(o,p){n(p)})})}});d.PluginManager.add("table",d.plugins.TablePlugin)})(tinymce);
(function(){var a=tinymce.each;tinymce.create("tinymce.plugins.TemplatePlugin",{init:function(b,c){var d=this;d.editor=b;b.addCommand("mceTemplate",function(e){b.windowManager.open({file:c+"/template.htm",width:b.getParam("template_popup_width",750),height:b.getParam("template_popup_height",600),inline:1},{plugin_url:c})});b.addCommand("mceInsertTemplate",d._insertTemplate,d);b.addButton("template",{title:"template.desc",cmd:"mceTemplate"});b.onPreProcess.add(function(e,g){var f=e.dom;a(f.select("div",g.node),function(h){if(f.hasClass(h,"mceTmpl")){a(f.select("*",h),function(i){if(f.hasClass(i,e.getParam("template_mdate_classes","mdate").replace(/\s+/g,"|"))){i.innerHTML=d._getDateTime(new Date(),e.getParam("template_mdate_format",e.getLang("template.mdate_format")))}});d._replaceVals(h)}})})},getInfo:function(){return{longname:"Template plugin",author:"Moxiecode Systems AB",authorurl:"http://www.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/template",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_insertTemplate:function(i,j){var k=this,g=k.editor,f,c,d=g.dom,b=g.selection.getContent();f=j.content;a(k.editor.getParam("template_replace_values"),function(l,h){if(typeof(l)!="function"){f=f.replace(new RegExp("\\{\\$"+h+"\\}","g"),l)}});c=d.create("div",null,f);n=d.select(".mceTmpl",c);if(n&&n.length>0){c=d.create("div",null);c.appendChild(n[0].cloneNode(true))}function e(l,h){return new RegExp("\\b"+h+"\\b","g").test(l.className)}a(d.select("*",c),function(h){if(e(h,g.getParam("template_cdate_classes","cdate").replace(/\s+/g,"|"))){h.innerHTML=k._getDateTime(new Date(),g.getParam("template_cdate_format",g.getLang("template.cdate_format")))}if(e(h,g.getParam("template_mdate_classes","mdate").replace(/\s+/g,"|"))){h.innerHTML=k._getDateTime(new Date(),g.getParam("template_mdate_format",g.getLang("template.mdate_format")))}if(e(h,g.getParam("template_selected_content_classes","selcontent").replace(/\s+/g,"|"))){h.innerHTML=b}});k._replaceVals(c);g.execCommand("mceInsertContent",false,c.innerHTML);g.addVisual()},_replaceVals:function(c){var d=this.editor.dom,b=this.editor.getParam("template_replace_values");a(d.select("*",c),function(f){a(b,function(g,e){if(d.hasClass(f,e)){if(typeof(b[e])=="function"){b[e](f)}}})})},_getDateTime:function(e,b){if(!b){return""}function c(g,d){var f;g=""+g;if(g.length<d){for(f=0;f<(d-g.length);f++){g="0"+g}}return g}b=b.replace("%D","%m/%d/%y");b=b.replace("%r","%I:%M:%S %p");b=b.replace("%Y",""+e.getFullYear());b=b.replace("%y",""+e.getYear());b=b.replace("%m",c(e.getMonth()+1,2));b=b.replace("%d",c(e.getDate(),2));b=b.replace("%H",""+c(e.getHours(),2));b=b.replace("%M",""+c(e.getMinutes(),2));b=b.replace("%S",""+c(e.getSeconds(),2));b=b.replace("%I",""+((e.getHours()+11)%12+1));b=b.replace("%p",""+(e.getHours()<12?"AM":"PM"));b=b.replace("%B",""+this.editor.getLang("template_months_long").split(",")[e.getMonth()]);b=b.replace("%b",""+this.editor.getLang("template_months_short").split(",")[e.getMonth()]);b=b.replace("%A",""+this.editor.getLang("template_day_long").split(",")[e.getDay()]);b=b.replace("%a",""+this.editor.getLang("template_day_short").split(",")[e.getDay()]);b=b.replace("%%","%");return b}});tinymce.PluginManager.add("template",tinymce.plugins.TemplatePlugin)})();
(function(){tinymce.create("tinymce.plugins.VisualBlocks",{init:function(a,b){var c;if(!window.NodeList){return}a.addCommand("mceVisualBlocks",function(){var e=a.dom,d;if(!c){c=e.uniqueId();d=e.create("link",{id:c,rel:"stylesheet",href:b+"/css/visualblocks.css"});a.getDoc().getElementsByTagName("head")[0].appendChild(d)}else{d=e.get(c);d.disabled=!d.disabled}a.controlManager.setActive("visualblocks",!d.disabled)});a.addButton("visualblocks",{title:"visualblocks.desc",cmd:"mceVisualBlocks"});a.onInit.add(function(){if(a.settings.visualblocks_default_state){a.execCommand("mceVisualBlocks",false,null,{skip_focus:true})}})},getInfo:function(){return{longname:"Visual blocks",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/visualblocks",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("visualblocks",tinymce.plugins.VisualBlocks)})();
(function(){tinymce.create("tinymce.plugins.Preview",{init:function(a,b){var d=this,c=tinymce.explode(a.settings.content_css);d.editor=a;tinymce.each(c,function(f,e){c[e]=a.documentBaseURI.toAbsolute(f)});a.addCommand("mcePreview",function(){a.windowManager.open({file:a.getParam("plugin_preview_pageurl",b+"/preview.html"),width:parseInt(a.getParam("plugin_preview_width","550")),height:parseInt(a.getParam("plugin_preview_height","600")),resizable:"yes",scrollbars:"yes",popup_css:c?c.join(","):a.baseURI.toAbsolute("themes/"+a.settings.theme+"/skins/"+a.settings.skin+"/content.css"),inline:a.getParam("plugin_preview_inline",1)},{base:a.documentBaseURI.getURI()})});a.addButton("preview",{title:"preview.preview_desc",cmd:"mcePreview"})},getInfo:function(){return{longname:"Preview",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/preview",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("preview",tinymce.plugins.Preview)})();
(function(){tinymce.create("tinymce.plugins.Print",{init:function(a,b){a.addCommand("mcePrint",function(){a.getWin().print()});a.addButton("print",{title:"print.print_desc",cmd:"mcePrint"})},getInfo:function(){return{longname:"Print",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/print",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("print",tinymce.plugins.Print)})();
(function(){tinymce.create("tinymce.plugins.StylePlugin",{init:function(a,b){a.addCommand("mceStyleProps",function(){var c=false;var f=a.selection.getSelectedBlocks();var d=[];if(f.length===1){d.push(a.selection.getNode().style.cssText)}else{tinymce.each(f,function(g){d.push(a.dom.getAttrib(g,"style"))});c=true}a.windowManager.open({file:b+"/props.htm",width:480+parseInt(a.getLang("style.delta_width",0)),height:340+parseInt(a.getLang("style.delta_height",0)),inline:1},{applyStyleToBlocks:c,plugin_url:b,styles:d})});a.addCommand("mceSetElementStyle",function(d,c){if(e=a.selection.getNode()){a.dom.setAttrib(e,"style",c);a.execCommand("mceRepaint")}});a.onNodeChange.add(function(d,c,f){c.setDisabled("styleprops",f.nodeName==="BODY")});a.addButton("styleprops",{title:"style.desc",cmd:"mceStyleProps"})},getInfo:function(){return{longname:"Style",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/style",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("style",tinymce.plugins.StylePlugin)})();
(function(){tinymce.create("tinymce.plugins.Save",{init:function(a,b){var c=this;c.editor=a;a.addCommand("mceSave",c._save,c);a.addCommand("mceCancel",c._cancel,c);a.addButton("save",{title:"save.save_desc",cmd:"mceSave"});a.addButton("cancel",{title:"save.cancel_desc",cmd:"mceCancel"});a.onNodeChange.add(c._nodeChange,c);a.addShortcut("ctrl+s",a.getLang("save.save_desc"),"mceSave")},getInfo:function(){return{longname:"Save",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/save",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_nodeChange:function(b,a,c){var b=this.editor;if(b.getParam("save_enablewhendirty")){a.setDisabled("save",!b.isDirty());a.setDisabled("cancel",!b.isDirty())}},_save:function(){var c=this.editor,a,e,d,b;a=tinymce.DOM.get(c.id).form||tinymce.DOM.getParent(c.id,"form");if(c.getParam("save_enablewhendirty")&&!c.isDirty()){return}tinyMCE.triggerSave();if(e=c.getParam("save_onsavecallback")){if(c.execCallback("save_onsavecallback",c)){c.startContent=tinymce.trim(c.getContent({format:"raw"}));c.nodeChanged()}return}if(a){c.isNotDirty=true;if(a.onsubmit==null||a.onsubmit()!=false){a.submit()}c.nodeChanged()}else{c.windowManager.alert("Error: No form element found.")}},_cancel:function(){var a=this.editor,c,b=tinymce.trim(a.startContent);if(c=a.getParam("save_oncancelcallback")){a.execCallback("save_oncancelcallback",a);return}a.setContent(b);a.undoManager.clear();a.nodeChanged()}});tinymce.PluginManager.add("save",tinymce.plugins.Save)})();
(function(){var c=tinymce.DOM,a=tinymce.dom.Event,d=tinymce.each,b=tinymce.explode;tinymce.create("tinymce.plugins.TabFocusPlugin",{init:function(f,g){function e(i,j){if(j.keyCode===9){return a.cancel(j)}}function h(l,p){var j,m,o,n,k;function q(t){n=c.select(":input:enabled,*[tabindex]:not(iframe)");function s(v){return v.nodeName==="BODY"||(v.type!="hidden"&&!(v.style.display=="none")&&!(v.style.visibility=="hidden")&&s(v.parentNode))}function i(v){return v.attributes.tabIndex.specified||v.nodeName=="INPUT"||v.nodeName=="TEXTAREA"}function u(){return tinymce.isIE6||tinymce.isIE7}function r(v){return((!u()||i(v)))&&v.getAttribute("tabindex")!="-1"&&s(v)}d(n,function(w,v){if(w.id==l.id){j=v;return false}});if(t>0){for(m=j+1;m<n.length;m++){if(r(n[m])){return n[m]}}}else{for(m=j-1;m>=0;m--){if(r(n[m])){return n[m]}}}return null}if(p.keyCode===9){k=b(l.getParam("tab_focus",l.getParam("tabfocus_elements",":prev,:next")));if(k.length==1){k[1]=k[0];k[0]=":prev"}if(p.shiftKey){if(k[0]==":prev"){n=q(-1)}else{n=c.get(k[0])}}else{if(k[1]==":next"){n=q(1)}else{n=c.get(k[1])}}if(n){if(n.id&&(l=tinymce.get(n.id||n.name))){l.focus()}else{window.setTimeout(function(){if(!tinymce.isWebKit){window.focus()}n.focus()},10)}return a.cancel(p)}}}f.onKeyUp.add(e);if(tinymce.isGecko){f.onKeyPress.add(h);f.onKeyDown.add(e)}else{f.onKeyDown.add(h)}},getInfo:function(){return{longname:"Tabfocus",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/tabfocus",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("tabfocus",tinymce.plugins.TabFocusPlugin)})();
(function(){tinymce.create("tinymce.plugins.SearchReplacePlugin",{init:function(a,c){function b(d){window.focus();a.windowManager.open({file:c+"/searchreplace.htm",width:420+parseInt(a.getLang("searchreplace.delta_width",0)),height:170+parseInt(a.getLang("searchreplace.delta_height",0)),inline:1,auto_focus:0},{mode:d,search_string:a.selection.getContent({format:"text"}),plugin_url:c})}a.addCommand("mceSearch",function(){b("search")});a.addCommand("mceReplace",function(){b("replace")});a.addButton("search",{title:"searchreplace.search_desc",cmd:"mceSearch"});a.addButton("replace",{title:"searchreplace.replace_desc",cmd:"mceReplace"});a.addShortcut("ctrl+f","searchreplace.search_desc","mceSearch")},getInfo:function(){return{longname:"Search/Replace",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/searchreplace",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("searchreplace",tinymce.plugins.SearchReplacePlugin)})();
(function(){var c=tinymce.each,a={paste_auto_cleanup_on_paste:true,paste_enable_default_filters:true,paste_block_drop:false,paste_retain_style_properties:"none",paste_strip_class_attributes:"mso",paste_remove_spans:false,paste_remove_styles:false,paste_remove_styles_if_webkit:true,paste_convert_middot_lists:true,paste_convert_headers_to_strong:false,paste_dialog_width:"450",paste_dialog_height:"400",paste_max_consecutive_linebreaks:2,paste_text_use_dialog:false,paste_text_sticky:false,paste_text_sticky_default:false,paste_text_notifyalways:false,paste_text_linebreaktype:"combined",paste_text_replacements:[[/\u2026/g,"..."],[/[\x93\x94\u201c\u201d]/g,'"'],[/[\x60\x91\x92\u2018\u2019]/g,"'"]]};function b(d,e){return d.getParam(e,a[e])}tinymce.create("tinymce.plugins.PastePlugin",{init:function(d,e){var f=this;f.editor=d;f.url=e;f.onPreProcess=new tinymce.util.Dispatcher(f);f.onPostProcess=new tinymce.util.Dispatcher(f);f.onPreProcess.add(f._preProcess);f.onPostProcess.add(f._postProcess);f.onPreProcess.add(function(i,j){d.execCallback("paste_preprocess",i,j)});f.onPostProcess.add(function(i,j){d.execCallback("paste_postprocess",i,j)});d.onKeyDown.addToTop(function(i,j){if(((tinymce.isMac?j.metaKey:j.ctrlKey)&&j.keyCode==86)||(j.shiftKey&&j.keyCode==45)){return false}});d.pasteAsPlainText=b(d,"paste_text_sticky_default");function h(l,j){var k=d.dom,i;f.onPreProcess.dispatch(f,l);l.node=k.create("div",0,l.content);if(tinymce.isGecko){i=d.selection.getRng(true);if(i.startContainer==i.endContainer&&i.startContainer.nodeType==3){if(l.node.childNodes.length===1&&/^(p|h[1-6]|pre)$/i.test(l.node.firstChild.nodeName)&&l.content.indexOf("__MCE_ITEM__")===-1){k.remove(l.node.firstChild,true)}}}f.onPostProcess.dispatch(f,l);l.content=d.serializer.serialize(l.node,{getInner:1,forced_root_block:""});if((!j)&&(d.pasteAsPlainText)){f._insertPlainText(l.content);if(!b(d,"paste_text_sticky")){d.pasteAsPlainText=false;d.controlManager.setActive("pastetext",false)}}else{f._insert(l.content)}}d.addCommand("mceInsertClipboardContent",function(i,j){h(j,true)});if(!b(d,"paste_text_use_dialog")){d.addCommand("mcePasteText",function(j,i){var k=tinymce.util.Cookie;d.pasteAsPlainText=!d.pasteAsPlainText;d.controlManager.setActive("pastetext",d.pasteAsPlainText);if((d.pasteAsPlainText)&&(!k.get("tinymcePasteText"))){if(b(d,"paste_text_sticky")){d.windowManager.alert(d.translate("paste.plaintext_mode_sticky"))}else{d.windowManager.alert(d.translate("paste.plaintext_mode"))}if(!b(d,"paste_text_notifyalways")){k.set("tinymcePasteText","1",new Date(new Date().getFullYear()+1,12,31))}}})}d.addButton("pastetext",{title:"paste.paste_text_desc",cmd:"mcePasteText"});d.addButton("selectall",{title:"paste.selectall_desc",cmd:"selectall"});function g(s){var l,p,j,t,k=d.selection,o=d.dom,q=d.getBody(),i,r;if(s.clipboardData||o.doc.dataTransfer){r=(s.clipboardData||o.doc.dataTransfer).getData("Text");if(d.pasteAsPlainText){s.preventDefault();h({content:o.encode(r).replace(/\r?\n/g,"<br />")});return}}if(o.get("_mcePaste")){return}l=o.add(q,"div",{id:"_mcePaste","class":"mcePaste","data-mce-bogus":"1"},"\uFEFF\uFEFF");if(q!=d.getDoc().body){i=o.getPos(d.selection.getStart(),q).y}else{i=q.scrollTop+o.getViewPort(d.getWin()).y}o.setStyles(l,{position:"absolute",left:tinymce.isGecko?-40:0,top:i-25,width:1,height:1,overflow:"hidden"});if(tinymce.isIE){t=k.getRng();j=o.doc.body.createTextRange();j.moveToElementText(l);j.execCommand("Paste");o.remove(l);if(l.innerHTML==="\uFEFF\uFEFF"){d.execCommand("mcePasteWord");s.preventDefault();return}k.setRng(t);k.setContent("");setTimeout(function(){h({content:l.innerHTML})},0);return tinymce.dom.Event.cancel(s)}else{function m(n){n.preventDefault()}o.bind(d.getDoc(),"mousedown",m);o.bind(d.getDoc(),"keydown",m);p=d.selection.getRng();l=l.firstChild;j=d.getDoc().createRange();j.setStart(l,0);j.setEnd(l,2);k.setRng(j);window.setTimeout(function(){var u="",n;if(!o.select("div.mcePaste > div.mcePaste").length){n=o.select("div.mcePaste");c(n,function(w){var v=w.firstChild;if(v&&v.nodeName=="DIV"&&v.style.marginTop&&v.style.backgroundColor){o.remove(v,1)}c(o.select("span.Apple-style-span",w),function(x){o.remove(x,1)});c(o.select("br[data-mce-bogus]",w),function(x){o.remove(x)});if(w.parentNode.className!="mcePaste"){u+=w.innerHTML}})}else{u="<p>"+o.encode(r).replace(/\r?\n\r?\n/g,"</p><p>").replace(/\r?\n/g,"<br />")+"</p>"}c(o.select("div.mcePaste"),function(v){o.remove(v)});if(p){k.setRng(p)}h({content:u});o.unbind(d.getDoc(),"mousedown",m);o.unbind(d.getDoc(),"keydown",m)},0)}}if(b(d,"paste_auto_cleanup_on_paste")){if(tinymce.isOpera||/Firefox\/2/.test(navigator.userAgent)){d.onKeyDown.addToTop(function(i,j){if(((tinymce.isMac?j.metaKey:j.ctrlKey)&&j.keyCode==86)||(j.shiftKey&&j.keyCode==45)){g(j)}})}else{d.onPaste.addToTop(function(i,j){return g(j)})}}d.onInit.add(function(){d.controlManager.setActive("pastetext",d.pasteAsPlainText);if(b(d,"paste_block_drop")){d.dom.bind(d.getBody(),["dragend","dragover","draggesture","dragdrop","drop","drag"],function(i){i.preventDefault();i.stopPropagation();return false})}});f._legacySupport()},getInfo:function(){return{longname:"Paste text/word",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/paste",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_preProcess:function(g,e){var k=this.editor,j=e.content,p=tinymce.grep,n=tinymce.explode,f=tinymce.trim,l,i;function d(h){c(h,function(o){if(o.constructor==RegExp){j=j.replace(o,"")}else{j=j.replace(o[0],o[1])}})}if(k.settings.paste_enable_default_filters==false){return}if(tinymce.isIE&&document.documentMode>=9&&/<(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)/.test(e.content)){d([[/(?:<br>&nbsp;[\s\r\n]+|<br>)*(<\/?(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)[^>]*>)(?:<br>&nbsp;[\s\r\n]+|<br>)*/g,"$1"]]);d([[/<br><br>/g,"<BR><BR>"],[/<br>/g," "],[/<BR><BR>/g,"<br>"]])}if(/class="?Mso|style="[^"]*\bmso-|w:WordDocument/i.test(j)||e.wordContent){e.wordContent=true;d([/^\s*(&nbsp;)+/gi,/(&nbsp;|<br[^>]*>)+\s*$/gi]);if(b(k,"paste_convert_headers_to_strong")){j=j.replace(/<p [^>]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi,"<p><strong>$1</strong></p>")}if(b(k,"paste_convert_middot_lists")){d([[/<!--\[if !supportLists\]-->/gi,"$&__MCE_ITEM__"],[/(<span[^>]+(?:mso-list:|:\s*symbol)[^>]+>)/gi,"$1__MCE_ITEM__"],[/(<p[^>]+(?:MsoListParagraph)[^>]+>)/gi,"$1__MCE_ITEM__"]])}d([/<!--[\s\S]+?-->/gi,/<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi,[/<(\/?)s>/gi,"<$1strike>"],[/&nbsp;/gi,"\u00a0"]]);do{l=j.length;j=j.replace(/(<?!(ol|ul)[^>]*\s)(?:id|name|language|type|on\w+|\w+:\w+)=(?:"[^"]*"|\w+)\s?/gi,"$1");j=j.replace(/(<(ol|ul)[^>]*\s)(?:id|name|language|on\w+|\w+:\w+)=(?:"[^"]*"|\w+)\s?/gi,"$1")}while(l!=j.length);if(b(k,"paste_retain_style_properties").replace(/^none$/i,"").length==0){j=j.replace(/<\/?span[^>]*>/gi,"")}else{d([[/<span\s+style\s*=\s*"\s*mso-spacerun\s*:\s*yes\s*;?\s*"\s*>([\s\u00a0]*)<\/span>/gi,function(o,h){return(h.length>0)?h.replace(/./," ").slice(Math.floor(h.length/2)).split("").join("\u00a0"):""}],[/(<[a-z][^>]*)\sstyle="([^"]*)"/gi,function(t,h,r){var u=[],o=0,q=n(f(r).replace(/&quot;/gi,"'"),";");c(q,function(s){var w,y,z=n(s,":");function x(A){return A+((A!=="0")&&(/\d$/.test(A)))?"px":""}if(z.length==2){w=z[0].toLowerCase();y=z[1].toLowerCase();switch(w){case"mso-padding-alt":case"mso-padding-top-alt":case"mso-padding-right-alt":case"mso-padding-bottom-alt":case"mso-padding-left-alt":case"mso-margin-alt":case"mso-margin-top-alt":case"mso-margin-right-alt":case"mso-margin-bottom-alt":case"mso-margin-left-alt":case"mso-table-layout-alt":case"mso-height":case"mso-width":case"mso-vertical-align-alt":u[o++]=w.replace(/^mso-|-alt$/g,"")+":"+x(y);return;case"horiz-align":u[o++]="text-align:"+y;return;case"vert-align":u[o++]="vertical-align:"+y;return;case"font-color":case"mso-foreground":u[o++]="color:"+y;return;case"mso-background":case"mso-highlight":u[o++]="background:"+y;return;case"mso-default-height":u[o++]="min-height:"+x(y);return;case"mso-default-width":u[o++]="min-width:"+x(y);return;case"mso-padding-between-alt":u[o++]="border-collapse:separate;border-spacing:"+x(y);return;case"text-line-through":if((y=="single")||(y=="double")){u[o++]="text-decoration:line-through"}return;case"mso-zero-height":if(y=="yes"){u[o++]="display:none"}return}if(/^(mso|column|font-emph|lang|layout|line-break|list-image|nav|panose|punct|row|ruby|sep|size|src|tab-|table-border|text-(?!align|decor|indent|trans)|top-bar|version|vnd|word-break)/.test(w)){return}u[o++]=w+":"+z[1]}});if(o>0){return h+' style="'+u.join(";")+'"'}else{return h}}]])}}if(b(k,"paste_convert_headers_to_strong")){d([[/<h[1-6][^>]*>/gi,"<p><strong>"],[/<\/h[1-6][^>]*>/gi,"</strong></p>"]])}d([[/Version:[\d.]+\nStartHTML:\d+\nEndHTML:\d+\nStartFragment:\d+\nEndFragment:\d+/gi,""]]);i=b(k,"paste_strip_class_attributes");if(i!=="none"){function m(q,o){if(i==="all"){return""}var h=p(n(o.replace(/^(["'])(.*)\1$/,"$2")," "),function(r){return(/^(?!mso)/i.test(r))});return h.length?' class="'+h.join(" ")+'"':""}j=j.replace(/ class="([^"]+)"/gi,m);j=j.replace(/ class=([\-\w]+)/gi,m)}if(b(k,"paste_remove_spans")){j=j.replace(/<\/?span[^>]*>/gi,"")}e.content=j},_postProcess:function(g,i){var f=this,e=f.editor,h=e.dom,d;if(e.settings.paste_enable_default_filters==false){return}if(i.wordContent){c(h.select("a",i.node),function(j){if(!j.href||j.href.indexOf("#_Toc")!=-1){h.remove(j,1)}});if(b(e,"paste_convert_middot_lists")){f._convertLists(g,i)}d=b(e,"paste_retain_style_properties");if((tinymce.is(d,"string"))&&(d!=="all")&&(d!=="*")){d=tinymce.explode(d.replace(/^none$/i,""));c(h.select("*",i.node),function(m){var n={},k=0,l,o,j;if(d){for(l=0;l<d.length;l++){o=d[l];j=h.getStyle(m,o);if(j){n[o]=j;k++}}}h.setAttrib(m,"style","");if(d&&k>0){h.setStyles(m,n)}else{if(m.nodeName=="SPAN"&&!m.className){h.remove(m,true)}}})}}if(b(e,"paste_remove_styles")||(b(e,"paste_remove_styles_if_webkit")&&tinymce.isWebKit)){c(h.select("*[style]",i.node),function(j){j.removeAttribute("style");j.removeAttribute("data-mce-style")})}else{if(tinymce.isWebKit){c(h.select("*",i.node),function(j){j.removeAttribute("data-mce-style")})}}},_convertLists:function(g,e){var i=g.editor.dom,h,l,d=-1,f,m=[],k,j;c(i.select("p",e.node),function(t){var q,u="",s,r,n,o;for(q=t.firstChild;q&&q.nodeType==3;q=q.nextSibling){u+=q.nodeValue}u=t.innerHTML.replace(/<\/?\w+[^>]*>/gi,"").replace(/&nbsp;/g,"\u00a0");if(/^(__MCE_ITEM__)+[\u2022\u00b7\u00a7\u00d8o\u25CF]\s*\u00a0*/.test(u)){s="ul"}if(/^__MCE_ITEM__\s*\w+\.\s*\u00a0+/.test(u)){s="ol"}if(s){f=parseFloat(t.style.marginLeft||0);if(f>d){m.push(f)}if(!h||s!=k){h=i.create(s);i.insertAfter(h,t)}else{if(f>d){h=l.appendChild(i.create(s))}else{if(f<d){n=tinymce.inArray(m,f);o=i.getParents(h.parentNode,s);h=o[o.length-1-n]||h}}}c(i.select("span",t),function(v){var p=v.innerHTML.replace(/<\/?\w+[^>]*>/gi,"");if(s=="ul"&&/^__MCE_ITEM__[\u2022\u00b7\u00a7\u00d8o\u25CF]/.test(p)){i.remove(v)}else{if(/^__MCE_ITEM__[\s\S]*\w+\.(&nbsp;|\u00a0)*\s*/.test(p)){i.remove(v)}}});r=t.innerHTML;if(s=="ul"){r=t.innerHTML.replace(/__MCE_ITEM__/g,"").replace(/^[\u2022\u00b7\u00a7\u00d8o\u25CF]\s*(&nbsp;|\u00a0)+\s*/,"")}else{r=t.innerHTML.replace(/__MCE_ITEM__/g,"").replace(/^\s*[\w|'<'|'>']+\.(&nbsp;|\u00a0)+\s*/,"")}l=h.appendChild(i.create("li",0,r));i.remove(t);d=f;k=s}else{h=d=0}});j=e.node.innerHTML;if(j.indexOf("__MCE_ITEM__")!=-1){e.node.innerHTML=j.replace(/__MCE_ITEM__/g,"")}},_insert:function(f,d){var e=this.editor,g=e.selection.getRng();if(!e.selection.isCollapsed()&&g.startContainer!=g.endContainer){e.getDoc().execCommand("Delete",false,null)}e.execCommand("mceInsertContent",false,f,{skip_undo:d})},_insertPlainText:function(j){var h=this.editor,f=b(h,"paste_text_linebreaktype"),k=b(h,"paste_text_replacements"),g=tinymce.is;function e(m){c(m,function(n){if(n.constructor==RegExp){j=j.replace(n,"")}else{j=j.replace(n[0],n[1])}})}if((typeof(j)==="string")&&(j.length>0)){if(/<(?:p|br|h[1-6]|ul|ol|dl|table|t[rdh]|div|blockquote|fieldset|pre|address|center)[^>]*>/i.test(j)){e([/[\n\r]+/g])}else{e([/\r+/g])}e([[/<\/(?:p|h[1-6]|ul|ol|dl|table|div|blockquote|fieldset|pre|address|center)>/gi,"\n\n"],[/<br[^>]*>|<\/tr>/gi,"\n"],[/<\/t[dh]>\s*<t[dh][^>]*>/gi,"\t"],/<[a-z!\/?][^>]*>/gi,[/&nbsp;/gi," "],[/(?:(?!\n)\s)*(\n+)(?:(?!\n)\s)*/gi,"$1"]]);var d=Number(b(h,"paste_max_consecutive_linebreaks"));if(d>-1){var l=new RegExp("\n{"+(d+1)+",}","g");var i="";while(i.length<d){i+="\n"}e([[l,i]])}j=h.dom.decode(tinymce.html.Entities.encodeRaw(j));if(g(k,"array")){e(k)}else{if(g(k,"string")){e(new RegExp(k,"gi"))}}if(f=="none"){e([[/\n+/g," "]])}else{if(f=="br"){e([[/\n/g,"<br />"]])}else{if(f=="p"){e([[/\n+/g,"</p><p>"],[/^(.*<\/p>)(<p>)$/,"<p>$1"]])}else{e([[/\n\n/g,"</p><p>"],[/^(.*<\/p>)(<p>)$/,"<p>$1"],[/\n/g,"<br />"]])}}}h.execCommand("mceInsertContent",false,j)}},_legacySupport:function(){var e=this,d=e.editor;d.addCommand("mcePasteWord",function(){d.windowManager.open({file:e.url+"/pasteword.htm",width:parseInt(b(d,"paste_dialog_width")),height:parseInt(b(d,"paste_dialog_height")),inline:1})});if(b(d,"paste_text_use_dialog")){d.addCommand("mcePasteText",function(){d.windowManager.open({file:e.url+"/pastetext.htm",width:parseInt(b(d,"paste_dialog_width")),height:parseInt(b(d,"paste_dialog_height")),inline:1})})}d.addButton("pasteword",{title:"paste.paste_word_desc",cmd:"mcePasteWord"})}});tinymce.PluginManager.add("paste",tinymce.plugins.PastePlugin)})();
(function(){var a=tinymce.explode("id,name,style,align,class,hspace,vspace,bgcolor,type"),c=tinymce.makeMap(a.join(",")),d=a.concat(tinymce.explode("width, height")),b=tinymce.makeMap(d.join(",")),h=tinymce.html.Node,g,j,i=tinymce.util.JSON;g=[["Flash","d27cdb6e-ae6d-11cf-96b8-444553540000","application/x-shockwave-flash","http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"],["ShockWave","166b1bca-3f9c-11cf-8075-444553540000","application/x-director","http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab#version=8,5,1,0"],["WindowsMedia","6bf52a52-394a-11d3-b153-00c04f79faa6,22d6f312-b0f6-11d0-94ab-0080c74c7e95,05589fa1-c356-11ce-bf01-00aa0055595a","application/x-mplayer2","http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701"],["QuickTime","02bf25d5-8c17-4b23-bc80-d3488abddc6b","video/quicktime","http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0"],["RealMedia","cfcdaa03-8be4-11cf-b84b-0020afbbccfa","audio/x-pn-realaudio-plugin","http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"],["Java","8ad9c840-044e-11d1-b3e9-00805f499d93","application/x-java-applet","http://java.sun.com/products/plugin/autodl/jinstall-1_5_0-windows-i586.cab#Version=1,5,0,0"],["Silverlight","dfeaf541-f3e1-4c24-acac-99c30715084a","application/x-silverlight-2"],["Iframe"],["Video"],["EmbeddedAudio"],["Audio"],["Object"]];function f(k){return typeof(k)=="string"?k.replace(/[^0-9%]/g,""):k}function e(m){var k,l;if(m&&!m.splice){k=[];for(l=0;true;l++){if(m[l]){k[l]=m[l]}else{break}}return k}return m}tinymce.create("tinymce.plugins.MediaPlugin",{init:function(o,k){var s=this,m={},n,q,r,l;function p(t){return t&&t.nodeName==="IMG"&&o.dom.hasClass(t,"mceItemMedia")}s.editor=o;s.url=k;j="";for(n=0;n<g.length;n++){l=g[n][0];r={name:l,clsids:tinymce.explode(g[n][1]||""),mimes:tinymce.explode(g[n][2]||""),codebase:g[n][3]};for(q=0;q<r.clsids.length;q++){m["clsid:"+r.clsids[q]]=r}for(q=0;q<r.mimes.length;q++){m[r.mimes[q]]=r}m["mceItem"+l]=r;m[l.toLowerCase()]=r;j+=(j?"|":"")+l}tinymce.each(o.getParam("media_types","video=mp4,m4v,ogv,webm;silverlight=xap;flash=swf,flv;shockwave=dcr;quicktime=mov,qt,mpg,mpeg;shockwave=dcr;windowsmedia=avi,wmv,wm,asf,asx,wmx,wvx;realmedia=rm,ra,ram;java=jar;audio=mp3,ogg").split(";"),function(w){var t,v,u;w=w.split(/=/);v=tinymce.explode(w[1].toLowerCase());for(t=0;t<v.length;t++){u=m[w[0].toLowerCase()];if(u){m[v[t]]=u}}});j=new RegExp("write("+j+")\\(([^)]+)\\)");s.lookup=m;o.onPreInit.add(function(){o.schema.addValidElements("object[id|style|width|height|classid|codebase|*],param[name|value],embed[id|style|width|height|type|src|*],video[*],audio[*],source[*]");o.parser.addNodeFilter("object,embed,video,audio,script,iframe",function(t){var u=t.length;while(u--){s.objectToImg(t[u])}});o.serializer.addNodeFilter("img",function(t,v,u){var w=t.length,x;while(w--){x=t[w];if((x.attr("class")||"").indexOf("mceItemMedia")!==-1){s.imgToObject(x,u)}}})});o.onInit.add(function(){if(o.theme&&o.theme.onResolveName){o.theme.onResolveName.add(function(t,u){if(u.name==="img"&&o.dom.hasClass(u.node,"mceItemMedia")){u.name="media"}})}if(o&&o.plugins.contextmenu){o.plugins.contextmenu.onContextMenu.add(function(u,v,t){if(t.nodeName==="IMG"&&t.className.indexOf("mceItemMedia")!==-1){v.add({title:"media.edit",icon:"media",cmd:"mceMedia"})}})}});o.addCommand("mceMedia",function(){var u,t;t=o.selection.getNode();if(p(t)){u=o.dom.getAttrib(t,"data-mce-json");if(u){u=i.parse(u);tinymce.each(d,function(v){var w=o.dom.getAttrib(t,v);if(w){u[v]=w}});u.type=s.getType(t.className).name.toLowerCase()}}if(!u){u={type:"flash",video:{sources:[]},params:{}}}o.windowManager.open({file:k+"/media.htm",width:430+parseInt(o.getLang("media.delta_width",0)),height:500+parseInt(o.getLang("media.delta_height",0)),inline:1},{plugin_url:k,data:u})});o.addButton("media",{title:"media.desc",cmd:"mceMedia"});o.onNodeChange.add(function(u,t,v){t.setActive("media",p(v))})},convertUrl:function(m,p){var l=this,o=l.editor,n=o.settings,q=n.url_converter,k=n.url_converter_scope||l;if(!m){return m}if(p){return o.documentBaseURI.toAbsolute(m)}return q.call(k,m,"src","object")},getInfo:function(){return{longname:"Media",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/media",version:tinymce.majorVersion+"."+tinymce.minorVersion}},dataToImg:function(p,q){var l=this,n,m,k,o;p.params.src=l.convertUrl(p.params.src,q);m=p.video.attrs;if(m){m.src=l.convertUrl(m.src,q)}if(m){m.poster=l.convertUrl(m.poster,q)}n=e(p.video.sources);if(n){for(o=0;o<n.length;o++){n[o].src=l.convertUrl(n[o].src,q)}}k=l.editor.dom.create("img",{id:p.id,style:p.style,align:p.align,hspace:p.hspace,vspace:p.vspace,src:l.editor.theme.url+"/img/trans.gif","class":"mceItemMedia mceItem"+l.getType(p.type).name,"data-mce-json":i.serialize(p,"'")});k.width=p.width=f(p.width||(p.type=="audio"?"300":"320"));k.height=p.height=f(p.height||(p.type=="audio"?"32":"240"));return k},dataToHtml:function(k,l){return this.editor.serializer.serialize(this.dataToImg(k,l),{forced_root_block:"",force_absolute:l})},htmlToData:function(m){var l,k,n;n={type:"flash",video:{sources:[]},params:{}};l=this.editor.parser.parse(m);k=l.getAll("img")[0];if(k){n=i.parse(k.attr("data-mce-json"));n.type=this.getType(k.attr("class")).name.toLowerCase();tinymce.each(d,function(o){var p=k.attr(o);if(p){n[o]=p}})}return n},getType:function(n){var l,k,m;k=tinymce.explode(n," ");for(l=0;l<k.length;l++){m=this.lookup[k[l]];if(m){return m}}},imgToObject:function(z,p){var u=this,q=u.editor,D,G,k,H,y,F,w,l,t,r,B,o,A,m,C,E;function s(n,I){var M,L,N,K,J;J=q.getParam("flash_video_player_url",u.convertUrl(u.url+"/moxieplayer.swf"));if(J){M=q.documentBaseURI;F.params.src=J;if(q.getParam("flash_video_player_absvideourl",true)){n=M.toAbsolute(n||"",true);I=M.toAbsolute(I||"",true)}N="";L=q.getParam("flash_video_player_flashvars",{url:"$url",poster:"$poster"});tinymce.each(L,function(P,O){P=P.replace(/\$url/,n||"");P=P.replace(/\$poster/,I||"");if(P.length>0){N+=(N?"&":"")+O+"="+escape(P)}});if(N.length){F.params.flashvars=N}K=q.getParam("flash_video_player_params",{allowfullscreen:true,allowscriptaccess:true});tinymce.each(K,function(P,O){F.params[O]=""+P})}}function x(K,J,I,L){G=new h("object",1).attr({id:K.attr("id"),width:f(K.attr("width")),height:f(K.attr("height")),style:J});tinymce.each(I,function(N){var O=L[N];if(N=="class"&&O){O=O.replace(/mceItem.+ ?/g,"")}if(O&&N!="type"){G.attr(N,O)}});for(var n in L.params){var M;M=new h("param",1);M.shortEnded=true;y=L.params[n];if(n==="src"&&r.name==="WindowsMedia"){n="url"}M.attr({name:n,value:y});G.append(M)}if(L.object_html){y=new h("#text",3);y.raw=true;y.value=L.object_html;G.append(y)}if(D){D.append(G)}return G}F=z.attr("data-mce-json");if(!F){return}F=i.parse(F);r=this.getType(z.attr("class"));C=z.attr("data-mce-style");if(!C){C=z.attr("style");if(C){C=q.dom.serializeStyle(q.dom.parseStyle(C,"img"))}}F.width=z.attr("width")||F.width;F.height=z.attr("height")||F.height;if(r.name==="Iframe"){A=new h("iframe",1);tinymce.each(d,function(n){var I=z.attr(n);if(n=="class"&&I){I=I.replace(/mceItem.+ ?/g,"")}if(I&&I.length>0){A.attr(n,I)}});for(H in F.params){A.attr(H,F.params[H])}A.attr({style:C,src:F.params.src});z.replace(A);return}if(this.editor.settings.media_use_script){A=new h("script",1).attr("type","text/javascript");y=new h("#text",3);y.value="write"+r.name+"("+i.serialize(tinymce.extend(F.params,{width:z.attr("width"),height:z.attr("height")}))+");";A.append(y);z.replace(A);return}if(r.name==="Video"&&F.video.sources[0]){D=new h("video",1).attr(tinymce.extend({id:z.attr("id"),width:f(z.attr("width")),height:f(z.attr("height")),style:C},F.video.attrs));if(F.video.attrs){m=F.video.attrs.poster}l=F.video.sources=e(F.video.sources);for(B=0;B<l.length;B++){if(/\.mp4$/.test(l[B].src)){o=l[B].src}}if(!l[0].type){D.attr("src",l[0].src);l.splice(0,1)}for(B=0;B<l.length;B++){w=new h("source",1).attr(l[B]);w.shortEnded=true;D.append(w)}if(o){s(o,m);r=u.getType("flash")}else{F.params.src=""}}if(r.name==="Audio"&&F.video.sources[0]){E=new h("audio",1).attr(tinymce.extend({id:z.attr("id"),width:f(z.attr("width")),height:f(z.attr("height")),style:C},F.video.attrs));if(F.video.attrs){m=F.video.attrs.poster}l=F.video.sources=e(F.video.sources);if(!l[0].type){E.attr("src",l[0].src);l.splice(0,1)}for(B=0;B<l.length;B++){w=new h("source",1).attr(l[B]);w.shortEnded=true;E.append(w)}F.params.src=""}if(r.name==="EmbeddedAudio"){k=new h("embed",1);k.shortEnded=true;k.attr({id:z.attr("id"),width:f(z.attr("width")),height:f(z.attr("height")),style:C,type:z.attr("type")});for(H in F.params){k.attr(H,F.params[H])}tinymce.each(d,function(n){if(F[n]&&n!="type"){k.attr(n,F[n])}});F.params.src=""}if(F.params.src){if(/\.flv$/i.test(F.params.src)){s(F.params.src,"")}if(p&&p.force_absolute){F.params.src=q.documentBaseURI.toAbsolute(F.params.src)}G=x(z,C,d,F,D);if(this.editor.getParam("media_strict",true)){G.attr({data:F.params.src,type:r.mimes[0]})}else{if(r.clsids[0]){G.attr({classid:"clsid:"+r.clsids[0],codebase:r.codebase})}k=new h("embed",1);k.shortEnded=true;k.attr({id:z.attr("id"),width:f(z.attr("width")),height:f(z.attr("height")),style:C,type:r.mimes[0]});for(H in F.params){k.attr(H,F.params[H])}tinymce.each(d,function(n){if(F[n]&&n!="type"){k.attr(n,F[n])}});G.append(k)}}else{if(r.name==="Object"){delete F.params.src;G=x(z,C,d,F,D)}}if(D){if(F.video_html){y=new h("#text",3);y.raw=true;y.value=F.video_html;D.append(y)}}if(E){if(F.video_html){y=new h("#text",3);y.raw=true;y.value=F.video_html;E.append(y)}}var v=D||E||G||k;if(v){z.replace(v)}else{z.remove()}},objectToImg:function(D){var M,l,G,t,N,O,z,B,y,H,F,u,r,J,C,m,L,p,I=this.lookup,n,A,w=this.editor.settings.url_converter,o=this.editor.settings.url_converter_scope,x,s,E,k;function v(P){return new tinymce.html.Serializer({inner:true,validate:false}).serialize(P)}function K(Q,P){return I[(Q.attr(P)||"").toLowerCase()]}function q(Q){var P=Q.replace(/^.*\.([^.]+)$/,"$1");return I[P.toLowerCase()||""]}if(!D.parent){return}if(D.name==="script"){if(D.firstChild){n=j.exec(D.firstChild.value)}if(!n){return}p=n[1];L={video:{},params:i.parse(n[2])};B=L.params.width;y=L.params.height}L=L||{video:{},params:{}};N=new h("img",1);N.attr({src:this.editor.theme.url+"/img/trans.gif"});O=D.name;if(O==="video"||O=="audio"){G=D;M=D.getAll("object")[0];l=D.getAll("embed")[0];B=G.attr("width");y=G.attr("height");z=G.attr("id");L.video={attrs:{},sources:[]};A=L.video.attrs;for(O in G.attributes.map){A[O]=G.attributes.map[O]}C=D.attr("src");if(C){L.video.sources.push({src:w.call(o,C,"src",D.name)})}m=G.getAll("source");for(F=0;F<m.length;F++){C=m[F].remove();L.video.sources.push({src:w.call(o,C.attr("src"),"src","source"),type:C.attr("type"),media:C.attr("media")})}if(A.poster){A.poster=w.call(o,A.poster,"poster",D.name)}}if(D.name==="object"){M=D;l=D.getAll("embed")[0]}if(D.name==="embed"){l=D}if(D.name==="iframe"){t=D;p="Iframe"}if(M){H=H||M.attr("style");z=z||M.attr("id");x=x||M.attr("hspace");s=s||M.attr("vspace");E=E||M.attr("align");k=k||M.attr("bgcolor");L.name=M.attr("name");L["class"]=M.attr("class");J=M.getAll("param");for(F=0;F<J.length;F++){r=J[F];O=r.remove().attr("name");if(!c[O]){L.params[O]=r.attr("value")}}B=B||M.attr("width")||L.params.width;y=y||M.attr("height")||L.params.height;L.params.src=L.params.src||M.attr("data")}if(l){B=B||l.attr("width");y=y||l.attr("height");H=H||l.attr("style");z=z||l.attr("id");x=x||l.attr("hspace");s=s||l.attr("vspace");E=E||l.attr("align");k=k||l.attr("bgcolor");for(O in l.attributes.map){if(!b[O]&&!L.params[O]){L.params[O]=l.attributes.map[O]}}}if(t){B=f(t.attr("width"));y=f(t.attr("height"));H=H||t.attr("style");z=t.attr("id");x=t.attr("hspace");s=t.attr("vspace");E=t.attr("align");k=t.attr("bgcolor");tinymce.each(d,function(P){N.attr(P,t.attr(P))});for(O in t.attributes.map){if(!b[O]&&!L.params[O]){L.params[O]=t.attributes.map[O]}}}if(L.params.movie){L.params.src=L.params.src||L.params.movie;delete L.params.movie}if(L.params.src){L.params.src=w.call(o,L.params.src,"src","object")}if(G){if(D.name==="video"){p=I.video.name}else{if(D.name==="audio"){p=I.audio.name}}}if(l&&!p){p=(K(l,"type")||q(L.params.src)||{}).name}if(M&&!p){p=(K(M,"clsid")||K(M,"classid")||K(M,"type")||{name:"Object"}).name}if(l&&p=="EmbeddedAudio"){L.params.type=l.attr("type")}D.replace(N);if(l){l.remove()}if(M){u=v(M.remove());if(u){L.object_html=u}}if(G){u=v(G.remove());if(u){L.video_html=u}}L.hspace=x;L.vspace=s;L.align=E;L.bgcolor=k;N.attr({id:z,"class":"mceItemMedia mceItem"+(p||"Flash"),style:H,width:B||(D.name=="audio"?"300":"320"),height:y||(D.name=="audio"?"32":"240"),hspace:x,vspace:s,align:E,bgcolor:k,"data-mce-json":i.serialize(L,"'")})}});tinymce.PluginManager.add("media",tinymce.plugins.MediaPlugin)})();
(function(){var e=tinymce.each,r=tinymce.dom.Event,g;function p(t,s){while(t&&(t.nodeType===8||(t.nodeType===3&&/^[ \t\n\r]*$/.test(t.nodeValue)))){t=s(t)}return t}function b(s){return p(s,function(t){return t.previousSibling})}function i(s){return p(s,function(t){return t.nextSibling})}function d(s,u,t){return s.dom.getParent(u,function(v){return tinymce.inArray(t,v)!==-1})}function n(s){return s&&(s.tagName==="OL"||s.tagName==="UL")}function c(u,v){var t,w,s;t=b(u.lastChild);while(n(t)){w=t;t=b(w.previousSibling)}if(w){s=v.create("li",{style:"list-style-type: none;"});v.split(u,w);v.insertAfter(s,w);s.appendChild(w);s.appendChild(w);u=s.previousSibling}return u}function m(t,s,u){t=a(t,s,u);return o(t,s,u)}function a(u,s,v){var t=b(u.previousSibling);if(t){return h(t,u,s?t:false,v)}else{return u}}function o(u,t,v){var s=i(u.nextSibling);if(s){return h(u,s,t?s:false,v)}else{return u}}function h(u,s,t,v){if(l(u,s,!!t,v)){return f(u,s,t)}else{if(u&&u.tagName==="LI"&&n(s)){u.appendChild(s)}}return s}function l(u,t,s,v){if(!u||!t){return false}else{if(u.tagName==="LI"&&t.tagName==="LI"){return t.style.listStyleType==="none"||j(t)}else{if(n(u)){return(u.tagName===t.tagName&&(s||u.style.listStyleType===t.style.listStyleType))||q(t)}else{return v&&u.tagName==="P"&&t.tagName==="P"}}}}function q(t){var s=i(t.firstChild),u=b(t.lastChild);return s&&u&&n(t)&&s===u&&(n(s)||s.style.listStyleType==="none"||j(s))}function j(u){var t=i(u.firstChild),s=b(u.lastChild);return t&&s&&t===s&&n(t)}function f(w,v,s){var u=b(w.lastChild),t=i(v.firstChild);if(w.tagName==="P"){w.appendChild(w.ownerDocument.createElement("br"))}while(v.firstChild){w.appendChild(v.firstChild)}if(s){w.style.listStyleType=s.style.listStyleType}v.parentNode.removeChild(v);h(u,t,false);return w}function k(t,u){var s;if(!u.is(t,"li,ol,ul")){s=u.getParent(t,"li");if(s){t=s}}return t}tinymce.create("tinymce.plugins.Lists",{init:function(y){var v="TABBING";var s="EMPTY";var J="ESCAPE";var z="PARAGRAPH";var N="UNKNOWN";var x=N;function E(U){return U.keyCode===tinymce.VK.TAB&&!(U.altKey||U.ctrlKey)&&(y.queryCommandState("InsertUnorderedList")||y.queryCommandState("InsertOrderedList"))}function w(){var U=B();var W=U.parentNode.parentNode;var V=U.parentNode.lastChild===U;return V&&!t(W)&&P(U)}function t(U){if(n(U)){return U.parentNode&&U.parentNode.tagName==="LI"}else{return U.tagName==="LI"}}function F(){return y.selection.isCollapsed()&&P(B())}function B(){var U=y.selection.getStart();return((U.tagName=="BR"||U.tagName=="")&&U.parentNode.tagName=="LI")?U.parentNode:U}function P(U){var V=U.childNodes.length;if(U.tagName==="LI"){return V==0?true:V==1&&(U.firstChild.tagName==""||U.firstChild.tagName=="BR"||H(U))}return false}function H(U){var V=tinymce.grep(U.parentNode.childNodes,function(Y){return Y.tagName=="LI"});var W=U==V[V.length-1];var X=U.firstChild;return tinymce.isIE9&&W&&(X.nodeValue==String.fromCharCode(160)||X.nodeValue==String.fromCharCode(32))}function T(U){return U.keyCode===tinymce.VK.ENTER}function A(U){return T(U)&&!U.shiftKey}function M(U){if(E(U)){return v}else{if(A(U)&&w()){return N}else{if(A(U)&&F()){return s}else{return N}}}}function D(U,V){if(x==v||x==s||tinymce.isGecko&&x==J){r.cancel(V)}}function C(){var U=y.selection.getRng(true);var V=U.startContainer;if(V.nodeType==3){var W=V.nodeValue;if(tinymce.isIE9&&W.length>1&&W.charCodeAt(W.length-1)==32){return(U.endOffset==W.length-1)}else{return(U.endOffset==W.length)}}else{if(V.nodeType==1){return U.endOffset==V.childNodes.length}}return false}function I(){var W=y.selection.getNode();var V="h1,h2,h3,h4,h5,h6,p,div";var U=y.dom.is(W,V)&&W.parentNode.tagName==="LI"&&W.parentNode.lastChild===W;return y.selection.isCollapsed()&&U&&C()}function K(W,Y){if(A(Y)&&I()){var X=W.selection.getNode();var V=W.dom.create("li");var U=W.dom.getParent(X,"li");W.dom.insertAfter(V,U);if(tinymce.isIE6||tinymce.isIE7||tinyMCE.isIE8){W.selection.setCursorLocation(V,1)}else{W.selection.setCursorLocation(V,0)}Y.preventDefault()}}function u(X,Z){var ac;if(!tinymce.isGecko){return}var V=X.selection.getStart();if(Z.keyCode!=tinymce.VK.BACKSPACE||V.tagName!=="IMG"){return}function W(ag){var ah=ag.firstChild;var af=null;do{if(!ah){break}if(ah.tagName==="LI"){af=ah}}while(ah=ah.nextSibling);return af}function ae(ag,af){while(ag.childNodes.length>0){af.appendChild(ag.childNodes[0])}}ac=V.parentNode.previousSibling;if(!ac){return}var aa;if(ac.tagName==="UL"||ac.tagName==="OL"){aa=ac}else{if(ac.previousSibling&&(ac.previousSibling.tagName==="UL"||ac.previousSibling.tagName==="OL")){aa=ac.previousSibling}else{return}}var ad=W(aa);var U=X.dom.createRng();U.setStart(ad,1);U.setEnd(ad,1);X.selection.setRng(U);X.selection.collapse(true);var Y=X.selection.getBookmark();var ab=V.parentNode.cloneNode(true);if(ab.tagName==="P"||ab.tagName==="DIV"){ae(ab,ad)}else{ad.appendChild(ab)}V.parentNode.parentNode.removeChild(V.parentNode);X.selection.moveToBookmark(Y)}function G(U){var V=y.dom.getParent(U,"ol,ul");if(V!=null){var W=V.lastChild;y.selection.setCursorLocation(W,0)}}this.ed=y;y.addCommand("Indent",this.indent,this);y.addCommand("Outdent",this.outdent,this);y.addCommand("InsertUnorderedList",function(){this.applyList("UL","OL")},this);y.addCommand("InsertOrderedList",function(){this.applyList("OL","UL")},this);y.onInit.add(function(){y.editorCommands.addCommands({outdent:function(){var V=y.selection,W=y.dom;function U(X){X=W.getParent(X,W.isBlock);return X&&(parseInt(y.dom.getStyle(X,"margin-left")||0,10)+parseInt(y.dom.getStyle(X,"padding-left")||0,10))>0}return U(V.getStart())||U(V.getEnd())||y.queryCommandState("InsertOrderedList")||y.queryCommandState("InsertUnorderedList")}},"state")});y.onKeyUp.add(function(V,W){if(x==v){V.execCommand(W.shiftKey?"Outdent":"Indent",true,null);x=N;return r.cancel(W)}else{if(x==s){var U=B();var Y=V.settings.list_outdent_on_enter===true||W.shiftKey;V.execCommand(Y?"Outdent":"Indent",true,null);if(tinymce.isIE){G(U)}return r.cancel(W)}else{if(x==J){if(tinymce.isIE6||tinymce.isIE7||tinymce.isIE8){var X=V.getDoc().createTextNode("\uFEFF");V.selection.getNode().appendChild(X)}else{if(tinymce.isIE9||tinymce.isGecko){V.execCommand("Outdent");return r.cancel(W)}}}}}});function L(V,U){var W=y.getDoc().createTextNode("\uFEFF");V.insertBefore(W,U);y.selection.setCursorLocation(W,0);y.execCommand("mceRepaint")}function R(V,X){if(T(X)){var U=B();if(U){var W=U.parentNode;var Y=W&&W.parentNode;if(Y&&Y.nodeName=="LI"&&Y.firstChild==W&&U==W.firstChild){L(Y,W)}}}}function S(V,X){if(T(X)){var U=B();if(V.dom.select("ul li",U).length===1){var W=U.firstChild;L(U,W)}}}function Q(W,aa){function X(ab){var ad=[];var ae=new tinymce.dom.TreeWalker(ab.firstChild,ab);for(var ac=ae.current();ac;ac=ae.next()){if(W.dom.is(ac,"ol,ul,li")){ad.push(ac)}}return ad}if(aa.keyCode==tinymce.VK.BACKSPACE){var U=B();if(U){var Z=W.dom.getParent(U,"ol,ul"),V=W.selection.getRng();if(Z&&Z.firstChild===U&&V.startOffset==0){var Y=X(U);Y.unshift(U);W.execCommand("Outdent",false,Y);W.undoManager.add();return r.cancel(aa)}}}}function O(V,X){var U=B();if(X.keyCode===tinymce.VK.BACKSPACE&&V.dom.is(U,"li")&&U.parentNode.firstChild!==U){if(V.dom.select("ul,ol",U).length===1){var Z=U.previousSibling;V.dom.remove(V.dom.select("br",U));V.dom.remove(U,true);var W=tinymce.grep(Z.childNodes,function(aa){return aa.nodeType===3});if(W.length===1){var Y=W[0];V.selection.setCursorLocation(Y,Y.length)}V.undoManager.add();return r.cancel(X)}}}y.onKeyDown.add(function(U,V){x=M(V)});y.onKeyDown.add(D);y.onKeyDown.add(u);y.onKeyDown.add(K);if(tinymce.isGecko){y.onKeyUp.add(R)}if(tinymce.isIE8){y.onKeyUp.add(S)}if(tinymce.isGecko||tinymce.isWebKit){y.onKeyDown.add(Q)}if(tinymce.isWebKit){y.onKeyDown.add(O)}},applyList:function(y,v){var C=this,z=C.ed,I=z.dom,s=[],H=false,u=false,w=false,B,G=z.selection.getSelectedBlocks();function E(t){if(t&&t.tagName==="BR"){I.remove(t)}}function F(M){var N=I.create(y),t;function L(O){if(O.style.marginLeft||O.style.paddingLeft){C.adjustPaddingFunction(false)(O)}}if(M.tagName==="LI"){}else{if(M.tagName==="P"||M.tagName==="DIV"||M.tagName==="BODY"){K(M,function(P,O){J(P,O,M.tagName==="BODY"?null:P.parentNode);t=P.parentNode;L(t);E(O)});if(t){if(t.tagName==="LI"&&(M.tagName==="P"||G.length>1)){I.split(t.parentNode.parentNode,t.parentNode)}m(t.parentNode,true)}return}else{t=I.create("li");I.insertAfter(t,M);t.appendChild(M);L(M);M=t}}I.insertAfter(N,M);N.appendChild(M);m(N,true);s.push(M)}function J(P,L,N){var t,O=P,M;while(!I.isBlock(P.parentNode)&&P.parentNode!==I.getRoot()){P=I.split(P.parentNode,P.previousSibling);P=P.nextSibling;O=P}if(N){t=N.cloneNode(true);P.parentNode.insertBefore(t,P);while(t.firstChild){I.remove(t.firstChild)}t=I.rename(t,"li")}else{t=I.create("li");P.parentNode.insertBefore(t,P)}while(O&&O!=L){M=O.nextSibling;t.appendChild(O);O=M}if(t.childNodes.length===0){t.innerHTML='<br _mce_bogus="1" />'}F(t)}function K(Q,T){var N,R,O=3,L=1,t="br,ul,ol,p,div,h1,h2,h3,h4,h5,h6,table,blockquote,address,pre,form,center,dl";function P(X,U){var V=I.createRng(),W;g.keep=true;z.selection.moveToBookmark(g);g.keep=false;W=z.selection.getRng(true);if(!U){U=X.parentNode.lastChild}V.setStartBefore(X);V.setEndAfter(U);return !(V.compareBoundaryPoints(O,W)>0||V.compareBoundaryPoints(L,W)<=0)}function S(U){if(U.nextSibling){return U.nextSibling}if(!I.isBlock(U.parentNode)&&U.parentNode!==I.getRoot()){return S(U.parentNode)}}N=Q.firstChild;var M=false;e(I.select(t,Q),function(U){if(U.hasAttribute&&U.hasAttribute("_mce_bogus")){return true}if(P(N,U)){I.addClass(U,"_mce_tagged_br");N=S(U)}});M=(N&&P(N,undefined));N=Q.firstChild;e(I.select(t,Q),function(V){var U=S(V);if(V.hasAttribute&&V.hasAttribute("_mce_bogus")){return true}if(I.hasClass(V,"_mce_tagged_br")){T(N,V,R);R=null}else{R=V}N=U});if(M){T(N,undefined,R)}}function D(t){K(t,function(M,L,N){J(M,L);E(L);E(N)})}function A(t){if(tinymce.inArray(s,t)!==-1){return}if(t.parentNode.tagName===v){I.split(t.parentNode,t);F(t);o(t.parentNode,false)}s.push(t)}function x(M){var O,N,L,t;if(tinymce.inArray(s,M)!==-1){return}M=c(M,I);while(I.is(M.parentNode,"ol,ul,li")){I.split(M.parentNode,M)}s.push(M);M=I.rename(M,"p");L=m(M,false,z.settings.force_br_newlines);if(L===M){O=M.firstChild;while(O){if(I.isBlock(O)){O=I.split(O.parentNode,O);t=true;N=O.nextSibling&&O.nextSibling.firstChild}else{N=O.nextSibling;if(t&&O.tagName==="BR"){I.remove(O)}t=false}O=N}}}e(G,function(t){t=k(t,I);if(t.tagName===v||(t.tagName==="LI"&&t.parentNode.tagName===v)){u=true}else{if(t.tagName===y||(t.tagName==="LI"&&t.parentNode.tagName===y)){H=true}else{w=true}}});if(w&&!H||u||G.length===0){B={LI:A,H1:F,H2:F,H3:F,H4:F,H5:F,H6:F,P:F,BODY:F,DIV:G.length>1?F:D,defaultAction:D,elements:this.selectedBlocks()}}else{B={defaultAction:x,elements:this.selectedBlocks(),processEvenIfEmpty:true}}this.process(B)},indent:function(){var u=this.ed,w=u.dom,x=[];function s(z){var y=w.create("li",{style:"list-style-type: none;"});w.insertAfter(y,z);return y}function t(B){var y=s(B),D=w.getParent(B,"ol,ul"),C=D.tagName,E=w.getStyle(D,"list-style-type"),A={},z;if(E!==""){A.style="list-style-type: "+E+";"}z=w.create(C,A);y.appendChild(z);return z}function v(z){if(!d(u,z,x)){z=c(z,w);var y=t(z);y.appendChild(z);m(y.parentNode,false);m(y,false);x.push(z)}}this.process({LI:v,defaultAction:this.adjustPaddingFunction(true),elements:this.selectedBlocks()})},outdent:function(y,x){var w=this,u=w.ed,z=u.dom,s=[];function A(t){var C,B,D;if(!d(u,t,s)){if(z.getStyle(t,"margin-left")!==""||z.getStyle(t,"padding-left")!==""){return w.adjustPaddingFunction(false)(t)}D=z.getStyle(t,"text-align",true);if(D==="center"||D==="right"){z.setStyle(t,"text-align","left");return}t=c(t,z);C=t.parentNode;B=t.parentNode.parentNode;if(B.tagName==="P"){z.split(B,t.parentNode)}else{z.split(C,t);if(B.tagName==="LI"){z.split(B,t)}else{if(!z.is(B,"ol,ul")){z.rename(t,"p")}}}s.push(t)}}var v=x&&tinymce.is(x,"array")?x:this.selectedBlocks();this.process({LI:A,defaultAction:this.adjustPaddingFunction(false),elements:v});e(s,m)},process:function(y){var F=this,w=F.ed.selection,z=F.ed.dom,E,u;function B(t){var s=tinymce.grep(t.childNodes,function(H){return !(H.nodeName==="BR"||H.nodeName==="SPAN"&&z.getAttrib(H,"data-mce-type")=="bookmark"||H.nodeType==3&&(H.nodeValue==String.fromCharCode(160)||H.nodeValue==""))});return s.length===0}function x(s){z.removeClass(s,"_mce_act_on");if(!s||s.nodeType!==1||!y.processEvenIfEmpty&&E.length>1&&B(s)){return}s=k(s,z);var t=y[s.tagName];if(!t){t=y.defaultAction}t(s)}function v(s){F.splitSafeEach(s.childNodes,x,true)}function C(s,t){return t>=0&&s.hasChildNodes()&&t<s.childNodes.length&&s.childNodes[t].tagName==="BR"}function D(){var t=w.getNode();var s=z.getParent(t,"td");return s!==null}E=y.elements;u=w.getRng(true);if(!u.collapsed){if(C(u.endContainer,u.endOffset-1)){u.setEnd(u.endContainer,u.endOffset-1);w.setRng(u)}if(C(u.startContainer,u.startOffset)){u.setStart(u.startContainer,u.startOffset+1);w.setRng(u)}}if(tinymce.isIE8){var G=F.ed.selection.getNode();if(G.tagName==="LI"&&!(G.parentNode.lastChild===G)){var A=F.ed.getDoc().createTextNode("\uFEFF");G.appendChild(A)}}g=w.getBookmark();y.OL=y.UL=v;F.splitSafeEach(E,x);w.moveToBookmark(g);g=null;if(!D()){F.ed.execCommand("mceRepaint")}},splitSafeEach:function(u,t,s){if(s||(tinymce.isGecko&&(/Firefox\/[12]\.[0-9]/.test(navigator.userAgent)||/Firefox\/3\.[0-4]/.test(navigator.userAgent)))){this.classBasedEach(u,t)}else{e(u,t)}},classBasedEach:function(v,u){var w=this.ed.dom,s,t;e(v,function(x){w.addClass(x,"_mce_act_on")});s=w.select("._mce_act_on");while(s.length>0){t=s.shift();w.removeClass(t,"_mce_act_on");u(t);s=w.select("._mce_act_on")}},adjustPaddingFunction:function(u){var s,v,t=this.ed;s=t.settings.indentation;v=/[a-z%]+/i.exec(s);s=parseInt(s,10);return function(w){var y,x;y=parseInt(t.dom.getStyle(w,"margin-left")||0,10)+parseInt(t.dom.getStyle(w,"padding-left")||0,10);if(u){x=y+s}else{x=y-s}t.dom.setStyle(w,"padding-left","");t.dom.setStyle(w,"margin-left",x>0?x+v:"")}},selectedBlocks:function(){var s=this.ed,t=s.selection.getSelectedBlocks();return t.length==0?[s.dom.getRoot()]:t},getInfo:function(){return{longname:"Lists",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/lists",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("lists",tinymce.plugins.Lists)}());
(function(){tinymce.create("tinymce.plugins.InsertDateTime",{init:function(a,b){var c=this;c.editor=a;a.addCommand("mceInsertDate",function(){var d=c._getDateTime(new Date(),a.getParam("plugin_insertdate_dateFormat",a.getLang("insertdatetime.date_fmt")));a.execCommand("mceInsertContent",false,d)});a.addCommand("mceInsertTime",function(){var d=c._getDateTime(new Date(),a.getParam("plugin_insertdate_timeFormat",a.getLang("insertdatetime.time_fmt")));a.execCommand("mceInsertContent",false,d)});a.addButton("insertdate",{title:"insertdatetime.insertdate_desc",cmd:"mceInsertDate"});a.addButton("inserttime",{title:"insertdatetime.inserttime_desc",cmd:"mceInsertTime"})},getInfo:function(){return{longname:"Insert date/time",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/insertdatetime",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_getDateTime:function(e,a){var c=this.editor;function b(g,d){g=""+g;if(g.length<d){for(var f=0;f<(d-g.length);f++){g="0"+g}}return g}a=a.replace("%D","%m/%d/%y");a=a.replace("%r","%I:%M:%S %p");a=a.replace("%Y",""+e.getFullYear());a=a.replace("%y",""+e.getYear());a=a.replace("%m",b(e.getMonth()+1,2));a=a.replace("%d",b(e.getDate(),2));a=a.replace("%H",""+b(e.getHours(),2));a=a.replace("%M",""+b(e.getMinutes(),2));a=a.replace("%S",""+b(e.getSeconds(),2));a=a.replace("%I",""+((e.getHours()+11)%12+1));a=a.replace("%p",""+(e.getHours()<12?"AM":"PM"));a=a.replace("%B",""+c.getLang("insertdatetime.months_long").split(",")[e.getMonth()]);a=a.replace("%b",""+c.getLang("insertdatetime.months_short").split(",")[e.getMonth()]);a=a.replace("%A",""+c.getLang("insertdatetime.day_long").split(",")[e.getDay()]);a=a.replace("%a",""+c.getLang("insertdatetime.day_short").split(",")[e.getDay()]);a=a.replace("%%","%");return a}});tinymce.PluginManager.add("insertdatetime",tinymce.plugins.InsertDateTime)})();
(function(){tinymce.PluginManager.requireLangPack("example");tinymce.create("tinymce.plugins.ExamplePlugin",{init:function(a,b){a.addCommand("mceExample",function(){a.windowManager.open({file:b+"/dialog.htm",width:320+parseInt(a.getLang("example.delta_width",0)),height:120+parseInt(a.getLang("example.delta_height",0)),inline:1},{plugin_url:b,some_custom_arg:"custom arg"})});a.addButton("example",{title:"example.desc",cmd:"mceExample",image:b+"/img/example.gif"});a.onNodeChange.add(function(d,c,e){c.setActive("example",e.nodeName=="IMG")})},createControl:function(b,a){return null},getInfo:function(){return{longname:"Example plugin",author:"Some author",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/example",version:"1.0"}}});tinymce.PluginManager.add("example",tinymce.plugins.ExamplePlugin)})();
(function(){tinymce.create("tinymce.plugins.PageBreakPlugin",{init:function(b,d){var f='<img src="'+b.theme.url+'/img/trans.gif" class="mcePageBreak mceItemNoResize" />',a="mcePageBreak",c=b.getParam("pagebreak_separator","<!-- pagebreak -->"),e;e=new RegExp(c.replace(/[\?\.\*\[\]\(\)\{\}\+\^\$\:]/g,function(g){return"\\"+g}),"g");b.addCommand("mcePageBreak",function(){b.execCommand("mceInsertContent",0,f)});b.addButton("pagebreak",{title:"pagebreak.desc",cmd:a});b.onInit.add(function(){if(b.theme.onResolveName){b.theme.onResolveName.add(function(g,h){if(h.node.nodeName=="IMG"&&b.dom.hasClass(h.node,a)){h.name="pagebreak"}})}});b.onClick.add(function(g,h){h=h.target;if(h.nodeName==="IMG"&&g.dom.hasClass(h,a)){g.selection.select(h)}});b.onNodeChange.add(function(h,g,i){g.setActive("pagebreak",i.nodeName==="IMG"&&h.dom.hasClass(i,a))});b.onBeforeSetContent.add(function(g,h){h.content=h.content.replace(e,f)});b.onPostProcess.add(function(g,h){if(h.get){h.content=h.content.replace(/<img[^>]+>/g,function(i){if(i.indexOf('class="mcePageBreak')!==-1){i=c}return i})}})},getInfo:function(){return{longname:"PageBreak",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/pagebreak",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("pagebreak",tinymce.plugins.PageBreakPlugin)})();
(function(){tinymce.create("tinymce.plugins.ExampleDependencyPlugin",{init:function(a,b){},getInfo:function(){return{longname:"Example Dependency plugin",author:"Some author",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/example_dependency",version:"1.0"}}});tinymce.PluginManager.add("example_dependency",tinymce.plugins.ExampleDependencyPlugin,["example"])})();
(function(){var h=tinymce.dom.TreeWalker;var d="contenteditable",c="data-mce-"+d;var i=tinymce.VK;var e,j,b;function g(t){var p=t.dom,v=t.selection,u="mce_noneditablecaret",x="\uFEFF",m=t.getParam("noneditable_prevent_delete");function s(z){var y;if(z.nodeType===1){y=z.getAttribute(c);if(y&&y!=="inherit"){return y}y=z.contentEditable;if(y!=="inherit"){return y}}return null}function l(y){var z;while(y){z=s(y);if(z){return z==="false"?y:null}y=y.parentNode}}function r(y){while(y){if(y.id===u){return y}y=y.parentNode}}function q(y){var z;if(y){z=new h(y,y);for(y=z.current();y;y=z.next()){if(y.nodeType===3){return y}}}}function k(B,A){var y,z;if(s(B)==="false"){if(p.isBlock(B)){v.select(B);return}}z=p.createRng();if(s(B)==="true"){if(!B.firstChild){B.appendChild(t.getDoc().createTextNode("\u00a0"))}B=B.firstChild;A=true}y=p.create("span",{id:u,"data-mce-bogus":true},x);if(A){B.parentNode.insertBefore(y,B)}else{p.insertAfter(y,B)}z.setStart(y.firstChild,1);z.collapse(true);v.setRng(z);return y}function o(y){var C,A,B,z;if(y){z=v.getRng(true);z.setStartBefore(y);z.setEndBefore(y);C=q(y);if(C&&C.nodeValue.charAt(0)==x){C=C.deleteData(0,1)}p.remove(y,true);v.setRng(z)}else{A=r(v.getStart());while((y=p.get(u))&&y!==B){if(A!==y){C=q(y);if(C&&C.nodeValue.charAt(0)==x){C=C.deleteData(0,1)}p.remove(y,true)}B=y}}}function w(){var y,C,A,z,B;function D(G,I){var F,K,J,H,E;F=z.startContainer;K=z.startOffset;if(F.nodeType==3){E=F.nodeValue.length;if((K>0&&K<E)||(I?K==E:K==0)){return}}else{if(K<F.childNodes.length){var L=!I&&K>0?K-1:K;F=F.childNodes[L];if(F.hasChildNodes()){F=F.firstChild}}else{return !I?G:null}}J=new h(F,G);while(H=J[I?"prev":"next"]()){if(H.nodeType===3&&H.nodeValue.length>0){return}else{if(s(H)==="true"){return H}}}return G}o();A=v.isCollapsed();y=l(v.getStart());C=l(v.getEnd());if(y||C){z=v.getRng(true);if(A){y=y||C;if(D(y,true)){B=D(y,true);k(B,true)}else{if(D(y,false)){B=D(y,false);k(B,false)}else{v.select(y)}}}else{z=v.getRng(true);if(y){z.setStartBefore(y)}if(C){z.setEndAfter(C)}v.setRng(z)}}}function n(D,Q){var G=Q.keyCode,S,J,y,F,A,I=false,z;function B(U,T){while(U=U[T?"previousSibling":"nextSibling"]){if(U.nodeType!==3||U.nodeValue.length>0){return U}}}function K(T,U){v.select(T);v.collapse(U)}function P(X){var W,V,Z,U;function T(ab){var aa=V;while(aa){if(aa===ab){return}aa=aa.parentNode}p.remove(ab);w()}function Y(){var ab,ac,aa=D.schema.getNonEmptyElements();ac=new tinymce.dom.TreeWalker(V,D.getBody());while(ab=(X?ac.prev():ac.next())){if(aa[ab.nodeName.toLowerCase()]){break}if(ab.nodeType===3&&tinymce.trim(ab.nodeValue).length>0){break}if(s(ab)==="false"){if(!m){T(ab)}return true}}if(l(ab)){return true}return false}W=v.getRng(true);V=W.startContainer;Z=W.startOffset;V=r(V)||V;if(v.isCollapsed()){if(U=l(V)){T(U);return false}if(V.nodeType==3&&(X?Z>0:Z<V.nodeValue.length)){return true}if(V.nodeType==1){V=V.childNodes[Z]||V}if(Y()){return false}}if(l(V)){U=l(V);T(U);return false}return true}function O(ab,Z,W){var U;var X=(ab===i.LEFT)||(ab===i.BACKSPACE)?"left":"right";var aa=X==="left"?i.LEFT:i.RIGHT;var T=X==="left"?i.BACKSPACE:i.DELETE;var V=X==="left"?true:false;var Y=v.getRng(true);if(ab===aa||ab===T){U=B(Z,V);if(U&&s(U)==="false"){if(ab===aa){K(U,V)}if(ab===T&&(Z.innerHTML===x||!tinymce.trim(Z.innerText||Z.textContent))){W.preventDefault();K(U,V);if(!m){p.remove(U);return}}}else{if(!m){o(Z)}}}}y=v.getStart();F=v.getEnd();S=l(y)||l(F);if(m&&!v.isCollapsed()){var E=v.getRng(true);var M=E.cloneContents();var I=false;tinymce.walk(M,function(T){I=s(T)==="false";return !I},"childNodes")}if(S&&(G<112||G>124)&&G!=i.DELETE&&G!=i.BACKSPACE){if((tinymce.isMac?Q.metaKey:Q.ctrlKey)&&(G==67||G==88||G==86)){return}Q.preventDefault();if(G==i.LEFT||G==i.RIGHT){var C=G==i.LEFT;if(D.dom.isBlock(S)){var H=C?S.previousSibling:S.nextSibling;var N=new h(H,H);var L=C?N.prev():N.next();K(L,!C)}else{K(S,C)}}}else{if(G==i.LEFT||G==i.RIGHT||G==i.BACKSPACE||G==i.DELETE){J=r(y);if(J){O(G,J,Q)}if((G==i.BACKSPACE||G==i.DELETE)&&!P(G==i.BACKSPACE)){Q.preventDefault();return false}if(m&&I){var R=confirm(D.getLang("noneditable.confirm_delete"));if(!R){Q.preventDefault();return false}}}}}t.onMouseUp.addToTop(w);t.onMouseDown.addToTop(w);t.onKeyDown.addToTop(n);t.onKeyUp.addToTop(w)}function a(k){return" "+tinymce.trim(k.getParam("noneditable_editable_class","mceEditable"))+" "}function f(k){return" "+tinymce.trim(k.getParam("noneditable_noneditable_class","mceNonEditable"))+" "}tinymce.PluginManager.requireLangPack("noneditable");tinymce.create("tinymce.plugins.NonEditablePlugin",{init:function(l){function k(n,o){var p=b.length,q=o.content,m=tinymce.trim(j);if(o.format=="raw"){return}while(p--){q=q.replace(b[p],function(t){var s=arguments,r=s[s.length-2];if(r>0&&q.charAt(r-1)=='"'){return t}return'<span class="'+m+'" data-mce-content="'+n.dom.encode(s[0])+'">'+n.dom.encode(typeof(s[1])==="string"?s[1]:s[0])+"</span>"})}o.content=q}e=a(l);j=f(l);b=l.getParam("noneditable_regexp");if(b&&!b.length){b=[b]}l.onPreInit.add(function(){g(l);if(b){l.selection.onBeforeSetContent.add(k);l.onBeforeSetContent.add(k)}l.parser.addAttributeFilter("class",function(m){var n=m.length,o,p;while(n--){p=m[n];o=" "+p.attr("class")+" ";if(o.indexOf(e)!==-1){p.attr(c,"true")}else{if(o.indexOf(j)!==-1){p.attr(c,"false")}}}});l.serializer.addAttributeFilter(c,function(m){var n=m.length,o;while(n--){o=m[n];if(b&&o.attr("data-mce-content")){o.name="#text";o.type=3;o.raw=true;o.value=o.attr("data-mce-content")}else{o.attr(d,null);o.attr(c,null)}}});l.parser.addAttributeFilter(d,function(m){var n=m.length,o;while(n--){o=m[n];o.attr(c,o.attr(d));o.attr(d,null)}})})},getInfo:function(){return{longname:"Non editable elements",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/noneditable",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("noneditable",tinymce.plugins.NonEditablePlugin)})();
(function(){var b=tinymce.each,a=tinymce.html.Node;tinymce.create("tinymce.plugins.FullPagePlugin",{init:function(c,d){var e=this;e.editor=c;c.addCommand("mceFullPageProperties",function(){c.windowManager.open({file:d+"/fullpage.htm",width:430+parseInt(c.getLang("fullpage.delta_width",0)),height:495+parseInt(c.getLang("fullpage.delta_height",0)),inline:1},{plugin_url:d,data:e._htmlToData()})});c.addButton("fullpage",{title:"fullpage.desc",cmd:"mceFullPageProperties"});c.onBeforeSetContent.add(e._setContent,e);c.onGetContent.add(e._getContent,e)},getInfo:function(){return{longname:"Fullpage",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/fullpage",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_htmlToData:function(){var e=this._parseHeader(),g={},h,f,d=this.editor;function c(k,i){var j=k.attr(i);return j||""}g.fontface=d.getParam("fullpage_default_fontface","");g.fontsize=d.getParam("fullpage_default_fontsize","");h=e.firstChild;if(h.type==7){g.xml_pi=true;f=/encoding="([^"]+)"/.exec(h.value);if(f){g.docencoding=f[1]}}h=e.getAll("#doctype")[0];if(h){g.doctype="<!DOCTYPE"+h.value+">"}h=e.getAll("title")[0];if(h&&h.firstChild){g.metatitle=h.firstChild.value}b(e.getAll("meta"),function(l){var j=l.attr("name"),i=l.attr("http-equiv"),k;if(j){g["meta"+j.toLowerCase()]=l.attr("content")}else{if(i=="Content-Type"){k=/charset\s*=\s*(.*)\s*/gi.exec(l.attr("content"));if(k){g.docencoding=k[1]}}}});h=e.getAll("html")[0];if(h){g.langcode=c(h,"lang")||c(h,"xml:lang")}h=e.getAll("link")[0];if(h&&h.attr("rel")=="stylesheet"){g.stylesheet=h.attr("href")}h=e.getAll("body")[0];if(h){g.langdir=c(h,"dir");g.style=c(h,"style");g.visited_color=c(h,"vlink");g.link_color=c(h,"link");g.active_color=c(h,"alink")}return g},_dataToHtml:function(g){var f,d,h,j,k,e=this.editor.dom;function c(n,l,m){n.attr(l,m?m:undefined)}function i(l){if(d.firstChild){d.insert(l,d.firstChild)}else{d.append(l)}}f=this._parseHeader();d=f.getAll("head")[0];if(!d){j=f.getAll("html")[0];d=new a("head",1);if(j.firstChild){j.insert(d,j.firstChild,true)}else{j.append(d)}}j=f.firstChild;if(g.xml_pi){k='version="1.0"';if(g.docencoding){k+=' encoding="'+g.docencoding+'"'}if(j.type!=7){j=new a("xml",7);f.insert(j,f.firstChild,true)}j.value=k}else{if(j&&j.type==7){j.remove()}}j=f.getAll("#doctype")[0];if(g.doctype){if(!j){j=new a("#doctype",10);if(g.xml_pi){f.insert(j,f.firstChild)}else{i(j)}}j.value=g.doctype.substring(9,g.doctype.length-1)}else{if(j){j.remove()}}j=f.getAll("title")[0];if(g.metatitle){if(!j){j=new a("title",1);j.append(new a("#text",3)).value=g.metatitle;i(j)}}if(g.docencoding){j=null;b(f.getAll("meta"),function(l){if(l.attr("http-equiv")=="Content-Type"){j=l}});if(!j){j=new a("meta",1);j.attr("http-equiv","Content-Type");j.shortEnded=true;i(j)}j.attr("content","text/html; charset="+g.docencoding)}b("keywords,description,author,copyright,robots".split(","),function(m){var l=f.getAll("meta"),n,p,o=g["meta"+m];for(n=0;n<l.length;n++){p=l[n];if(p.attr("name")==m){if(o){p.attr("content",o)}else{p.remove()}return}}if(o){j=new a("meta",1);j.attr("name",m);j.attr("content",o);j.shortEnded=true;i(j)}});j=f.getAll("link")[0];if(j&&j.attr("rel")=="stylesheet"){if(g.stylesheet){j.attr("href",g.stylesheet)}else{j.remove()}}else{if(g.stylesheet){j=new a("link",1);j.attr({rel:"stylesheet",text:"text/css",href:g.stylesheet});j.shortEnded=true;i(j)}}j=f.getAll("body")[0];if(j){c(j,"dir",g.langdir);c(j,"style",g.style);c(j,"vlink",g.visited_color);c(j,"link",g.link_color);c(j,"alink",g.active_color);e.setAttribs(this.editor.getBody(),{style:g.style,dir:g.dir,vLink:g.visited_color,link:g.link_color,aLink:g.active_color})}j=f.getAll("html")[0];if(j){c(j,"lang",g.langcode);c(j,"xml:lang",g.langcode)}h=new tinymce.html.Serializer({validate:false,indent:true,apply_source_formatting:true,indent_before:"head,html,body,meta,title,script,link,style",indent_after:"head,html,body,meta,title,script,link,style"}).serialize(f);this.head=h.substring(0,h.indexOf("</body>"))},_parseHeader:function(){return new tinymce.html.DomParser({validate:false,root_name:"#document"}).parse(this.head)},_setContent:function(g,d){var m=this,i,c,h=d.content,f,l="",e=m.editor.dom,j;function k(n){return n.replace(/<\/?[A-Z]+/g,function(o){return o.toLowerCase()})}if(d.format=="raw"&&m.head){return}if(d.source_view&&g.getParam("fullpage_hide_in_source_view")){return}h=h.replace(/<(\/?)BODY/gi,"<$1body");i=h.indexOf("<body");if(i!=-1){i=h.indexOf(">",i);m.head=k(h.substring(0,i+1));c=h.indexOf("</body",i);if(c==-1){c=h.length}d.content=h.substring(i+1,c);m.foot=k(h.substring(c))}else{m.head=this._getDefaultHeader();m.foot="\n</body>\n</html>"}f=m._parseHeader();b(f.getAll("style"),function(n){if(n.firstChild){l+=n.firstChild.value}});j=f.getAll("body")[0];if(j){e.setAttribs(m.editor.getBody(),{style:j.attr("style")||"",dir:j.attr("dir")||"",vLink:j.attr("vlink")||"",link:j.attr("link")||"",aLink:j.attr("alink")||""})}e.remove("fullpage_styles");if(l){e.add(m.editor.getDoc().getElementsByTagName("head")[0],"style",{id:"fullpage_styles"},l);j=e.get("fullpage_styles");if(j.styleSheet){j.styleSheet.cssText=l}}},_getDefaultHeader:function(){var f="",c=this.editor,e,d="";if(c.getParam("fullpage_default_xml_pi")){f+='<?xml version="1.0" encoding="'+c.getParam("fullpage_default_encoding","ISO-8859-1")+'" ?>\n'}f+=c.getParam("fullpage_default_doctype",'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">');f+="\n<html>\n<head>\n";if(e=c.getParam("fullpage_default_title")){f+="<title>"+e+"</title>\n"}if(e=c.getParam("fullpage_default_encoding")){f+='<meta http-equiv="Content-Type" content="text/html; charset='+e+'" />\n'}if(e=c.getParam("fullpage_default_font_family")){d+="font-family: "+e+";"}if(e=c.getParam("fullpage_default_font_size")){d+="font-size: "+e+";"}if(e=c.getParam("fullpage_default_text_color")){d+="color: "+e+";"}f+="</head>\n<body"+(d?' style="'+d+'"':"")+">\n";return f},_getContent:function(d,e){var c=this;if(!e.source_view||!d.getParam("fullpage_hide_in_source_view")){e.content=tinymce.trim(c.head)+"\n"+tinymce.trim(e.content)+"\n"+tinymce.trim(c.foot)}}});tinymce.PluginManager.add("fullpage",tinymce.plugins.FullPagePlugin)})();
(function(){function a(b){do{if(b.className&&b.className.indexOf("mceItemLayer")!=-1){return b}}while(b=b.parentNode)}tinymce.create("tinymce.plugins.Layer",{init:function(b,c){var d=this;d.editor=b;b.addCommand("mceInsertLayer",d._insertLayer,d);b.addCommand("mceMoveForward",function(){d._move(1)});b.addCommand("mceMoveBackward",function(){d._move(-1)});b.addCommand("mceMakeAbsolute",function(){d._toggleAbsolute()});b.addButton("moveforward",{title:"layer.forward_desc",cmd:"mceMoveForward"});b.addButton("movebackward",{title:"layer.backward_desc",cmd:"mceMoveBackward"});b.addButton("absolute",{title:"layer.absolute_desc",cmd:"mceMakeAbsolute"});b.addButton("insertlayer",{title:"layer.insertlayer_desc",cmd:"mceInsertLayer"});b.onInit.add(function(){var e=b.dom;if(tinymce.isIE){b.getDoc().execCommand("2D-Position",false,true)}});b.onMouseUp.add(function(f,h){var g=a(h.target);if(g){f.dom.setAttrib(g,"data-mce-style","")}});b.onMouseDown.add(function(f,j){var h=j.target,i=f.getDoc(),g;if(tinymce.isGecko){if(a(h)){if(i.designMode!=="on"){i.designMode="on";h=i.body;g=h.parentNode;g.removeChild(h);g.appendChild(h)}}else{if(i.designMode=="on"){i.designMode="off"}}}});b.onNodeChange.add(d._nodeChange,d);b.onVisualAid.add(d._visualAid,d)},getInfo:function(){return{longname:"Layer",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/layer",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_nodeChange:function(c,b,f){var d,e;d=this._getParentLayer(f);e=c.dom.getParent(f,"DIV,P,IMG");if(!e){b.setDisabled("absolute",1);b.setDisabled("moveforward",1);b.setDisabled("movebackward",1)}else{b.setDisabled("absolute",0);b.setDisabled("moveforward",!d);b.setDisabled("movebackward",!d);b.setActive("absolute",d&&d.style.position.toLowerCase()=="absolute")}},_visualAid:function(b,d,c){var f=b.dom;tinymce.each(f.select("div,p",d),function(g){if(/^(absolute|relative|fixed)$/i.test(g.style.position)){if(c){f.addClass(g,"mceItemVisualAid")}else{f.removeClass(g,"mceItemVisualAid")}f.addClass(g,"mceItemLayer")}})},_move:function(j){var c=this.editor,g,h=[],f=this._getParentLayer(c.selection.getNode()),e=-1,k=-1,b;b=[];tinymce.walk(c.getBody(),function(d){if(d.nodeType==1&&/^(absolute|relative|static)$/i.test(d.style.position)){b.push(d)}},"childNodes");for(g=0;g<b.length;g++){h[g]=b[g].style.zIndex?parseInt(b[g].style.zIndex):0;if(e<0&&b[g]==f){e=g}}if(j<0){for(g=0;g<h.length;g++){if(h[g]<h[e]){k=g;break}}if(k>-1){b[e].style.zIndex=h[k];b[k].style.zIndex=h[e]}else{if(h[e]>0){b[e].style.zIndex=h[e]-1}}}else{for(g=0;g<h.length;g++){if(h[g]>h[e]){k=g;break}}if(k>-1){b[e].style.zIndex=h[k];b[k].style.zIndex=h[e]}else{b[e].style.zIndex=h[e]+1}}c.execCommand("mceRepaint")},_getParentLayer:function(b){return this.editor.dom.getParent(b,function(c){return c.nodeType==1&&/^(absolute|relative|static)$/i.test(c.style.position)})},_insertLayer:function(){var c=this.editor,e=c.dom,d=e.getPos(e.getParent(c.selection.getNode(),"*")),b=c.getBody();c.dom.add(b,"div",{style:{position:"absolute",left:d.x,top:(d.y>20?d.y:20),width:100,height:100},"class":"mceItemVisualAid mceItemLayer"},c.selection.getContent()||c.getLang("layer.content"));if(tinymce.isIE){e.setHTML(b,b.innerHTML)}},_toggleAbsolute:function(){var b=this.editor,c=this._getParentLayer(b.selection.getNode());if(!c){c=b.dom.getParent(b.selection.getNode(),"DIV,P,IMG")}if(c){if(c.style.position.toLowerCase()=="absolute"){b.dom.setStyles(c,{position:"",left:"",top:"",width:"",height:""});b.dom.removeClass(c,"mceItemVisualAid");b.dom.removeClass(c,"mceItemLayer")}else{if(c.style.left==""){c.style.left=20+"px"}if(c.style.top==""){c.style.top=20+"px"}if(c.style.width==""){c.style.width=c.width?(c.width+"px"):"100px"}if(c.style.height==""){c.style.height=c.height?(c.height+"px"):"100px"}c.style.position="absolute";b.dom.setAttrib(c,"data-mce-style","");b.addVisual(b.getBody())}b.execCommand("mceRepaint");b.nodeChanged()}}});tinymce.PluginManager.add("layer",tinymce.plugins.Layer)})();
(function(a){a.onAddEditor.addToTop(function(c,b){b.settings.inline_styles=false});a.create("tinymce.plugins.LegacyOutput",{init:function(b){b.onInit.add(function(){var c="p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img",e=a.explode(b.settings.font_size_style_values),d=b.schema;b.formatter.register({alignleft:{selector:c,attributes:{align:"left"}},aligncenter:{selector:c,attributes:{align:"center"}},alignright:{selector:c,attributes:{align:"right"}},alignfull:{selector:c,attributes:{align:"justify"}},bold:[{inline:"b",remove:"all"},{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}}],italic:[{inline:"i",remove:"all"},{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}}],underline:[{inline:"u",remove:"all"},{inline:"span",styles:{textDecoration:"underline"},exact:true}],strikethrough:[{inline:"strike",remove:"all"},{inline:"span",styles:{textDecoration:"line-through"},exact:true}],fontname:{inline:"font",attributes:{face:"%value"}},fontsize:{inline:"font",attributes:{size:function(f){return a.inArray(e,f.value)+1}}},forecolor:{inline:"font",attributes:{color:"%value"}},hilitecolor:{inline:"font",styles:{backgroundColor:"%value"}}});a.each("b,i,u,strike".split(","),function(f){d.addValidElements(f+"[*]")});if(!d.getElementRule("font")){d.addValidElements("font[face|size|color|style]")}a.each(c.split(","),function(f){var h=d.getElementRule(f),g;if(h){if(!h.attributes.align){h.attributes.align={};h.attributesOrder.push("align")}}});b.onNodeChange.add(function(g,k){var j,f,h,i;f=g.dom.getParent(g.selection.getNode(),"font");if(f){h=f.face;i=f.size}if(j=k.get("fontselect")){j.select(function(l){return l==h})}if(j=k.get("fontsizeselect")){j.select(function(m){var l=a.inArray(e,m.fontSize);return l+1==i})}})})},getInfo:function(){return{longname:"LegacyOutput",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/legacyoutput",version:a.majorVersion+"."+a.minorVersion}}});a.PluginManager.add("legacyoutput",a.plugins.LegacyOutput)})(tinymce);
(function(){var b=tinymce.DOM;var a=function(d,f,e){var c=function(g){var i=d.controlManager.get(g);var h=f.controlManager.get(g);if(i&&h){h.displayColor(i.value)}};c("forecolor");c("backcolor");f.setContent(d.getContent({format:"raw"}),{format:"raw"});f.selection.moveToBookmark(e);if(d.plugins.spellchecker&&f.plugins.spellchecker){f.plugins.spellchecker.setLanguage(d.plugins.spellchecker.selectedLang)}};tinymce.create("tinymce.plugins.FullScreenPlugin",{init:function(i,c){var l=this,m={},k=b.doc.documentElement,d,o,h,g,f,e,j;i.addCommand("mceFullScreen",function(){var q,r;if(i.getParam("fullscreen_is_enabled")){if(i.getParam("fullscreen_new_window")){closeFullscreen()}else{b.win.setTimeout(function(){var t=i;var s=tinyMCE.get(t.getParam("fullscreen_editor_id"));s.plugins.fullscreen.saveState(t);tinyMCE.remove(t)},10)}return}if(i.getParam("fullscreen_new_window")){l.fullscreenSettings={bookmark:i.selection.getBookmark()};q=b.win.open(c+"/fullscreen.htm","mceFullScreenPopup","fullscreen=yes,menubar=no,toolbar=no,scrollbars=no,resizable=yes,left=0,top=0,width="+screen.availWidth+",height="+screen.availHeight);try{q.resizeTo(screen.availWidth,screen.availHeight)}catch(p){}}else{o=b.getStyle(b.doc.body,"overflow",1)||"auto";h=b.getStyle(k,"overflow",1);d=b.getViewPort();g=d.x;f=d.y;if(tinymce.isOpera&&o=="visible"){o="auto"}if(tinymce.isIE&&o=="scroll"){o="auto"}if(tinymce.isIE&&(h=="visible"||h=="scroll")){h="auto"}if(o=="0px"){o=""}b.setStyle(b.doc.body,"overflow","hidden");k.style.overflow="hidden";d=b.getViewPort();b.win.scrollTo(0,0);if(tinymce.isIE){d.h-=1}if(tinymce.isIE6||document.compatMode=="BackCompat"){e="absolute;top:"+d.y}else{e="fixed;top:0"}n=b.add(b.doc.body,"div",{id:"mce_fullscreen_container",style:"position:"+e+";left:0;width:"+d.w+"px;height:"+d.h+"px;z-index:200000;"});b.add(n,"div",{id:"mce_fullscreen"});tinymce.each(i.settings,function(s,t){m[t]=s});m.id="mce_fullscreen";m.width=n.clientWidth;m.height=n.clientHeight-15;m.fullscreen_is_enabled=true;m.fullscreen_editor_id=i.id;m.theme_advanced_resizing=false;m.save_onsavecallback=function(){i.setContent(tinyMCE.get(m.id).getContent());i.execCommand("mceSave")};tinymce.each(i.getParam("fullscreen_settings"),function(t,s){m[s]=t});l.fullscreenSettings={bookmark:i.selection.getBookmark(),fullscreen_overflow:o,fullscreen_html_overflow:h,fullscreen_scrollx:g,fullscreen_scrolly:f};if(m.theme_advanced_toolbar_location==="external"){m.theme_advanced_toolbar_location="top"}tinyMCE.oldSettings=tinyMCE.settings;l.fullscreenEditor=new tinymce.Editor("mce_fullscreen",m);l.fullscreenEditor.onInit.add(function(){l.loadState(l.fullscreenEditor)});l.fullscreenEditor.render();l.fullscreenElement=new tinymce.dom.Element("mce_fullscreen_container");l.fullscreenElement.update();l.resizeFunc=tinymce.dom.Event.add(b.win,"resize",function(){var v=tinymce.DOM.getViewPort(),t=l.fullscreenEditor,s,u;s=t.dom.getSize(t.getContainer().getElementsByTagName("table")[0]);u=t.dom.getSize(t.getContainer().getElementsByTagName("iframe")[0]);t.theme.resizeTo(v.w-s.w+u.w,v.h-s.h+u.h)})}});i.addButton("fullscreen",{title:"fullscreen.desc",cmd:"mceFullScreen"});i.onNodeChange.add(function(q,p){p.setActive("fullscreen",q.getParam("fullscreen_is_enabled"))});l.loadState=function(p){if(!(p&&l.fullscreenSettings)){throw"No fullscreen editor to load to"}a(i,p,l.fullscreenSettings.bookmark);p.focus()};l.saveState=function(q){if(!(q&&l.fullscreenSettings)){throw"No fullscreen editor to restore from"}var p=l.fullscreenSettings;a(q,i,q.selection.getBookmark());if(!i.getParam("fullscreen_new_window")){tinymce.dom.Event.remove(b.win,"resize",l.resizeFunc);delete l.resizeFunc;b.remove("mce_fullscreen_container");b.doc.documentElement.style.overflow=p.fullscreen_html_overflow;b.setStyle(b.doc.body,"overflow",p.fullscreen_overflow);b.win.scrollTo(p.fullscreen_scrollx,p.fullscreen_scrolly)}tinyMCE.settings=tinyMCE.oldSettings;delete tinyMCE.oldSettings;delete l.fullscreenEditor;delete l.fullscreenElement;delete l.fullscreenSettings;b.win.setTimeout(function(){i.selection.moveToBookmark(j);i.focus()},10)}},getInfo:function(){return{longname:"Fullscreen",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/fullscreen",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("fullscreen",tinymce.plugins.FullScreenPlugin)})();
(function(){tinymce.create("tinymce.plugins.IESpell",{init:function(a,b){var c=this,d;if(!tinymce.isIE){return}c.editor=a;a.addCommand("mceIESpell",function(){try{d=new ActiveXObject("ieSpell.ieSpellExtension");d.CheckDocumentNode(a.getDoc().documentElement)}catch(f){if(f.number==-2146827859){a.windowManager.confirm(a.getLang("iespell.download"),function(e){if(e){window.open("http://www.iespell.com/download.php","ieSpellDownload","")}})}else{a.windowManager.alert("Error Loading ieSpell: Exception "+f.number)}}});a.addButton("iespell",{title:"iespell.iespell_desc",cmd:"mceIESpell"})},getInfo:function(){return{longname:"IESpell (IE Only)",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/iespell",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("iespell",tinymce.plugins.IESpell)})();
(function(){var d=tinymce.DOM,b=tinymce.dom.Element,a=tinymce.dom.Event,e=tinymce.each,c=tinymce.is;tinymce.create("tinymce.plugins.InlinePopups",{init:function(f,g){f.onBeforeRenderUI.add(function(){f.windowManager=new tinymce.InlineWindowManager(f);d.loadCSS(g+"/skins/"+(f.settings.inlinepopups_skin||"clearlooks2")+"/window.css")})},getInfo:function(){return{longname:"InlinePopups",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/inlinepopups",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.create("tinymce.InlineWindowManager:tinymce.WindowManager",{InlineWindowManager:function(f){var g=this;g.parent(f);g.zIndex=300000;g.count=0;g.windows={}},open:function(s,j){var z=this,i,k="",r=z.editor,g=0,v=0,h,m,o,q,l,x,y,n;s=s||{};j=j||{};if(!s.inline){return z.parent(s,j)}n=z._frontWindow();if(n&&d.get(n.id+"_ifr")){n.focussedElement=d.get(n.id+"_ifr").contentWindow.document.activeElement}if(!s.type){z.bookmark=r.selection.getBookmark(1)}i=d.uniqueId("mce_inlinepopups_");h=d.getViewPort();s.width=parseInt(s.width||320);s.height=parseInt(s.height||240)+(tinymce.isIE?8:0);s.min_width=parseInt(s.min_width||150);s.min_height=parseInt(s.min_height||100);s.max_width=parseInt(s.max_width||2000);s.max_height=parseInt(s.max_height||2000);s.left=s.left||Math.round(Math.max(h.x,h.x+(h.w/2)-(s.width/2)));s.top=s.top||Math.round(Math.max(h.y,h.y+(h.h/2)-(s.height/2)));s.movable=s.resizable=true;j.mce_width=s.width;j.mce_height=s.height;j.mce_inline=true;j.mce_window_id=i;j.mce_auto_focus=s.auto_focus;z.features=s;z.params=j;z.onOpen.dispatch(z,s,j);if(s.type){k+=" mceModal";if(s.type){k+=" mce"+s.type.substring(0,1).toUpperCase()+s.type.substring(1)}s.resizable=false}if(s.statusbar){k+=" mceStatusbar"}if(s.resizable){k+=" mceResizable"}if(s.minimizable){k+=" mceMinimizable"}if(s.maximizable){k+=" mceMaximizable"}if(s.movable){k+=" mceMovable"}z._addAll(d.doc.body,["div",{id:i,role:"dialog","aria-labelledby":s.type?i+"_content":i+"_title","class":(r.settings.inlinepopups_skin||"clearlooks2")+(tinymce.isIE&&window.getSelection?" ie9":""),style:"width:100px;height:100px"},["div",{id:i+"_wrapper","class":"mceWrapper"+k},["div",{id:i+"_top","class":"mceTop"},["div",{"class":"mceLeft"}],["div",{"class":"mceCenter"}],["div",{"class":"mceRight"}],["span",{id:i+"_title"},s.title||""]],["div",{id:i+"_middle","class":"mceMiddle"},["div",{id:i+"_left","class":"mceLeft",tabindex:"0"}],["span",{id:i+"_content"}],["div",{id:i+"_right","class":"mceRight",tabindex:"0"}]],["div",{id:i+"_bottom","class":"mceBottom"},["div",{"class":"mceLeft"}],["div",{"class":"mceCenter"}],["div",{"class":"mceRight"}],["span",{id:i+"_status"},"Content"]],["a",{"class":"mceMove",tabindex:"-1",href:"javascript:;"}],["a",{"class":"mceMin",tabindex:"-1",href:"javascript:;",onmousedown:"return false;"}],["a",{"class":"mceMax",tabindex:"-1",href:"javascript:;",onmousedown:"return false;"}],["a",{"class":"mceMed",tabindex:"-1",href:"javascript:;",onmousedown:"return false;"}],["a",{"class":"mceClose",tabindex:"-1",href:"javascript:;",onmousedown:"return false;"}],["a",{id:i+"_resize_n","class":"mceResize mceResizeN",tabindex:"-1",href:"javascript:;"}],["a",{id:i+"_resize_s","class":"mceResize mceResizeS",tabindex:"-1",href:"javascript:;"}],["a",{id:i+"_resize_w","class":"mceResize mceResizeW",tabindex:"-1",href:"javascript:;"}],["a",{id:i+"_resize_e","class":"mceResize mceResizeE",tabindex:"-1",href:"javascript:;"}],["a",{id:i+"_resize_nw","class":"mceResize mceResizeNW",tabindex:"-1",href:"javascript:;"}],["a",{id:i+"_resize_ne","class":"mceResize mceResizeNE",tabindex:"-1",href:"javascript:;"}],["a",{id:i+"_resize_sw","class":"mceResize mceResizeSW",tabindex:"-1",href:"javascript:;"}],["a",{id:i+"_resize_se","class":"mceResize mceResizeSE",tabindex:"-1",href:"javascript:;"}]]]);d.setStyles(i,{top:-10000,left:-10000});if(tinymce.isGecko){d.setStyle(i,"overflow","auto")}if(!s.type){g+=d.get(i+"_left").clientWidth;g+=d.get(i+"_right").clientWidth;v+=d.get(i+"_top").clientHeight;v+=d.get(i+"_bottom").clientHeight}d.setStyles(i,{top:s.top,left:s.left,width:s.width+g,height:s.height+v});y=s.url||s.file;if(y){if(tinymce.relaxedDomain){y+=(y.indexOf("?")==-1?"?":"&")+"mce_rdomain="+tinymce.relaxedDomain}y=tinymce._addVer(y)}if(!s.type){d.add(i+"_content","iframe",{id:i+"_ifr",src:'javascript:""',frameBorder:0,style:"border:0;width:10px;height:10px"});d.setStyles(i+"_ifr",{width:s.width,height:s.height});d.setAttrib(i+"_ifr","src",y)}else{d.add(i+"_wrapper","a",{id:i+"_ok","class":"mceButton mceOk",href:"javascript:;",onmousedown:"return false;"},"Ok");if(s.type=="confirm"){d.add(i+"_wrapper","a",{"class":"mceButton mceCancel",href:"javascript:;",onmousedown:"return false;"},"Cancel")}d.add(i+"_middle","div",{"class":"mceIcon"});d.setHTML(i+"_content",s.content.replace("\n","<br />"));a.add(i,"keyup",function(f){var p=27;if(f.keyCode===p){s.button_func(false);return a.cancel(f)}});a.add(i,"keydown",function(f){var t,p=9;if(f.keyCode===p){t=d.select("a.mceCancel",i+"_wrapper")[0];if(t&&t!==f.target){t.focus()}else{d.get(i+"_ok").focus()}return a.cancel(f)}})}o=a.add(i,"mousedown",function(t){var u=t.target,f,p;f=z.windows[i];z.focus(i);if(u.nodeName=="A"||u.nodeName=="a"){if(u.className=="mceClose"){z.close(null,i);return a.cancel(t)}else{if(u.className=="mceMax"){f.oldPos=f.element.getXY();f.oldSize=f.element.getSize();p=d.getViewPort();p.w-=2;p.h-=2;f.element.moveTo(p.x,p.y);f.element.resizeTo(p.w,p.h);d.setStyles(i+"_ifr",{width:p.w-f.deltaWidth,height:p.h-f.deltaHeight});d.addClass(i+"_wrapper","mceMaximized")}else{if(u.className=="mceMed"){f.element.moveTo(f.oldPos.x,f.oldPos.y);f.element.resizeTo(f.oldSize.w,f.oldSize.h);f.iframeElement.resizeTo(f.oldSize.w-f.deltaWidth,f.oldSize.h-f.deltaHeight);d.removeClass(i+"_wrapper","mceMaximized")}else{if(u.className=="mceMove"){return z._startDrag(i,t,u.className)}else{if(d.hasClass(u,"mceResize")){return z._startDrag(i,t,u.className.substring(13))}}}}}}});q=a.add(i,"click",function(f){var p=f.target;z.focus(i);if(p.nodeName=="A"||p.nodeName=="a"){switch(p.className){case"mceClose":z.close(null,i);return a.cancel(f);case"mceButton mceOk":case"mceButton mceCancel":s.button_func(p.className=="mceButton mceOk");return a.cancel(f)}}});a.add([i+"_left",i+"_right"],"focus",function(p){var t=d.get(i+"_ifr");if(t){var f=t.contentWindow.document.body;var u=d.select(":input:enabled,*[tabindex=0]",f);if(p.target.id===(i+"_left")){u[u.length-1].focus()}else{u[0].focus()}}else{d.get(i+"_ok").focus()}});x=z.windows[i]={id:i,mousedown_func:o,click_func:q,element:new b(i,{blocker:1,container:r.getContainer()}),iframeElement:new b(i+"_ifr"),features:s,deltaWidth:g,deltaHeight:v};x.iframeElement.on("focus",function(){z.focus(i)});if(z.count==0&&z.editor.getParam("dialog_type","modal")=="modal"){d.add(d.doc.body,"div",{id:"mceModalBlocker","class":(z.editor.settings.inlinepopups_skin||"clearlooks2")+"_modalBlocker",style:{zIndex:z.zIndex-1}});d.show("mceModalBlocker");d.setAttrib(d.doc.body,"aria-hidden","true")}else{d.setStyle("mceModalBlocker","z-index",z.zIndex-1)}if(tinymce.isIE6||/Firefox\/2\./.test(navigator.userAgent)||(tinymce.isIE&&!d.boxModel)){d.setStyles("mceModalBlocker",{position:"absolute",left:h.x,top:h.y,width:h.w-2,height:h.h-2})}d.setAttrib(i,"aria-hidden","false");z.focus(i);z._fixIELayout(i,1);if(d.get(i+"_ok")){d.get(i+"_ok").focus()}z.count++;return x},focus:function(h){var g=this,f;if(f=g.windows[h]){f.zIndex=this.zIndex++;f.element.setStyle("zIndex",f.zIndex);f.element.update();h=h+"_wrapper";d.removeClass(g.lastId,"mceFocus");d.addClass(h,"mceFocus");g.lastId=h;if(f.focussedElement){f.focussedElement.focus()}else{if(d.get(h+"_ok")){d.get(f.id+"_ok").focus()}else{if(d.get(f.id+"_ifr")){d.get(f.id+"_ifr").focus()}}}}},_addAll:function(k,h){var g,l,f=this,j=tinymce.DOM;if(c(h,"string")){k.appendChild(j.doc.createTextNode(h))}else{if(h.length){k=k.appendChild(j.create(h[0],h[1]));for(g=2;g<h.length;g++){f._addAll(k,h[g])}}}},_startDrag:function(v,G,E){var o=this,u,z,C=d.doc,f,l=o.windows[v],h=l.element,y=h.getXY(),x,q,F,g,A,s,r,j,i,m,k,n,B;g={x:0,y:0};A=d.getViewPort();A.w-=2;A.h-=2;j=G.screenX;i=G.screenY;m=k=n=B=0;u=a.add(C,"mouseup",function(p){a.remove(C,"mouseup",u);a.remove(C,"mousemove",z);if(f){f.remove()}h.moveBy(m,k);h.resizeBy(n,B);q=h.getSize();d.setStyles(v+"_ifr",{width:q.w-l.deltaWidth,height:q.h-l.deltaHeight});o._fixIELayout(v,1);return a.cancel(p)});if(E!="Move"){D()}function D(){if(f){return}o._fixIELayout(v,0);d.add(C.body,"div",{id:"mceEventBlocker","class":"mceEventBlocker "+(o.editor.settings.inlinepopups_skin||"clearlooks2"),style:{zIndex:o.zIndex+1}});if(tinymce.isIE6||(tinymce.isIE&&!d.boxModel)){d.setStyles("mceEventBlocker",{position:"absolute",left:A.x,top:A.y,width:A.w-2,height:A.h-2})}f=new b("mceEventBlocker");f.update();x=h.getXY();q=h.getSize();s=g.x+x.x-A.x;r=g.y+x.y-A.y;d.add(f.get(),"div",{id:"mcePlaceHolder","class":"mcePlaceHolder",style:{left:s,top:r,width:q.w,height:q.h}});F=new b("mcePlaceHolder")}z=a.add(C,"mousemove",function(w){var p,H,t;D();p=w.screenX-j;H=w.screenY-i;switch(E){case"ResizeW":m=p;n=0-p;break;case"ResizeE":n=p;break;case"ResizeN":case"ResizeNW":case"ResizeNE":if(E=="ResizeNW"){m=p;n=0-p}else{if(E=="ResizeNE"){n=p}}k=H;B=0-H;break;case"ResizeS":case"ResizeSW":case"ResizeSE":if(E=="ResizeSW"){m=p;n=0-p}else{if(E=="ResizeSE"){n=p}}B=H;break;case"mceMove":m=p;k=H;break}if(n<(t=l.features.min_width-q.w)){if(m!==0){m+=n-t}n=t}if(B<(t=l.features.min_height-q.h)){if(k!==0){k+=B-t}B=t}n=Math.min(n,l.features.max_width-q.w);B=Math.min(B,l.features.max_height-q.h);m=Math.max(m,A.x-(s+A.x));k=Math.max(k,A.y-(r+A.y));m=Math.min(m,(A.w+A.x)-(s+q.w+A.x));k=Math.min(k,(A.h+A.y)-(r+q.h+A.y));if(m+k!==0){if(s+m<0){m=0}if(r+k<0){k=0}F.moveTo(s+m,r+k)}if(n+B!==0){F.resizeTo(q.w+n,q.h+B)}return a.cancel(w)});return a.cancel(G)},resizeBy:function(g,h,i){var f=this.windows[i];if(f){f.element.resizeBy(g,h);f.iframeElement.resizeBy(g,h)}},close:function(i,k){var g=this,f,j=d.doc,h,k;k=g._findId(k||i);if(!g.windows[k]){g.parent(i);return}g.count--;if(g.count==0){d.remove("mceModalBlocker");d.setAttrib(d.doc.body,"aria-hidden","false");g.editor.focus()}if(f=g.windows[k]){g.onClose.dispatch(g);a.remove(j,"mousedown",f.mousedownFunc);a.remove(j,"click",f.clickFunc);a.clear(k);a.clear(k+"_ifr");d.setAttrib(k+"_ifr","src",'javascript:""');f.element.remove();delete g.windows[k];h=g._frontWindow();if(h){g.focus(h.id)}}},_frontWindow:function(){var g,f=0;e(this.windows,function(h){if(h.zIndex>f){g=h;f=h.zIndex}});return g},setTitle:function(f,g){var h;f=this._findId(f);if(h=d.get(f+"_title")){h.innerHTML=d.encode(g)}},alert:function(g,f,j){var i=this,h;h=i.open({title:i,type:"alert",button_func:function(k){if(f){f.call(k||i,k)}i.close(null,h.id)},content:d.encode(i.editor.getLang(g,g)),inline:1,width:400,height:130})},confirm:function(g,f,j){var i=this,h;h=i.open({title:i,type:"confirm",button_func:function(k){if(f){f.call(k||i,k)}i.close(null,h.id)},content:d.encode(i.editor.getLang(g,g)),inline:1,width:400,height:130})},_findId:function(f){var g=this;if(typeof(f)=="string"){return f}e(g.windows,function(h){var i=d.get(h.id+"_ifr");if(i&&f==i.contentWindow){f=h.id;return false}});return f},_fixIELayout:function(i,h){var f,g;if(!tinymce.isIE6){return}e(["n","s","w","e","nw","ne","sw","se"],function(j){var k=d.get(i+"_resize_"+j);d.setStyles(k,{width:h?k.clientWidth:"",height:h?k.clientHeight:"",cursor:d.getStyle(k,"cursor",1)});d.setStyle(i+"_bottom","bottom","-1px");k=0});if(f=this.windows[i]){f.element.hide();f.element.show();e(d.select("div,a",i),function(k,j){if(k.currentStyle.backgroundImage!="none"){g=new Image();g.src=k.currentStyle.backgroundImage.replace(/url\(\"(.+)\"\)/,"$1")}});d.get(i).style.filter=""}}});tinymce.PluginManager.add("inlinepopups",tinymce.plugins.InlinePopups)})();
(function(){tinymce.create("tinymce.plugins.AutoResizePlugin",{init:function(a,c){var d=this,e=0;if(a.getParam("fullscreen_is_enabled")){return}function b(){var j,i=a.getDoc(),f=i.body,l=i.documentElement,h=tinymce.DOM,k=d.autoresize_min_height,g;g=tinymce.isIE?f.scrollHeight:(tinymce.isWebKit&&f.clientHeight==0?0:f.offsetHeight);if(g>d.autoresize_min_height){k=g}if(d.autoresize_max_height&&g>d.autoresize_max_height){k=d.autoresize_max_height;f.style.overflowY="auto";l.style.overflowY="auto"}else{f.style.overflowY="hidden";l.style.overflowY="hidden";f.scrollTop=0}if(k!==e){j=k-e;h.setStyle(h.get(a.id+"_ifr"),"height",k+"px");e=k;if(tinymce.isWebKit&&j<0){b()}}}d.editor=a;d.autoresize_min_height=parseInt(a.getParam("autoresize_min_height",a.getElement().offsetHeight));d.autoresize_max_height=parseInt(a.getParam("autoresize_max_height",0));a.onInit.add(function(f){f.dom.setStyle(f.getBody(),"paddingBottom",f.getParam("autoresize_bottom_margin",50)+"px")});a.onChange.add(b);a.onSetContent.add(b);a.onPaste.add(b);a.onKeyUp.add(b);a.onPostRender.add(b);if(a.getParam("autoresize_on_init",true)){a.onLoad.add(b);a.onLoadContent.add(b)}a.addCommand("mceAutoResize",b)},getInfo:function(){return{longname:"Auto Resize",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/autoresize",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("autoresize",tinymce.plugins.AutoResizePlugin)})();
(function(){var a=tinymce.dom.Event,c=tinymce.each,b=tinymce.DOM;tinymce.create("tinymce.plugins.ContextMenu",{init:function(f){var i=this,g,d,j,e;i.editor=f;d=f.settings.contextmenu_never_use_native;i.onContextMenu=new tinymce.util.Dispatcher(this);e=function(k){h(f,k)};g=f.onContextMenu.add(function(k,l){if((j!==0?j:l.ctrlKey)&&!d){return}a.cancel(l);if(l.target.nodeName=="IMG"){k.selection.select(l.target)}i._getMenu(k).showMenu(l.clientX||l.pageX,l.clientY||l.pageY);a.add(k.getDoc(),"click",e);k.nodeChanged()});f.onRemove.add(function(){if(i._menu){i._menu.removeAll()}});function h(k,l){j=0;if(l&&l.button==2){j=l.ctrlKey;return}if(i._menu){i._menu.removeAll();i._menu.destroy();a.remove(k.getDoc(),"click",e);i._menu=null}}f.onMouseDown.add(h);f.onKeyDown.add(h);f.onKeyDown.add(function(k,l){if(l.shiftKey&&!l.ctrlKey&&!l.altKey&&l.keyCode===121){a.cancel(l);g(k,l)}})},getInfo:function(){return{longname:"Contextmenu",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/contextmenu",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_getMenu:function(e){var g=this,d=g._menu,j=e.selection,f=j.isCollapsed(),h=j.getNode()||e.getBody(),i,k;if(d){d.removeAll();d.destroy()}k=b.getPos(e.getContentAreaContainer());d=e.controlManager.createDropMenu("contextmenu",{offset_x:k.x+e.getParam("contextmenu_offset_x",0),offset_y:k.y+e.getParam("contextmenu_offset_y",0),constrain:1,keyboard_focus:true});g._menu=d;d.add({title:"advanced.cut_desc",icon:"cut",cmd:"Cut"}).setDisabled(f);d.add({title:"advanced.copy_desc",icon:"copy",cmd:"Copy"}).setDisabled(f);d.add({title:"advanced.paste_desc",icon:"paste",cmd:"Paste"});if((h.nodeName=="A"&&!e.dom.getAttrib(h,"name"))||!f){d.addSeparator();d.add({title:"advanced.link_desc",icon:"link",cmd:e.plugins.advlink?"mceAdvLink":"mceLink",ui:true});d.add({title:"advanced.unlink_desc",icon:"unlink",cmd:"UnLink"})}d.addSeparator();d.add({title:"advanced.image_desc",icon:"image",cmd:e.plugins.advimage?"mceAdvImage":"mceImage",ui:true});d.addSeparator();i=d.addMenu({title:"contextmenu.align"});i.add({title:"contextmenu.left",icon:"justifyleft",cmd:"JustifyLeft"});i.add({title:"contextmenu.center",icon:"justifycenter",cmd:"JustifyCenter"});i.add({title:"contextmenu.right",icon:"justifyright",cmd:"JustifyRight"});i.add({title:"contextmenu.full",icon:"justifyfull",cmd:"JustifyFull"});g.onContextMenu.dispatch(g,d,h,f);return d}});tinymce.PluginManager.add("contextmenu",tinymce.plugins.ContextMenu)})();
(function(){var a=tinymce.each;tinymce.create("tinymce.plugins.AdvListPlugin",{init:function(b,c){var d=this;d.editor=b;function e(g){var f=[];a(g.split(/,/),function(h){f.push({title:"advlist."+(h=="default"?"def":h.replace(/-/g,"_")),styles:{listStyleType:h=="default"?"":h}})});return f}d.numlist=b.getParam("advlist_number_styles")||e("default,lower-alpha,lower-greek,lower-roman,upper-alpha,upper-roman");d.bullist=b.getParam("advlist_bullet_styles")||e("default,circle,disc,square");if(tinymce.isIE&&/MSIE [2-7]/.test(navigator.userAgent)){d.isIE7=true}},createControl:function(d,b){var f=this,e,i,g=f.editor;if(d=="numlist"||d=="bullist"){if(f[d][0].title=="advlist.def"){i=f[d][0]}function c(j,l){var k=true;a(l.styles,function(n,m){if(g.dom.getStyle(j,m)!=n){k=false;return false}});return k}function h(){var k,l=g.dom,j=g.selection;k=l.getParent(j.getNode(),"ol,ul");if(!k||k.nodeName==(d=="bullist"?"OL":"UL")||c(k,i)){g.execCommand(d=="bullist"?"InsertUnorderedList":"InsertOrderedList")}if(i){k=l.getParent(j.getNode(),"ol,ul");if(k){l.setStyles(k,i.styles);k.removeAttribute("data-mce-style")}}g.focus()}e=b.createSplitButton(d,{title:"advanced."+d+"_desc","class":"mce_"+d,onclick:function(){h()}});e.onRenderMenu.add(function(j,k){k.onHideMenu.add(function(){if(f.bookmark){g.selection.moveToBookmark(f.bookmark);f.bookmark=0}});k.onShowMenu.add(function(){var n=g.dom,m=n.getParent(g.selection.getNode(),"ol,ul"),l;if(m||i){l=f[d];a(k.items,function(o){var p=true;o.setSelected(0);if(m&&!o.isDisabled()){a(l,function(q){if(q.id==o.id){if(!c(m,q)){p=false;return false}}});if(p){o.setSelected(1)}}});if(!m){k.items[i.id].setSelected(1)}}g.focus();if(tinymce.isIE){f.bookmark=g.selection.getBookmark(1)}});k.add({id:g.dom.uniqueId(),title:"advlist.types","class":"mceMenuItemTitle",titleItem:true}).setDisabled(1);a(f[d],function(l){if(f.isIE7&&l.styles.listStyleType=="lower-greek"){return}l.id=g.dom.uniqueId();k.add({id:l.id,title:l.title,onclick:function(){i=l;h()}})})});return e}},getInfo:function(){return{longname:"Advanced lists",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/advlist",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("advlist",tinymce.plugins.AdvListPlugin)})();
(function(){tinymce.create("tinymce.plugins.AutolinkPlugin",{init:function(a,b){var c=this;a.onKeyDown.addToTop(function(d,f){if(f.keyCode==13){return c.handleEnter(d)}});if(tinyMCE.isIE){return}a.onKeyPress.add(function(d,f){if(f.which==41){return c.handleEclipse(d)}});a.onKeyUp.add(function(d,f){if(f.keyCode==32){return c.handleSpacebar(d)}})},handleEclipse:function(a){this.parseCurrentLine(a,-1,"(",true)},handleSpacebar:function(a){this.parseCurrentLine(a,0,"",true)},handleEnter:function(a){this.parseCurrentLine(a,-1,"",false)},parseCurrentLine:function(i,d,b,g){var a,f,c,n,k,m,h,e,j;a=i.selection.getRng(true).cloneRange();if(a.startOffset<5){e=a.endContainer.previousSibling;if(e==null){if(a.endContainer.firstChild==null||a.endContainer.firstChild.nextSibling==null){return}e=a.endContainer.firstChild.nextSibling}j=e.length;a.setStart(e,j);a.setEnd(e,j);if(a.endOffset<5){return}f=a.endOffset;n=e}else{n=a.endContainer;if(n.nodeType!=3&&n.firstChild){while(n.nodeType!=3&&n.firstChild){n=n.firstChild}if(n.nodeType==3){a.setStart(n,0);a.setEnd(n,n.nodeValue.length)}}if(a.endOffset==1){f=2}else{f=a.endOffset-1-d}}c=f;do{a.setStart(n,f>=2?f-2:0);a.setEnd(n,f>=1?f-1:0);f-=1}while(a.toString()!=" "&&a.toString()!=""&&a.toString().charCodeAt(0)!=160&&(f-2)>=0&&a.toString()!=b);if(a.toString()==b||a.toString().charCodeAt(0)==160){a.setStart(n,f);a.setEnd(n,c);f+=1}else{if(a.startOffset==0){a.setStart(n,0);a.setEnd(n,c)}else{a.setStart(n,f);a.setEnd(n,c)}}var m=a.toString();if(m.charAt(m.length-1)=="."){a.setEnd(n,c-1)}m=a.toString();h=m.match(/^(https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.|(?:mailto:)?[A-Z0-9._%+-]+@)(.+)$/i);if(h){if(h[1]=="www."){h[1]="http://www."}else{if(/@$/.test(h[1])&&!/^mailto:/.test(h[1])){h[1]="mailto:"+h[1]}}k=i.selection.getBookmark();i.selection.setRng(a);tinyMCE.execCommand("createlink",false,h[1]+h[2]);i.selection.moveToBookmark(k);i.nodeChanged();if(tinyMCE.isWebKit){i.selection.collapse(false);var l=Math.min(n.length,c+1);a.setStart(n,l);a.setEnd(n,l);i.selection.setRng(a)}}},getInfo:function(){return{longname:"Autolink",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/autolink",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("autolink",tinymce.plugins.AutolinkPlugin)})();
(function(){tinymce.create("tinymce.plugins.Directionality",{init:function(b,c){var d=this;d.editor=b;function a(e){var h=b.dom,g,f=b.selection.getSelectedBlocks();if(f.length){g=h.getAttrib(f[0],"dir");tinymce.each(f,function(i){if(!h.getParent(i.parentNode,"*[dir='"+e+"']",h.getRoot())){if(g!=e){h.setAttrib(i,"dir",e)}else{h.setAttrib(i,"dir",null)}}});b.nodeChanged()}}b.addCommand("mceDirectionLTR",function(){a("ltr")});b.addCommand("mceDirectionRTL",function(){a("rtl")});b.addButton("ltr",{title:"directionality.ltr_desc",cmd:"mceDirectionLTR"});b.addButton("rtl",{title:"directionality.rtl_desc",cmd:"mceDirectionRTL"});b.onNodeChange.add(d._nodeChange,d)},getInfo:function(){return{longname:"Directionality",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/directionality",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_nodeChange:function(b,a,e){var d=b.dom,c;e=d.getParent(e,d.isBlock);if(!e){a.setDisabled("ltr",1);a.setDisabled("rtl",1);return}c=d.getAttrib(e,"dir");a.setActive("ltr",c=="ltr");a.setDisabled("ltr",0);a.setActive("rtl",c=="rtl");a.setDisabled("rtl",0)}});tinymce.PluginManager.add("directionality",tinymce.plugins.Directionality)})();
(function(a){a.create("tinymce.plugins.EmotionsPlugin",{init:function(b,c){b.addCommand("mceEmotion",function(){b.windowManager.open({file:c+"/emotions.htm",width:250+parseInt(b.getLang("emotions.delta_width",0)),height:160+parseInt(b.getLang("emotions.delta_height",0)),inline:1},{plugin_url:c})});b.addButton("emotions",{title:"emotions.emotions_desc",cmd:"mceEmotion"})},getInfo:function(){return{longname:"Emotions",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/emotions",version:a.majorVersion+"."+a.minorVersion}}});a.PluginManager.add("emotions",a.plugins.EmotionsPlugin)})(tinymce);
(function(i){var k=i.DOM,h=i.dom.Event,c=i.extend,f=i.each,a=i.util.Cookie,e,d=i.explode;var g=function(m,n){var o=n.theme_advanced_default_font_size;var l=n.theme_advanced_default_font_family;if(o){m.style.fontSize=o}if(l){m.style.fontFamily=l}};var j=function(m){var n=m.theme_advanced_default_font_size;var l=m.theme_advanced_default_font_family;return !!(n||l)};function b(r,o){var m,n,q=r.dom,l="",p,t;previewStyles=r.settings.preview_styles;if(previewStyles===false){return""}if(!previewStyles){previewStyles="font-family font-size font-weight text-decoration text-transform color background-color"}function s(u){return u.replace(/%(\w+)/g,"")}m=o.block||o.inline||"span";n=q.create(m);f(o.styles,function(v,u){v=s(v);if(v){q.setStyle(n,u,v)}});f(o.attributes,function(v,u){v=s(v);if(v){q.setAttrib(n,u,v)}});f(o.classes,function(u){u=s(u);if(!q.hasClass(n,u)){q.addClass(n,u)}});q.setStyles(n,{position:"absolute",left:-65535});r.getBody().appendChild(n);p=q.getStyle(r.getBody(),"fontSize",true);p=/px$/.test(p)?parseInt(p,10):0;f(previewStyles.split(" "),function(u){var v=q.getStyle(n,u,true);if(u=="background-color"&&/transparent|rgba\s*\([^)]+,\s*0\)/.test(v)){v=q.getStyle(r.getBody(),u,true);if(q.toHex(v).toLowerCase()=="#ffffff"){return}}if(u=="font-size"){if(/em|%$/.test(v)){if(p===0){return}v=parseFloat(v,10)/(/%$/.test(v)?100:1);v=(v*p)+"px"}}l+=u+":"+v+";"});q.remove(n);return l}i.ThemeManager.requireLangPack("advanced");i.create("tinymce.themes.AdvancedTheme",{sizes:[8,10,12,14,18,24,36],controls:{bold:["bold_desc","Bold"],italic:["italic_desc","Italic"],underline:["underline_desc","Underline"],strikethrough:["striketrough_desc","Strikethrough"],justifyleft:["justifyleft_desc","JustifyLeft"],justifycenter:["justifycenter_desc","JustifyCenter"],justifyright:["justifyright_desc","JustifyRight"],justifyfull:["justifyfull_desc","JustifyFull"],bullist:["bullist_desc","InsertUnorderedList"],numlist:["numlist_desc","InsertOrderedList"],outdent:["outdent_desc","Outdent"],indent:["indent_desc","Indent"],cut:["cut_desc","Cut"],copy:["copy_desc","Copy"],paste:["paste_desc","Paste"],undo:["undo_desc","Undo"],redo:["redo_desc","Redo"],link:["link_desc","mceLink"],unlink:["unlink_desc","unlink"],image:["image_desc","mceImage"],cleanup:["cleanup_desc","mceCleanup"],help:["help_desc","mceHelp"],code:["code_desc","mceCodeEditor"],hr:["hr_desc","InsertHorizontalRule"],removeformat:["removeformat_desc","RemoveFormat"],sub:["sub_desc","subscript"],sup:["sup_desc","superscript"],forecolor:["forecolor_desc","ForeColor"],forecolorpicker:["forecolor_desc","mceForeColor"],backcolor:["backcolor_desc","HiliteColor"],backcolorpicker:["backcolor_desc","mceBackColor"],charmap:["charmap_desc","mceCharMap"],visualaid:["visualaid_desc","mceToggleVisualAid"],anchor:["anchor_desc","mceInsertAnchor"],newdocument:["newdocument_desc","mceNewDocument"],blockquote:["blockquote_desc","mceBlockQuote"]},stateControls:["bold","italic","underline","strikethrough","bullist","numlist","justifyleft","justifycenter","justifyright","justifyfull","sub","sup","blockquote"],init:function(m,n){var p=this,q,l,r;p.editor=m;p.url=n;p.onResolveName=new i.util.Dispatcher(this);q=m.settings;m.forcedHighContrastMode=m.settings.detect_highcontrast&&p._isHighContrast();m.settings.skin=m.forcedHighContrastMode?"highcontrast":m.settings.skin;if(!q.theme_advanced_buttons1){q=c({theme_advanced_buttons1:"bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,styleselect,formatselect",theme_advanced_buttons2:"bullist,numlist,|,outdent,indent,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code",theme_advanced_buttons3:"hr,removeformat,visualaid,|,sub,sup,|,charmap"},q)}p.settings=q=c({theme_advanced_path:true,theme_advanced_toolbar_location:"top",theme_advanced_blockformats:"p,address,pre,h1,h2,h3,h4,h5,h6",theme_advanced_toolbar_align:"left",theme_advanced_statusbar_location:"bottom",theme_advanced_fonts:"Andale Mono=andale mono,monospace;Arial=arial,helvetica,sans-serif;Arial Black=arial black,sans-serif;Book Antiqua=book antiqua,palatino,serif;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier,monospace;Georgia=georgia,palatino,serif;Helvetica=helvetica,arial,sans-serif;Impact=impact,sans-serif;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco,monospace;Times New Roman=times new roman,times,serif;Trebuchet MS=trebuchet ms,geneva,sans-serif;Verdana=verdana,geneva,sans-serif;Webdings=webdings;Wingdings=wingdings,zapf dingbats",theme_advanced_more_colors:1,theme_advanced_row_height:23,theme_advanced_resize_horizontal:1,theme_advanced_resizing_use_cookie:1,theme_advanced_font_sizes:"1,2,3,4,5,6,7",theme_advanced_font_selector:"span",theme_advanced_show_current_color:0,readonly:m.settings.readonly},q);if(!q.font_size_style_values){q.font_size_style_values="8pt,10pt,12pt,14pt,18pt,24pt,36pt"}if(i.is(q.theme_advanced_font_sizes,"string")){q.font_size_style_values=i.explode(q.font_size_style_values);q.font_size_classes=i.explode(q.font_size_classes||"");r={};m.settings.theme_advanced_font_sizes=q.theme_advanced_font_sizes;f(m.getParam("theme_advanced_font_sizes","","hash"),function(t,s){var o;if(s==t&&t>=1&&t<=7){s=t+" ("+p.sizes[t-1]+"pt)";o=q.font_size_classes[t-1];t=q.font_size_style_values[t-1]||(p.sizes[t-1]+"pt")}if(/^\s*\./.test(t)){o=t.replace(/\./g,"")}r[s]=o?{"class":o}:{fontSize:t}});q.theme_advanced_font_sizes=r}if((l=q.theme_advanced_path_location)&&l!="none"){q.theme_advanced_statusbar_location=q.theme_advanced_path_location}if(q.theme_advanced_statusbar_location=="none"){q.theme_advanced_statusbar_location=0}if(m.settings.content_css!==false){m.contentCSS.push(m.baseURI.toAbsolute(n+"/skins/"+m.settings.skin+"/content.css"))}m.onInit.add(function(){if(!m.settings.readonly){m.onNodeChange.add(p._nodeChanged,p);m.onKeyUp.add(p._updateUndoStatus,p);m.onMouseUp.add(p._updateUndoStatus,p);m.dom.bind(m.dom.getRoot(),"dragend",function(){p._updateUndoStatus(m)})}g(m.getBody(),m.settings)});m.onSetProgressState.add(function(t,o,u){var v,w=t.id,s;if(o){p.progressTimer=setTimeout(function(){v=t.getContainer();v=v.insertBefore(k.create("DIV",{style:"position:relative"}),v.firstChild);s=k.get(t.id+"_tbl");k.add(v,"div",{id:w+"_blocker","class":"mceBlocker",style:{width:s.clientWidth+2,height:s.clientHeight+2}});k.add(v,"div",{id:w+"_progress","class":"mceProgress",style:{left:s.clientWidth/2,top:s.clientHeight/2}})},u||0)}else{k.remove(w+"_blocker");k.remove(w+"_progress");clearTimeout(p.progressTimer)}});k.loadCSS(q.editor_css?m.documentBaseURI.toAbsolute(q.editor_css):n+"/skins/"+m.settings.skin+"/ui.css");if(q.skin_variant){k.loadCSS(n+"/skins/"+m.settings.skin+"/ui_"+q.skin_variant+".css")}},_isHighContrast:function(){var l,m=k.add(k.getRoot(),"div",{style:"background-color: rgb(171,239,86);"});l=(k.getStyle(m,"background-color",true)+"").toLowerCase().replace(/ /g,"");k.remove(m);return l!="rgb(171,239,86)"&&l!="#abef56"},createControl:function(p,l){var m,o;if(o=l.createControl(p)){return o}switch(p){case"styleselect":return this._createStyleSelect();case"formatselect":return this._createBlockFormats();case"fontselect":return this._createFontSelect();case"fontsizeselect":return this._createFontSizeSelect();case"forecolor":return this._createForeColorMenu();case"backcolor":return this._createBackColorMenu()}if((m=this.controls[p])){return l.createButton(p,{title:"advanced."+m[0],cmd:m[1],ui:m[2],value:m[3]})}},execCommand:function(n,m,o){var l=this["_"+n];if(l){l.call(this,m,o);return true}return false},_importClasses:function(n){var l=this.editor,m=l.controlManager.get("styleselect");if(m.getLength()==0){f(l.dom.getClasses(),function(s,p){var r="style_"+p,q;q={inline:"span",attributes:{"class":s["class"]},selector:"*"};l.formatter.register(r,q);m.add(s["class"],r,{style:function(){return b(l,q)}})})}},_createStyleSelect:function(q){var o=this,l=o.editor,m=l.controlManager,p;p=m.createListBox("styleselect",{title:"advanced.style_select",onselect:function(s){var t,n=[],r;f(p.items,function(u){n.push(u.value)});l.focus();l.undoManager.add();t=l.formatter.matchAll(n);i.each(t,function(u){if(!s||u==s){if(u){l.formatter.remove(u)}r=true}});if(!r){l.formatter.apply(s)}l.undoManager.add();l.nodeChanged();return false}});l.onPreInit.add(function(){var r=0,n=l.getParam("style_formats");if(n){f(n,function(s){var t,u=0;f(s,function(){u++});if(u>1){t=s.name=s.name||"style_"+(r++);l.formatter.register(t,s);p.add(s.title,t,{style:function(){return b(l,s)}})}else{p.add(s.title)}})}else{f(l.getParam("theme_advanced_styles","","hash"),function(v,u){var t,s;if(v){t="style_"+(r++);s={inline:"span",classes:v,selector:"*"};l.formatter.register(t,s);p.add(o.editor.translate(u),t,{style:function(){return b(l,s)}})}})}});if(p.getLength()==0){p.onPostRender.add(function(r,s){if(!p.NativeListBox){h.add(s.id+"_text","focus",o._importClasses,o);h.add(s.id+"_text","mousedown",o._importClasses,o);h.add(s.id+"_open","focus",o._importClasses,o);h.add(s.id+"_open","mousedown",o._importClasses,o)}else{h.add(s.id,"focus",o._importClasses,o)}})}return p},_createFontSelect:function(){var n,m=this,l=m.editor;n=l.controlManager.createListBox("fontselect",{title:"advanced.fontdefault",onselect:function(o){var p=n.items[n.selectedIndex];if(!o&&p){l.execCommand("FontName",false,p.value);return}l.execCommand("FontName",false,o);n.select(function(q){return o==q});if(p&&p.value==o){n.select(null)}return false}});if(n){f(l.getParam("theme_advanced_fonts",m.settings.theme_advanced_fonts,"hash"),function(p,o){n.add(l.translate(o),p,{style:p.indexOf("dings")==-1?"font-family:"+p:""})})}return n},_createFontSizeSelect:function(){var o=this,m=o.editor,p,n=0,l=[];p=m.controlManager.createListBox("fontsizeselect",{title:"advanced.font_size",onselect:function(q){var r=p.items[p.selectedIndex];if(!q&&r){r=r.value;if(r["class"]){m.formatter.toggle("fontsize_class",{value:r["class"]});m.undoManager.add();m.nodeChanged()}else{m.execCommand("FontSize",false,r.fontSize)}return}if(q["class"]){m.focus();m.undoManager.add();m.formatter.toggle("fontsize_class",{value:q["class"]});m.undoManager.add();m.nodeChanged()}else{m.execCommand("FontSize",false,q.fontSize)}p.select(function(s){return q==s});if(r&&(r.value.fontSize==q.fontSize||r.value["class"]&&r.value["class"]==q["class"])){p.select(null)}return false}});if(p){f(o.settings.theme_advanced_font_sizes,function(r,q){var s=r.fontSize;if(s>=1&&s<=7){s=o.sizes[parseInt(s)-1]+"pt"}p.add(q,r,{style:"font-size:"+s,"class":"mceFontSize"+(n++)+(" "+(r["class"]||""))})})}return p},_createBlockFormats:function(){var n,l={p:"advanced.paragraph",address:"advanced.address",pre:"advanced.pre",h1:"advanced.h1",h2:"advanced.h2",h3:"advanced.h3",h4:"advanced.h4",h5:"advanced.h5",h6:"advanced.h6",div:"advanced.div",blockquote:"advanced.blockquote",code:"advanced.code",dt:"advanced.dt",dd:"advanced.dd",samp:"advanced.samp"},m=this;n=m.editor.controlManager.createListBox("formatselect",{title:"advanced.block",onselect:function(o){m.editor.execCommand("FormatBlock",false,o);return false}});if(n){f(m.editor.getParam("theme_advanced_blockformats",m.settings.theme_advanced_blockformats,"hash"),function(p,o){n.add(m.editor.translate(o!=p?o:l[p]),p,{"class":"mce_formatPreview mce_"+p,style:function(){return b(m.editor,{block:p})}})})}return n},_createForeColorMenu:function(){var q,m=this,n=m.settings,p={},l;if(n.theme_advanced_more_colors){p.more_colors_func=function(){m._mceColorPicker(0,{color:q.value,func:function(o){q.setColor(o)}})}}if(l=n.theme_advanced_text_colors){p.colors=l}if(n.theme_advanced_default_foreground_color){p.default_color=n.theme_advanced_default_foreground_color}p.title="advanced.forecolor_desc";p.cmd="ForeColor";p.scope=this;q=m.editor.controlManager.createColorSplitButton("forecolor",p);return q},_createBackColorMenu:function(){var q,m=this,n=m.settings,p={},l;if(n.theme_advanced_more_colors){p.more_colors_func=function(){m._mceColorPicker(0,{color:q.value,func:function(o){q.setColor(o)}})}}if(l=n.theme_advanced_background_colors){p.colors=l}if(n.theme_advanced_default_background_color){p.default_color=n.theme_advanced_default_background_color}p.title="advanced.backcolor_desc";p.cmd="HiliteColor";p.scope=this;q=m.editor.controlManager.createColorSplitButton("backcolor",p);return q},renderUI:function(q){var u,r,v,y=this,w=y.editor,z=y.settings,x,m,l;if(w.settings){w.settings.aria_label=z.aria_label+w.getLang("advanced.help_shortcut")}u=m=k.create("span",{role:"application","aria-labelledby":w.id+"_voice",id:w.id+"_parent","class":"mceEditor "+w.settings.skin+"Skin"+(z.skin_variant?" "+w.settings.skin+"Skin"+y._ufirst(z.skin_variant):"")+(w.settings.directionality=="rtl"?" mceRtl":"")});k.add(u,"span",{"class":"mceVoiceLabel",style:"display:none;",id:w.id+"_voice"},z.aria_label);if(!k.boxModel){u=k.add(u,"div",{"class":"mceOldBoxModel"})}u=x=k.add(u,"table",{role:"presentation",id:w.id+"_tbl","class":"mceLayout",cellSpacing:0,cellPadding:0});u=v=k.add(u,"tbody");switch((z.theme_advanced_layout_manager||"").toLowerCase()){case"rowlayout":r=y._rowLayout(z,v,q);break;case"customlayout":r=w.execCallback("theme_advanced_custom_layout",z,v,q,m);break;default:r=y._simpleLayout(z,v,q,m)}u=q.targetNode;l=x.rows;k.addClass(l[0],"mceFirst");k.addClass(l[l.length-1],"mceLast");f(k.select("tr",v),function(o){k.addClass(o.firstChild,"mceFirst");k.addClass(o.childNodes[o.childNodes.length-1],"mceLast")});if(k.get(z.theme_advanced_toolbar_container)){k.get(z.theme_advanced_toolbar_container).appendChild(m)}else{k.insertAfter(m,u)}h.add(w.id+"_path_row","click",function(n){n=n.target;if(n.nodeName=="A"){y._sel(n.className.replace(/^.*mcePath_([0-9]+).*$/,"$1"));return false}});if(!w.getParam("accessibility_focus")){h.add(k.add(m,"a",{href:"#"},"<!-- IE -->"),"focus",function(){tinyMCE.get(w.id).focus()})}if(z.theme_advanced_toolbar_location=="external"){q.deltaHeight=0}y.deltaHeight=q.deltaHeight;q.targetNode=null;w.onKeyDown.add(function(p,n){var s=121,o=122;if(n.altKey){if(n.keyCode===s){if(i.isWebKit){window.focus()}y.toolbarGroup.focus();return h.cancel(n)}else{if(n.keyCode===o){k.get(p.id+"_path_row").focus();return h.cancel(n)}}}});w.addShortcut("alt+0","","mceShortcuts",y);return{iframeContainer:r,editorContainer:w.id+"_parent",sizeContainer:x,deltaHeight:q.deltaHeight}},getInfo:function(){return{longname:"Advanced theme",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",version:i.majorVersion+"."+i.minorVersion}},resizeBy:function(l,m){var n=k.get(this.editor.id+"_ifr");this.resizeTo(n.clientWidth+l,n.clientHeight+m)},resizeTo:function(l,p,n){var m=this.editor,o=this.settings,q=k.get(m.id+"_tbl"),r=k.get(m.id+"_ifr");l=Math.max(o.theme_advanced_resizing_min_width||100,l);p=Math.max(o.theme_advanced_resizing_min_height||100,p);l=Math.min(o.theme_advanced_resizing_max_width||65535,l);p=Math.min(o.theme_advanced_resizing_max_height||65535,p);k.setStyle(q,"height","");k.setStyle(r,"height",p);if(o.theme_advanced_resize_horizontal){k.setStyle(q,"width","");k.setStyle(r,"width",l);if(l<q.clientWidth){l=q.clientWidth;k.setStyle(r,"width",q.clientWidth)}}if(n&&o.theme_advanced_resizing_use_cookie){a.setHash("TinyMCE_"+m.id+"_size",{cw:l,ch:p})}},destroy:function(){var l=this.editor.id;h.clear(l+"_resize");h.clear(l+"_path_row");h.clear(l+"_external_close")},_simpleLayout:function(B,w,q,l){var A=this,x=A.editor,y=B.theme_advanced_toolbar_location,u=B.theme_advanced_statusbar_location,r,m,v,z;if(B.readonly){r=k.add(w,"tr");r=m=k.add(r,"td",{"class":"mceIframeContainer"});return m}if(y=="top"){A._addToolbars(w,q)}if(y=="external"){r=z=k.create("div",{style:"position:relative"});r=k.add(r,"div",{id:x.id+"_external","class":"mceExternalToolbar"});k.add(r,"a",{id:x.id+"_external_close",href:"javascript:;","class":"mceExternalClose"});r=k.add(r,"table",{id:x.id+"_tblext",cellSpacing:0,cellPadding:0});v=k.add(r,"tbody");if(l.firstChild.className=="mceOldBoxModel"){l.firstChild.appendChild(z)}else{l.insertBefore(z,l.firstChild)}A._addToolbars(v,q);x.onMouseUp.add(function(){var o=k.get(x.id+"_external");k.show(o);k.hide(e);var n=h.add(x.id+"_external_close","click",function(){k.hide(x.id+"_external");h.remove(x.id+"_external_close","click",n);return false});k.show(o);k.setStyle(o,"top",0-k.getRect(x.id+"_tblext").h-1);k.hide(o);k.show(o);o.style.filter="";e=x.id+"_external";o=null})}if(u=="top"){A._addStatusBar(w,q)}if(!B.theme_advanced_toolbar_container){r=k.add(w,"tr");r=m=k.add(r,"td",{"class":"mceIframeContainer"})}if(y=="bottom"){A._addToolbars(w,q)}if(u=="bottom"){A._addStatusBar(w,q)}return m},_rowLayout:function(z,r,p){var y=this,u=y.editor,x,A,l=u.controlManager,q,m,w,v;x=z.theme_advanced_containers_default_class||"";A=z.theme_advanced_containers_default_align||"center";f(d(z.theme_advanced_containers||""),function(s,o){var n=z["theme_advanced_container_"+s]||"";switch(s.toLowerCase()){case"mceeditor":q=k.add(r,"tr");q=m=k.add(q,"td",{"class":"mceIframeContainer"});break;case"mceelementpath":y._addStatusBar(r,p);break;default:v=(z["theme_advanced_container_"+s+"_align"]||A).toLowerCase();v="mce"+y._ufirst(v);q=k.add(k.add(r,"tr"),"td",{"class":"mceToolbar "+(z["theme_advanced_container_"+s+"_class"]||x)+" "+v||A});w=l.createToolbar("toolbar"+o);y._addControls(n,w);k.setHTML(q,w.renderHTML());p.deltaHeight-=z.theme_advanced_row_height}});return m},_addControls:function(m,l){var n=this,o=n.settings,p,q=n.editor.controlManager;if(o.theme_advanced_disable&&!n._disabled){p={};f(d(o.theme_advanced_disable),function(r){p[r]=1});n._disabled=p}else{p=n._disabled}f(d(m),function(s){var r;if(p&&p[s]){return}if(s=="tablecontrols"){f(["table","|","row_props","cell_props","|","row_before","row_after","delete_row","|","col_before","col_after","delete_col","|","split_cells","merge_cells"],function(t){t=n.createControl(t,q);if(t){l.add(t)}});return}r=n.createControl(s,q);if(r){l.add(r)}})},_addToolbars:function(A,m){var D=this,u,r,x=D.editor,E=D.settings,C,l=x.controlManager,y,p,w=[],B,z,q=false;z=l.createToolbarGroup("toolbargroup",{name:x.getLang("advanced.toolbar"),tab_focus_toolbar:x.getParam("theme_advanced_tab_focus_toolbar")});D.toolbarGroup=z;B=E.theme_advanced_toolbar_align.toLowerCase();B="mce"+D._ufirst(B);p=k.add(k.add(A,"tr",{role:"presentation"}),"td",{"class":"mceToolbar "+B,role:"toolbar"});for(u=1;(C=E["theme_advanced_buttons"+u]);u++){q=true;r=l.createToolbar("toolbar"+u,{"class":"mceToolbarRow"+u});if(E["theme_advanced_buttons"+u+"_add"]){C+=","+E["theme_advanced_buttons"+u+"_add"]}if(E["theme_advanced_buttons"+u+"_add_before"]){C=E["theme_advanced_buttons"+u+"_add_before"]+","+C}D._addControls(C,r);z.add(r);m.deltaHeight-=E.theme_advanced_row_height}if(!q){m.deltaHeight-=E.theme_advanced_row_height}w.push(z.renderHTML());w.push(k.createHTML("a",{href:"#",accesskey:"z",title:x.getLang("advanced.toolbar_focus"),onfocus:"tinyMCE.getInstanceById('"+x.id+"').focus();"},"<!-- IE -->"));k.setHTML(p,w.join(""))},_addStatusBar:function(u,m){var p,y=this,v=y.editor,z=y.settings,l,w,x,q;p=k.add(u,"tr");p=q=k.add(p,"td",{"class":"mceStatusbar"});p=k.add(p,"div",{id:v.id+"_path_row",role:"group","aria-labelledby":v.id+"_path_voice"});if(z.theme_advanced_path){k.add(p,"span",{id:v.id+"_path_voice"},v.translate("advanced.path"));k.add(p,"span",{},": ")}else{k.add(p,"span",{},"&#160;")}if(z.theme_advanced_resizing){k.add(q,"a",{id:v.id+"_resize",href:"javascript:;",onclick:"return false;","class":"mceResize",tabIndex:"-1"});if(z.theme_advanced_resizing_use_cookie){v.onPostRender.add(function(){var n=a.getHash("TinyMCE_"+v.id+"_size"),r=k.get(v.id+"_tbl");if(!n){return}y.resizeTo(n.cw,n.ch)})}v.onPostRender.add(function(){h.add(v.id+"_resize","click",function(n){n.preventDefault()});h.add(v.id+"_resize","mousedown",function(G){var t,r,s,o,F,C,D,I,n,H,A;function B(J){J.preventDefault();n=D+(J.screenX-F);H=I+(J.screenY-C);y.resizeTo(n,H)}function E(J){h.remove(k.doc,"mousemove",t);h.remove(v.getDoc(),"mousemove",r);h.remove(k.doc,"mouseup",s);h.remove(v.getDoc(),"mouseup",o);n=D+(J.screenX-F);H=I+(J.screenY-C);y.resizeTo(n,H,true);v.nodeChanged()}G.preventDefault();F=G.screenX;C=G.screenY;A=k.get(y.editor.id+"_ifr");D=n=A.clientWidth;I=H=A.clientHeight;t=h.add(k.doc,"mousemove",B);r=h.add(v.getDoc(),"mousemove",B);s=h.add(k.doc,"mouseup",E);o=h.add(v.getDoc(),"mouseup",E)})})}m.deltaHeight-=21;p=u=null},_updateUndoStatus:function(m){var l=m.controlManager,n=m.undoManager;l.setDisabled("undo",!n.hasUndo()&&!n.typing);l.setDisabled("redo",!n.hasRedo())},_nodeChanged:function(u,z,I,y,J){var D=this,H,K=0,C,L,E=D.settings,B,q,A,G,r,o,m;i.each(D.stateControls,function(n){z.setActive(n,u.queryCommandState(D.controls[n][1]))});var x=function(t,n){var p,t,s=n;if(typeof(n)=="string"){s=function(v){return v.nodeName==n}}for(p=0;p<t.length;p++){if(s(t[p])){return t[p]}}};function w(n){return x(J.parents,n)}function l(n){return x([].concat(J.parents).concat([u.getBody()]),n)}z.setActive("visualaid",u.hasVisual);D._updateUndoStatus(u);z.setDisabled("outdent",!u.queryCommandState("Outdent"));H=w("A");if(L=z.get("link")){L.setDisabled((!H&&y)||(H&&!H.href));L.setActive(!!H&&(!H.name&&!H.id))}if(L=z.get("unlink")){L.setDisabled(!H&&y);L.setActive(!!H&&!H.name&&!H.id)}if(L=z.get("anchor")){L.setActive(!y&&!!H&&(H.name||(H.id&&!H.href)))}H=w("IMG");if(L=z.get("image")){L.setActive(!y&&!!H&&I.className.indexOf("mceItem")==-1)}if(L=z.get("styleselect")){D._importClasses();o=[];f(L.items,function(n){o.push(n.value)});m=u.formatter.matchAll(o);L.select(m[0]);i.each(m,function(p,n){if(n>0){L.mark(p)}})}if(L=z.get("formatselect")){H=w(u.dom.isBlock);if(H){L.select(H.nodeName.toLowerCase())}}l(function(s){var p;if(s.nodeName==="SPAN"){if(!B&&s.className){B=s.className}}p=u.dom.is(s,E.theme_advanced_font_selector);if(p||j(u.settings)){if(!q&&s.style.fontSize){q=s.style.fontSize}if(!A&&s.style.fontFamily){A=s.style.fontFamily.replace(/[\"\']+/g,"").replace(/^([^,]+).*/,"$1").toLowerCase()}if(!G&&s.style.color){G=s.style.color}if(!r&&s.style.backgroundColor){r=s.style.backgroundColor}}return false});if(L=z.get("fontselect")){L.select(function(n){return n.replace(/^([^,]+).*/,"$1").toLowerCase()==A})}if(L=z.get("fontsizeselect")){if(E.theme_advanced_runtime_fontsize&&!q&&!B){q=u.dom.getStyle(I,"fontSize",true)}L.select(function(n){if(n.fontSize&&n.fontSize===q){return true}if(n["class"]&&n["class"]===B){return true}})}if(E.theme_advanced_show_current_color){function F(p,n){if(L=z.get(p)){if(!n){n=L.settings.default_color}if(n!==L.value){L.displayColor(n)}}}F("forecolor",G);F("backcolor",r)}if(E.theme_advanced_show_current_color){function F(p,n){if(L=z.get(p)){if(!n){n=L.settings.default_color}if(n!==L.value){L.displayColor(n)}}}F("forecolor",G);F("backcolor",r)}if(E.theme_advanced_path&&E.theme_advanced_statusbar_location){H=k.get(u.id+"_path")||k.add(u.id+"_path_row","span",{id:u.id+"_path"});if(D.statusKeyboardNavigation){D.statusKeyboardNavigation.destroy();D.statusKeyboardNavigation=null}k.setHTML(H,"");w(function(M){var p=M.nodeName.toLowerCase(),s,v,t="";if(M.nodeType!=1||p==="br"||M.getAttribute("data-mce-bogus")||k.hasClass(M,"mceItemHidden")||k.hasClass(M,"mceItemRemoved")){return}if(i.isIE&&M.scopeName!=="HTML"&&M.scopeName){p=M.scopeName+":"+p}p=p.replace(/mce\:/g,"");switch(p){case"b":p="strong";break;case"i":p="em";break;case"img":if(C=k.getAttrib(M,"src")){t+="src: "+C+" "}break;case"a":if(C=k.getAttrib(M,"name")){t+="name: "+C+" ";p+="#"+C}if(C=k.getAttrib(M,"href")){t+="href: "+C+" "}break;case"font":if(C=k.getAttrib(M,"face")){t+="font: "+C+" "}if(C=k.getAttrib(M,"size")){t+="size: "+C+" "}if(C=k.getAttrib(M,"color")){t+="color: "+C+" "}break;case"span":if(C=k.getAttrib(M,"style")){t+="style: "+C+" "}break}if(C=k.getAttrib(M,"id")){t+="id: "+C+" "}if(C=M.className){C=C.replace(/\b\s*(webkit|mce|Apple-)\w+\s*\b/g,"");if(C){t+="class: "+C+" ";if(u.dom.isBlock(M)||p=="img"||p=="span"){p+="."+C}}}p=p.replace(/(html:)/g,"");p={name:p,node:M,title:t};D.onResolveName.dispatch(D,p);t=p.title;p=p.name;v=k.create("a",{href:"javascript:;",role:"button",onmousedown:"return false;",title:t,"class":"mcePath_"+(K++)},p);if(H.hasChildNodes()){H.insertBefore(k.create("span",{"aria-hidden":"true"},"\u00a0\u00bb "),H.firstChild);H.insertBefore(v,H.firstChild)}else{H.appendChild(v)}},u.getBody());if(k.select("a",H).length>0){D.statusKeyboardNavigation=new i.ui.KeyboardNavigation({root:u.id+"_path_row",items:k.select("a",H),excludeFromTabOrder:true,onCancel:function(){u.focus()}},k)}}},_sel:function(l){this.editor.execCommand("mceSelectNodeDepth",false,l)},_mceInsertAnchor:function(n,m){var l=this.editor;l.windowManager.open({url:this.url+"/anchor.htm",width:320+parseInt(l.getLang("advanced.anchor_delta_width",0)),height:90+parseInt(l.getLang("advanced.anchor_delta_height",0)),inline:true},{theme_url:this.url})},_mceCharMap:function(){var l=this.editor;l.windowManager.open({url:this.url+"/charmap.htm",width:550+parseInt(l.getLang("advanced.charmap_delta_width",0)),height:265+parseInt(l.getLang("advanced.charmap_delta_height",0)),inline:true},{theme_url:this.url})},_mceHelp:function(){var l=this.editor;l.windowManager.open({url:this.url+"/about.htm",width:480,height:380,inline:true},{theme_url:this.url})},_mceShortcuts:function(){var l=this.editor;l.windowManager.open({url:this.url+"/shortcuts.htm",width:480,height:380,inline:true},{theme_url:this.url})},_mceColorPicker:function(n,m){var l=this.editor;m=m||{};l.windowManager.open({url:this.url+"/color_picker.htm",width:375+parseInt(l.getLang("advanced.colorpicker_delta_width",0)),height:250+parseInt(l.getLang("advanced.colorpicker_delta_height",0)),close_previous:false,inline:true},{input_color:m.color,func:m.func,theme_url:this.url})},_mceCodeEditor:function(m,n){var l=this.editor;l.windowManager.open({url:this.url+"/source_editor.htm",width:parseInt(l.getParam("theme_advanced_source_editor_width",720)),height:parseInt(l.getParam("theme_advanced_source_editor_height",580)),inline:true,resizable:true,maximizable:true},{theme_url:this.url})},_mceImage:function(m,n){var l=this.editor;if(l.dom.getAttrib(l.selection.getNode(),"class","").indexOf("mceItem")!=-1){return}l.windowManager.open({url:this.url+"/image.htm",width:355+parseInt(l.getLang("advanced.image_delta_width",0)),height:275+parseInt(l.getLang("advanced.image_delta_height",0)),inline:true},{theme_url:this.url})},_mceLink:function(m,n){var l=this.editor;l.windowManager.open({url:this.url+"/link.htm",width:310+parseInt(l.getLang("advanced.link_delta_width",0)),height:200+parseInt(l.getLang("advanced.link_delta_height",0)),inline:true},{theme_url:this.url})},_mceNewDocument:function(){var l=this.editor;l.windowManager.confirm("advanced.newdocument",function(m){if(m){l.execCommand("mceSetContent",false,"")}})},_mceForeColor:function(){var l=this;this._mceColorPicker(0,{color:l.fgColor,func:function(m){l.fgColor=m;l.editor.execCommand("ForeColor",false,m)}})},_mceBackColor:function(){var l=this;this._mceColorPicker(0,{color:l.bgColor,func:function(m){l.bgColor=m;l.editor.execCommand("HiliteColor",false,m)}})},_ufirst:function(l){return l.substring(0,1).toUpperCase()+l.substring(1)}});i.ThemeManager.add("advanced",i.themes.AdvancedTheme)}(tinymce));
(function(){tinymce.create("tinymce.plugins.AdvancedHRPlugin",{init:function(a,b){a.addCommand("mceAdvancedHr",function(){a.windowManager.open({file:b+"/rule.htm",width:250+parseInt(a.getLang("advhr.delta_width",0)),height:160+parseInt(a.getLang("advhr.delta_height",0)),inline:1},{plugin_url:b})});a.addButton("advhr",{title:"advhr.advhr_desc",cmd:"mceAdvancedHr"});a.onNodeChange.add(function(d,c,e){c.setActive("advhr",e.nodeName=="HR")});a.onClick.add(function(c,d){d=d.target;if(d.nodeName==="HR"){c.selection.select(d)}})},getInfo:function(){return{longname:"Advanced HR",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/advhr",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("advhr",tinymce.plugins.AdvancedHRPlugin)})();
(function(){tinymce.create("tinymce.plugins.AdvancedLinkPlugin",{init:function(a,b){this.editor=a;a.addCommand("mceAdvLink",function(){var c=a.selection;if(c.isCollapsed()&&!a.dom.getParent(c.getNode(),"A")){return}a.windowManager.open({file:b+"/link.htm",width:480+parseInt(a.getLang("advlink.delta_width",0)),height:400+parseInt(a.getLang("advlink.delta_height",0)),inline:1},{plugin_url:b})});a.addButton("link",{title:"advlink.link_desc",cmd:"mceAdvLink"});a.addShortcut("ctrl+k","advlink.advlink_desc","mceAdvLink");a.onNodeChange.add(function(d,c,f,e){c.setDisabled("link",e&&f.nodeName!="A");c.setActive("link",f.nodeName=="A"&&!f.name)})},getInfo:function(){return{longname:"Advanced link",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/advlink",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("advlink",tinymce.plugins.AdvancedLinkPlugin)})();
(function(){tinymce.create("tinymce.plugins.AdvancedImagePlugin",{init:function(a,b){a.addCommand("mceAdvImage",function(){if(a.dom.getAttrib(a.selection.getNode(),"class","").indexOf("mceItem")!=-1){return}a.windowManager.open({file:b+"/image.htm",width:480+parseInt(a.getLang("advimage.delta_width",0)),height:385+parseInt(a.getLang("advimage.delta_height",0)),inline:1},{plugin_url:b})});a.addButton("image",{title:"advimage.image_desc",cmd:"mceAdvImage"})},getInfo:function(){return{longname:"Advanced image",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/advimage",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("advimage",tinymce.plugins.AdvancedImagePlugin)})();
(function(){tinymce.create("tinymce.plugins.Nonbreaking",{init:function(a,b){var c=this;c.editor=a;a.addCommand("mceNonBreaking",function(){a.execCommand("mceInsertContent",false,(a.plugins.visualchars&&a.plugins.visualchars.state)?'<span data-mce-bogus="1" class="mceItemHidden mceItemNbsp">&nbsp;</span>':"&nbsp;")});a.addButton("nonbreaking",{title:"nonbreaking.nonbreaking_desc",cmd:"mceNonBreaking"});if(a.getParam("nonbreaking_force_tab")){a.onKeyDown.add(function(d,f){if(f.keyCode==9){f.preventDefault();d.execCommand("mceNonBreaking");d.execCommand("mceNonBreaking");d.execCommand("mceNonBreaking")}})}},getInfo:function(){return{longname:"Nonbreaking space",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/nonbreaking",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("nonbreaking",tinymce.plugins.Nonbreaking)})();
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

// This is CodeMirror (http://codemirror.net), a code editor
// implemented in JavaScript on top of the browser's DOM.
//
// You can find some technical background for some of the code below
// at http://marijnhaverbeke.nl/blog/#cm-internals .

(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  typeof define === 'function' && define.amd ? define(factory) :
  (global.CodeMirror = factory());
}(this, (function () { 'use strict';

// Kludges for bugs and behavior differences that can't be feature
// detected are enabled based on userAgent etc sniffing.
var userAgent = navigator.userAgent
var platform = navigator.platform

var gecko = /gecko\/\d/i.test(userAgent)
var ie_upto10 = /MSIE \d/.test(userAgent)
var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent)
var edge = /Edge\/(\d+)/.exec(userAgent)
var ie = ie_upto10 || ie_11up || edge
var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1])
var webkit = !edge && /WebKit\//.test(userAgent)
var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent)
var chrome = !edge && /Chrome\//.test(userAgent)
var presto = /Opera\//.test(userAgent)
var safari = /Apple Computer/.test(navigator.vendor)
var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent)
var phantom = /PhantomJS/.test(userAgent)

var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent)
var android = /Android/.test(userAgent)
// This is woefully incomplete. Suggestions for alternative methods welcome.
var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent)
var mac = ios || /Mac/.test(platform)
var chromeOS = /\bCrOS\b/.test(userAgent)
var windows = /win/i.test(platform)

var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/)
if (presto_version) { presto_version = Number(presto_version[1]) }
if (presto_version && presto_version >= 15) { presto = false; webkit = true }
// Some browsers use the wrong event properties to signal cmd/ctrl on OS X
var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11))
var captureRightClick = gecko || (ie && ie_version >= 9)

function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }

var rmClass = function(node, cls) {
  var current = node.className
  var match = classTest(cls).exec(current)
  if (match) {
    var after = current.slice(match.index + match[0].length)
    node.className = current.slice(0, match.index) + (after ? match[1] + after : "")
  }
}

function removeChildren(e) {
  for (var count = e.childNodes.length; count > 0; --count)
    { e.removeChild(e.firstChild) }
  return e
}

function removeChildrenAndAdd(parent, e) {
  return removeChildren(parent).appendChild(e)
}

function elt(tag, content, className, style) {
  var e = document.createElement(tag)
  if (className) { e.className = className }
  if (style) { e.style.cssText = style }
  if (typeof content == "string") { e.appendChild(document.createTextNode(content)) }
  else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]) } }
  return e
}
// wrapper for elt, which removes the elt from the accessibility tree
function eltP(tag, content, className, style) {
  var e = elt(tag, content, className, style)
  e.setAttribute("role", "presentation")
  return e
}

var range
if (document.createRange) { range = function(node, start, end, endNode) {
  var r = document.createRange()
  r.setEnd(endNode || node, end)
  r.setStart(node, start)
  return r
} }
else { range = function(node, start, end) {
  var r = document.body.createTextRange()
  try { r.moveToElementText(node.parentNode) }
  catch(e) { return r }
  r.collapse(true)
  r.moveEnd("character", end)
  r.moveStart("character", start)
  return r
} }

function contains(parent, child) {
  if (child.nodeType == 3) // Android browser always returns false when child is a textnode
    { child = child.parentNode }
  if (parent.contains)
    { return parent.contains(child) }
  do {
    if (child.nodeType == 11) { child = child.host }
    if (child == parent) { return true }
  } while (child = child.parentNode)
}

function activeElt() {
  // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
  // IE < 10 will throw when accessed while the page is loading or in an iframe.
  // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
  var activeElement
  try {
    activeElement = document.activeElement
  } catch(e) {
    activeElement = document.body || null
  }
  while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
    { activeElement = activeElement.shadowRoot.activeElement }
  return activeElement
}

function addClass(node, cls) {
  var current = node.className
  if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls }
}
function joinClasses(a, b) {
  var as = a.split(" ")
  for (var i = 0; i < as.length; i++)
    { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i] } }
  return b
}

var selectInput = function(node) { node.select() }
if (ios) // Mobile Safari apparently has a bug where select() is broken.
  { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length } }
else if (ie) // Suppress mysterious IE10 errors
  { selectInput = function(node) { try { node.select() } catch(_e) {} } }

function bind(f) {
  var args = Array.prototype.slice.call(arguments, 1)
  return function(){return f.apply(null, args)}
}

function copyObj(obj, target, overwrite) {
  if (!target) { target = {} }
  for (var prop in obj)
    { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
      { target[prop] = obj[prop] } }
  return target
}

// Counts the column offset in a string, taking tabs into account.
// Used mostly to find indentation.
function countColumn(string, end, tabSize, startIndex, startValue) {
  if (end == null) {
    end = string.search(/[^\s\u00a0]/)
    if (end == -1) { end = string.length }
  }
  for (var i = startIndex || 0, n = startValue || 0;;) {
    var nextTab = string.indexOf("\t", i)
    if (nextTab < 0 || nextTab >= end)
      { return n + (end - i) }
    n += nextTab - i
    n += tabSize - (n % tabSize)
    i = nextTab + 1
  }
}

var Delayed = function() {this.id = null};
Delayed.prototype.set = function (ms, f) {
  clearTimeout(this.id)
  this.id = setTimeout(f, ms)
};

function indexOf(array, elt) {
  for (var i = 0; i < array.length; ++i)
    { if (array[i] == elt) { return i } }
  return -1
}

// Number of pixels added to scroller and sizer to hide scrollbar
var scrollerGap = 30

// Returned or thrown by various protocols to signal 'I'm not
// handling this'.
var Pass = {toString: function(){return "CodeMirror.Pass"}}

// Reused option objects for setSelection & friends
var sel_dontScroll = {scroll: false};
var sel_mouse = {origin: "*mouse"};
var sel_move = {origin: "+move"};
// The inverse of countColumn -- find the offset that corresponds to
// a particular column.
function findColumn(string, goal, tabSize) {
  for (var pos = 0, col = 0;;) {
    var nextTab = string.indexOf("\t", pos)
    if (nextTab == -1) { nextTab = string.length }
    var skipped = nextTab - pos
    if (nextTab == string.length || col + skipped >= goal)
      { return pos + Math.min(skipped, goal - col) }
    col += nextTab - pos
    col += tabSize - (col % tabSize)
    pos = nextTab + 1
    if (col >= goal) { return pos }
  }
}

var spaceStrs = [""]
function spaceStr(n) {
  while (spaceStrs.length <= n)
    { spaceStrs.push(lst(spaceStrs) + " ") }
  return spaceStrs[n]
}

function lst(arr) { return arr[arr.length-1] }

function map(array, f) {
  var out = []
  for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i) }
  return out
}

function insertSorted(array, value, score) {
  var pos = 0, priority = score(value)
  while (pos < array.length && score(array[pos]) <= priority) { pos++ }
  array.splice(pos, 0, value)
}

function nothing() {}

function createObj(base, props) {
  var inst
  if (Object.create) {
    inst = Object.create(base)
  } else {
    nothing.prototype = base
    inst = new nothing()
  }
  if (props) { copyObj(props, inst) }
  return inst
}

var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/
function isWordCharBasic(ch) {
  return /\w/.test(ch) || ch > "\x80" &&
    (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
}
function isWordChar(ch, helper) {
  if (!helper) { return isWordCharBasic(ch) }
  if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
  return helper.test(ch)
}

function isEmpty(obj) {
  for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
  return true
}

// Extending unicode characters. A series of a non-extending char +
// any number of extending chars is treated as a single unit as far
// as editing and measuring is concerned. This is not fully correct,
// since some scripts/fonts/browsers also treat other configurations
// of code points as a group.
var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/
function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }

// Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
function skipExtendingChars(str, pos, dir) {
  while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir }
  return pos
}

// Returns the value from the range [`from`; `to`] that satisfies
// `pred` and is closest to `from`. Assumes that at least `to` satisfies `pred`.
function findFirst(pred, from, to) {
  for (;;) {
    if (Math.abs(from - to) <= 1) { return pred(from) ? from : to }
    var mid = Math.floor((from + to) / 2)
    if (pred(mid)) { to = mid }
    else { from = mid }
  }
}

// The display handles the DOM integration, both for input reading
// and content drawing. It holds references to DOM nodes and
// display-related state.

function Display(place, doc, input) {
  var d = this
  this.input = input

  // Covers bottom-right square when both scrollbars are present.
  d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler")
  d.scrollbarFiller.setAttribute("cm-not-content", "true")
  // Covers bottom of gutter when coverGutterNextToScrollbar is on
  // and h scrollbar is present.
  d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler")
  d.gutterFiller.setAttribute("cm-not-content", "true")
  // Will contain the actual code, positioned to cover the viewport.
  d.lineDiv = eltP("div", null, "CodeMirror-code")
  // Elements are added to these to represent selection and cursors.
  d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1")
  d.cursorDiv = elt("div", null, "CodeMirror-cursors")
  // A visibility: hidden element used to find the size of things.
  d.measure = elt("div", null, "CodeMirror-measure")
  // When lines outside of the viewport are measured, they are drawn in this.
  d.lineMeasure = elt("div", null, "CodeMirror-measure")
  // Wraps everything that needs to exist inside the vertically-padded coordinate system
  d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
                    null, "position: relative; outline: none")
  var lines = eltP("div", [d.lineSpace], "CodeMirror-lines")
  // Moved around its parent to cover visible view.
  d.mover = elt("div", [lines], null, "position: relative")
  // Set to the height of the document, allowing scrolling.
  d.sizer = elt("div", [d.mover], "CodeMirror-sizer")
  d.sizerWidth = null
  // Behavior of elts with overflow: auto and padding is
  // inconsistent across browsers. This is used to ensure the
  // scrollable area is big enough.
  d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;")
  // Will contain the gutters, if any.
  d.gutters = elt("div", null, "CodeMirror-gutters")
  d.lineGutter = null
  // Actual scrollable element.
  d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll")
  d.scroller.setAttribute("tabIndex", "-1")
  // The element in which the editor lives.
  d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror")

  // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
  if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0 }
  if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true }

  if (place) {
    if (place.appendChild) { place.appendChild(d.wrapper) }
    else { place(d.wrapper) }
  }

  // Current rendered range (may be bigger than the view window).
  d.viewFrom = d.viewTo = doc.first
  d.reportedViewFrom = d.reportedViewTo = doc.first
  // Information about the rendered lines.
  d.view = []
  d.renderedView = null
  // Holds info about a single rendered line when it was rendered
  // for measurement, while not in view.
  d.externalMeasured = null
  // Empty space (in pixels) above the view
  d.viewOffset = 0
  d.lastWrapHeight = d.lastWrapWidth = 0
  d.updateLineNumbers = null

  d.nativeBarWidth = d.barHeight = d.barWidth = 0
  d.scrollbarsClipped = false

  // Used to only resize the line number gutter when necessary (when
  // the amount of lines crosses a boundary that makes its width change)
  d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null
  // Set to true when a non-horizontal-scrolling line widget is
  // added. As an optimization, line widget aligning is skipped when
  // this is false.
  d.alignWidgets = false

  d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null

  // Tracks the maximum line length so that the horizontal scrollbar
  // can be kept static when scrolling.
  d.maxLine = null
  d.maxLineLength = 0
  d.maxLineChanged = false

  // Used for measuring wheel scrolling granularity
  d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null

  // True when shift is held down.
  d.shift = false

  // Used to track whether anything happened since the context menu
  // was opened.
  d.selForContextMenu = null

  d.activeTouch = null

  input.init(d)
}

// Find the line object corresponding to the given line number.
function getLine(doc, n) {
  n -= doc.first
  if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
  var chunk = doc
  while (!chunk.lines) {
    for (var i = 0;; ++i) {
      var child = chunk.children[i], sz = child.chunkSize()
      if (n < sz) { chunk = child; break }
      n -= sz
    }
  }
  return chunk.lines[n]
}

// Get the part of a document between two positions, as an array of
// strings.
function getBetween(doc, start, end) {
  var out = [], n = start.line
  doc.iter(start.line, end.line + 1, function (line) {
    var text = line.text
    if (n == end.line) { text = text.slice(0, end.ch) }
    if (n == start.line) { text = text.slice(start.ch) }
    out.push(text)
    ++n
  })
  return out
}
// Get the lines between from and to, as array of strings.
function getLines(doc, from, to) {
  var out = []
  doc.iter(from, to, function (line) { out.push(line.text) }) // iter aborts when callback returns truthy value
  return out
}

// Update the height of a line, propagating the height change
// upwards to parent nodes.
function updateLineHeight(line, height) {
  var diff = height - line.height
  if (diff) { for (var n = line; n; n = n.parent) { n.height += diff } }
}

// Given a line object, find its line number by walking up through
// its parent links.
function lineNo(line) {
  if (line.parent == null) { return null }
  var cur = line.parent, no = indexOf(cur.lines, line)
  for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
    for (var i = 0;; ++i) {
      if (chunk.children[i] == cur) { break }
      no += chunk.children[i].chunkSize()
    }
  }
  return no + cur.first
}

// Find the line at the given vertical position, using the height
// information in the document tree.
function lineAtHeight(chunk, h) {
  var n = chunk.first
  outer: do {
    for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
      var child = chunk.children[i$1], ch = child.height
      if (h < ch) { chunk = child; continue outer }
      h -= ch
      n += child.chunkSize()
    }
    return n
  } while (!chunk.lines)
  var i = 0
  for (; i < chunk.lines.length; ++i) {
    var line = chunk.lines[i], lh = line.height
    if (h < lh) { break }
    h -= lh
  }
  return n + i
}

function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}

function lineNumberFor(options, i) {
  return String(options.lineNumberFormatter(i + options.firstLineNumber))
}

// A Pos instance represents a position within the text.
function Pos(line, ch, sticky) {
  if ( sticky === void 0 ) sticky = null;

  if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
  this.line = line
  this.ch = ch
  this.sticky = sticky
}

// Compare two positions, return 0 if they are the same, a negative
// number when a is less, and a positive number otherwise.
function cmp(a, b) { return a.line - b.line || a.ch - b.ch }

function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }

function copyPos(x) {return Pos(x.line, x.ch)}
function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
function minPos(a, b) { return cmp(a, b) < 0 ? a : b }

// Most of the external API clips given positions to make sure they
// actually exist within the document.
function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
function clipPos(doc, pos) {
  if (pos.line < doc.first) { return Pos(doc.first, 0) }
  var last = doc.first + doc.size - 1
  if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
  return clipToLen(pos, getLine(doc, pos.line).text.length)
}
function clipToLen(pos, linelen) {
  var ch = pos.ch
  if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
  else if (ch < 0) { return Pos(pos.line, 0) }
  else { return pos }
}
function clipPosArray(doc, array) {
  var out = []
  for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]) }
  return out
}

// Optimize some code when these features are not used.
var sawReadOnlySpans = false;
var sawCollapsedSpans = false;
function seeReadOnlySpans() {
  sawReadOnlySpans = true
}

function seeCollapsedSpans() {
  sawCollapsedSpans = true
}

// TEXTMARKER SPANS

function MarkedSpan(marker, from, to) {
  this.marker = marker
  this.from = from; this.to = to
}

// Search an array of spans for a span matching the given marker.
function getMarkedSpanFor(spans, marker) {
  if (spans) { for (var i = 0; i < spans.length; ++i) {
    var span = spans[i]
    if (span.marker == marker) { return span }
  } }
}
// Remove a span from an array, returning undefined if no spans are
// left (we don't store arrays for lines without spans).
function removeMarkedSpan(spans, span) {
  var r
  for (var i = 0; i < spans.length; ++i)
    { if (spans[i] != span) { (r || (r = [])).push(spans[i]) } }
  return r
}
// Add a span to a line.
function addMarkedSpan(line, span) {
  line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]
  span.marker.attachLine(line)
}

// Used for the algorithm that adjusts markers for a change in the
// document. These functions cut an array of spans at a given
// character position, returning an array of remaining chunks (or
// undefined if nothing remains).
function markedSpansBefore(old, startCh, isInsert) {
  var nw
  if (old) { for (var i = 0; i < old.length; ++i) {
    var span = old[i], marker = span.marker
    var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh)
    if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
      var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
      ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to))
    }
  } }
  return nw
}
function markedSpansAfter(old, endCh, isInsert) {
  var nw
  if (old) { for (var i = 0; i < old.length; ++i) {
    var span = old[i], marker = span.marker
    var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh)
    if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
      var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
      ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
                                            span.to == null ? null : span.to - endCh))
    }
  } }
  return nw
}

// Given a change object, compute the new set of marker spans that
// cover the line in which the change took place. Removes spans
// entirely within the change, reconnects spans belonging to the
// same marker that appear on both sides of the change, and cuts off
// spans partially within the change. Returns an array of span
// arrays with one element for each line in (after) the change.
function stretchSpansOverChange(doc, change) {
  if (change.full) { return null }
  var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans
  var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans
  if (!oldFirst && !oldLast) { return null }

  var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0
  // Get the spans that 'stick out' on both sides
  var first = markedSpansBefore(oldFirst, startCh, isInsert)
  var last = markedSpansAfter(oldLast, endCh, isInsert)

  // Next, merge those two ends
  var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0)
  if (first) {
    // Fix up .to properties of first
    for (var i = 0; i < first.length; ++i) {
      var span = first[i]
      if (span.to == null) {
        var found = getMarkedSpanFor(last, span.marker)
        if (!found) { span.to = startCh }
        else if (sameLine) { span.to = found.to == null ? null : found.to + offset }
      }
    }
  }
  if (last) {
    // Fix up .from in last (or move them into first in case of sameLine)
    for (var i$1 = 0; i$1 < last.length; ++i$1) {
      var span$1 = last[i$1]
      if (span$1.to != null) { span$1.to += offset }
      if (span$1.from == null) {
        var found$1 = getMarkedSpanFor(first, span$1.marker)
        if (!found$1) {
          span$1.from = offset
          if (sameLine) { (first || (first = [])).push(span$1) }
        }
      } else {
        span$1.from += offset
        if (sameLine) { (first || (first = [])).push(span$1) }
      }
    }
  }
  // Make sure we didn't create any zero-length spans
  if (first) { first = clearEmptySpans(first) }
  if (last && last != first) { last = clearEmptySpans(last) }

  var newMarkers = [first]
  if (!sameLine) {
    // Fill gap with whole-line-spans
    var gap = change.text.length - 2, gapMarkers
    if (gap > 0 && first)
      { for (var i$2 = 0; i$2 < first.length; ++i$2)
        { if (first[i$2].to == null)
          { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)) } } }
    for (var i$3 = 0; i$3 < gap; ++i$3)
      { newMarkers.push(gapMarkers) }
    newMarkers.push(last)
  }
  return newMarkers
}

// Remove spans that are empty and don't have a clearWhenEmpty
// option of false.
function clearEmptySpans(spans) {
  for (var i = 0; i < spans.length; ++i) {
    var span = spans[i]
    if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
      { spans.splice(i--, 1) }
  }
  if (!spans.length) { return null }
  return spans
}

// Used to 'clip' out readOnly ranges when making a change.
function removeReadOnlyRanges(doc, from, to) {
  var markers = null
  doc.iter(from.line, to.line + 1, function (line) {
    if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
      var mark = line.markedSpans[i].marker
      if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
        { (markers || (markers = [])).push(mark) }
    } }
  })
  if (!markers) { return null }
  var parts = [{from: from, to: to}]
  for (var i = 0; i < markers.length; ++i) {
    var mk = markers[i], m = mk.find(0)
    for (var j = 0; j < parts.length; ++j) {
      var p = parts[j]
      if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
      var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to)
      if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
        { newParts.push({from: p.from, to: m.from}) }
      if (dto > 0 || !mk.inclusiveRight && !dto)
        { newParts.push({from: m.to, to: p.to}) }
      parts.splice.apply(parts, newParts)
      j += newParts.length - 3
    }
  }
  return parts
}

// Connect or disconnect spans from a line.
function detachMarkedSpans(line) {
  var spans = line.markedSpans
  if (!spans) { return }
  for (var i = 0; i < spans.length; ++i)
    { spans[i].marker.detachLine(line) }
  line.markedSpans = null
}
function attachMarkedSpans(line, spans) {
  if (!spans) { return }
  for (var i = 0; i < spans.length; ++i)
    { spans[i].marker.attachLine(line) }
  line.markedSpans = spans
}

// Helpers used when computing which overlapping collapsed span
// counts as the larger one.
function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }

// Returns a number indicating which of two overlapping collapsed
// spans is larger (and thus includes the other). Falls back to
// comparing ids when the spans cover exactly the same range.
function compareCollapsedMarkers(a, b) {
  var lenDiff = a.lines.length - b.lines.length
  if (lenDiff != 0) { return lenDiff }
  var aPos = a.find(), bPos = b.find()
  var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b)
  if (fromCmp) { return -fromCmp }
  var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b)
  if (toCmp) { return toCmp }
  return b.id - a.id
}

// Find out whether a line ends or starts in a collapsed span. If
// so, return the marker for that span.
function collapsedSpanAtSide(line, start) {
  var sps = sawCollapsedSpans && line.markedSpans, found
  if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
    sp = sps[i]
    if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
        (!found || compareCollapsedMarkers(found, sp.marker) < 0))
      { found = sp.marker }
  } }
  return found
}
function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }

// Test whether there exists a collapsed span that partially
// overlaps (covers the start or end, but not both) of a new span.
// Such overlap is not allowed.
function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
  var line = getLine(doc, lineNo)
  var sps = sawCollapsedSpans && line.markedSpans
  if (sps) { for (var i = 0; i < sps.length; ++i) {
    var sp = sps[i]
    if (!sp.marker.collapsed) { continue }
    var found = sp.marker.find(0)
    var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker)
    var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker)
    if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
    if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
        fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
      { return true }
  } }
}

// A visual line is a line as drawn on the screen. Folding, for
// example, can cause multiple logical lines to appear on the same
// visual line. This finds the start of the visual line that the
// given line is part of (usually that is the line itself).
function visualLine(line) {
  var merged
  while (merged = collapsedSpanAtStart(line))
    { line = merged.find(-1, true).line }
  return line
}

function visualLineEnd(line) {
  var merged
  while (merged = collapsedSpanAtEnd(line))
    { line = merged.find(1, true).line }
  return line
}

// Returns an array of logical lines that continue the visual line
// started by the argument, or undefined if there are no such lines.
function visualLineContinued(line) {
  var merged, lines
  while (merged = collapsedSpanAtEnd(line)) {
    line = merged.find(1, true).line
    ;(lines || (lines = [])).push(line)
  }
  return lines
}

// Get the line number of the start of the visual line that the
// given line number is part of.
function visualLineNo(doc, lineN) {
  var line = getLine(doc, lineN), vis = visualLine(line)
  if (line == vis) { return lineN }
  return lineNo(vis)
}

// Get the line number of the start of the next visual line after
// the given line.
function visualLineEndNo(doc, lineN) {
  if (lineN > doc.lastLine()) { return lineN }
  var line = getLine(doc, lineN), merged
  if (!lineIsHidden(doc, line)) { return lineN }
  while (merged = collapsedSpanAtEnd(line))
    { line = merged.find(1, true).line }
  return lineNo(line) + 1
}

// Compute whether a line is hidden. Lines count as hidden when they
// are part of a visual line that starts with another line, or when
// they are entirely covered by collapsed, non-widget span.
function lineIsHidden(doc, line) {
  var sps = sawCollapsedSpans && line.markedSpans
  if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
    sp = sps[i]
    if (!sp.marker.collapsed) { continue }
    if (sp.from == null) { return true }
    if (sp.marker.widgetNode) { continue }
    if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
      { return true }
  } }
}
function lineIsHiddenInner(doc, line, span) {
  if (span.to == null) {
    var end = span.marker.find(1, true)
    return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
  }
  if (span.marker.inclusiveRight && span.to == line.text.length)
    { return true }
  for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
    sp = line.markedSpans[i]
    if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
        (sp.to == null || sp.to != span.from) &&
        (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
        lineIsHiddenInner(doc, line, sp)) { return true }
  }
}

// Find the height above the given line.
function heightAtLine(lineObj) {
  lineObj = visualLine(lineObj)

  var h = 0, chunk = lineObj.parent
  for (var i = 0; i < chunk.lines.length; ++i) {
    var line = chunk.lines[i]
    if (line == lineObj) { break }
    else { h += line.height }
  }
  for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
    for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
      var cur = p.children[i$1]
      if (cur == chunk) { break }
      else { h += cur.height }
    }
  }
  return h
}

// Compute the character length of a line, taking into account
// collapsed ranges (see markText) that might hide parts, and join
// other lines onto it.
function lineLength(line) {
  if (line.height == 0) { return 0 }
  var len = line.text.length, merged, cur = line
  while (merged = collapsedSpanAtStart(cur)) {
    var found = merged.find(0, true)
    cur = found.from.line
    len += found.from.ch - found.to.ch
  }
  cur = line
  while (merged = collapsedSpanAtEnd(cur)) {
    var found$1 = merged.find(0, true)
    len -= cur.text.length - found$1.from.ch
    cur = found$1.to.line
    len += cur.text.length - found$1.to.ch
  }
  return len
}

// Find the longest line in the document.
function findMaxLine(cm) {
  var d = cm.display, doc = cm.doc
  d.maxLine = getLine(doc, doc.first)
  d.maxLineLength = lineLength(d.maxLine)
  d.maxLineChanged = true
  doc.iter(function (line) {
    var len = lineLength(line)
    if (len > d.maxLineLength) {
      d.maxLineLength = len
      d.maxLine = line
    }
  })
}

// BIDI HELPERS

function iterateBidiSections(order, from, to, f) {
  if (!order) { return f(from, to, "ltr") }
  var found = false
  for (var i = 0; i < order.length; ++i) {
    var part = order[i]
    if (part.from < to && part.to > from || from == to && part.to == from) {
      f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr")
      found = true
    }
  }
  if (!found) { f(from, to, "ltr") }
}

var bidiOther = null
function getBidiPartAt(order, ch, sticky) {
  var found
  bidiOther = null
  for (var i = 0; i < order.length; ++i) {
    var cur = order[i]
    if (cur.from < ch && cur.to > ch) { return i }
    if (cur.to == ch) {
      if (cur.from != cur.to && sticky == "before") { found = i }
      else { bidiOther = i }
    }
    if (cur.from == ch) {
      if (cur.from != cur.to && sticky != "before") { found = i }
      else { bidiOther = i }
    }
  }
  return found != null ? found : bidiOther
}

// Bidirectional ordering algorithm
// See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
// that this (partially) implements.

// One-char codes used for character types:
// L (L):   Left-to-Right
// R (R):   Right-to-Left
// r (AL):  Right-to-Left Arabic
// 1 (EN):  European Number
// + (ES):  European Number Separator
// % (ET):  European Number Terminator
// n (AN):  Arabic Number
// , (CS):  Common Number Separator
// m (NSM): Non-Spacing Mark
// b (BN):  Boundary Neutral
// s (B):   Paragraph Separator
// t (S):   Segment Separator
// w (WS):  Whitespace
// N (ON):  Other Neutrals

// Returns null if characters are ordered as they appear
// (left-to-right), or an array of sections ({from, to, level}
// objects) in the order in which they occur visually.
var bidiOrdering = (function() {
  // Character types for codepoints 0 to 0xff
  var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"
  // Character types for codepoints 0x600 to 0x6f9
  var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"
  function charType(code) {
    if (code <= 0xf7) { return lowTypes.charAt(code) }
    else if (0x590 <= code && code <= 0x5f4) { return "R" }
    else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
    else if (0x6ee <= code && code <= 0x8ac) { return "r" }
    else if (0x2000 <= code && code <= 0x200b) { return "w" }
    else if (code == 0x200c) { return "b" }
    else { return "L" }
  }

  var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/
  var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/

  function BidiSpan(level, from, to) {
    this.level = level
    this.from = from; this.to = to
  }

  return function(str, direction) {
    var outerType = direction == "ltr" ? "L" : "R"

    if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
    var len = str.length, types = []
    for (var i = 0; i < len; ++i)
      { types.push(charType(str.charCodeAt(i))) }

    // W1. Examine each non-spacing mark (NSM) in the level run, and
    // change the type of the NSM to the type of the previous
    // character. If the NSM is at the start of the level run, it will
    // get the type of sor.
    for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
      var type = types[i$1]
      if (type == "m") { types[i$1] = prev }
      else { prev = type }
    }

    // W2. Search backwards from each instance of a European number
    // until the first strong type (R, L, AL, or sor) is found. If an
    // AL is found, change the type of the European number to Arabic
    // number.
    // W3. Change all ALs to R.
    for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
      var type$1 = types[i$2]
      if (type$1 == "1" && cur == "r") { types[i$2] = "n" }
      else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R" } }
    }

    // W4. A single European separator between two European numbers
    // changes to a European number. A single common separator between
    // two numbers of the same type changes to that type.
    for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
      var type$2 = types[i$3]
      if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1" }
      else if (type$2 == "," && prev$1 == types[i$3+1] &&
               (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1 }
      prev$1 = type$2
    }

    // W5. A sequence of European terminators adjacent to European
    // numbers changes to all European numbers.
    // W6. Otherwise, separators and terminators change to Other
    // Neutral.
    for (var i$4 = 0; i$4 < len; ++i$4) {
      var type$3 = types[i$4]
      if (type$3 == ",") { types[i$4] = "N" }
      else if (type$3 == "%") {
        var end = (void 0)
        for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
        var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"
        for (var j = i$4; j < end; ++j) { types[j] = replace }
        i$4 = end - 1
      }
    }

    // W7. Search backwards from each instance of a European number
    // until the first strong type (R, L, or sor) is found. If an L is
    // found, then change the type of the European number to L.
    for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
      var type$4 = types[i$5]
      if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L" }
      else if (isStrong.test(type$4)) { cur$1 = type$4 }
    }

    // N1. A sequence of neutrals takes the direction of the
    // surrounding strong text if the text on both sides has the same
    // direction. European and Arabic numbers act as if they were R in
    // terms of their influence on neutrals. Start-of-level-run (sor)
    // and end-of-level-run (eor) are used at level run boundaries.
    // N2. Any remaining neutrals take the embedding direction.
    for (var i$6 = 0; i$6 < len; ++i$6) {
      if (isNeutral.test(types[i$6])) {
        var end$1 = (void 0)
        for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
        var before = (i$6 ? types[i$6-1] : outerType) == "L"
        var after = (end$1 < len ? types[end$1] : outerType) == "L"
        var replace$1 = before == after ? (before ? "L" : "R") : outerType
        for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1 }
        i$6 = end$1 - 1
      }
    }

    // Here we depart from the documented algorithm, in order to avoid
    // building up an actual levels array. Since there are only three
    // levels (0, 1, 2) in an implementation that doesn't take
    // explicit embedding into account, we can build up the order on
    // the fly, without following the level-based algorithm.
    var order = [], m
    for (var i$7 = 0; i$7 < len;) {
      if (countsAsLeft.test(types[i$7])) {
        var start = i$7
        for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
        order.push(new BidiSpan(0, start, i$7))
      } else {
        var pos = i$7, at = order.length
        for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
        for (var j$2 = pos; j$2 < i$7;) {
          if (countsAsNum.test(types[j$2])) {
            if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)) }
            var nstart = j$2
            for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
            order.splice(at, 0, new BidiSpan(2, nstart, j$2))
            pos = j$2
          } else { ++j$2 }
        }
        if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)) }
      }
    }
    if (order[0].level == 1 && (m = str.match(/^\s+/))) {
      order[0].from = m[0].length
      order.unshift(new BidiSpan(0, 0, m[0].length))
    }
    if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
      lst(order).to -= m[0].length
      order.push(new BidiSpan(0, len - m[0].length, len))
    }

    return direction == "rtl" ? order.reverse() : order
  }
})()

// Get the bidi ordering for the given line (and cache it). Returns
// false for lines that are fully left-to-right, and an array of
// BidiSpan objects otherwise.
function getOrder(line, direction) {
  var order = line.order
  if (order == null) { order = line.order = bidiOrdering(line.text, direction) }
  return order
}

function moveCharLogically(line, ch, dir) {
  var target = skipExtendingChars(line.text, ch + dir, dir)
  return target < 0 || target > line.text.length ? null : target
}

function moveLogically(line, start, dir) {
  var ch = moveCharLogically(line, start.ch, dir)
  return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
}

function endOfLine(visually, cm, lineObj, lineNo, dir) {
  if (visually) {
    var order = getOrder(lineObj, cm.doc.direction)
    if (order) {
      var part = dir < 0 ? lst(order) : order[0]
      var moveInStorageOrder = (dir < 0) == (part.level == 1)
      var sticky = moveInStorageOrder ? "after" : "before"
      var ch
      // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
      // it could be that the last bidi part is not on the last visual line,
      // since visual lines contain content order-consecutive chunks.
      // Thus, in rtl, we are looking for the first (content-order) character
      // in the rtl chunk that is on the last line (that is, the same line
      // as the last (content-order) character).
      if (part.level > 0) {
        var prep = prepareMeasureForLine(cm, lineObj)
        ch = dir < 0 ? lineObj.text.length - 1 : 0
        var targetTop = measureCharPrepared(cm, prep, ch).top
        ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch)
        if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1, true) }
      } else { ch = dir < 0 ? part.to : part.from }
      return new Pos(lineNo, ch, sticky)
    }
  }
  return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
}

function moveVisually(cm, line, start, dir) {
  var bidi = getOrder(line, cm.doc.direction)
  if (!bidi) { return moveLogically(line, start, dir) }
  if (start.ch >= line.text.length) {
    start.ch = line.text.length
    start.sticky = "before"
  } else if (start.ch <= 0) {
    start.ch = 0
    start.sticky = "after"
  }
  var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]
  if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
    // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
    // nothing interesting happens.
    return moveLogically(line, start, dir)
  }

  var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }
  var prep
  var getWrappedLineExtent = function (ch) {
    if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
    prep = prep || prepareMeasureForLine(cm, line)
    return wrappedLineExtentChar(cm, line, prep, ch)
  }
  var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch)

  if (cm.doc.direction == "rtl" || part.level == 1) {
    var moveInStorageOrder = (part.level == 1) == (dir < 0)
    var ch = mv(start, moveInStorageOrder ? 1 : -1)
    if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
      // Case 2: We move within an rtl part or in an rtl editor on the same visual line
      var sticky = moveInStorageOrder ? "before" : "after"
      return new Pos(start.line, ch, sticky)
    }
  }

  // Case 3: Could not move within this bidi part in this visual line, so leave
  // the current bidi part

  var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
    var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
      ? new Pos(start.line, mv(ch, 1), "before")
      : new Pos(start.line, ch, "after"); }

    for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
      var part = bidi[partPos]
      var moveInStorageOrder = (dir > 0) == (part.level != 1)
      var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1)
      if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
      ch = moveInStorageOrder ? part.from : mv(part.to, -1)
      if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
    }
  }

  // Case 3a: Look for other bidi parts on the same visual line
  var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent)
  if (res) { return res }

  // Case 3b: Look for other bidi parts on the next visual line
  var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1)
  if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
    res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh))
    if (res) { return res }
  }

  // Case 4: Nowhere to move
  return null
}

// EVENT HANDLING

// Lightweight event framework. on/off also work on DOM nodes,
// registering native DOM handlers.

var noHandlers = []

var on = function(emitter, type, f) {
  if (emitter.addEventListener) {
    emitter.addEventListener(type, f, false)
  } else if (emitter.attachEvent) {
    emitter.attachEvent("on" + type, f)
  } else {
    var map = emitter._handlers || (emitter._handlers = {})
    map[type] = (map[type] || noHandlers).concat(f)
  }
}

function getHandlers(emitter, type) {
  return emitter._handlers && emitter._handlers[type] || noHandlers
}

function off(emitter, type, f) {
  if (emitter.removeEventListener) {
    emitter.removeEventListener(type, f, false)
  } else if (emitter.detachEvent) {
    emitter.detachEvent("on" + type, f)
  } else {
    var map = emitter._handlers, arr = map && map[type]
    if (arr) {
      var index = indexOf(arr, f)
      if (index > -1)
        { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)) }
    }
  }
}

function signal(emitter, type /*, values...*/) {
  var handlers = getHandlers(emitter, type)
  if (!handlers.length) { return }
  var args = Array.prototype.slice.call(arguments, 2)
  for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args) }
}

// The DOM events that CodeMirror handles can be overridden by
// registering a (non-DOM) handler on the editor for the event name,
// and preventDefault-ing the event in that handler.
function signalDOMEvent(cm, e, override) {
  if (typeof e == "string")
    { e = {type: e, preventDefault: function() { this.defaultPrevented = true }} }
  signal(cm, override || e.type, cm, e)
  return e_defaultPrevented(e) || e.codemirrorIgnore
}

function signalCursorActivity(cm) {
  var arr = cm._handlers && cm._handlers.cursorActivity
  if (!arr) { return }
  var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = [])
  for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
    { set.push(arr[i]) } }
}

function hasHandler(emitter, type) {
  return getHandlers(emitter, type).length > 0
}

// Add on and off methods to a constructor's prototype, to make
// registering events on such objects more convenient.
function eventMixin(ctor) {
  ctor.prototype.on = function(type, f) {on(this, type, f)}
  ctor.prototype.off = function(type, f) {off(this, type, f)}
}

// Due to the fact that we still support jurassic IE versions, some
// compatibility wrappers are needed.

function e_preventDefault(e) {
  if (e.preventDefault) { e.preventDefault() }
  else { e.returnValue = false }
}
function e_stopPropagation(e) {
  if (e.stopPropagation) { e.stopPropagation() }
  else { e.cancelBubble = true }
}
function e_defaultPrevented(e) {
  return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
}
function e_stop(e) {e_preventDefault(e); e_stopPropagation(e)}

function e_target(e) {return e.target || e.srcElement}
function e_button(e) {
  var b = e.which
  if (b == null) {
    if (e.button & 1) { b = 1 }
    else if (e.button & 2) { b = 3 }
    else if (e.button & 4) { b = 2 }
  }
  if (mac && e.ctrlKey && b == 1) { b = 3 }
  return b
}

// Detect drag-and-drop
var dragAndDrop = function() {
  // There is *some* kind of drag-and-drop support in IE6-8, but I
  // couldn't get it to work yet.
  if (ie && ie_version < 9) { return false }
  var div = elt('div')
  return "draggable" in div || "dragDrop" in div
}()

var zwspSupported
function zeroWidthElement(measure) {
  if (zwspSupported == null) {
    var test = elt("span", "\u200b")
    removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]))
    if (measure.firstChild.offsetHeight != 0)
      { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8) }
  }
  var node = zwspSupported ? elt("span", "\u200b") :
    elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px")
  node.setAttribute("cm-text", "")
  return node
}

// Feature-detect IE's crummy client rect reporting for bidi text
var badBidiRects
function hasBadBidiRects(measure) {
  if (badBidiRects != null) { return badBidiRects }
  var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"))
  var r0 = range(txt, 0, 1).getBoundingClientRect()
  var r1 = range(txt, 1, 2).getBoundingClientRect()
  removeChildren(measure)
  if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
  return badBidiRects = (r1.right - r0.right < 3)
}

// See if "".split is the broken IE version, if so, provide an
// alternative way to split lines.
var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
  var pos = 0, result = [], l = string.length
  while (pos <= l) {
    var nl = string.indexOf("\n", pos)
    if (nl == -1) { nl = string.length }
    var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl)
    var rt = line.indexOf("\r")
    if (rt != -1) {
      result.push(line.slice(0, rt))
      pos += rt + 1
    } else {
      result.push(line)
      pos = nl + 1
    }
  }
  return result
} : function (string) { return string.split(/\r\n?|\n/); }

var hasSelection = window.getSelection ? function (te) {
  try { return te.selectionStart != te.selectionEnd }
  catch(e) { return false }
} : function (te) {
  var range
  try {range = te.ownerDocument.selection.createRange()}
  catch(e) {}
  if (!range || range.parentElement() != te) { return false }
  return range.compareEndPoints("StartToEnd", range) != 0
}

var hasCopyEvent = (function () {
  var e = elt("div")
  if ("oncopy" in e) { return true }
  e.setAttribute("oncopy", "return;")
  return typeof e.oncopy == "function"
})()

var badZoomedRects = null
function hasBadZoomedRects(measure) {
  if (badZoomedRects != null) { return badZoomedRects }
  var node = removeChildrenAndAdd(measure, elt("span", "x"))
  var normal = node.getBoundingClientRect()
  var fromRange = range(node, 0, 1).getBoundingClientRect()
  return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
}

var modes = {};
var mimeModes = {};
// Extra arguments are stored as the mode's dependencies, which is
// used by (legacy) mechanisms like loadmode.js to automatically
// load a mode. (Preferred mechanism is the require/define calls.)
function defineMode(name, mode) {
  if (arguments.length > 2)
    { mode.dependencies = Array.prototype.slice.call(arguments, 2) }
  modes[name] = mode
}

function defineMIME(mime, spec) {
  mimeModes[mime] = spec
}

// Given a MIME type, a {name, ...options} config object, or a name
// string, return a mode config object.
function resolveMode(spec) {
  if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
    spec = mimeModes[spec]
  } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
    var found = mimeModes[spec.name]
    if (typeof found == "string") { found = {name: found} }
    spec = createObj(found, spec)
    spec.name = found.name
  } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
    return resolveMode("application/xml")
  } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
    return resolveMode("application/json")
  }
  if (typeof spec == "string") { return {name: spec} }
  else { return spec || {name: "null"} }
}

// Given a mode spec (anything that resolveMode accepts), find and
// initialize an actual mode object.
function getMode(options, spec) {
  spec = resolveMode(spec)
  var mfactory = modes[spec.name]
  if (!mfactory) { return getMode(options, "text/plain") }
  var modeObj = mfactory(options, spec)
  if (modeExtensions.hasOwnProperty(spec.name)) {
    var exts = modeExtensions[spec.name]
    for (var prop in exts) {
      if (!exts.hasOwnProperty(prop)) { continue }
      if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop] }
      modeObj[prop] = exts[prop]
    }
  }
  modeObj.name = spec.name
  if (spec.helperType) { modeObj.helperType = spec.helperType }
  if (spec.modeProps) { for (var prop$1 in spec.modeProps)
    { modeObj[prop$1] = spec.modeProps[prop$1] } }

  return modeObj
}

// This can be used to attach properties to mode objects from
// outside the actual mode definition.
var modeExtensions = {}
function extendMode(mode, properties) {
  var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {})
  copyObj(properties, exts)
}

function copyState(mode, state) {
  if (state === true) { return state }
  if (mode.copyState) { return mode.copyState(state) }
  var nstate = {}
  for (var n in state) {
    var val = state[n]
    if (val instanceof Array) { val = val.concat([]) }
    nstate[n] = val
  }
  return nstate
}

// Given a mode and a state (for that mode), find the inner mode and
// state at the position that the state refers to.
function innerMode(mode, state) {
  var info
  while (mode.innerMode) {
    info = mode.innerMode(state)
    if (!info || info.mode == mode) { break }
    state = info.state
    mode = info.mode
  }
  return info || {mode: mode, state: state}
}

function startState(mode, a1, a2) {
  return mode.startState ? mode.startState(a1, a2) : true
}

// STRING STREAM

// Fed to the mode parsers, provides helper functions to make
// parsers more succinct.

var StringStream = function(string, tabSize) {
  this.pos = this.start = 0
  this.string = string
  this.tabSize = tabSize || 8
  this.lastColumnPos = this.lastColumnValue = 0
  this.lineStart = 0
};

StringStream.prototype.eol = function () {return this.pos >= this.string.length};
StringStream.prototype.sol = function () {return this.pos == this.lineStart};
StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
StringStream.prototype.next = function () {
  if (this.pos < this.string.length)
    { return this.string.charAt(this.pos++) }
};
StringStream.prototype.eat = function (match) {
  var ch = this.string.charAt(this.pos)
  var ok
  if (typeof match == "string") { ok = ch == match }
  else { ok = ch && (match.test ? match.test(ch) : match(ch)) }
  if (ok) {++this.pos; return ch}
};
StringStream.prototype.eatWhile = function (match) {
  var start = this.pos
  while (this.eat(match)){}
  return this.pos > start
};
StringStream.prototype.eatSpace = function () {
    var this$1 = this;

  var start = this.pos
  while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos }
  return this.pos > start
};
StringStream.prototype.skipToEnd = function () {this.pos = this.string.length};
StringStream.prototype.skipTo = function (ch) {
  var found = this.string.indexOf(ch, this.pos)
  if (found > -1) {this.pos = found; return true}
};
StringStream.prototype.backUp = function (n) {this.pos -= n};
StringStream.prototype.column = function () {
  if (this.lastColumnPos < this.start) {
    this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue)
    this.lastColumnPos = this.start
  }
  return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
};
StringStream.prototype.indentation = function () {
  return countColumn(this.string, null, this.tabSize) -
    (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
};
StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
  if (typeof pattern == "string") {
    var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }
    var substr = this.string.substr(this.pos, pattern.length)
    if (cased(substr) == cased(pattern)) {
      if (consume !== false) { this.pos += pattern.length }
      return true
    }
  } else {
    var match = this.string.slice(this.pos).match(pattern)
    if (match && match.index > 0) { return null }
    if (match && consume !== false) { this.pos += match[0].length }
    return match
  }
};
StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
StringStream.prototype.hideFirstChars = function (n, inner) {
  this.lineStart += n
  try { return inner() }
  finally { this.lineStart -= n }
};

// Compute a style array (an array starting with a mode generation
// -- for invalidation -- followed by pairs of end positions and
// style strings), which is used to highlight the tokens on the
// line.
function highlightLine(cm, line, state, forceToEnd) {
  // A styles array always starts with a number identifying the
  // mode/overlays that it is based on (for easy invalidation).
  var st = [cm.state.modeGen], lineClasses = {}
  // Compute the base array of styles
  runMode(cm, line.text, cm.doc.mode, state, function (end, style) { return st.push(end, style); },
    lineClasses, forceToEnd)

  // Run overlays, adjust style array.
  var loop = function ( o ) {
    var overlay = cm.state.overlays[o], i = 1, at = 0
    runMode(cm, line.text, overlay.mode, true, function (end, style) {
      var start = i
      // Ensure there's a token end at the current position, and that i points at it
      while (at < end) {
        var i_end = st[i]
        if (i_end > end)
          { st.splice(i, 1, end, st[i+1], i_end) }
        i += 2
        at = Math.min(end, i_end)
      }
      if (!style) { return }
      if (overlay.opaque) {
        st.splice(start, i - start, end, "overlay " + style)
        i = start + 2
      } else {
        for (; start < i; start += 2) {
          var cur = st[start+1]
          st[start+1] = (cur ? cur + " " : "") + "overlay " + style
        }
      }
    }, lineClasses)
  };

  for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );

  return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
}

function getLineStyles(cm, line, updateFrontier) {
  if (!line.styles || line.styles[0] != cm.state.modeGen) {
    var state = getStateBefore(cm, lineNo(line))
    var result = highlightLine(cm, line, line.text.length > cm.options.maxHighlightLength ? copyState(cm.doc.mode, state) : state)
    line.stateAfter = state
    line.styles = result.styles
    if (result.classes) { line.styleClasses = result.classes }
    else if (line.styleClasses) { line.styleClasses = null }
    if (updateFrontier === cm.doc.frontier) { cm.doc.frontier++ }
  }
  return line.styles
}

function getStateBefore(cm, n, precise) {
  var doc = cm.doc, display = cm.display
  if (!doc.mode.startState) { return true }
  var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter
  if (!state) { state = startState(doc.mode) }
  else { state = copyState(doc.mode, state) }
  doc.iter(pos, n, function (line) {
    processLine(cm, line.text, state)
    var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo
    line.stateAfter = save ? copyState(doc.mode, state) : null
    ++pos
  })
  if (precise) { doc.frontier = pos }
  return state
}

// Lightweight form of highlight -- proceed over this line and
// update state, but don't save a style array. Used for lines that
// aren't currently visible.
function processLine(cm, text, state, startAt) {
  var mode = cm.doc.mode
  var stream = new StringStream(text, cm.options.tabSize)
  stream.start = stream.pos = startAt || 0
  if (text == "") { callBlankLine(mode, state) }
  while (!stream.eol()) {
    readToken(mode, stream, state)
    stream.start = stream.pos
  }
}

function callBlankLine(mode, state) {
  if (mode.blankLine) { return mode.blankLine(state) }
  if (!mode.innerMode) { return }
  var inner = innerMode(mode, state)
  if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
}

function readToken(mode, stream, state, inner) {
  for (var i = 0; i < 10; i++) {
    if (inner) { inner[0] = innerMode(mode, state).mode }
    var style = mode.token(stream, state)
    if (stream.pos > stream.start) { return style }
  }
  throw new Error("Mode " + mode.name + " failed to advance stream.")
}

// Utility for getTokenAt and getLineTokens
function takeToken(cm, pos, precise, asArray) {
  var getObj = function (copy) { return ({
    start: stream.start, end: stream.pos,
    string: stream.current(),
    type: style || null,
    state: copy ? copyState(doc.mode, state) : state
  }); }

  var doc = cm.doc, mode = doc.mode, style
  pos = clipPos(doc, pos)
  var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise)
  var stream = new StringStream(line.text, cm.options.tabSize), tokens
  if (asArray) { tokens = [] }
  while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
    stream.start = stream.pos
    style = readToken(mode, stream, state)
    if (asArray) { tokens.push(getObj(true)) }
  }
  return asArray ? tokens : getObj()
}

function extractLineClasses(type, output) {
  if (type) { for (;;) {
    var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/)
    if (!lineClass) { break }
    type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length)
    var prop = lineClass[1] ? "bgClass" : "textClass"
    if (output[prop] == null)
      { output[prop] = lineClass[2] }
    else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
      { output[prop] += " " + lineClass[2] }
  } }
  return type
}

// Run the given mode's parser over a line, calling f for each token.
function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
  var flattenSpans = mode.flattenSpans
  if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans }
  var curStart = 0, curStyle = null
  var stream = new StringStream(text, cm.options.tabSize), style
  var inner = cm.options.addModeClass && [null]
  if (text == "") { extractLineClasses(callBlankLine(mode, state), lineClasses) }
  while (!stream.eol()) {
    if (stream.pos > cm.options.maxHighlightLength) {
      flattenSpans = false
      if (forceToEnd) { processLine(cm, text, state, stream.pos) }
      stream.pos = text.length
      style = null
    } else {
      style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses)
    }
    if (inner) {
      var mName = inner[0].name
      if (mName) { style = "m-" + (style ? mName + " " + style : mName) }
    }
    if (!flattenSpans || curStyle != style) {
      while (curStart < stream.start) {
        curStart = Math.min(stream.start, curStart + 5000)
        f(curStart, curStyle)
      }
      curStyle = style
    }
    stream.start = stream.pos
  }
  while (curStart < stream.pos) {
    // Webkit seems to refuse to render text nodes longer than 57444
    // characters, and returns inaccurate measurements in nodes
    // starting around 5000 chars.
    var pos = Math.min(stream.pos, curStart + 5000)
    f(pos, curStyle)
    curStart = pos
  }
}

// Finds the line to start with when starting a parse. Tries to
// find a line with a stateAfter, so that it can start with a
// valid state. If that fails, it returns the line with the
// smallest indentation, which tends to need the least context to
// parse correctly.
function findStartLine(cm, n, precise) {
  var minindent, minline, doc = cm.doc
  var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100)
  for (var search = n; search > lim; --search) {
    if (search <= doc.first) { return doc.first }
    var line = getLine(doc, search - 1)
    if (line.stateAfter && (!precise || search <= doc.frontier)) { return search }
    var indented = countColumn(line.text, null, cm.options.tabSize)
    if (minline == null || minindent > indented) {
      minline = search - 1
      minindent = indented
    }
  }
  return minline
}

// LINE DATA STRUCTURE

// Line objects. These hold state related to a line, including
// highlighting info (the styles array).
var Line = function(text, markedSpans, estimateHeight) {
  this.text = text
  attachMarkedSpans(this, markedSpans)
  this.height = estimateHeight ? estimateHeight(this) : 1
};

Line.prototype.lineNo = function () { return lineNo(this) };
eventMixin(Line)

// Change the content (text, markers) of a line. Automatically
// invalidates cached information and tries to re-estimate the
// line's height.
function updateLine(line, text, markedSpans, estimateHeight) {
  line.text = text
  if (line.stateAfter) { line.stateAfter = null }
  if (line.styles) { line.styles = null }
  if (line.order != null) { line.order = null }
  detachMarkedSpans(line)
  attachMarkedSpans(line, markedSpans)
  var estHeight = estimateHeight ? estimateHeight(line) : 1
  if (estHeight != line.height) { updateLineHeight(line, estHeight) }
}

// Detach a line from the document tree and its markers.
function cleanUpLine(line) {
  line.parent = null
  detachMarkedSpans(line)
}

// Convert a style as returned by a mode (either null, or a string
// containing one or more styles) to a CSS style. This is cached,
// and also looks for line-wide styles.
var styleToClassCache = {};
var styleToClassCacheWithMode = {};
function interpretTokenStyle(style, options) {
  if (!style || /^\s*$/.test(style)) { return null }
  var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache
  return cache[style] ||
    (cache[style] = style.replace(/\S+/g, "cm-$&"))
}

// Render the DOM representation of the text of a line. Also builds
// up a 'line map', which points at the DOM nodes that represent
// specific stretches of text, and is used by the measuring code.
// The returned object contains the DOM node, this map, and
// information about line-wide styles that were set by the mode.
function buildLineContent(cm, lineView) {
  // The padding-right forces the element to have a 'border', which
  // is needed on Webkit to be able to get line-level bounding
  // rectangles for it (in measureChar).
  var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null)
  var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
                 col: 0, pos: 0, cm: cm,
                 trailingSpace: false,
                 splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")}
  lineView.measure = {}

  // Iterate over the logical lines that make up this visual line.
  for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
    var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0)
    builder.pos = 0
    builder.addToken = buildToken
    // Optionally wire in some hacks into the token-rendering
    // algorithm, to deal with browser quirks.
    if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
      { builder.addToken = buildTokenBadBidi(builder.addToken, order) }
    builder.map = []
    var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line)
    insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate))
    if (line.styleClasses) {
      if (line.styleClasses.bgClass)
        { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "") }
      if (line.styleClasses.textClass)
        { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "") }
    }

    // Ensure at least a single node is present, for measuring.
    if (builder.map.length == 0)
      { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))) }

    // Store the map and a cache object for the current logical line
    if (i == 0) {
      lineView.measure.map = builder.map
      lineView.measure.cache = {}
    } else {
      ;(lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
      ;(lineView.measure.caches || (lineView.measure.caches = [])).push({})
    }
  }

  // See issue #2901
  if (webkit) {
    var last = builder.content.lastChild
    if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
      { builder.content.className = "cm-tab-wrap-hack" }
  }

  signal(cm, "renderLine", cm, lineView.line, builder.pre)
  if (builder.pre.className)
    { builder.textClass = joinClasses(builder.pre.className, builder.textClass || "") }

  return builder
}

function defaultSpecialCharPlaceholder(ch) {
  var token = elt("span", "\u2022", "cm-invalidchar")
  token.title = "\\u" + ch.charCodeAt(0).toString(16)
  token.setAttribute("aria-label", token.title)
  return token
}

// Build up the DOM representation for a single token, and add it to
// the line map. Takes care to render special characters separately.
function buildToken(builder, text, style, startStyle, endStyle, title, css) {
  if (!text) { return }
  var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text
  var special = builder.cm.state.specialChars, mustWrap = false
  var content
  if (!special.test(text)) {
    builder.col += text.length
    content = document.createTextNode(displayText)
    builder.map.push(builder.pos, builder.pos + text.length, content)
    if (ie && ie_version < 9) { mustWrap = true }
    builder.pos += text.length
  } else {
    content = document.createDocumentFragment()
    var pos = 0
    while (true) {
      special.lastIndex = pos
      var m = special.exec(text)
      var skipped = m ? m.index - pos : text.length - pos
      if (skipped) {
        var txt = document.createTextNode(displayText.slice(pos, pos + skipped))
        if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])) }
        else { content.appendChild(txt) }
        builder.map.push(builder.pos, builder.pos + skipped, txt)
        builder.col += skipped
        builder.pos += skipped
      }
      if (!m) { break }
      pos += skipped + 1
      var txt$1 = (void 0)
      if (m[0] == "\t") {
        var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize
        txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"))
        txt$1.setAttribute("role", "presentation")
        txt$1.setAttribute("cm-text", "\t")
        builder.col += tabWidth
      } else if (m[0] == "\r" || m[0] == "\n") {
        txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"))
        txt$1.setAttribute("cm-text", m[0])
        builder.col += 1
      } else {
        txt$1 = builder.cm.options.specialCharPlaceholder(m[0])
        txt$1.setAttribute("cm-text", m[0])
        if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])) }
        else { content.appendChild(txt$1) }
        builder.col += 1
      }
      builder.map.push(builder.pos, builder.pos + 1, txt$1)
      builder.pos++
    }
  }
  builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32
  if (style || startStyle || endStyle || mustWrap || css) {
    var fullStyle = style || ""
    if (startStyle) { fullStyle += startStyle }
    if (endStyle) { fullStyle += endStyle }
    var token = elt("span", [content], fullStyle, css)
    if (title) { token.title = title }
    return builder.content.appendChild(token)
  }
  builder.content.appendChild(content)
}

function splitSpaces(text, trailingBefore) {
  if (text.length > 1 && !/  /.test(text)) { return text }
  var spaceBefore = trailingBefore, result = ""
  for (var i = 0; i < text.length; i++) {
    var ch = text.charAt(i)
    if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
      { ch = "\u00a0" }
    result += ch
    spaceBefore = ch == " "
  }
  return result
}

// Work around nonsense dimensions being reported for stretches of
// right-to-left text.
function buildTokenBadBidi(inner, order) {
  return function (builder, text, style, startStyle, endStyle, title, css) {
    style = style ? style + " cm-force-border" : "cm-force-border"
    var start = builder.pos, end = start + text.length
    for (;;) {
      // Find the part that overlaps with the start of this text
      var part = (void 0)
      for (var i = 0; i < order.length; i++) {
        part = order[i]
        if (part.to > start && part.from <= start) { break }
      }
      if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, title, css) }
      inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css)
      startStyle = null
      text = text.slice(part.to - start)
      start = part.to
    }
  }
}

function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
  var widget = !ignoreWidget && marker.widgetNode
  if (widget) { builder.map.push(builder.pos, builder.pos + size, widget) }
  if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
    if (!widget)
      { widget = builder.content.appendChild(document.createElement("span")) }
    widget.setAttribute("cm-marker", marker.id)
  }
  if (widget) {
    builder.cm.display.input.setUneditable(widget)
    builder.content.appendChild(widget)
  }
  builder.pos += size
  builder.trailingSpace = false
}

// Outputs a number of spans to make up a line, taking highlighting
// and marked text into account.
function insertLineContent(line, builder, styles) {
  var spans = line.markedSpans, allText = line.text, at = 0
  if (!spans) {
    for (var i$1 = 1; i$1 < styles.length; i$1+=2)
      { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)) }
    return
  }

  var len = allText.length, pos = 0, i = 1, text = "", style, css
  var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed
  for (;;) {
    if (nextChange == pos) { // Update current marker set
      spanStyle = spanEndStyle = spanStartStyle = title = css = ""
      collapsed = null; nextChange = Infinity
      var foundBookmarks = [], endStyles = (void 0)
      for (var j = 0; j < spans.length; ++j) {
        var sp = spans[j], m = sp.marker
        if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
          foundBookmarks.push(m)
        } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
          if (sp.to != null && sp.to != pos && nextChange > sp.to) {
            nextChange = sp.to
            spanEndStyle = ""
          }
          if (m.className) { spanStyle += " " + m.className }
          if (m.css) { css = (css ? css + ";" : "") + m.css }
          if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle }
          if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to) }
          if (m.title && !title) { title = m.title }
          if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
            { collapsed = sp }
        } else if (sp.from > pos && nextChange > sp.from) {
          nextChange = sp.from
        }
      }
      if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
        { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1] } } }

      if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
        { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]) } }
      if (collapsed && (collapsed.from || 0) == pos) {
        buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
                           collapsed.marker, collapsed.from == null)
        if (collapsed.to == null) { return }
        if (collapsed.to == pos) { collapsed = false }
      }
    }
    if (pos >= len) { break }

    var upto = Math.min(len, nextChange)
    while (true) {
      if (text) {
        var end = pos + text.length
        if (!collapsed) {
          var tokenText = end > upto ? text.slice(0, upto - pos) : text
          builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
                           spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css)
        }
        if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
        pos = end
        spanStartStyle = ""
      }
      text = allText.slice(at, at = styles[i++])
      style = interpretTokenStyle(styles[i++], builder.cm.options)
    }
  }
}


// These objects are used to represent the visible (currently drawn)
// part of the document. A LineView may correspond to multiple
// logical lines, if those are connected by collapsed ranges.
function LineView(doc, line, lineN) {
  // The starting line
  this.line = line
  // Continuing lines, if any
  this.rest = visualLineContinued(line)
  // Number of logical lines in this visual line
  this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1
  this.node = this.text = null
  this.hidden = lineIsHidden(doc, line)
}

// Create a range of LineView objects for the given lines.
function buildViewArray(cm, from, to) {
  var array = [], nextPos
  for (var pos = from; pos < to; pos = nextPos) {
    var view = new LineView(cm.doc, getLine(cm.doc, pos), pos)
    nextPos = pos + view.size
    array.push(view)
  }
  return array
}

var operationGroup = null

function pushOperation(op) {
  if (operationGroup) {
    operationGroup.ops.push(op)
  } else {
    op.ownsGroup = operationGroup = {
      ops: [op],
      delayedCallbacks: []
    }
  }
}

function fireCallbacksForOps(group) {
  // Calls delayed callbacks and cursorActivity handlers until no
  // new ones appear
  var callbacks = group.delayedCallbacks, i = 0
  do {
    for (; i < callbacks.length; i++)
      { callbacks[i].call(null) }
    for (var j = 0; j < group.ops.length; j++) {
      var op = group.ops[j]
      if (op.cursorActivityHandlers)
        { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
          { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm) } }
    }
  } while (i < callbacks.length)
}

function finishOperation(op, endCb) {
  var group = op.ownsGroup
  if (!group) { return }

  try { fireCallbacksForOps(group) }
  finally {
    operationGroup = null
    endCb(group)
  }
}

var orphanDelayedCallbacks = null

// Often, we want to signal events at a point where we are in the
// middle of some work, but don't want the handler to start calling
// other methods on the editor, which might be in an inconsistent
// state or simply not expect any other events to happen.
// signalLater looks whether there are any handlers, and schedules
// them to be executed when the last operation ends, or, if no
// operation is active, when a timeout fires.
function signalLater(emitter, type /*, values...*/) {
  var arr = getHandlers(emitter, type)
  if (!arr.length) { return }
  var args = Array.prototype.slice.call(arguments, 2), list
  if (operationGroup) {
    list = operationGroup.delayedCallbacks
  } else if (orphanDelayedCallbacks) {
    list = orphanDelayedCallbacks
  } else {
    list = orphanDelayedCallbacks = []
    setTimeout(fireOrphanDelayed, 0)
  }
  var loop = function ( i ) {
    list.push(function () { return arr[i].apply(null, args); })
  };

  for (var i = 0; i < arr.length; ++i)
    loop( i );
}

function fireOrphanDelayed() {
  var delayed = orphanDelayedCallbacks
  orphanDelayedCallbacks = null
  for (var i = 0; i < delayed.length; ++i) { delayed[i]() }
}

// When an aspect of a line changes, a string is added to
// lineView.changes. This updates the relevant part of the line's
// DOM structure.
function updateLineForChanges(cm, lineView, lineN, dims) {
  for (var j = 0; j < lineView.changes.length; j++) {
    var type = lineView.changes[j]
    if (type == "text") { updateLineText(cm, lineView) }
    else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims) }
    else if (type == "class") { updateLineClasses(cm, lineView) }
    else if (type == "widget") { updateLineWidgets(cm, lineView, dims) }
  }
  lineView.changes = null
}

// Lines with gutter elements, widgets or a background class need to
// be wrapped, and have the extra elements added to the wrapper div
function ensureLineWrapped(lineView) {
  if (lineView.node == lineView.text) {
    lineView.node = elt("div", null, null, "position: relative")
    if (lineView.text.parentNode)
      { lineView.text.parentNode.replaceChild(lineView.node, lineView.text) }
    lineView.node.appendChild(lineView.text)
    if (ie && ie_version < 8) { lineView.node.style.zIndex = 2 }
  }
  return lineView.node
}

function updateLineBackground(cm, lineView) {
  var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass
  if (cls) { cls += " CodeMirror-linebackground" }
  if (lineView.background) {
    if (cls) { lineView.background.className = cls }
    else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null }
  } else if (cls) {
    var wrap = ensureLineWrapped(lineView)
    lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild)
    cm.display.input.setUneditable(lineView.background)
  }
}

// Wrapper around buildLineContent which will reuse the structure
// in display.externalMeasured when possible.
function getLineContent(cm, lineView) {
  var ext = cm.display.externalMeasured
  if (ext && ext.line == lineView.line) {
    cm.display.externalMeasured = null
    lineView.measure = ext.measure
    return ext.built
  }
  return buildLineContent(cm, lineView)
}

// Redraw the line's text. Interacts with the background and text
// classes because the mode may output tokens that influence these
// classes.
function updateLineText(cm, lineView) {
  var cls = lineView.text.className
  var built = getLineContent(cm, lineView)
  if (lineView.text == lineView.node) { lineView.node = built.pre }
  lineView.text.parentNode.replaceChild(built.pre, lineView.text)
  lineView.text = built.pre
  if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
    lineView.bgClass = built.bgClass
    lineView.textClass = built.textClass
    updateLineClasses(cm, lineView)
  } else if (cls) {
    lineView.text.className = cls
  }
}

function updateLineClasses(cm, lineView) {
  updateLineBackground(cm, lineView)
  if (lineView.line.wrapClass)
    { ensureLineWrapped(lineView).className = lineView.line.wrapClass }
  else if (lineView.node != lineView.text)
    { lineView.node.className = "" }
  var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass
  lineView.text.className = textClass || ""
}

function updateLineGutter(cm, lineView, lineN, dims) {
  if (lineView.gutter) {
    lineView.node.removeChild(lineView.gutter)
    lineView.gutter = null
  }
  if (lineView.gutterBackground) {
    lineView.node.removeChild(lineView.gutterBackground)
    lineView.gutterBackground = null
  }
  if (lineView.line.gutterClass) {
    var wrap = ensureLineWrapped(lineView)
    lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
                                    ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"))
    cm.display.input.setUneditable(lineView.gutterBackground)
    wrap.insertBefore(lineView.gutterBackground, lineView.text)
  }
  var markers = lineView.line.gutterMarkers
  if (cm.options.lineNumbers || markers) {
    var wrap$1 = ensureLineWrapped(lineView)
    var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"))
    cm.display.input.setUneditable(gutterWrap)
    wrap$1.insertBefore(gutterWrap, lineView.text)
    if (lineView.line.gutterClass)
      { gutterWrap.className += " " + lineView.line.gutterClass }
    if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
      { lineView.lineNumber = gutterWrap.appendChild(
        elt("div", lineNumberFor(cm.options, lineN),
            "CodeMirror-linenumber CodeMirror-gutter-elt",
            ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))) }
    if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) {
      var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id]
      if (found)
        { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
                                   ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))) }
    } }
  }
}

function updateLineWidgets(cm, lineView, dims) {
  if (lineView.alignable) { lineView.alignable = null }
  for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
    next = node.nextSibling
    if (node.className == "CodeMirror-linewidget")
      { lineView.node.removeChild(node) }
  }
  insertLineWidgets(cm, lineView, dims)
}

// Build a line's DOM representation from scratch
function buildLineElement(cm, lineView, lineN, dims) {
  var built = getLineContent(cm, lineView)
  lineView.text = lineView.node = built.pre
  if (built.bgClass) { lineView.bgClass = built.bgClass }
  if (built.textClass) { lineView.textClass = built.textClass }

  updateLineClasses(cm, lineView)
  updateLineGutter(cm, lineView, lineN, dims)
  insertLineWidgets(cm, lineView, dims)
  return lineView.node
}

// A lineView may contain multiple logical lines (when merged by
// collapsed spans). The widgets for all of them need to be drawn.
function insertLineWidgets(cm, lineView, dims) {
  insertLineWidgetsFor(cm, lineView.line, lineView, dims, true)
  if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
    { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false) } }
}

function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
  if (!line.widgets) { return }
  var wrap = ensureLineWrapped(lineView)
  for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
    var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget")
    if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true") }
    positionLineWidget(widget, node, lineView, dims)
    cm.display.input.setUneditable(node)
    if (allowAbove && widget.above)
      { wrap.insertBefore(node, lineView.gutter || lineView.text) }
    else
      { wrap.appendChild(node) }
    signalLater(widget, "redraw")
  }
}

function positionLineWidget(widget, node, lineView, dims) {
  if (widget.noHScroll) {
    ;(lineView.alignable || (lineView.alignable = [])).push(node)
    var width = dims.wrapperWidth
    node.style.left = dims.fixedPos + "px"
    if (!widget.coverGutter) {
      width -= dims.gutterTotalWidth
      node.style.paddingLeft = dims.gutterTotalWidth + "px"
    }
    node.style.width = width + "px"
  }
  if (widget.coverGutter) {
    node.style.zIndex = 5
    node.style.position = "relative"
    if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px" }
  }
}

function widgetHeight(widget) {
  if (widget.height != null) { return widget.height }
  var cm = widget.doc.cm
  if (!cm) { return 0 }
  if (!contains(document.body, widget.node)) {
    var parentStyle = "position: relative;"
    if (widget.coverGutter)
      { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;" }
    if (widget.noHScroll)
      { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;" }
    removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle))
  }
  return widget.height = widget.node.parentNode.offsetHeight
}

// Return true when the given mouse event happened in a widget
function eventInWidget(display, e) {
  for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
    if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
        (n.parentNode == display.sizer && n != display.mover))
      { return true }
  }
}

// POSITION MEASUREMENT

function paddingTop(display) {return display.lineSpace.offsetTop}
function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
function paddingH(display) {
  if (display.cachedPaddingH) { return display.cachedPaddingH }
  var e = removeChildrenAndAdd(display.measure, elt("pre", "x"))
  var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle
  var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}
  if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data }
  return data
}

function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
function displayWidth(cm) {
  return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
}
function displayHeight(cm) {
  return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
}

// Ensure the lineView.wrapping.heights array is populated. This is
// an array of bottom offsets for the lines that make up a drawn
// line. When lineWrapping is on, there might be more than one
// height.
function ensureLineHeights(cm, lineView, rect) {
  var wrapping = cm.options.lineWrapping
  var curWidth = wrapping && displayWidth(cm)
  if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
    var heights = lineView.measure.heights = []
    if (wrapping) {
      lineView.measure.width = curWidth
      var rects = lineView.text.firstChild.getClientRects()
      for (var i = 0; i < rects.length - 1; i++) {
        var cur = rects[i], next = rects[i + 1]
        if (Math.abs(cur.bottom - next.bottom) > 2)
          { heights.push((cur.bottom + next.top) / 2 - rect.top) }
      }
    }
    heights.push(rect.bottom - rect.top)
  }
}

// Find a line map (mapping character offsets to text nodes) and a
// measurement cache for the given line number. (A line view might
// contain multiple lines when collapsed ranges are present.)
function mapFromLineView(lineView, line, lineN) {
  if (lineView.line == line)
    { return {map: lineView.measure.map, cache: lineView.measure.cache} }
  for (var i = 0; i < lineView.rest.length; i++)
    { if (lineView.rest[i] == line)
      { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
  for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
    { if (lineNo(lineView.rest[i$1]) > lineN)
      { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
}

// Render a line into the hidden node display.externalMeasured. Used
// when measurement is needed for a line that's not in the viewport.
function updateExternalMeasurement(cm, line) {
  line = visualLine(line)
  var lineN = lineNo(line)
  var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN)
  view.lineN = lineN
  var built = view.built = buildLineContent(cm, view)
  view.text = built.pre
  removeChildrenAndAdd(cm.display.lineMeasure, built.pre)
  return view
}

// Get a {top, bottom, left, right} box (in line-local coordinates)
// for a given character.
function measureChar(cm, line, ch, bias) {
  return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
}

// Find a line view that corresponds to the given line number.
function findViewForLine(cm, lineN) {
  if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
    { return cm.display.view[findViewIndex(cm, lineN)] }
  var ext = cm.display.externalMeasured
  if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
    { return ext }
}

// Measurement can be split in two steps, the set-up work that
// applies to the whole line, and the measurement of the actual
// character. Functions like coordsChar, that need to do a lot of
// measurements in a row, can thus ensure that the set-up work is
// only done once.
function prepareMeasureForLine(cm, line) {
  var lineN = lineNo(line)
  var view = findViewForLine(cm, lineN)
  if (view && !view.text) {
    view = null
  } else if (view && view.changes) {
    updateLineForChanges(cm, view, lineN, getDimensions(cm))
    cm.curOp.forceUpdate = true
  }
  if (!view)
    { view = updateExternalMeasurement(cm, line) }

  var info = mapFromLineView(view, line, lineN)
  return {
    line: line, view: view, rect: null,
    map: info.map, cache: info.cache, before: info.before,
    hasHeights: false
  }
}

// Given a prepared measurement object, measures the position of an
// actual character (or fetches it from the cache).
function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
  if (prepared.before) { ch = -1 }
  var key = ch + (bias || ""), found
  if (prepared.cache.hasOwnProperty(key)) {
    found = prepared.cache[key]
  } else {
    if (!prepared.rect)
      { prepared.rect = prepared.view.text.getBoundingClientRect() }
    if (!prepared.hasHeights) {
      ensureLineHeights(cm, prepared.view, prepared.rect)
      prepared.hasHeights = true
    }
    found = measureCharInner(cm, prepared, ch, bias)
    if (!found.bogus) { prepared.cache[key] = found }
  }
  return {left: found.left, right: found.right,
          top: varHeight ? found.rtop : found.top,
          bottom: varHeight ? found.rbottom : found.bottom}
}

var nullRect = {left: 0, right: 0, top: 0, bottom: 0}

function nodeAndOffsetInLineMap(map, ch, bias) {
  var node, start, end, collapse, mStart, mEnd
  // First, search the line map for the text node corresponding to,
  // or closest to, the target character.
  for (var i = 0; i < map.length; i += 3) {
    mStart = map[i]
    mEnd = map[i + 1]
    if (ch < mStart) {
      start = 0; end = 1
      collapse = "left"
    } else if (ch < mEnd) {
      start = ch - mStart
      end = start + 1
    } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
      end = mEnd - mStart
      start = end - 1
      if (ch >= mEnd) { collapse = "right" }
    }
    if (start != null) {
      node = map[i + 2]
      if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
        { collapse = bias }
      if (bias == "left" && start == 0)
        { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
          node = map[(i -= 3) + 2]
          collapse = "left"
        } }
      if (bias == "right" && start == mEnd - mStart)
        { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
          node = map[(i += 3) + 2]
          collapse = "right"
        } }
      break
    }
  }
  return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
}

function getUsefulRect(rects, bias) {
  var rect = nullRect
  if (bias == "left") { for (var i = 0; i < rects.length; i++) {
    if ((rect = rects[i]).left != rect.right) { break }
  } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
    if ((rect = rects[i$1]).left != rect.right) { break }
  } }
  return rect
}

function measureCharInner(cm, prepared, ch, bias) {
  var place = nodeAndOffsetInLineMap(prepared.map, ch, bias)
  var node = place.node, start = place.start, end = place.end, collapse = place.collapse

  var rect
  if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
    for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
      while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start }
      while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end }
      if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
        { rect = node.parentNode.getBoundingClientRect() }
      else
        { rect = getUsefulRect(range(node, start, end).getClientRects(), bias) }
      if (rect.left || rect.right || start == 0) { break }
      end = start
      start = start - 1
      collapse = "right"
    }
    if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect) }
  } else { // If it is a widget, simply get the box for the whole widget.
    if (start > 0) { collapse = bias = "right" }
    var rects
    if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
      { rect = rects[bias == "right" ? rects.length - 1 : 0] }
    else
      { rect = node.getBoundingClientRect() }
  }
  if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
    var rSpan = node.parentNode.getClientRects()[0]
    if (rSpan)
      { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom} }
    else
      { rect = nullRect }
  }

  var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top
  var mid = (rtop + rbot) / 2
  var heights = prepared.view.measure.heights
  var i = 0
  for (; i < heights.length - 1; i++)
    { if (mid < heights[i]) { break } }
  var top = i ? heights[i - 1] : 0, bot = heights[i]
  var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
                right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
                top: top, bottom: bot}
  if (!rect.left && !rect.right) { result.bogus = true }
  if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot }

  return result
}

// Work around problem with bounding client rects on ranges being
// returned incorrectly when zoomed on IE10 and below.
function maybeUpdateRectForZooming(measure, rect) {
  if (!window.screen || screen.logicalXDPI == null ||
      screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
    { return rect }
  var scaleX = screen.logicalXDPI / screen.deviceXDPI
  var scaleY = screen.logicalYDPI / screen.deviceYDPI
  return {left: rect.left * scaleX, right: rect.right * scaleX,
          top: rect.top * scaleY, bottom: rect.bottom * scaleY}
}

function clearLineMeasurementCacheFor(lineView) {
  if (lineView.measure) {
    lineView.measure.cache = {}
    lineView.measure.heights = null
    if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
      { lineView.measure.caches[i] = {} } }
  }
}

function clearLineMeasurementCache(cm) {
  cm.display.externalMeasure = null
  removeChildren(cm.display.lineMeasure)
  for (var i = 0; i < cm.display.view.length; i++)
    { clearLineMeasurementCacheFor(cm.display.view[i]) }
}

function clearCaches(cm) {
  clearLineMeasurementCache(cm)
  cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null
  if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true }
  cm.display.lineNumChars = null
}

function pageScrollX() {
  // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
  // which causes page_Offset and bounding client rects to use
  // different reference viewports and invalidate our calculations.
  if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
  return window.pageXOffset || (document.documentElement || document.body).scrollLeft
}
function pageScrollY() {
  if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
  return window.pageYOffset || (document.documentElement || document.body).scrollTop
}

// Converts a {top, bottom, left, right} box from line-local
// coordinates into another coordinate system. Context may be one of
// "line", "div" (display.lineDiv), "local"./null (editor), "window",
// or "page".
function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
  if (!includeWidgets && lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) {
    var size = widgetHeight(lineObj.widgets[i])
    rect.top += size; rect.bottom += size
  } } }
  if (context == "line") { return rect }
  if (!context) { context = "local" }
  var yOff = heightAtLine(lineObj)
  if (context == "local") { yOff += paddingTop(cm.display) }
  else { yOff -= cm.display.viewOffset }
  if (context == "page" || context == "window") {
    var lOff = cm.display.lineSpace.getBoundingClientRect()
    yOff += lOff.top + (context == "window" ? 0 : pageScrollY())
    var xOff = lOff.left + (context == "window" ? 0 : pageScrollX())
    rect.left += xOff; rect.right += xOff
  }
  rect.top += yOff; rect.bottom += yOff
  return rect
}

// Coverts a box from "div" coords to another coordinate system.
// Context may be "window", "page", "div", or "local"./null.
function fromCoordSystem(cm, coords, context) {
  if (context == "div") { return coords }
  var left = coords.left, top = coords.top
  // First move into "page" coordinate system
  if (context == "page") {
    left -= pageScrollX()
    top -= pageScrollY()
  } else if (context == "local" || !context) {
    var localBox = cm.display.sizer.getBoundingClientRect()
    left += localBox.left
    top += localBox.top
  }

  var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect()
  return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
}

function charCoords(cm, pos, context, lineObj, bias) {
  if (!lineObj) { lineObj = getLine(cm.doc, pos.line) }
  return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
}

// Returns a box for a given cursor position, which may have an
// 'other' property containing the position of the secondary cursor
// on a bidi boundary.
// A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
// and after `char - 1` in writing order of `char - 1`
// A cursor Pos(line, char, "after") is on the same visual line as `char`
// and before `char` in writing order of `char`
// Examples (upper-case letters are RTL, lower-case are LTR):
//     Pos(0, 1, ...)
//     before   after
// ab     a|b     a|b
// aB     a|B     aB|
// Ab     |Ab     A|b
// AB     B|A     B|A
// Every position after the last character on a line is considered to stick
// to the last character on the line.
function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
  lineObj = lineObj || getLine(cm.doc, pos.line)
  if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj) }
  function get(ch, right) {
    var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight)
    if (right) { m.left = m.right; } else { m.right = m.left }
    return intoCoordSystem(cm, lineObj, m, context)
  }
  var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky
  if (ch >= lineObj.text.length) {
    ch = lineObj.text.length
    sticky = "before"
  } else if (ch <= 0) {
    ch = 0
    sticky = "after"
  }
  if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }

  function getBidi(ch, partPos, invert) {
    var part = order[partPos], right = (part.level % 2) != 0
    return get(invert ? ch - 1 : ch, right != invert)
  }
  var partPos = getBidiPartAt(order, ch, sticky)
  var other = bidiOther
  var val = getBidi(ch, partPos, sticky == "before")
  if (other != null) { val.other = getBidi(ch, other, sticky != "before") }
  return val
}

// Used to cheaply estimate the coordinates for a position. Used for
// intermediate scroll updates.
function estimateCoords(cm, pos) {
  var left = 0
  pos = clipPos(cm.doc, pos)
  if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch }
  var lineObj = getLine(cm.doc, pos.line)
  var top = heightAtLine(lineObj) + paddingTop(cm.display)
  return {left: left, right: left, top: top, bottom: top + lineObj.height}
}

// Positions returned by coordsChar contain some extra information.
// xRel is the relative x position of the input coordinates compared
// to the found position (so xRel > 0 means the coordinates are to
// the right of the character position, for example). When outside
// is true, that means the coordinates lie outside the line's
// vertical range.
function PosWithInfo(line, ch, sticky, outside, xRel) {
  var pos = Pos(line, ch, sticky)
  pos.xRel = xRel
  if (outside) { pos.outside = true }
  return pos
}

// Compute the character position closest to the given coordinates.
// Input must be lineSpace-local ("div" coordinate system).
function coordsChar(cm, x, y) {
  var doc = cm.doc
  y += cm.display.viewOffset
  if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) }
  var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1
  if (lineN > last)
    { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) }
  if (x < 0) { x = 0 }

  var lineObj = getLine(doc, lineN)
  for (;;) {
    var found = coordsCharInner(cm, lineObj, lineN, x, y)
    var merged = collapsedSpanAtEnd(lineObj)
    var mergedPos = merged && merged.find(0, true)
    if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
      { lineN = lineNo(lineObj = mergedPos.to.line) }
    else
      { return found }
  }
}

function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
  var measure = function (ch) { return intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line"); }
  var end = lineObj.text.length
  var begin = findFirst(function (ch) { return measure(ch - 1).bottom <= y; }, end, 0)
  end = findFirst(function (ch) { return measure(ch).top > y; }, begin, end)
  return {begin: begin, end: end}
}

function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
  var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top
  return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
}

function coordsCharInner(cm, lineObj, lineNo, x, y) {
  y -= heightAtLine(lineObj)
  var begin = 0, end = lineObj.text.length
  var preparedMeasure = prepareMeasureForLine(cm, lineObj)
  var pos
  var order = getOrder(lineObj, cm.doc.direction)
  if (order) {
    if (cm.options.lineWrapping) {
      ;var assign;
      ((assign = wrappedLineExtent(cm, lineObj, preparedMeasure, y), begin = assign.begin, end = assign.end, assign))
    }
    pos = new Pos(lineNo, begin)
    var beginLeft = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left
    var dir = beginLeft < x ? 1 : -1
    var prevDiff, diff = beginLeft - x, prevPos
    do {
      prevDiff = diff
      prevPos = pos
      pos = moveVisually(cm, lineObj, pos, dir)
      if (pos == null || pos.ch < begin || end <= (pos.sticky == "before" ? pos.ch - 1 : pos.ch)) {
        pos = prevPos
        break
      }
      diff = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left - x
    } while ((dir < 0) != (diff < 0) && (Math.abs(diff) <= Math.abs(prevDiff)))
    if (Math.abs(diff) > Math.abs(prevDiff)) {
      if ((diff < 0) == (prevDiff < 0)) { throw new Error("Broke out of infinite loop in coordsCharInner") }
      pos = prevPos
    }
  } else {
    var ch = findFirst(function (ch) {
      var box = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line")
      if (box.top > y) {
        // For the cursor stickiness
        end = Math.min(ch, end)
        return true
      }
      else if (box.bottom <= y) { return false }
      else if (box.left > x) { return true }
      else if (box.right < x) { return false }
      else { return (x - box.left < box.right - x) }
    }, begin, end)
    ch = skipExtendingChars(lineObj.text, ch, 1)
    pos = new Pos(lineNo, ch, ch == end ? "before" : "after")
  }
  var coords = cursorCoords(cm, pos, "line", lineObj, preparedMeasure)
  if (y < coords.top || coords.bottom < y) { pos.outside = true }
  pos.xRel = x < coords.left ? -1 : (x > coords.right ? 1 : 0)
  return pos
}

var measureText
// Compute the default text height.
function textHeight(display) {
  if (display.cachedTextHeight != null) { return display.cachedTextHeight }
  if (measureText == null) {
    measureText = elt("pre")
    // Measure a bunch of lines, for browsers that compute
    // fractional heights.
    for (var i = 0; i < 49; ++i) {
      measureText.appendChild(document.createTextNode("x"))
      measureText.appendChild(elt("br"))
    }
    measureText.appendChild(document.createTextNode("x"))
  }
  removeChildrenAndAdd(display.measure, measureText)
  var height = measureText.offsetHeight / 50
  if (height > 3) { display.cachedTextHeight = height }
  removeChildren(display.measure)
  return height || 1
}

// Compute the default character width.
function charWidth(display) {
  if (display.cachedCharWidth != null) { return display.cachedCharWidth }
  var anchor = elt("span", "xxxxxxxxxx")
  var pre = elt("pre", [anchor])
  removeChildrenAndAdd(display.measure, pre)
  var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10
  if (width > 2) { display.cachedCharWidth = width }
  return width || 10
}

// Do a bulk-read of the DOM positions and sizes needed to draw the
// view, so that we don't interleave reading and writing to the DOM.
function getDimensions(cm) {
  var d = cm.display, left = {}, width = {}
  var gutterLeft = d.gutters.clientLeft
  for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
    left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft
    width[cm.options.gutters[i]] = n.clientWidth
  }
  return {fixedPos: compensateForHScroll(d),
          gutterTotalWidth: d.gutters.offsetWidth,
          gutterLeft: left,
          gutterWidth: width,
          wrapperWidth: d.wrapper.clientWidth}
}

// Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
// but using getBoundingClientRect to get a sub-pixel-accurate
// result.
function compensateForHScroll(display) {
  return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
}

// Returns a function that estimates the height of a line, to use as
// first approximation until the line becomes visible (and is thus
// properly measurable).
function estimateHeight(cm) {
  var th = textHeight(cm.display), wrapping = cm.options.lineWrapping
  var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3)
  return function (line) {
    if (lineIsHidden(cm.doc, line)) { return 0 }

    var widgetsHeight = 0
    if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
      if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height }
    } }

    if (wrapping)
      { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
    else
      { return widgetsHeight + th }
  }
}

function estimateLineHeights(cm) {
  var doc = cm.doc, est = estimateHeight(cm)
  doc.iter(function (line) {
    var estHeight = est(line)
    if (estHeight != line.height) { updateLineHeight(line, estHeight) }
  })
}

// Given a mouse event, find the corresponding position. If liberal
// is false, it checks whether a gutter or scrollbar was clicked,
// and returns null if it was. forRect is used by rectangular
// selections, and tries to estimate a character position even for
// coordinates beyond the right of the text.
function posFromMouse(cm, e, liberal, forRect) {
  var display = cm.display
  if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }

  var x, y, space = display.lineSpace.getBoundingClientRect()
  // Fails unpredictably on IE[67] when mouse is dragged around quickly.
  try { x = e.clientX - space.left; y = e.clientY - space.top }
  catch (e) { return null }
  var coords = coordsChar(cm, x, y), line
  if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
    var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length
    coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff))
  }
  return coords
}

// Find the view element corresponding to a given line. Return null
// when the line isn't visible.
function findViewIndex(cm, n) {
  if (n >= cm.display.viewTo) { return null }
  n -= cm.display.viewFrom
  if (n < 0) { return null }
  var view = cm.display.view
  for (var i = 0; i < view.length; i++) {
    n -= view[i].size
    if (n < 0) { return i }
  }
}

function updateSelection(cm) {
  cm.display.input.showSelection(cm.display.input.prepareSelection())
}

function prepareSelection(cm, primary) {
  var doc = cm.doc, result = {}
  var curFragment = result.cursors = document.createDocumentFragment()
  var selFragment = result.selection = document.createDocumentFragment()

  for (var i = 0; i < doc.sel.ranges.length; i++) {
    if (primary === false && i == doc.sel.primIndex) { continue }
    var range = doc.sel.ranges[i]
    if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue }
    var collapsed = range.empty()
    if (collapsed || cm.options.showCursorWhenSelecting)
      { drawSelectionCursor(cm, range.head, curFragment) }
    if (!collapsed)
      { drawSelectionRange(cm, range, selFragment) }
  }
  return result
}

// Draws a cursor for the given range
function drawSelectionCursor(cm, head, output) {
  var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine)

  var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"))
  cursor.style.left = pos.left + "px"
  cursor.style.top = pos.top + "px"
  cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"

  if (pos.other) {
    // Secondary cursor, shown when on a 'jump' in bi-directional text
    var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"))
    otherCursor.style.display = ""
    otherCursor.style.left = pos.other.left + "px"
    otherCursor.style.top = pos.other.top + "px"
    otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"
  }
}

// Draws the given range as a highlighted selection
function drawSelectionRange(cm, range, output) {
  var display = cm.display, doc = cm.doc
  var fragment = document.createDocumentFragment()
  var padding = paddingH(cm.display), leftSide = padding.left
  var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right

  function add(left, top, width, bottom) {
    if (top < 0) { top = 0 }
    top = Math.round(top)
    bottom = Math.round(bottom)
    fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n                             top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n                             height: " + (bottom - top) + "px")))
  }

  function drawForLine(line, fromArg, toArg) {
    var lineObj = getLine(doc, line)
    var lineLen = lineObj.text.length
    var start, end
    function coords(ch, bias) {
      return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
    }

    iterateBidiSections(getOrder(lineObj, doc.direction), fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir) {
      var leftPos = coords(from, "left"), rightPos, left, right
      if (from == to) {
        rightPos = leftPos
        left = right = leftPos.left
      } else {
        rightPos = coords(to - 1, "right")
        if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp }
        left = leftPos.left
        right = rightPos.right
      }
      if (fromArg == null && from == 0) { left = leftSide }
      if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
        add(left, leftPos.top, null, leftPos.bottom)
        left = leftSide
        if (leftPos.bottom < rightPos.top) { add(left, leftPos.bottom, null, rightPos.top) }
      }
      if (toArg == null && to == lineLen) { right = rightSide }
      if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
        { start = leftPos }
      if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
        { end = rightPos }
      if (left < leftSide + 1) { left = leftSide }
      add(left, rightPos.top, right - left, rightPos.bottom)
    })
    return {start: start, end: end}
  }

  var sFrom = range.from(), sTo = range.to()
  if (sFrom.line == sTo.line) {
    drawForLine(sFrom.line, sFrom.ch, sTo.ch)
  } else {
    var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line)
    var singleVLine = visualLine(fromLine) == visualLine(toLine)
    var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end
    var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start
    if (singleVLine) {
      if (leftEnd.top < rightStart.top - 2) {
        add(leftEnd.right, leftEnd.top, null, leftEnd.bottom)
        add(leftSide, rightStart.top, rightStart.left, rightStart.bottom)
      } else {
        add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom)
      }
    }
    if (leftEnd.bottom < rightStart.top)
      { add(leftSide, leftEnd.bottom, null, rightStart.top) }
  }

  output.appendChild(fragment)
}

// Cursor-blinking
function restartBlink(cm) {
  if (!cm.state.focused) { return }
  var display = cm.display
  clearInterval(display.blinker)
  var on = true
  display.cursorDiv.style.visibility = ""
  if (cm.options.cursorBlinkRate > 0)
    { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
      cm.options.cursorBlinkRate) }
  else if (cm.options.cursorBlinkRate < 0)
    { display.cursorDiv.style.visibility = "hidden" }
}

function ensureFocus(cm) {
  if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm) }
}

function delayBlurEvent(cm) {
  cm.state.delayingBlurEvent = true
  setTimeout(function () { if (cm.state.delayingBlurEvent) {
    cm.state.delayingBlurEvent = false
    onBlur(cm)
  } }, 100)
}

function onFocus(cm, e) {
  if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false }

  if (cm.options.readOnly == "nocursor") { return }
  if (!cm.state.focused) {
    signal(cm, "focus", cm, e)
    cm.state.focused = true
    addClass(cm.display.wrapper, "CodeMirror-focused")
    // This test prevents this from firing when a context
    // menu is closed (since the input reset would kill the
    // select-all detection hack)
    if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
      cm.display.input.reset()
      if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20) } // Issue #1730
    }
    cm.display.input.receivedFocus()
  }
  restartBlink(cm)
}
function onBlur(cm, e) {
  if (cm.state.delayingBlurEvent) { return }

  if (cm.state.focused) {
    signal(cm, "blur", cm, e)
    cm.state.focused = false
    rmClass(cm.display.wrapper, "CodeMirror-focused")
  }
  clearInterval(cm.display.blinker)
  setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false } }, 150)
}

// Re-align line numbers and gutter marks to compensate for
// horizontal scrolling.
function alignHorizontally(cm) {
  var display = cm.display, view = display.view
  if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
  var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft
  var gutterW = display.gutters.offsetWidth, left = comp + "px"
  for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
    if (cm.options.fixedGutter) {
      if (view[i].gutter)
        { view[i].gutter.style.left = left }
      if (view[i].gutterBackground)
        { view[i].gutterBackground.style.left = left }
    }
    var align = view[i].alignable
    if (align) { for (var j = 0; j < align.length; j++)
      { align[j].style.left = left } }
  } }
  if (cm.options.fixedGutter)
    { display.gutters.style.left = (comp + gutterW) + "px" }
}

// Used to ensure that the line number gutter is still the right
// size for the current document size. Returns true when an update
// is needed.
function maybeUpdateLineNumberWidth(cm) {
  if (!cm.options.lineNumbers) { return false }
  var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display
  if (last.length != display.lineNumChars) {
    var test = display.measure.appendChild(elt("div", [elt("div", last)],
                                               "CodeMirror-linenumber CodeMirror-gutter-elt"))
    var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW
    display.lineGutter.style.width = ""
    display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1
    display.lineNumWidth = display.lineNumInnerWidth + padding
    display.lineNumChars = display.lineNumInnerWidth ? last.length : -1
    display.lineGutter.style.width = display.lineNumWidth + "px"
    updateGutterSpace(cm)
    return true
  }
  return false
}

// Read the actual heights of the rendered lines, and update their
// stored heights to match.
function updateHeightsInViewport(cm) {
  var display = cm.display
  var prevBottom = display.lineDiv.offsetTop
  for (var i = 0; i < display.view.length; i++) {
    var cur = display.view[i], height = (void 0)
    if (cur.hidden) { continue }
    if (ie && ie_version < 8) {
      var bot = cur.node.offsetTop + cur.node.offsetHeight
      height = bot - prevBottom
      prevBottom = bot
    } else {
      var box = cur.node.getBoundingClientRect()
      height = box.bottom - box.top
    }
    var diff = cur.line.height - height
    if (height < 2) { height = textHeight(display) }
    if (diff > .001 || diff < -.001) {
      updateLineHeight(cur.line, height)
      updateWidgetHeight(cur.line)
      if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
        { updateWidgetHeight(cur.rest[j]) } }
    }
  }
}

// Read and store the height of line widgets associated with the
// given line.
function updateWidgetHeight(line) {
  if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i)
    { line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight } }
}

// Compute the lines that are visible in a given viewport (defaults
// the the current scroll position). viewport may contain top,
// height, and ensure (see op.scrollToPos) properties.
function visibleLines(display, doc, viewport) {
  var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop
  top = Math.floor(top - paddingTop(display))
  var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight

  var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom)
  // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
  // forces those lines into the viewport (if possible).
  if (viewport && viewport.ensure) {
    var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line
    if (ensureFrom < from) {
      from = ensureFrom
      to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight)
    } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
      from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight)
      to = ensureTo
    }
  }
  return {from: from, to: Math.max(to, from + 1)}
}

// Sync the scrollable area and scrollbars, ensure the viewport
// covers the visible area.
function setScrollTop(cm, val) {
  if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
  cm.doc.scrollTop = val
  if (!gecko) { updateDisplaySimple(cm, {top: val}) }
  if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val }
  cm.display.scrollbars.setScrollTop(val)
  if (gecko) { updateDisplaySimple(cm) }
  startWorker(cm, 100)
}
// Sync scroller and scrollbar, ensure the gutter elements are
// aligned.
function setScrollLeft(cm, val, isScroller) {
  if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) { return }
  val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)
  cm.doc.scrollLeft = val
  alignHorizontally(cm)
  if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val }
  cm.display.scrollbars.setScrollLeft(val)
}

// Since the delta values reported on mouse wheel events are
// unstandardized between browsers and even browser versions, and
// generally horribly unpredictable, this code starts by measuring
// the scroll effect that the first few mouse wheel events have,
// and, from that, detects the way it can convert deltas to pixel
// offsets afterwards.
//
// The reason we want to know the amount a wheel event will scroll
// is that it gives us a chance to update the display before the
// actual scrolling happens, reducing flickering.

var wheelSamples = 0;
var wheelPixelsPerUnit = null;
// Fill in a browser-detected starting value on browsers where we
// know one. These don't have to be accurate -- the result of them
// being wrong would just be a slight flicker on the first wheel
// scroll (if it is large enough).
if (ie) { wheelPixelsPerUnit = -.53 }
else if (gecko) { wheelPixelsPerUnit = 15 }
else if (chrome) { wheelPixelsPerUnit = -.7 }
else if (safari) { wheelPixelsPerUnit = -1/3 }

function wheelEventDelta(e) {
  var dx = e.wheelDeltaX, dy = e.wheelDeltaY
  if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail }
  if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail }
  else if (dy == null) { dy = e.wheelDelta }
  return {x: dx, y: dy}
}
function wheelEventPixels(e) {
  var delta = wheelEventDelta(e)
  delta.x *= wheelPixelsPerUnit
  delta.y *= wheelPixelsPerUnit
  return delta
}

function onScrollWheel(cm, e) {
  var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y

  var display = cm.display, scroll = display.scroller
  // Quit if there's nothing to scroll here
  var canScrollX = scroll.scrollWidth > scroll.clientWidth
  var canScrollY = scroll.scrollHeight > scroll.clientHeight
  if (!(dx && canScrollX || dy && canScrollY)) { return }

  // Webkit browsers on OS X abort momentum scrolls when the target
  // of the scroll event is removed from the scrollable element.
  // This hack (see related code in patchDisplay) makes sure the
  // element is kept around.
  if (dy && mac && webkit) {
    outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
      for (var i = 0; i < view.length; i++) {
        if (view[i].node == cur) {
          cm.display.currentWheelTarget = cur
          break outer
        }
      }
    }
  }

  // On some browsers, horizontal scrolling will cause redraws to
  // happen before the gutter has been realigned, causing it to
  // wriggle around in a most unseemly way. When we have an
  // estimated pixels/delta value, we just handle horizontal
  // scrolling entirely here. It'll be slightly off from native, but
  // better than glitching out.
  if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
    if (dy && canScrollY)
      { setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight))) }
    setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)))
    // Only prevent default scrolling if vertical scrolling is
    // actually possible. Otherwise, it causes vertical scroll
    // jitter on OSX trackpads when deltaX is small and deltaY
    // is large (issue #3579)
    if (!dy || (dy && canScrollY))
      { e_preventDefault(e) }
    display.wheelStartX = null // Abort measurement, if in progress
    return
  }

  // 'Project' the visible viewport to cover the area that is being
  // scrolled into view (if we know enough to estimate it).
  if (dy && wheelPixelsPerUnit != null) {
    var pixels = dy * wheelPixelsPerUnit
    var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight
    if (pixels < 0) { top = Math.max(0, top + pixels - 50) }
    else { bot = Math.min(cm.doc.height, bot + pixels + 50) }
    updateDisplaySimple(cm, {top: top, bottom: bot})
  }

  if (wheelSamples < 20) {
    if (display.wheelStartX == null) {
      display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop
      display.wheelDX = dx; display.wheelDY = dy
      setTimeout(function () {
        if (display.wheelStartX == null) { return }
        var movedX = scroll.scrollLeft - display.wheelStartX
        var movedY = scroll.scrollTop - display.wheelStartY
        var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
          (movedX && display.wheelDX && movedX / display.wheelDX)
        display.wheelStartX = display.wheelStartY = null
        if (!sample) { return }
        wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1)
        ++wheelSamples
      }, 200)
    } else {
      display.wheelDX += dx; display.wheelDY += dy
    }
  }
}

// SCROLLBARS

// Prepare DOM reads needed to update the scrollbars. Done in one
// shot to minimize update/measure roundtrips.
function measureForScrollbars(cm) {
  var d = cm.display, gutterW = d.gutters.offsetWidth
  var docH = Math.round(cm.doc.height + paddingVert(cm.display))
  return {
    clientHeight: d.scroller.clientHeight,
    viewHeight: d.wrapper.clientHeight,
    scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
    viewWidth: d.wrapper.clientWidth,
    barLeft: cm.options.fixedGutter ? gutterW : 0,
    docHeight: docH,
    scrollHeight: docH + scrollGap(cm) + d.barHeight,
    nativeBarWidth: d.nativeBarWidth,
    gutterWidth: gutterW
  }
}

var NativeScrollbars = function(place, scroll, cm) {
  this.cm = cm
  var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar")
  var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar")
  place(vert); place(horiz)

  on(vert, "scroll", function () {
    if (vert.clientHeight) { scroll(vert.scrollTop, "vertical") }
  })
  on(horiz, "scroll", function () {
    if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal") }
  })

  this.checkedZeroWidth = false
  // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
  if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px" }
};

NativeScrollbars.prototype.update = function (measure) {
  var needsH = measure.scrollWidth > measure.clientWidth + 1
  var needsV = measure.scrollHeight > measure.clientHeight + 1
  var sWidth = measure.nativeBarWidth

  if (needsV) {
    this.vert.style.display = "block"
    this.vert.style.bottom = needsH ? sWidth + "px" : "0"
    var totalHeight = measure.viewHeight - (needsH ? sWidth : 0)
    // A bug in IE8 can cause this value to be negative, so guard it.
    this.vert.firstChild.style.height =
      Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"
  } else {
    this.vert.style.display = ""
    this.vert.firstChild.style.height = "0"
  }

  if (needsH) {
    this.horiz.style.display = "block"
    this.horiz.style.right = needsV ? sWidth + "px" : "0"
    this.horiz.style.left = measure.barLeft + "px"
    var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0)
    this.horiz.firstChild.style.width =
      Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"
  } else {
    this.horiz.style.display = ""
    this.horiz.firstChild.style.width = "0"
  }

  if (!this.checkedZeroWidth && measure.clientHeight > 0) {
    if (sWidth == 0) { this.zeroWidthHack() }
    this.checkedZeroWidth = true
  }

  return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
};

NativeScrollbars.prototype.setScrollLeft = function (pos) {
  if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos }
  if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz") }
};

NativeScrollbars.prototype.setScrollTop = function (pos) {
  if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos }
  if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert") }
};

NativeScrollbars.prototype.zeroWidthHack = function () {
  var w = mac && !mac_geMountainLion ? "12px" : "18px"
  this.horiz.style.height = this.vert.style.width = w
  this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"
  this.disableHoriz = new Delayed
  this.disableVert = new Delayed
};

NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
  bar.style.pointerEvents = "auto"
  function maybeDisable() {
    // To find out whether the scrollbar is still visible, we
    // check whether the element under the pixel in the bottom
    // right corner of the scrollbar box is the scrollbar box
    // itself (when the bar is still visible) or its filler child
    // (when the bar is hidden). If it is still visible, we keep
    // it enabled, if it's hidden, we disable pointer events.
    var box = bar.getBoundingClientRect()
    var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
        : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1)
    if (elt != bar) { bar.style.pointerEvents = "none" }
    else { delay.set(1000, maybeDisable) }
  }
  delay.set(1000, maybeDisable)
};

NativeScrollbars.prototype.clear = function () {
  var parent = this.horiz.parentNode
  parent.removeChild(this.horiz)
  parent.removeChild(this.vert)
};

var NullScrollbars = function () {};

NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
NullScrollbars.prototype.setScrollLeft = function () {};
NullScrollbars.prototype.setScrollTop = function () {};
NullScrollbars.prototype.clear = function () {};

function updateScrollbars(cm, measure) {
  if (!measure) { measure = measureForScrollbars(cm) }
  var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight
  updateScrollbarsInner(cm, measure)
  for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
    if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
      { updateHeightsInViewport(cm) }
    updateScrollbarsInner(cm, measureForScrollbars(cm))
    startWidth = cm.display.barWidth; startHeight = cm.display.barHeight
  }
}

// Re-synchronize the fake scrollbars with the actual size of the
// content.
function updateScrollbarsInner(cm, measure) {
  var d = cm.display
  var sizes = d.scrollbars.update(measure)

  d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"
  d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"
  d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"

  if (sizes.right && sizes.bottom) {
    d.scrollbarFiller.style.display = "block"
    d.scrollbarFiller.style.height = sizes.bottom + "px"
    d.scrollbarFiller.style.width = sizes.right + "px"
  } else { d.scrollbarFiller.style.display = "" }
  if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
    d.gutterFiller.style.display = "block"
    d.gutterFiller.style.height = sizes.bottom + "px"
    d.gutterFiller.style.width = measure.gutterWidth + "px"
  } else { d.gutterFiller.style.display = "" }
}

var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}

function initScrollbars(cm) {
  if (cm.display.scrollbars) {
    cm.display.scrollbars.clear()
    if (cm.display.scrollbars.addClass)
      { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass) }
  }

  cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
    cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller)
    // Prevent clicks in the scrollbars from killing focus
    on(node, "mousedown", function () {
      if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0) }
    })
    node.setAttribute("cm-not-content", "true")
  }, function (pos, axis) {
    if (axis == "horizontal") { setScrollLeft(cm, pos) }
    else { setScrollTop(cm, pos) }
  }, cm)
  if (cm.display.scrollbars.addClass)
    { addClass(cm.display.wrapper, cm.display.scrollbars.addClass) }
}

// SCROLLING THINGS INTO VIEW

// If an editor sits on the top or bottom of the window, partially
// scrolled out of view, this ensures that the cursor is visible.
function maybeScrollWindow(cm, rect) {
  if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }

  var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null
  if (rect.top + box.top < 0) { doScroll = true }
  else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false }
  if (doScroll != null && !phantom) {
    var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n                         top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n                         height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n                         left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;"))
    cm.display.lineSpace.appendChild(scrollNode)
    scrollNode.scrollIntoView(doScroll)
    cm.display.lineSpace.removeChild(scrollNode)
  }
}

// Scroll a given position into view (immediately), verifying that
// it actually became visible (as line heights are accurately
// measured, the position of something may 'drift' during drawing).
function scrollPosIntoView(cm, pos, end, margin) {
  if (margin == null) { margin = 0 }
  var rect
  for (var limit = 0; limit < 5; limit++) {
    var changed = false
    var coords = cursorCoords(cm, pos)
    var endCoords = !end || end == pos ? coords : cursorCoords(cm, end)
    rect = {left: Math.min(coords.left, endCoords.left),
            top: Math.min(coords.top, endCoords.top) - margin,
            right: Math.max(coords.left, endCoords.left),
            bottom: Math.max(coords.bottom, endCoords.bottom) + margin}
    var scrollPos = calculateScrollPos(cm, rect)
    var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft
    if (scrollPos.scrollTop != null) {
      setScrollTop(cm, scrollPos.scrollTop)
      if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true }
    }
    if (scrollPos.scrollLeft != null) {
      setScrollLeft(cm, scrollPos.scrollLeft)
      if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true }
    }
    if (!changed) { break }
  }
  return rect
}

// Scroll a given set of coordinates into view (immediately).
function scrollIntoView(cm, rect) {
  var scrollPos = calculateScrollPos(cm, rect)
  if (scrollPos.scrollTop != null) { setScrollTop(cm, scrollPos.scrollTop) }
  if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft) }
}

// Calculate a new scroll position needed to scroll the given
// rectangle into view. Returns an object with scrollTop and
// scrollLeft properties. When these are undefined, the
// vertical/horizontal position does not need to be adjusted.
function calculateScrollPos(cm, rect) {
  var display = cm.display, snapMargin = textHeight(cm.display)
  if (rect.top < 0) { rect.top = 0 }
  var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop
  var screen = displayHeight(cm), result = {}
  if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen }
  var docBottom = cm.doc.height + paddingVert(display)
  var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin
  if (rect.top < screentop) {
    result.scrollTop = atTop ? 0 : rect.top
  } else if (rect.bottom > screentop + screen) {
    var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen)
    if (newTop != screentop) { result.scrollTop = newTop }
  }

  var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft
  var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0)
  var tooWide = rect.right - rect.left > screenw
  if (tooWide) { rect.right = rect.left + screenw }
  if (rect.left < 10)
    { result.scrollLeft = 0 }
  else if (rect.left < screenleft)
    { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)) }
  else if (rect.right > screenw + screenleft - 3)
    { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw }
  return result
}

// Store a relative adjustment to the scroll position in the current
// operation (to be applied when the operation finishes).
function addToScrollPos(cm, left, top) {
  if (left != null || top != null) { resolveScrollToPos(cm) }
  if (left != null)
    { cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left }
  if (top != null)
    { cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top }
}

// Make sure that at the end of the operation the current cursor is
// shown.
function ensureCursorVisible(cm) {
  resolveScrollToPos(cm)
  var cur = cm.getCursor(), from = cur, to = cur
  if (!cm.options.lineWrapping) {
    from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur
    to = Pos(cur.line, cur.ch + 1)
  }
  cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin}
}

// When an operation has its scrollToPos property set, and another
// scroll action is applied before the end of the operation, this
// 'simulates' scrolling that position into view in a cheap way, so
// that the effect of intermediate scroll commands is not ignored.
function resolveScrollToPos(cm) {
  var range = cm.curOp.scrollToPos
  if (range) {
    cm.curOp.scrollToPos = null
    var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to)
    var sPos = calculateScrollPos(cm, {
      left: Math.min(from.left, to.left),
      top: Math.min(from.top, to.top) - range.margin,
      right: Math.max(from.right, to.right),
      bottom: Math.max(from.bottom, to.bottom) + range.margin
    })
    cm.scrollTo(sPos.scrollLeft, sPos.scrollTop)
  }
}

// Operations are used to wrap a series of changes to the editor
// state in such a way that each change won't have to update the
// cursor and display (which would be awkward, slow, and
// error-prone). Instead, display updates are batched and then all
// combined and executed at once.

var nextOpId = 0
// Start a new operation.
function startOperation(cm) {
  cm.curOp = {
    cm: cm,
    viewChanged: false,      // Flag that indicates that lines might need to be redrawn
    startHeight: cm.doc.height, // Used to detect need to update scrollbar
    forceUpdate: false,      // Used to force a redraw
    updateInput: null,       // Whether to reset the input textarea
    typing: false,           // Whether this reset should be careful to leave existing text (for compositing)
    changeObjs: null,        // Accumulated changes, for firing change events
    cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
    cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
    selectionChanged: false, // Whether the selection needs to be redrawn
    updateMaxLine: false,    // Set when the widest line needs to be determined anew
    scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
    scrollToPos: null,       // Used to scroll to a specific position
    focus: false,
    id: ++nextOpId           // Unique ID
  }
  pushOperation(cm.curOp)
}

// Finish an operation, updating the display and signalling delayed events
function endOperation(cm) {
  var op = cm.curOp
  finishOperation(op, function (group) {
    for (var i = 0; i < group.ops.length; i++)
      { group.ops[i].cm.curOp = null }
    endOperations(group)
  })
}

// The DOM updates done when an operation finishes are batched so
// that the minimum number of relayouts are required.
function endOperations(group) {
  var ops = group.ops
  for (var i = 0; i < ops.length; i++) // Read DOM
    { endOperation_R1(ops[i]) }
  for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
    { endOperation_W1(ops[i$1]) }
  for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
    { endOperation_R2(ops[i$2]) }
  for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
    { endOperation_W2(ops[i$3]) }
  for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
    { endOperation_finish(ops[i$4]) }
}

function endOperation_R1(op) {
  var cm = op.cm, display = cm.display
  maybeClipScrollbars(cm)
  if (op.updateMaxLine) { findMaxLine(cm) }

  op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
    op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
                       op.scrollToPos.to.line >= display.viewTo) ||
    display.maxLineChanged && cm.options.lineWrapping
  op.update = op.mustUpdate &&
    new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate)
}

function endOperation_W1(op) {
  op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update)
}

function endOperation_R2(op) {
  var cm = op.cm, display = cm.display
  if (op.updatedDisplay) { updateHeightsInViewport(cm) }

  op.barMeasure = measureForScrollbars(cm)

  // If the max line changed since it was last measured, measure it,
  // and ensure the document's width matches it.
  // updateDisplay_W2 will use these properties to do the actual resizing
  if (display.maxLineChanged && !cm.options.lineWrapping) {
    op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3
    cm.display.sizerWidth = op.adjustWidthTo
    op.barMeasure.scrollWidth =
      Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth)
    op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm))
  }

  if (op.updatedDisplay || op.selectionChanged)
    { op.preparedSelection = display.input.prepareSelection(op.focus) }
}

function endOperation_W2(op) {
  var cm = op.cm

  if (op.adjustWidthTo != null) {
    cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"
    if (op.maxScrollLeft < cm.doc.scrollLeft)
      { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true) }
    cm.display.maxLineChanged = false
  }

  var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus())
  if (op.preparedSelection)
    { cm.display.input.showSelection(op.preparedSelection, takeFocus) }
  if (op.updatedDisplay || op.startHeight != cm.doc.height)
    { updateScrollbars(cm, op.barMeasure) }
  if (op.updatedDisplay)
    { setDocumentHeight(cm, op.barMeasure) }

  if (op.selectionChanged) { restartBlink(cm) }

  if (cm.state.focused && op.updateInput)
    { cm.display.input.reset(op.typing) }
  if (takeFocus) { ensureFocus(op.cm) }
}

function endOperation_finish(op) {
  var cm = op.cm, display = cm.display, doc = cm.doc

  if (op.updatedDisplay) { postUpdateDisplay(cm, op.update) }

  // Abort mouse wheel delta measurement, when scrolling explicitly
  if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
    { display.wheelStartX = display.wheelStartY = null }

  // Propagate the scroll position to the actual DOM scroller
  if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {
    doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop))
    display.scrollbars.setScrollTop(doc.scrollTop)
    display.scroller.scrollTop = doc.scrollTop
  }
  if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
    doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, op.scrollLeft))
    display.scrollbars.setScrollLeft(doc.scrollLeft)
    display.scroller.scrollLeft = doc.scrollLeft
    alignHorizontally(cm)
  }
  // If we need to scroll a specific position into view, do so.
  if (op.scrollToPos) {
    var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
                                 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin)
    maybeScrollWindow(cm, rect)
  }

  // Fire events for markers that are hidden/unidden by editing or
  // undoing
  var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers
  if (hidden) { for (var i = 0; i < hidden.length; ++i)
    { if (!hidden[i].lines.length) { signal(hidden[i], "hide") } } }
  if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
    { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide") } } }

  if (display.wrapper.offsetHeight)
    { doc.scrollTop = cm.display.scroller.scrollTop }

  // Fire change events, and delayed event handlers
  if (op.changeObjs)
    { signal(cm, "changes", cm, op.changeObjs) }
  if (op.update)
    { op.update.finish() }
}

// Run the given function in an operation
function runInOp(cm, f) {
  if (cm.curOp) { return f() }
  startOperation(cm)
  try { return f() }
  finally { endOperation(cm) }
}
// Wraps a function in an operation. Returns the wrapped function.
function operation(cm, f) {
  return function() {
    if (cm.curOp) { return f.apply(cm, arguments) }
    startOperation(cm)
    try { return f.apply(cm, arguments) }
    finally { endOperation(cm) }
  }
}
// Used to add methods to editor and doc instances, wrapping them in
// operations.
function methodOp(f) {
  return function() {
    if (this.curOp) { return f.apply(this, arguments) }
    startOperation(this)
    try { return f.apply(this, arguments) }
    finally { endOperation(this) }
  }
}
function docMethodOp(f) {
  return function() {
    var cm = this.cm
    if (!cm || cm.curOp) { return f.apply(this, arguments) }
    startOperation(cm)
    try { return f.apply(this, arguments) }
    finally { endOperation(cm) }
  }
}

// Updates the display.view data structure for a given change to the
// document. From and to are in pre-change coordinates. Lendiff is
// the amount of lines added or subtracted by the change. This is
// used for changes that span multiple lines, or change the way
// lines are divided into visual lines. regLineChange (below)
// registers single-line changes.
function regChange(cm, from, to, lendiff) {
  if (from == null) { from = cm.doc.first }
  if (to == null) { to = cm.doc.first + cm.doc.size }
  if (!lendiff) { lendiff = 0 }

  var display = cm.display
  if (lendiff && to < display.viewTo &&
      (display.updateLineNumbers == null || display.updateLineNumbers > from))
    { display.updateLineNumbers = from }

  cm.curOp.viewChanged = true

  if (from >= display.viewTo) { // Change after
    if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
      { resetView(cm) }
  } else if (to <= display.viewFrom) { // Change before
    if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
      resetView(cm)
    } else {
      display.viewFrom += lendiff
      display.viewTo += lendiff
    }
  } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
    resetView(cm)
  } else if (from <= display.viewFrom) { // Top overlap
    var cut = viewCuttingPoint(cm, to, to + lendiff, 1)
    if (cut) {
      display.view = display.view.slice(cut.index)
      display.viewFrom = cut.lineN
      display.viewTo += lendiff
    } else {
      resetView(cm)
    }
  } else if (to >= display.viewTo) { // Bottom overlap
    var cut$1 = viewCuttingPoint(cm, from, from, -1)
    if (cut$1) {
      display.view = display.view.slice(0, cut$1.index)
      display.viewTo = cut$1.lineN
    } else {
      resetView(cm)
    }
  } else { // Gap in the middle
    var cutTop = viewCuttingPoint(cm, from, from, -1)
    var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1)
    if (cutTop && cutBot) {
      display.view = display.view.slice(0, cutTop.index)
        .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
        .concat(display.view.slice(cutBot.index))
      display.viewTo += lendiff
    } else {
      resetView(cm)
    }
  }

  var ext = display.externalMeasured
  if (ext) {
    if (to < ext.lineN)
      { ext.lineN += lendiff }
    else if (from < ext.lineN + ext.size)
      { display.externalMeasured = null }
  }
}

// Register a change to a single line. Type must be one of "text",
// "gutter", "class", "widget"
function regLineChange(cm, line, type) {
  cm.curOp.viewChanged = true
  var display = cm.display, ext = cm.display.externalMeasured
  if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
    { display.externalMeasured = null }

  if (line < display.viewFrom || line >= display.viewTo) { return }
  var lineView = display.view[findViewIndex(cm, line)]
  if (lineView.node == null) { return }
  var arr = lineView.changes || (lineView.changes = [])
  if (indexOf(arr, type) == -1) { arr.push(type) }
}

// Clear the view.
function resetView(cm) {
  cm.display.viewFrom = cm.display.viewTo = cm.doc.first
  cm.display.view = []
  cm.display.viewOffset = 0
}

function viewCuttingPoint(cm, oldN, newN, dir) {
  var index = findViewIndex(cm, oldN), diff, view = cm.display.view
  if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
    { return {index: index, lineN: newN} }
  var n = cm.display.viewFrom
  for (var i = 0; i < index; i++)
    { n += view[i].size }
  if (n != oldN) {
    if (dir > 0) {
      if (index == view.length - 1) { return null }
      diff = (n + view[index].size) - oldN
      index++
    } else {
      diff = n - oldN
    }
    oldN += diff; newN += diff
  }
  while (visualLineNo(cm.doc, newN) != newN) {
    if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
    newN += dir * view[index - (dir < 0 ? 1 : 0)].size
    index += dir
  }
  return {index: index, lineN: newN}
}

// Force the view to cover a given range, adding empty view element
// or clipping off existing ones as needed.
function adjustView(cm, from, to) {
  var display = cm.display, view = display.view
  if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
    display.view = buildViewArray(cm, from, to)
    display.viewFrom = from
  } else {
    if (display.viewFrom > from)
      { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view) }
    else if (display.viewFrom < from)
      { display.view = display.view.slice(findViewIndex(cm, from)) }
    display.viewFrom = from
    if (display.viewTo < to)
      { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)) }
    else if (display.viewTo > to)
      { display.view = display.view.slice(0, findViewIndex(cm, to)) }
  }
  display.viewTo = to
}

// Count the number of lines in the view whose DOM representation is
// out of date (or nonexistent).
function countDirtyView(cm) {
  var view = cm.display.view, dirty = 0
  for (var i = 0; i < view.length; i++) {
    var lineView = view[i]
    if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty }
  }
  return dirty
}

// HIGHLIGHT WORKER

function startWorker(cm, time) {
  if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
    { cm.state.highlight.set(time, bind(highlightWorker, cm)) }
}

function highlightWorker(cm) {
  var doc = cm.doc
  if (doc.frontier < doc.first) { doc.frontier = doc.first }
  if (doc.frontier >= cm.display.viewTo) { return }
  var end = +new Date + cm.options.workTime
  var state = copyState(doc.mode, getStateBefore(cm, doc.frontier))
  var changedLines = []

  doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
    if (doc.frontier >= cm.display.viewFrom) { // Visible
      var oldStyles = line.styles, tooLong = line.text.length > cm.options.maxHighlightLength
      var highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true)
      line.styles = highlighted.styles
      var oldCls = line.styleClasses, newCls = highlighted.classes
      if (newCls) { line.styleClasses = newCls }
      else if (oldCls) { line.styleClasses = null }
      var ischange = !oldStyles || oldStyles.length != line.styles.length ||
        oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass)
      for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i] }
      if (ischange) { changedLines.push(doc.frontier) }
      line.stateAfter = tooLong ? state : copyState(doc.mode, state)
    } else {
      if (line.text.length <= cm.options.maxHighlightLength)
        { processLine(cm, line.text, state) }
      line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null
    }
    ++doc.frontier
    if (+new Date > end) {
      startWorker(cm, cm.options.workDelay)
      return true
    }
  })
  if (changedLines.length) { runInOp(cm, function () {
    for (var i = 0; i < changedLines.length; i++)
      { regLineChange(cm, changedLines[i], "text") }
  }) }
}

// DISPLAY DRAWING

var DisplayUpdate = function(cm, viewport, force) {
  var display = cm.display

  this.viewport = viewport
  // Store some values that we'll need later (but don't want to force a relayout for)
  this.visible = visibleLines(display, cm.doc, viewport)
  this.editorIsHidden = !display.wrapper.offsetWidth
  this.wrapperHeight = display.wrapper.clientHeight
  this.wrapperWidth = display.wrapper.clientWidth
  this.oldDisplayWidth = displayWidth(cm)
  this.force = force
  this.dims = getDimensions(cm)
  this.events = []
};

DisplayUpdate.prototype.signal = function (emitter, type) {
  if (hasHandler(emitter, type))
    { this.events.push(arguments) }
};
DisplayUpdate.prototype.finish = function () {
    var this$1 = this;

  for (var i = 0; i < this.events.length; i++)
    { signal.apply(null, this$1.events[i]) }
};

function maybeClipScrollbars(cm) {
  var display = cm.display
  if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
    display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth
    display.heightForcer.style.height = scrollGap(cm) + "px"
    display.sizer.style.marginBottom = -display.nativeBarWidth + "px"
    display.sizer.style.borderRightWidth = scrollGap(cm) + "px"
    display.scrollbarsClipped = true
  }
}

// Does the actual updating of the line display. Bails out
// (returning false) when there is nothing to be done and forced is
// false.
function updateDisplayIfNeeded(cm, update) {
  var display = cm.display, doc = cm.doc

  if (update.editorIsHidden) {
    resetView(cm)
    return false
  }

  // Bail out if the visible area is already rendered and nothing changed.
  if (!update.force &&
      update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
      (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
      display.renderedView == display.view && countDirtyView(cm) == 0)
    { return false }

  if (maybeUpdateLineNumberWidth(cm)) {
    resetView(cm)
    update.dims = getDimensions(cm)
  }

  // Compute a suitable new viewport (from & to)
  var end = doc.first + doc.size
  var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first)
  var to = Math.min(end, update.visible.to + cm.options.viewportMargin)
  if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom) }
  if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo) }
  if (sawCollapsedSpans) {
    from = visualLineNo(cm.doc, from)
    to = visualLineEndNo(cm.doc, to)
  }

  var different = from != display.viewFrom || to != display.viewTo ||
    display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth
  adjustView(cm, from, to)

  display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom))
  // Position the mover div to align with the current scroll position
  cm.display.mover.style.top = display.viewOffset + "px"

  var toUpdate = countDirtyView(cm)
  if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
      (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
    { return false }

  // For big changes, we hide the enclosing element during the
  // update, since that speeds up the operations on most browsers.
  var focused = activeElt()
  if (toUpdate > 4) { display.lineDiv.style.display = "none" }
  patchDisplay(cm, display.updateLineNumbers, update.dims)
  if (toUpdate > 4) { display.lineDiv.style.display = "" }
  display.renderedView = display.view
  // There might have been a widget with a focused element that got
  // hidden or updated, if so re-focus it.
  if (focused && activeElt() != focused && focused.offsetHeight) { focused.focus() }

  // Prevent selection and cursors from interfering with the scroll
  // width and height.
  removeChildren(display.cursorDiv)
  removeChildren(display.selectionDiv)
  display.gutters.style.height = display.sizer.style.minHeight = 0

  if (different) {
    display.lastWrapHeight = update.wrapperHeight
    display.lastWrapWidth = update.wrapperWidth
    startWorker(cm, 400)
  }

  display.updateLineNumbers = null

  return true
}

function postUpdateDisplay(cm, update) {
  var viewport = update.viewport

  for (var first = true;; first = false) {
    if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
      // Clip forced viewport to actual scrollable area.
      if (viewport && viewport.top != null)
        { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)} }
      // Updated line heights might result in the drawn area not
      // actually covering the viewport. Keep looping until it does.
      update.visible = visibleLines(cm.display, cm.doc, viewport)
      if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
        { break }
    }
    if (!updateDisplayIfNeeded(cm, update)) { break }
    updateHeightsInViewport(cm)
    var barMeasure = measureForScrollbars(cm)
    updateSelection(cm)
    updateScrollbars(cm, barMeasure)
    setDocumentHeight(cm, barMeasure)
  }

  update.signal(cm, "update", cm)
  if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
    update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo)
    cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo
  }
}

function updateDisplaySimple(cm, viewport) {
  var update = new DisplayUpdate(cm, viewport)
  if (updateDisplayIfNeeded(cm, update)) {
    updateHeightsInViewport(cm)
    postUpdateDisplay(cm, update)
    var barMeasure = measureForScrollbars(cm)
    updateSelection(cm)
    updateScrollbars(cm, barMeasure)
    setDocumentHeight(cm, barMeasure)
    update.finish()
  }
}

// Sync the actual display DOM structure with display.view, removing
// nodes for lines that are no longer in view, and creating the ones
// that are not there yet, and updating the ones that are out of
// date.
function patchDisplay(cm, updateNumbersFrom, dims) {
  var display = cm.display, lineNumbers = cm.options.lineNumbers
  var container = display.lineDiv, cur = container.firstChild

  function rm(node) {
    var next = node.nextSibling
    // Works around a throw-scroll bug in OS X Webkit
    if (webkit && mac && cm.display.currentWheelTarget == node)
      { node.style.display = "none" }
    else
      { node.parentNode.removeChild(node) }
    return next
  }

  var view = display.view, lineN = display.viewFrom
  // Loop over the elements in the view, syncing cur (the DOM nodes
  // in display.lineDiv) with the view as we go.
  for (var i = 0; i < view.length; i++) {
    var lineView = view[i]
    if (lineView.hidden) {
    } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
      var node = buildLineElement(cm, lineView, lineN, dims)
      container.insertBefore(node, cur)
    } else { // Already drawn
      while (cur != lineView.node) { cur = rm(cur) }
      var updateNumber = lineNumbers && updateNumbersFrom != null &&
        updateNumbersFrom <= lineN && lineView.lineNumber
      if (lineView.changes) {
        if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false }
        updateLineForChanges(cm, lineView, lineN, dims)
      }
      if (updateNumber) {
        removeChildren(lineView.lineNumber)
        lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)))
      }
      cur = lineView.node.nextSibling
    }
    lineN += lineView.size
  }
  while (cur) { cur = rm(cur) }
}

function updateGutterSpace(cm) {
  var width = cm.display.gutters.offsetWidth
  cm.display.sizer.style.marginLeft = width + "px"
}

function setDocumentHeight(cm, measure) {
  cm.display.sizer.style.minHeight = measure.docHeight + "px"
  cm.display.heightForcer.style.top = measure.docHeight + "px"
  cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"
}

// Rebuild the gutter elements, ensure the margin to the left of the
// code matches their width.
function updateGutters(cm) {
  var gutters = cm.display.gutters, specs = cm.options.gutters
  removeChildren(gutters)
  var i = 0
  for (; i < specs.length; ++i) {
    var gutterClass = specs[i]
    var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass))
    if (gutterClass == "CodeMirror-linenumbers") {
      cm.display.lineGutter = gElt
      gElt.style.width = (cm.display.lineNumWidth || 1) + "px"
    }
  }
  gutters.style.display = i ? "" : "none"
  updateGutterSpace(cm)
}

// Make sure the gutters options contains the element
// "CodeMirror-linenumbers" when the lineNumbers option is true.
function setGuttersForLineNumbers(options) {
  var found = indexOf(options.gutters, "CodeMirror-linenumbers")
  if (found == -1 && options.lineNumbers) {
    options.gutters = options.gutters.concat(["CodeMirror-linenumbers"])
  } else if (found > -1 && !options.lineNumbers) {
    options.gutters = options.gutters.slice(0)
    options.gutters.splice(found, 1)
  }
}

// Selection objects are immutable. A new one is created every time
// the selection changes. A selection is one or more non-overlapping
// (and non-touching) ranges, sorted, and an integer that indicates
// which one is the primary selection (the one that's scrolled into
// view, that getCursor returns, etc).
var Selection = function(ranges, primIndex) {
  this.ranges = ranges
  this.primIndex = primIndex
};

Selection.prototype.primary = function () { return this.ranges[this.primIndex] };

Selection.prototype.equals = function (other) {
    var this$1 = this;

  if (other == this) { return true }
  if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
  for (var i = 0; i < this.ranges.length; i++) {
    var here = this$1.ranges[i], there = other.ranges[i]
    if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
  }
  return true
};

Selection.prototype.deepCopy = function () {
    var this$1 = this;

  var out = []
  for (var i = 0; i < this.ranges.length; i++)
    { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)) }
  return new Selection(out, this.primIndex)
};

Selection.prototype.somethingSelected = function () {
    var this$1 = this;

  for (var i = 0; i < this.ranges.length; i++)
    { if (!this$1.ranges[i].empty()) { return true } }
  return false
};

Selection.prototype.contains = function (pos, end) {
    var this$1 = this;

  if (!end) { end = pos }
  for (var i = 0; i < this.ranges.length; i++) {
    var range = this$1.ranges[i]
    if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
      { return i }
  }
  return -1
};

var Range = function(anchor, head) {
  this.anchor = anchor; this.head = head
};

Range.prototype.from = function () { return minPos(this.anchor, this.head) };
Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };

// Take an unsorted, potentially overlapping set of ranges, and
// build a selection out of it. 'Consumes' ranges array (modifying
// it).
function normalizeSelection(ranges, primIndex) {
  var prim = ranges[primIndex]
  ranges.sort(function (a, b) { return cmp(a.from(), b.from()); })
  primIndex = indexOf(ranges, prim)
  for (var i = 1; i < ranges.length; i++) {
    var cur = ranges[i], prev = ranges[i - 1]
    if (cmp(prev.to(), cur.from()) >= 0) {
      var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to())
      var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head
      if (i <= primIndex) { --primIndex }
      ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to))
    }
  }
  return new Selection(ranges, primIndex)
}

function simpleSelection(anchor, head) {
  return new Selection([new Range(anchor, head || anchor)], 0)
}

// Compute the position of the end of a change (its 'to' property
// refers to the pre-change end).
function changeEnd(change) {
  if (!change.text) { return change.to }
  return Pos(change.from.line + change.text.length - 1,
             lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
}

// Adjust a position to refer to the post-change position of the
// same text, or the end of the change if the change covers it.
function adjustForChange(pos, change) {
  if (cmp(pos, change.from) < 0) { return pos }
  if (cmp(pos, change.to) <= 0) { return changeEnd(change) }

  var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch
  if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch }
  return Pos(line, ch)
}

function computeSelAfterChange(doc, change) {
  var out = []
  for (var i = 0; i < doc.sel.ranges.length; i++) {
    var range = doc.sel.ranges[i]
    out.push(new Range(adjustForChange(range.anchor, change),
                       adjustForChange(range.head, change)))
  }
  return normalizeSelection(out, doc.sel.primIndex)
}

function offsetPos(pos, old, nw) {
  if (pos.line == old.line)
    { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
  else
    { return Pos(nw.line + (pos.line - old.line), pos.ch) }
}

// Used by replaceSelections to allow moving the selection to the
// start or around the replaced test. Hint may be "start" or "around".
function computeReplacedSel(doc, changes, hint) {
  var out = []
  var oldPrev = Pos(doc.first, 0), newPrev = oldPrev
  for (var i = 0; i < changes.length; i++) {
    var change = changes[i]
    var from = offsetPos(change.from, oldPrev, newPrev)
    var to = offsetPos(changeEnd(change), oldPrev, newPrev)
    oldPrev = change.to
    newPrev = to
    if (hint == "around") {
      var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0
      out[i] = new Range(inv ? to : from, inv ? from : to)
    } else {
      out[i] = new Range(from, from)
    }
  }
  return new Selection(out, doc.sel.primIndex)
}

// Used to get the editor into a consistent state again when options change.

function loadMode(cm) {
  cm.doc.mode = getMode(cm.options, cm.doc.modeOption)
  resetModeState(cm)
}

function resetModeState(cm) {
  cm.doc.iter(function (line) {
    if (line.stateAfter) { line.stateAfter = null }
    if (line.styles) { line.styles = null }
  })
  cm.doc.frontier = cm.doc.first
  startWorker(cm, 100)
  cm.state.modeGen++
  if (cm.curOp) { regChange(cm) }
}

// DOCUMENT DATA STRUCTURE

// By default, updates that start and end at the beginning of a line
// are treated specially, in order to make the association of line
// widgets and marker elements with the text behave more intuitive.
function isWholeLineUpdate(doc, change) {
  return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
    (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
}

// Perform a change on the document data structure.
function updateDoc(doc, change, markedSpans, estimateHeight) {
  function spansFor(n) {return markedSpans ? markedSpans[n] : null}
  function update(line, text, spans) {
    updateLine(line, text, spans, estimateHeight)
    signalLater(line, "change", line, change)
  }
  function linesFor(start, end) {
    var result = []
    for (var i = start; i < end; ++i)
      { result.push(new Line(text[i], spansFor(i), estimateHeight)) }
    return result
  }

  var from = change.from, to = change.to, text = change.text
  var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line)
  var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line

  // Adjust the line structure
  if (change.full) {
    doc.insert(0, linesFor(0, text.length))
    doc.remove(text.length, doc.size - text.length)
  } else if (isWholeLineUpdate(doc, change)) {
    // This is a whole-line replace. Treated specially to make
    // sure line objects move the way they are supposed to.
    var added = linesFor(0, text.length - 1)
    update(lastLine, lastLine.text, lastSpans)
    if (nlines) { doc.remove(from.line, nlines) }
    if (added.length) { doc.insert(from.line, added) }
  } else if (firstLine == lastLine) {
    if (text.length == 1) {
      update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans)
    } else {
      var added$1 = linesFor(1, text.length - 1)
      added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight))
      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
      doc.insert(from.line + 1, added$1)
    }
  } else if (text.length == 1) {
    update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0))
    doc.remove(from.line + 1, nlines)
  } else {
    update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
    update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans)
    var added$2 = linesFor(1, text.length - 1)
    if (nlines > 1) { doc.remove(from.line + 1, nlines - 1) }
    doc.insert(from.line + 1, added$2)
  }

  signalLater(doc, "change", doc, change)
}

// Call f for all linked documents.
function linkedDocs(doc, f, sharedHistOnly) {
  function propagate(doc, skip, sharedHist) {
    if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
      var rel = doc.linked[i]
      if (rel.doc == skip) { continue }
      var shared = sharedHist && rel.sharedHist
      if (sharedHistOnly && !shared) { continue }
      f(rel.doc, shared)
      propagate(rel.doc, doc, shared)
    } }
  }
  propagate(doc, null, true)
}

// Attach a document to an editor.
function attachDoc(cm, doc) {
  if (doc.cm) { throw new Error("This document is already in use.") }
  cm.doc = doc
  doc.cm = cm
  estimateLineHeights(cm)
  loadMode(cm)
  setDirectionClass(cm)
  if (!cm.options.lineWrapping) { findMaxLine(cm) }
  cm.options.mode = doc.modeOption
  regChange(cm)
}

function setDirectionClass(cm) {
  ;(cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl")
}

function directionChanged(cm) {
  runInOp(cm, function () {
    setDirectionClass(cm)
    regChange(cm)
  })
}

function History(startGen) {
  // Arrays of change events and selections. Doing something adds an
  // event to done and clears undo. Undoing moves events from done
  // to undone, redoing moves them in the other direction.
  this.done = []; this.undone = []
  this.undoDepth = Infinity
  // Used to track when changes can be merged into a single undo
  // event
  this.lastModTime = this.lastSelTime = 0
  this.lastOp = this.lastSelOp = null
  this.lastOrigin = this.lastSelOrigin = null
  // Used by the isClean() method
  this.generation = this.maxGeneration = startGen || 1
}

// Create a history change event from an updateDoc-style change
// object.
function historyChangeFromChange(doc, change) {
  var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}
  attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1)
  linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true)
  return histChange
}

// Pop all selection events off the end of a history array. Stop at
// a change event.
function clearSelectionEvents(array) {
  while (array.length) {
    var last = lst(array)
    if (last.ranges) { array.pop() }
    else { break }
  }
}

// Find the top change event in the history. Pop off selection
// events that are in the way.
function lastChangeEvent(hist, force) {
  if (force) {
    clearSelectionEvents(hist.done)
    return lst(hist.done)
  } else if (hist.done.length && !lst(hist.done).ranges) {
    return lst(hist.done)
  } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
    hist.done.pop()
    return lst(hist.done)
  }
}

// Register a change in the history. Merges changes that are within
// a single operation, or are close together with an origin that
// allows merging (starting with "+") into a single event.
function addChangeToHistory(doc, change, selAfter, opId) {
  var hist = doc.history
  hist.undone.length = 0
  var time = +new Date, cur
  var last

  if ((hist.lastOp == opId ||
       hist.lastOrigin == change.origin && change.origin &&
       ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
        change.origin.charAt(0) == "*")) &&
      (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
    // Merge this change into the last event
    last = lst(cur.changes)
    if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
      // Optimized case for simple insertion -- don't want to add
      // new changesets for every character typed
      last.to = changeEnd(change)
    } else {
      // Add new sub-event
      cur.changes.push(historyChangeFromChange(doc, change))
    }
  } else {
    // Can not be merged, start a new event.
    var before = lst(hist.done)
    if (!before || !before.ranges)
      { pushSelectionToHistory(doc.sel, hist.done) }
    cur = {changes: [historyChangeFromChange(doc, change)],
           generation: hist.generation}
    hist.done.push(cur)
    while (hist.done.length > hist.undoDepth) {
      hist.done.shift()
      if (!hist.done[0].ranges) { hist.done.shift() }
    }
  }
  hist.done.push(selAfter)
  hist.generation = ++hist.maxGeneration
  hist.lastModTime = hist.lastSelTime = time
  hist.lastOp = hist.lastSelOp = opId
  hist.lastOrigin = hist.lastSelOrigin = change.origin

  if (!last) { signal(doc, "historyAdded") }
}

function selectionEventCanBeMerged(doc, origin, prev, sel) {
  var ch = origin.charAt(0)
  return ch == "*" ||
    ch == "+" &&
    prev.ranges.length == sel.ranges.length &&
    prev.somethingSelected() == sel.somethingSelected() &&
    new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
}

// Called whenever the selection changes, sets the new selection as
// the pending selection in the history, and pushes the old pending
// selection into the 'done' array when it was significantly
// different (in number of selected ranges, emptiness, or time).
function addSelectionToHistory(doc, sel, opId, options) {
  var hist = doc.history, origin = options && options.origin

  // A new event is started when the previous origin does not match
  // the current, or the origins don't allow matching. Origins
  // starting with * are always merged, those starting with + are
  // merged when similar and close together in time.
  if (opId == hist.lastSelOp ||
      (origin && hist.lastSelOrigin == origin &&
       (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
        selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
    { hist.done[hist.done.length - 1] = sel }
  else
    { pushSelectionToHistory(sel, hist.done) }

  hist.lastSelTime = +new Date
  hist.lastSelOrigin = origin
  hist.lastSelOp = opId
  if (options && options.clearRedo !== false)
    { clearSelectionEvents(hist.undone) }
}

function pushSelectionToHistory(sel, dest) {
  var top = lst(dest)
  if (!(top && top.ranges && top.equals(sel)))
    { dest.push(sel) }
}

// Used to store marked span information in the history.
function attachLocalSpans(doc, change, from, to) {
  var existing = change["spans_" + doc.id], n = 0
  doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
    if (line.markedSpans)
      { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans }
    ++n
  })
}

// When un/re-doing restores text containing marked spans, those
// that have been explicitly cleared should not be restored.
function removeClearedSpans(spans) {
  if (!spans) { return null }
  var out
  for (var i = 0; i < spans.length; ++i) {
    if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i) } }
    else if (out) { out.push(spans[i]) }
  }
  return !out ? spans : out.length ? out : null
}

// Retrieve and filter the old marked spans stored in a change event.
function getOldSpans(doc, change) {
  var found = change["spans_" + doc.id]
  if (!found) { return null }
  var nw = []
  for (var i = 0; i < change.text.length; ++i)
    { nw.push(removeClearedSpans(found[i])) }
  return nw
}

// Used for un/re-doing changes from the history. Combines the
// result of computing the existing spans with the set of spans that
// existed in the history (so that deleting around a span and then
// undoing brings back the span).
function mergeOldSpans(doc, change) {
  var old = getOldSpans(doc, change)
  var stretched = stretchSpansOverChange(doc, change)
  if (!old) { return stretched }
  if (!stretched) { return old }

  for (var i = 0; i < old.length; ++i) {
    var oldCur = old[i], stretchCur = stretched[i]
    if (oldCur && stretchCur) {
      spans: for (var j = 0; j < stretchCur.length; ++j) {
        var span = stretchCur[j]
        for (var k = 0; k < oldCur.length; ++k)
          { if (oldCur[k].marker == span.marker) { continue spans } }
        oldCur.push(span)
      }
    } else if (stretchCur) {
      old[i] = stretchCur
    }
  }
  return old
}

// Used both to provide a JSON-safe object in .getHistory, and, when
// detaching a document, to split the history in two
function copyHistoryArray(events, newGroup, instantiateSel) {
  var copy = []
  for (var i = 0; i < events.length; ++i) {
    var event = events[i]
    if (event.ranges) {
      copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event)
      continue
    }
    var changes = event.changes, newChanges = []
    copy.push({changes: newChanges})
    for (var j = 0; j < changes.length; ++j) {
      var change = changes[j], m = (void 0)
      newChanges.push({from: change.from, to: change.to, text: change.text})
      if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
        if (indexOf(newGroup, Number(m[1])) > -1) {
          lst(newChanges)[prop] = change[prop]
          delete change[prop]
        }
      } } }
    }
  }
  return copy
}

// The 'scroll' parameter given to many of these indicated whether
// the new cursor position should be scrolled into view after
// modifying the selection.

// If shift is held or the extend flag is set, extends a range to
// include a given position (and optionally a second position).
// Otherwise, simply returns the range between the given positions.
// Used for cursor motion and such.
function extendRange(doc, range, head, other) {
  if (doc.cm && doc.cm.display.shift || doc.extend) {
    var anchor = range.anchor
    if (other) {
      var posBefore = cmp(head, anchor) < 0
      if (posBefore != (cmp(other, anchor) < 0)) {
        anchor = head
        head = other
      } else if (posBefore != (cmp(head, other) < 0)) {
        head = other
      }
    }
    return new Range(anchor, head)
  } else {
    return new Range(other || head, head)
  }
}

// Extend the primary selection range, discard the rest.
function extendSelection(doc, head, other, options) {
  setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options)
}

// Extend all selections (pos is an array of selections with length
// equal the number of selections)
function extendSelections(doc, heads, options) {
  var out = []
  for (var i = 0; i < doc.sel.ranges.length; i++)
    { out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null) }
  var newSel = normalizeSelection(out, doc.sel.primIndex)
  setSelection(doc, newSel, options)
}

// Updates a single range in the selection.
function replaceOneSelection(doc, i, range, options) {
  var ranges = doc.sel.ranges.slice(0)
  ranges[i] = range
  setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options)
}

// Reset the selection to a single range.
function setSimpleSelection(doc, anchor, head, options) {
  setSelection(doc, simpleSelection(anchor, head), options)
}

// Give beforeSelectionChange handlers a change to influence a
// selection update.
function filterSelectionChange(doc, sel, options) {
  var obj = {
    ranges: sel.ranges,
    update: function(ranges) {
      var this$1 = this;

      this.ranges = []
      for (var i = 0; i < ranges.length; i++)
        { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
                                   clipPos(doc, ranges[i].head)) }
    },
    origin: options && options.origin
  }
  signal(doc, "beforeSelectionChange", doc, obj)
  if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj) }
  if (obj.ranges != sel.ranges) { return normalizeSelection(obj.ranges, obj.ranges.length - 1) }
  else { return sel }
}

function setSelectionReplaceHistory(doc, sel, options) {
  var done = doc.history.done, last = lst(done)
  if (last && last.ranges) {
    done[done.length - 1] = sel
    setSelectionNoUndo(doc, sel, options)
  } else {
    setSelection(doc, sel, options)
  }
}

// Set a new selection.
function setSelection(doc, sel, options) {
  setSelectionNoUndo(doc, sel, options)
  addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options)
}

function setSelectionNoUndo(doc, sel, options) {
  if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
    { sel = filterSelectionChange(doc, sel, options) }

  var bias = options && options.bias ||
    (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1)
  setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true))

  if (!(options && options.scroll === false) && doc.cm)
    { ensureCursorVisible(doc.cm) }
}

function setSelectionInner(doc, sel) {
  if (sel.equals(doc.sel)) { return }

  doc.sel = sel

  if (doc.cm) {
    doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true
    signalCursorActivity(doc.cm)
  }
  signalLater(doc, "cursorActivity", doc)
}

// Verify that the selection does not partially select any atomic
// marked ranges.
function reCheckSelection(doc) {
  setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll)
}

// Return a selection that does not partially select any atomic
// ranges.
function skipAtomicInSelection(doc, sel, bias, mayClear) {
  var out
  for (var i = 0; i < sel.ranges.length; i++) {
    var range = sel.ranges[i]
    var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]
    var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear)
    var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear)
    if (out || newAnchor != range.anchor || newHead != range.head) {
      if (!out) { out = sel.ranges.slice(0, i) }
      out[i] = new Range(newAnchor, newHead)
    }
  }
  return out ? normalizeSelection(out, sel.primIndex) : sel
}

function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
  var line = getLine(doc, pos.line)
  if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
    var sp = line.markedSpans[i], m = sp.marker
    if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
        (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
      if (mayClear) {
        signal(m, "beforeCursorEnter")
        if (m.explicitlyCleared) {
          if (!line.markedSpans) { break }
          else {--i; continue}
        }
      }
      if (!m.atomic) { continue }

      if (oldPos) {
        var near = m.find(dir < 0 ? 1 : -1), diff = (void 0)
        if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)
          { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null) }
        if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
          { return skipAtomicInner(doc, near, pos, dir, mayClear) }
      }

      var far = m.find(dir < 0 ? -1 : 1)
      if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)
        { far = movePos(doc, far, dir, far.line == pos.line ? line : null) }
      return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
    }
  } }
  return pos
}

// Ensure a given position is not inside an atomic range.
function skipAtomic(doc, pos, oldPos, bias, mayClear) {
  var dir = bias || 1
  var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
      (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
      skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
      (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true))
  if (!found) {
    doc.cantEdit = true
    return Pos(doc.first, 0)
  }
  return found
}

function movePos(doc, pos, dir, line) {
  if (dir < 0 && pos.ch == 0) {
    if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
    else { return null }
  } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
    if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
    else { return null }
  } else {
    return new Pos(pos.line, pos.ch + dir)
  }
}

function selectAll(cm) {
  cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll)
}

// UPDATING

// Allow "beforeChange" event handlers to influence a change
function filterChange(doc, change, update) {
  var obj = {
    canceled: false,
    from: change.from,
    to: change.to,
    text: change.text,
    origin: change.origin,
    cancel: function () { return obj.canceled = true; }
  }
  if (update) { obj.update = function (from, to, text, origin) {
    if (from) { obj.from = clipPos(doc, from) }
    if (to) { obj.to = clipPos(doc, to) }
    if (text) { obj.text = text }
    if (origin !== undefined) { obj.origin = origin }
  } }
  signal(doc, "beforeChange", doc, obj)
  if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj) }

  if (obj.canceled) { return null }
  return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
}

// Apply a change to a document, and add it to the document's
// history, and propagating it to all linked documents.
function makeChange(doc, change, ignoreReadOnly) {
  if (doc.cm) {
    if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
    if (doc.cm.state.suppressEdits) { return }
  }

  if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
    change = filterChange(doc, change, true)
    if (!change) { return }
  }

  // Possibly split or suppress the update based on the presence
  // of read-only spans in its range.
  var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to)
  if (split) {
    for (var i = split.length - 1; i >= 0; --i)
      { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text}) }
  } else {
    makeChangeInner(doc, change)
  }
}

function makeChangeInner(doc, change) {
  if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
  var selAfter = computeSelAfterChange(doc, change)
  addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN)

  makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change))
  var rebased = []

  linkedDocs(doc, function (doc, sharedHist) {
    if (!sharedHist && indexOf(rebased, doc.history) == -1) {
      rebaseHist(doc.history, change)
      rebased.push(doc.history)
    }
    makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change))
  })
}

// Revert a change stored in a document's history.
function makeChangeFromHistory(doc, type, allowSelectionOnly) {
  if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return }

  var hist = doc.history, event, selAfter = doc.sel
  var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done

  // Verify that there is a useable event (so that ctrl-z won't
  // needlessly clear selection events)
  var i = 0
  for (; i < source.length; i++) {
    event = source[i]
    if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
      { break }
  }
  if (i == source.length) { return }
  hist.lastOrigin = hist.lastSelOrigin = null

  for (;;) {
    event = source.pop()
    if (event.ranges) {
      pushSelectionToHistory(event, dest)
      if (allowSelectionOnly && !event.equals(doc.sel)) {
        setSelection(doc, event, {clearRedo: false})
        return
      }
      selAfter = event
    }
    else { break }
  }

  // Build up a reverse change object to add to the opposite history
  // stack (redo when undoing, and vice versa).
  var antiChanges = []
  pushSelectionToHistory(selAfter, dest)
  dest.push({changes: antiChanges, generation: hist.generation})
  hist.generation = event.generation || ++hist.maxGeneration

  var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")

  var loop = function ( i ) {
    var change = event.changes[i]
    change.origin = type
    if (filter && !filterChange(doc, change, false)) {
      source.length = 0
      return {}
    }

    antiChanges.push(historyChangeFromChange(doc, change))

    var after = i ? computeSelAfterChange(doc, change) : lst(source)
    makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change))
    if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}) }
    var rebased = []

    // Propagate to the linked documents
    linkedDocs(doc, function (doc, sharedHist) {
      if (!sharedHist && indexOf(rebased, doc.history) == -1) {
        rebaseHist(doc.history, change)
        rebased.push(doc.history)
      }
      makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change))
    })
  };

  for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
    var returned = loop( i$1 );

    if ( returned ) return returned.v;
  }
}

// Sub-views need their line numbers shifted when text is added
// above or below them in the parent document.
function shiftDoc(doc, distance) {
  if (distance == 0) { return }
  doc.first += distance
  doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
    Pos(range.anchor.line + distance, range.anchor.ch),
    Pos(range.head.line + distance, range.head.ch)
  ); }), doc.sel.primIndex)
  if (doc.cm) {
    regChange(doc.cm, doc.first, doc.first - distance, distance)
    for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
      { regLineChange(doc.cm, l, "gutter") }
  }
}

// More lower-level change function, handling only a single document
// (not linked ones).
function makeChangeSingleDoc(doc, change, selAfter, spans) {
  if (doc.cm && !doc.cm.curOp)
    { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }

  if (change.to.line < doc.first) {
    shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line))
    return
  }
  if (change.from.line > doc.lastLine()) { return }

  // Clip the change to the size of this doc
  if (change.from.line < doc.first) {
    var shift = change.text.length - 1 - (doc.first - change.from.line)
    shiftDoc(doc, shift)
    change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
              text: [lst(change.text)], origin: change.origin}
  }
  var last = doc.lastLine()
  if (change.to.line > last) {
    change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
              text: [change.text[0]], origin: change.origin}
  }

  change.removed = getBetween(doc, change.from, change.to)

  if (!selAfter) { selAfter = computeSelAfterChange(doc, change) }
  if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans) }
  else { updateDoc(doc, change, spans) }
  setSelectionNoUndo(doc, selAfter, sel_dontScroll)
}

// Handle the interaction of a change to a document with the editor
// that this document is part of.
function makeChangeSingleDocInEditor(cm, change, spans) {
  var doc = cm.doc, display = cm.display, from = change.from, to = change.to

  var recomputeMaxLength = false, checkWidthStart = from.line
  if (!cm.options.lineWrapping) {
    checkWidthStart = lineNo(visualLine(getLine(doc, from.line)))
    doc.iter(checkWidthStart, to.line + 1, function (line) {
      if (line == display.maxLine) {
        recomputeMaxLength = true
        return true
      }
    })
  }

  if (doc.sel.contains(change.from, change.to) > -1)
    { signalCursorActivity(cm) }

  updateDoc(doc, change, spans, estimateHeight(cm))

  if (!cm.options.lineWrapping) {
    doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
      var len = lineLength(line)
      if (len > display.maxLineLength) {
        display.maxLine = line
        display.maxLineLength = len
        display.maxLineChanged = true
        recomputeMaxLength = false
      }
    })
    if (recomputeMaxLength) { cm.curOp.updateMaxLine = true }
  }

  // Adjust frontier, schedule worker
  doc.frontier = Math.min(doc.frontier, from.line)
  startWorker(cm, 400)

  var lendiff = change.text.length - (to.line - from.line) - 1
  // Remember that these lines changed, for updating the display
  if (change.full)
    { regChange(cm) }
  else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
    { regLineChange(cm, from.line, "text") }
  else
    { regChange(cm, from.line, to.line + 1, lendiff) }

  var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change")
  if (changeHandler || changesHandler) {
    var obj = {
      from: from, to: to,
      text: change.text,
      removed: change.removed,
      origin: change.origin
    }
    if (changeHandler) { signalLater(cm, "change", cm, obj) }
    if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj) }
  }
  cm.display.selForContextMenu = null
}

function replaceRange(doc, code, from, to, origin) {
  if (!to) { to = from }
  if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp }
  if (typeof code == "string") { code = doc.splitLines(code) }
  makeChange(doc, {from: from, to: to, text: code, origin: origin})
}

// Rebasing/resetting history to deal with externally-sourced changes

function rebaseHistSelSingle(pos, from, to, diff) {
  if (to < pos.line) {
    pos.line += diff
  } else if (from < pos.line) {
    pos.line = from
    pos.ch = 0
  }
}

// Tries to rebase an array of history events given a change in the
// document. If the change touches the same lines as the event, the
// event, and everything 'behind' it, is discarded. If the change is
// before the event, the event's positions are updated. Uses a
// copy-on-write scheme for the positions, to avoid having to
// reallocate them all on every rebase, but also avoid problems with
// shared position objects being unsafely updated.
function rebaseHistArray(array, from, to, diff) {
  for (var i = 0; i < array.length; ++i) {
    var sub = array[i], ok = true
    if (sub.ranges) {
      if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true }
      for (var j = 0; j < sub.ranges.length; j++) {
        rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff)
        rebaseHistSelSingle(sub.ranges[j].head, from, to, diff)
      }
      continue
    }
    for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
      var cur = sub.changes[j$1]
      if (to < cur.from.line) {
        cur.from = Pos(cur.from.line + diff, cur.from.ch)
        cur.to = Pos(cur.to.line + diff, cur.to.ch)
      } else if (from <= cur.to.line) {
        ok = false
        break
      }
    }
    if (!ok) {
      array.splice(0, i + 1)
      i = 0
    }
  }
}

function rebaseHist(hist, change) {
  var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1
  rebaseHistArray(hist.done, from, to, diff)
  rebaseHistArray(hist.undone, from, to, diff)
}

// Utility for applying a change to a line by handle or number,
// returning the number and optionally registering the line as
// changed.
function changeLine(doc, handle, changeType, op) {
  var no = handle, line = handle
  if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)) }
  else { no = lineNo(handle) }
  if (no == null) { return null }
  if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType) }
  return line
}

// The document is represented as a BTree consisting of leaves, with
// chunk of lines in them, and branches, with up to ten leaves or
// other branch nodes below them. The top node is always a branch
// node, and is the document object itself (meaning it has
// additional methods and properties).
//
// All nodes have parent links. The tree is used both to go from
// line numbers to line objects, and to go from objects to numbers.
// It also indexes by height, and is used to convert between height
// and line object, and to find the total height of the document.
//
// See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html

var LeafChunk = function(lines) {
  var this$1 = this;

  this.lines = lines
  this.parent = null
  var height = 0
  for (var i = 0; i < lines.length; ++i) {
    lines[i].parent = this$1
    height += lines[i].height
  }
  this.height = height
};

LeafChunk.prototype.chunkSize = function () { return this.lines.length };

// Remove the n lines at offset 'at'.
LeafChunk.prototype.removeInner = function (at, n) {
    var this$1 = this;

  for (var i = at, e = at + n; i < e; ++i) {
    var line = this$1.lines[i]
    this$1.height -= line.height
    cleanUpLine(line)
    signalLater(line, "delete")
  }
  this.lines.splice(at, n)
};

// Helper used to collapse a small branch into a single leaf.
LeafChunk.prototype.collapse = function (lines) {
  lines.push.apply(lines, this.lines)
};

// Insert the given array of lines at offset 'at', count them as
// having the given height.
LeafChunk.prototype.insertInner = function (at, lines, height) {
    var this$1 = this;

  this.height += height
  this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at))
  for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1 }
};

// Used to iterate over a part of the tree.
LeafChunk.prototype.iterN = function (at, n, op) {
    var this$1 = this;

  for (var e = at + n; at < e; ++at)
    { if (op(this$1.lines[at])) { return true } }
};

var BranchChunk = function(children) {
  var this$1 = this;

  this.children = children
  var size = 0, height = 0
  for (var i = 0; i < children.length; ++i) {
    var ch = children[i]
    size += ch.chunkSize(); height += ch.height
    ch.parent = this$1
  }
  this.size = size
  this.height = height
  this.parent = null
};

BranchChunk.prototype.chunkSize = function () { return this.size };

BranchChunk.prototype.removeInner = function (at, n) {
    var this$1 = this;

  this.size -= n
  for (var i = 0; i < this.children.length; ++i) {
    var child = this$1.children[i], sz = child.chunkSize()
    if (at < sz) {
      var rm = Math.min(n, sz - at), oldHeight = child.height
      child.removeInner(at, rm)
      this$1.height -= oldHeight - child.height
      if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null }
      if ((n -= rm) == 0) { break }
      at = 0
    } else { at -= sz }
  }
  // If the result is smaller than 25 lines, ensure that it is a
  // single leaf node.
  if (this.size - n < 25 &&
      (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
    var lines = []
    this.collapse(lines)
    this.children = [new LeafChunk(lines)]
    this.children[0].parent = this
  }
};

BranchChunk.prototype.collapse = function (lines) {
    var this$1 = this;

  for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines) }
};

BranchChunk.prototype.insertInner = function (at, lines, height) {
    var this$1 = this;

  this.size += lines.length
  this.height += height
  for (var i = 0; i < this.children.length; ++i) {
    var child = this$1.children[i], sz = child.chunkSize()
    if (at <= sz) {
      child.insertInner(at, lines, height)
      if (child.lines && child.lines.length > 50) {
        // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
        // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
        var remaining = child.lines.length % 25 + 25
        for (var pos = remaining; pos < child.lines.length;) {
          var leaf = new LeafChunk(child.lines.slice(pos, pos += 25))
          child.height -= leaf.height
          this$1.children.splice(++i, 0, leaf)
          leaf.parent = this$1
        }
        child.lines = child.lines.slice(0, remaining)
        this$1.maybeSpill()
      }
      break
    }
    at -= sz
  }
};

// When a node has grown, check whether it should be split.
BranchChunk.prototype.maybeSpill = function () {
  if (this.children.length <= 10) { return }
  var me = this
  do {
    var spilled = me.children.splice(me.children.length - 5, 5)
    var sibling = new BranchChunk(spilled)
    if (!me.parent) { // Become the parent node
      var copy = new BranchChunk(me.children)
      copy.parent = me
      me.children = [copy, sibling]
      me = copy
   } else {
      me.size -= sibling.size
      me.height -= sibling.height
      var myIndex = indexOf(me.parent.children, me)
      me.parent.children.splice(myIndex + 1, 0, sibling)
    }
    sibling.parent = me.parent
  } while (me.children.length > 10)
  me.parent.maybeSpill()
};

BranchChunk.prototype.iterN = function (at, n, op) {
    var this$1 = this;

  for (var i = 0; i < this.children.length; ++i) {
    var child = this$1.children[i], sz = child.chunkSize()
    if (at < sz) {
      var used = Math.min(n, sz - at)
      if (child.iterN(at, used, op)) { return true }
      if ((n -= used) == 0) { break }
      at = 0
    } else { at -= sz }
  }
};

// Line widgets are block elements displayed above or below a line.

var LineWidget = function(doc, node, options) {
  var this$1 = this;

  if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
    { this$1[opt] = options[opt] } } }
  this.doc = doc
  this.node = node
};

LineWidget.prototype.clear = function () {
    var this$1 = this;

  var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line)
  if (no == null || !ws) { return }
  for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1) } }
  if (!ws.length) { line.widgets = null }
  var height = widgetHeight(this)
  updateLineHeight(line, Math.max(0, line.height - height))
  if (cm) {
    runInOp(cm, function () {
      adjustScrollWhenAboveVisible(cm, line, -height)
      regLineChange(cm, no, "widget")
    })
    signalLater(cm, "lineWidgetCleared", cm, this, no)
  }
};

LineWidget.prototype.changed = function () {
    var this$1 = this;

  var oldH = this.height, cm = this.doc.cm, line = this.line
  this.height = null
  var diff = widgetHeight(this) - oldH
  if (!diff) { return }
  updateLineHeight(line, line.height + diff)
  if (cm) {
    runInOp(cm, function () {
      cm.curOp.forceUpdate = true
      adjustScrollWhenAboveVisible(cm, line, diff)
      signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line))
    })
  }
};
eventMixin(LineWidget)

function adjustScrollWhenAboveVisible(cm, line, diff) {
  if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
    { addToScrollPos(cm, null, diff) }
}

function addLineWidget(doc, handle, node, options) {
  var widget = new LineWidget(doc, node, options)
  var cm = doc.cm
  if (cm && widget.noHScroll) { cm.display.alignWidgets = true }
  changeLine(doc, handle, "widget", function (line) {
    var widgets = line.widgets || (line.widgets = [])
    if (widget.insertAt == null) { widgets.push(widget) }
    else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget) }
    widget.line = line
    if (cm && !lineIsHidden(doc, line)) {
      var aboveVisible = heightAtLine(line) < doc.scrollTop
      updateLineHeight(line, line.height + widgetHeight(widget))
      if (aboveVisible) { addToScrollPos(cm, null, widget.height) }
      cm.curOp.forceUpdate = true
    }
    return true
  })
  signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle))
  return widget
}

// TEXTMARKERS

// Created with markText and setBookmark methods. A TextMarker is a
// handle that can be used to clear or find a marked position in the
// document. Line objects hold arrays (markedSpans) containing
// {from, to, marker} object pointing to such marker objects, and
// indicating that such a marker is present on that line. Multiple
// lines may point to the same marker when it spans across lines.
// The spans will have null for their from/to properties when the
// marker continues beyond the start/end of the line. Markers have
// links back to the lines they currently touch.

// Collapsed markers have unique ids, in order to be able to order
// them, which is needed for uniquely determining an outer marker
// when they overlap (they may nest, but not partially overlap).
var nextMarkerId = 0

var TextMarker = function(doc, type) {
  this.lines = []
  this.type = type
  this.doc = doc
  this.id = ++nextMarkerId
};

// Clear the marker.
TextMarker.prototype.clear = function () {
    var this$1 = this;

  if (this.explicitlyCleared) { return }
  var cm = this.doc.cm, withOp = cm && !cm.curOp
  if (withOp) { startOperation(cm) }
  if (hasHandler(this, "clear")) {
    var found = this.find()
    if (found) { signalLater(this, "clear", found.from, found.to) }
  }
  var min = null, max = null
  for (var i = 0; i < this.lines.length; ++i) {
    var line = this$1.lines[i]
    var span = getMarkedSpanFor(line.markedSpans, this$1)
    if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text") }
    else if (cm) {
      if (span.to != null) { max = lineNo(line) }
      if (span.from != null) { min = lineNo(line) }
    }
    line.markedSpans = removeMarkedSpan(line.markedSpans, span)
    if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
      { updateLineHeight(line, textHeight(cm.display)) }
  }
  if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
    var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual)
    if (len > cm.display.maxLineLength) {
      cm.display.maxLine = visual
      cm.display.maxLineLength = len
      cm.display.maxLineChanged = true
    }
  } }

  if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1) }
  this.lines.length = 0
  this.explicitlyCleared = true
  if (this.atomic && this.doc.cantEdit) {
    this.doc.cantEdit = false
    if (cm) { reCheckSelection(cm.doc) }
  }
  if (cm) { signalLater(cm, "markerCleared", cm, this, min, max) }
  if (withOp) { endOperation(cm) }
  if (this.parent) { this.parent.clear() }
};

// Find the position of the marker in the document. Returns a {from,
// to} object by default. Side can be passed to get a specific side
// -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
// Pos objects returned contain a line object, rather than a line
// number (used to prevent looking up the same line twice).
TextMarker.prototype.find = function (side, lineObj) {
    var this$1 = this;

  if (side == null && this.type == "bookmark") { side = 1 }
  var from, to
  for (var i = 0; i < this.lines.length; ++i) {
    var line = this$1.lines[i]
    var span = getMarkedSpanFor(line.markedSpans, this$1)
    if (span.from != null) {
      from = Pos(lineObj ? line : lineNo(line), span.from)
      if (side == -1) { return from }
    }
    if (span.to != null) {
      to = Pos(lineObj ? line : lineNo(line), span.to)
      if (side == 1) { return to }
    }
  }
  return from && {from: from, to: to}
};

// Signals that the marker's widget changed, and surrounding layout
// should be recomputed.
TextMarker.prototype.changed = function () {
    var this$1 = this;

  var pos = this.find(-1, true), widget = this, cm = this.doc.cm
  if (!pos || !cm) { return }
  runInOp(cm, function () {
    var line = pos.line, lineN = lineNo(pos.line)
    var view = findViewForLine(cm, lineN)
    if (view) {
      clearLineMeasurementCacheFor(view)
      cm.curOp.selectionChanged = cm.curOp.forceUpdate = true
    }
    cm.curOp.updateMaxLine = true
    if (!lineIsHidden(widget.doc, line) && widget.height != null) {
      var oldHeight = widget.height
      widget.height = null
      var dHeight = widgetHeight(widget) - oldHeight
      if (dHeight)
        { updateLineHeight(line, line.height + dHeight) }
    }
    signalLater(cm, "markerChanged", cm, this$1)
  })
};

TextMarker.prototype.attachLine = function (line) {
  if (!this.lines.length && this.doc.cm) {
    var op = this.doc.cm.curOp
    if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
      { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this) }
  }
  this.lines.push(line)
};

TextMarker.prototype.detachLine = function (line) {
  this.lines.splice(indexOf(this.lines, line), 1)
  if (!this.lines.length && this.doc.cm) {
    var op = this.doc.cm.curOp
    ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this)
  }
};
eventMixin(TextMarker)

// Create a marker, wire it up to the right lines, and
function markText(doc, from, to, options, type) {
  // Shared markers (across linked documents) are handled separately
  // (markTextShared will call out to this again, once per
  // document).
  if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
  // Ensure we are in an operation.
  if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }

  var marker = new TextMarker(doc, type), diff = cmp(from, to)
  if (options) { copyObj(options, marker, false) }
  // Don't connect empty markers unless clearWhenEmpty is false
  if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
    { return marker }
  if (marker.replacedWith) {
    // Showing up as a widget implies collapsed (widget replaces text)
    marker.collapsed = true
    marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget")
    if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true") }
    if (options.insertLeft) { marker.widgetNode.insertLeft = true }
  }
  if (marker.collapsed) {
    if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
        from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
      { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
    seeCollapsedSpans()
  }

  if (marker.addToHistory)
    { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN) }

  var curLine = from.line, cm = doc.cm, updateMaxLine
  doc.iter(curLine, to.line + 1, function (line) {
    if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
      { updateMaxLine = true }
    if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0) }
    addMarkedSpan(line, new MarkedSpan(marker,
                                       curLine == from.line ? from.ch : null,
                                       curLine == to.line ? to.ch : null))
    ++curLine
  })
  // lineIsHidden depends on the presence of the spans, so needs a second pass
  if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
    if (lineIsHidden(doc, line)) { updateLineHeight(line, 0) }
  }) }

  if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }) }

  if (marker.readOnly) {
    seeReadOnlySpans()
    if (doc.history.done.length || doc.history.undone.length)
      { doc.clearHistory() }
  }
  if (marker.collapsed) {
    marker.id = ++nextMarkerId
    marker.atomic = true
  }
  if (cm) {
    // Sync editor state
    if (updateMaxLine) { cm.curOp.updateMaxLine = true }
    if (marker.collapsed)
      { regChange(cm, from.line, to.line + 1) }
    else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
      { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text") } }
    if (marker.atomic) { reCheckSelection(cm.doc) }
    signalLater(cm, "markerAdded", cm, marker)
  }
  return marker
}

// SHARED TEXTMARKERS

// A shared marker spans multiple linked documents. It is
// implemented as a meta-marker-object controlling multiple normal
// markers.
var SharedTextMarker = function(markers, primary) {
  var this$1 = this;

  this.markers = markers
  this.primary = primary
  for (var i = 0; i < markers.length; ++i)
    { markers[i].parent = this$1 }
};

SharedTextMarker.prototype.clear = function () {
    var this$1 = this;

  if (this.explicitlyCleared) { return }
  this.explicitlyCleared = true
  for (var i = 0; i < this.markers.length; ++i)
    { this$1.markers[i].clear() }
  signalLater(this, "clear")
};

SharedTextMarker.prototype.find = function (side, lineObj) {
  return this.primary.find(side, lineObj)
};
eventMixin(SharedTextMarker)

function markTextShared(doc, from, to, options, type) {
  options = copyObj(options)
  options.shared = false
  var markers = [markText(doc, from, to, options, type)], primary = markers[0]
  var widget = options.widgetNode
  linkedDocs(doc, function (doc) {
    if (widget) { options.widgetNode = widget.cloneNode(true) }
    markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type))
    for (var i = 0; i < doc.linked.length; ++i)
      { if (doc.linked[i].isParent) { return } }
    primary = lst(markers)
  })
  return new SharedTextMarker(markers, primary)
}

function findSharedMarkers(doc) {
  return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
}

function copySharedMarkers(doc, markers) {
  for (var i = 0; i < markers.length; i++) {
    var marker = markers[i], pos = marker.find()
    var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to)
    if (cmp(mFrom, mTo)) {
      var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type)
      marker.markers.push(subMark)
      subMark.parent = marker
    }
  }
}

function detachSharedMarkers(markers) {
  var loop = function ( i ) {
    var marker = markers[i], linked = [marker.primary.doc]
    linkedDocs(marker.primary.doc, function (d) { return linked.push(d); })
    for (var j = 0; j < marker.markers.length; j++) {
      var subMarker = marker.markers[j]
      if (indexOf(linked, subMarker.doc) == -1) {
        subMarker.parent = null
        marker.markers.splice(j--, 1)
      }
    }
  };

  for (var i = 0; i < markers.length; i++) loop( i );
}

var nextDocId = 0
var Doc = function(text, mode, firstLine, lineSep, direction) {
  if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
  if (firstLine == null) { firstLine = 0 }

  BranchChunk.call(this, [new LeafChunk([new Line("", null)])])
  this.first = firstLine
  this.scrollTop = this.scrollLeft = 0
  this.cantEdit = false
  this.cleanGeneration = 1
  this.frontier = firstLine
  var start = Pos(firstLine, 0)
  this.sel = simpleSelection(start)
  this.history = new History(null)
  this.id = ++nextDocId
  this.modeOption = mode
  this.lineSep = lineSep
  this.direction = (direction == "rtl") ? "rtl" : "ltr"
  this.extend = false

  if (typeof text == "string") { text = this.splitLines(text) }
  updateDoc(this, {from: start, to: start, text: text})
  setSelection(this, simpleSelection(start), sel_dontScroll)
}

Doc.prototype = createObj(BranchChunk.prototype, {
  constructor: Doc,
  // Iterate over the document. Supports two forms -- with only one
  // argument, it calls that for each line in the document. With
  // three, it iterates over the range given by the first two (with
  // the second being non-inclusive).
  iter: function(from, to, op) {
    if (op) { this.iterN(from - this.first, to - from, op) }
    else { this.iterN(this.first, this.first + this.size, from) }
  },

  // Non-public interface for adding and removing lines.
  insert: function(at, lines) {
    var height = 0
    for (var i = 0; i < lines.length; ++i) { height += lines[i].height }
    this.insertInner(at - this.first, lines, height)
  },
  remove: function(at, n) { this.removeInner(at - this.first, n) },

  // From here, the methods are part of the public interface. Most
  // are also available from CodeMirror (editor) instances.

  getValue: function(lineSep) {
    var lines = getLines(this, this.first, this.first + this.size)
    if (lineSep === false) { return lines }
    return lines.join(lineSep || this.lineSeparator())
  },
  setValue: docMethodOp(function(code) {
    var top = Pos(this.first, 0), last = this.first + this.size - 1
    makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
                      text: this.splitLines(code), origin: "setValue", full: true}, true)
    if (this.cm) { this.cm.scrollTo(0, 0) }
    setSelection(this, simpleSelection(top), sel_dontScroll)
  }),
  replaceRange: function(code, from, to, origin) {
    from = clipPos(this, from)
    to = to ? clipPos(this, to) : from
    replaceRange(this, code, from, to, origin)
  },
  getRange: function(from, to, lineSep) {
    var lines = getBetween(this, clipPos(this, from), clipPos(this, to))
    if (lineSep === false) { return lines }
    return lines.join(lineSep || this.lineSeparator())
  },

  getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},

  getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
  getLineNumber: function(line) {return lineNo(line)},

  getLineHandleVisualStart: function(line) {
    if (typeof line == "number") { line = getLine(this, line) }
    return visualLine(line)
  },

  lineCount: function() {return this.size},
  firstLine: function() {return this.first},
  lastLine: function() {return this.first + this.size - 1},

  clipPos: function(pos) {return clipPos(this, pos)},

  getCursor: function(start) {
    var range = this.sel.primary(), pos
    if (start == null || start == "head") { pos = range.head }
    else if (start == "anchor") { pos = range.anchor }
    else if (start == "end" || start == "to" || start === false) { pos = range.to() }
    else { pos = range.from() }
    return pos
  },
  listSelections: function() { return this.sel.ranges },
  somethingSelected: function() {return this.sel.somethingSelected()},

  setCursor: docMethodOp(function(line, ch, options) {
    setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options)
  }),
  setSelection: docMethodOp(function(anchor, head, options) {
    setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options)
  }),
  extendSelection: docMethodOp(function(head, other, options) {
    extendSelection(this, clipPos(this, head), other && clipPos(this, other), options)
  }),
  extendSelections: docMethodOp(function(heads, options) {
    extendSelections(this, clipPosArray(this, heads), options)
  }),
  extendSelectionsBy: docMethodOp(function(f, options) {
    var heads = map(this.sel.ranges, f)
    extendSelections(this, clipPosArray(this, heads), options)
  }),
  setSelections: docMethodOp(function(ranges, primary, options) {
    var this$1 = this;

    if (!ranges.length) { return }
    var out = []
    for (var i = 0; i < ranges.length; i++)
      { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
                         clipPos(this$1, ranges[i].head)) }
    if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex) }
    setSelection(this, normalizeSelection(out, primary), options)
  }),
  addSelection: docMethodOp(function(anchor, head, options) {
    var ranges = this.sel.ranges.slice(0)
    ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)))
    setSelection(this, normalizeSelection(ranges, ranges.length - 1), options)
  }),

  getSelection: function(lineSep) {
    var this$1 = this;

    var ranges = this.sel.ranges, lines
    for (var i = 0; i < ranges.length; i++) {
      var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())
      lines = lines ? lines.concat(sel) : sel
    }
    if (lineSep === false) { return lines }
    else { return lines.join(lineSep || this.lineSeparator()) }
  },
  getSelections: function(lineSep) {
    var this$1 = this;

    var parts = [], ranges = this.sel.ranges
    for (var i = 0; i < ranges.length; i++) {
      var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())
      if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()) }
      parts[i] = sel
    }
    return parts
  },
  replaceSelection: function(code, collapse, origin) {
    var dup = []
    for (var i = 0; i < this.sel.ranges.length; i++)
      { dup[i] = code }
    this.replaceSelections(dup, collapse, origin || "+input")
  },
  replaceSelections: docMethodOp(function(code, collapse, origin) {
    var this$1 = this;

    var changes = [], sel = this.sel
    for (var i = 0; i < sel.ranges.length; i++) {
      var range = sel.ranges[i]
      changes[i] = {from: range.from(), to: range.to(), text: this$1.splitLines(code[i]), origin: origin}
    }
    var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse)
    for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
      { makeChange(this$1, changes[i$1]) }
    if (newSel) { setSelectionReplaceHistory(this, newSel) }
    else if (this.cm) { ensureCursorVisible(this.cm) }
  }),
  undo: docMethodOp(function() {makeChangeFromHistory(this, "undo")}),
  redo: docMethodOp(function() {makeChangeFromHistory(this, "redo")}),
  undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true)}),
  redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true)}),

  setExtending: function(val) {this.extend = val},
  getExtending: function() {return this.extend},

  historySize: function() {
    var hist = this.history, done = 0, undone = 0
    for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done } }
    for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone } }
    return {undo: done, redo: undone}
  },
  clearHistory: function() {this.history = new History(this.history.maxGeneration)},

  markClean: function() {
    this.cleanGeneration = this.changeGeneration(true)
  },
  changeGeneration: function(forceSplit) {
    if (forceSplit)
      { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null }
    return this.history.generation
  },
  isClean: function (gen) {
    return this.history.generation == (gen || this.cleanGeneration)
  },

  getHistory: function() {
    return {done: copyHistoryArray(this.history.done),
            undone: copyHistoryArray(this.history.undone)}
  },
  setHistory: function(histData) {
    var hist = this.history = new History(this.history.maxGeneration)
    hist.done = copyHistoryArray(histData.done.slice(0), null, true)
    hist.undone = copyHistoryArray(histData.undone.slice(0), null, true)
  },

  setGutterMarker: docMethodOp(function(line, gutterID, value) {
    return changeLine(this, line, "gutter", function (line) {
      var markers = line.gutterMarkers || (line.gutterMarkers = {})
      markers[gutterID] = value
      if (!value && isEmpty(markers)) { line.gutterMarkers = null }
      return true
    })
  }),

  clearGutter: docMethodOp(function(gutterID) {
    var this$1 = this;

    this.iter(function (line) {
      if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
        changeLine(this$1, line, "gutter", function () {
          line.gutterMarkers[gutterID] = null
          if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null }
          return true
        })
      }
    })
  }),

  lineInfo: function(line) {
    var n
    if (typeof line == "number") {
      if (!isLine(this, line)) { return null }
      n = line
      line = getLine(this, line)
      if (!line) { return null }
    } else {
      n = lineNo(line)
      if (n == null) { return null }
    }
    return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
            textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
            widgets: line.widgets}
  },

  addLineClass: docMethodOp(function(handle, where, cls) {
    return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
      var prop = where == "text" ? "textClass"
               : where == "background" ? "bgClass"
               : where == "gutter" ? "gutterClass" : "wrapClass"
      if (!line[prop]) { line[prop] = cls }
      else if (classTest(cls).test(line[prop])) { return false }
      else { line[prop] += " " + cls }
      return true
    })
  }),
  removeLineClass: docMethodOp(function(handle, where, cls) {
    return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
      var prop = where == "text" ? "textClass"
               : where == "background" ? "bgClass"
               : where == "gutter" ? "gutterClass" : "wrapClass"
      var cur = line[prop]
      if (!cur) { return false }
      else if (cls == null) { line[prop] = null }
      else {
        var found = cur.match(classTest(cls))
        if (!found) { return false }
        var end = found.index + found[0].length
        line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null
      }
      return true
    })
  }),

  addLineWidget: docMethodOp(function(handle, node, options) {
    return addLineWidget(this, handle, node, options)
  }),
  removeLineWidget: function(widget) { widget.clear() },

  markText: function(from, to, options) {
    return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
  },
  setBookmark: function(pos, options) {
    var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
                    insertLeft: options && options.insertLeft,
                    clearWhenEmpty: false, shared: options && options.shared,
                    handleMouseEvents: options && options.handleMouseEvents}
    pos = clipPos(this, pos)
    return markText(this, pos, pos, realOpts, "bookmark")
  },
  findMarksAt: function(pos) {
    pos = clipPos(this, pos)
    var markers = [], spans = getLine(this, pos.line).markedSpans
    if (spans) { for (var i = 0; i < spans.length; ++i) {
      var span = spans[i]
      if ((span.from == null || span.from <= pos.ch) &&
          (span.to == null || span.to >= pos.ch))
        { markers.push(span.marker.parent || span.marker) }
    } }
    return markers
  },
  findMarks: function(from, to, filter) {
    from = clipPos(this, from); to = clipPos(this, to)
    var found = [], lineNo = from.line
    this.iter(from.line, to.line + 1, function (line) {
      var spans = line.markedSpans
      if (spans) { for (var i = 0; i < spans.length; i++) {
        var span = spans[i]
        if (!(span.to != null && lineNo == from.line && from.ch >= span.to ||
              span.from == null && lineNo != from.line ||
              span.from != null && lineNo == to.line && span.from >= to.ch) &&
            (!filter || filter(span.marker)))
          { found.push(span.marker.parent || span.marker) }
      } }
      ++lineNo
    })
    return found
  },
  getAllMarks: function() {
    var markers = []
    this.iter(function (line) {
      var sps = line.markedSpans
      if (sps) { for (var i = 0; i < sps.length; ++i)
        { if (sps[i].from != null) { markers.push(sps[i].marker) } } }
    })
    return markers
  },

  posFromIndex: function(off) {
    var ch, lineNo = this.first, sepSize = this.lineSeparator().length
    this.iter(function (line) {
      var sz = line.text.length + sepSize
      if (sz > off) { ch = off; return true }
      off -= sz
      ++lineNo
    })
    return clipPos(this, Pos(lineNo, ch))
  },
  indexFromPos: function (coords) {
    coords = clipPos(this, coords)
    var index = coords.ch
    if (coords.line < this.first || coords.ch < 0) { return 0 }
    var sepSize = this.lineSeparator().length
    this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
      index += line.text.length + sepSize
    })
    return index
  },

  copy: function(copyHistory) {
    var doc = new Doc(getLines(this, this.first, this.first + this.size),
                      this.modeOption, this.first, this.lineSep, this.direction)
    doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft
    doc.sel = this.sel
    doc.extend = false
    if (copyHistory) {
      doc.history.undoDepth = this.history.undoDepth
      doc.setHistory(this.getHistory())
    }
    return doc
  },

  linkedDoc: function(options) {
    if (!options) { options = {} }
    var from = this.first, to = this.first + this.size
    if (options.from != null && options.from > from) { from = options.from }
    if (options.to != null && options.to < to) { to = options.to }
    var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction)
    if (options.sharedHist) { copy.history = this.history
    ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist})
    copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]
    copySharedMarkers(copy, findSharedMarkers(this))
    return copy
  },
  unlinkDoc: function(other) {
    var this$1 = this;

    if (other instanceof CodeMirror) { other = other.doc }
    if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
      var link = this$1.linked[i]
      if (link.doc != other) { continue }
      this$1.linked.splice(i, 1)
      other.unlinkDoc(this$1)
      detachSharedMarkers(findSharedMarkers(this$1))
      break
    } }
    // If the histories were shared, split them again
    if (other.history == this.history) {
      var splitIds = [other.id]
      linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true)
      other.history = new History(null)
      other.history.done = copyHistoryArray(this.history.done, splitIds)
      other.history.undone = copyHistoryArray(this.history.undone, splitIds)
    }
  },
  iterLinkedDocs: function(f) {linkedDocs(this, f)},

  getMode: function() {return this.mode},
  getEditor: function() {return this.cm},

  splitLines: function(str) {
    if (this.lineSep) { return str.split(this.lineSep) }
    return splitLinesAuto(str)
  },
  lineSeparator: function() { return this.lineSep || "\n" },

  setDirection: docMethodOp(function (dir) {
    if (dir != "rtl") { dir = "ltr" }
    if (dir == this.direction) { return }
    this.direction = dir
    this.iter(function (line) { return line.order = null; })
    if (this.cm) { directionChanged(this.cm) }
  })
})

// Public alias.
Doc.prototype.eachLine = Doc.prototype.iter

// Kludge to work around strange IE behavior where it'll sometimes
// re-fire a series of drag-related events right after the drop (#1551)
var lastDrop = 0

function onDrop(e) {
  var cm = this
  clearDragCursor(cm)
  if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
    { return }
  e_preventDefault(e)
  if (ie) { lastDrop = +new Date }
  var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files
  if (!pos || cm.isReadOnly()) { return }
  // Might be a file drop, in which case we simply extract the text
  // and insert it.
  if (files && files.length && window.FileReader && window.File) {
    var n = files.length, text = Array(n), read = 0
    var loadFile = function (file, i) {
      if (cm.options.allowDropFileTypes &&
          indexOf(cm.options.allowDropFileTypes, file.type) == -1)
        { return }

      var reader = new FileReader
      reader.onload = operation(cm, function () {
        var content = reader.result
        if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = "" }
        text[i] = content
        if (++read == n) {
          pos = clipPos(cm.doc, pos)
          var change = {from: pos, to: pos,
                        text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
                        origin: "paste"}
          makeChange(cm.doc, change)
          setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)))
        }
      })
      reader.readAsText(file)
    }
    for (var i = 0; i < n; ++i) { loadFile(files[i], i) }
  } else { // Normal drop
    // Don't do a replace if the drop happened inside of the selected text.
    if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
      cm.state.draggingText(e)
      // Ensure the editor is re-focused
      setTimeout(function () { return cm.display.input.focus(); }, 20)
      return
    }
    try {
      var text$1 = e.dataTransfer.getData("Text")
      if (text$1) {
        var selected
        if (cm.state.draggingText && !cm.state.draggingText.copy)
          { selected = cm.listSelections() }
        setSelectionNoUndo(cm.doc, simpleSelection(pos, pos))
        if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
          { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag") } }
        cm.replaceSelection(text$1, "around", "paste")
        cm.display.input.focus()
      }
    }
    catch(e){}
  }
}

function onDragStart(cm, e) {
  if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
  if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }

  e.dataTransfer.setData("Text", cm.getSelection())
  e.dataTransfer.effectAllowed = "copyMove"

  // Use dummy image instead of default browsers image.
  // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
  if (e.dataTransfer.setDragImage && !safari) {
    var img = elt("img", null, null, "position: fixed; left: 0; top: 0;")
    img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
    if (presto) {
      img.width = img.height = 1
      cm.display.wrapper.appendChild(img)
      // Force a relayout, or Opera won't use our image for some obscure reason
      img._top = img.offsetTop
    }
    e.dataTransfer.setDragImage(img, 0, 0)
    if (presto) { img.parentNode.removeChild(img) }
  }
}

function onDragOver(cm, e) {
  var pos = posFromMouse(cm, e)
  if (!pos) { return }
  var frag = document.createDocumentFragment()
  drawSelectionCursor(cm, pos, frag)
  if (!cm.display.dragCursor) {
    cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors")
    cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv)
  }
  removeChildrenAndAdd(cm.display.dragCursor, frag)
}

function clearDragCursor(cm) {
  if (cm.display.dragCursor) {
    cm.display.lineSpace.removeChild(cm.display.dragCursor)
    cm.display.dragCursor = null
  }
}

// These must be handled carefully, because naively registering a
// handler for each editor will cause the editors to never be
// garbage collected.

function forEachCodeMirror(f) {
  if (!document.body.getElementsByClassName) { return }
  var byClass = document.body.getElementsByClassName("CodeMirror")
  for (var i = 0; i < byClass.length; i++) {
    var cm = byClass[i].CodeMirror
    if (cm) { f(cm) }
  }
}

var globalsRegistered = false
function ensureGlobalHandlers() {
  if (globalsRegistered) { return }
  registerGlobalHandlers()
  globalsRegistered = true
}
function registerGlobalHandlers() {
  // When the window resizes, we need to refresh active editors.
  var resizeTimer
  on(window, "resize", function () {
    if (resizeTimer == null) { resizeTimer = setTimeout(function () {
      resizeTimer = null
      forEachCodeMirror(onResize)
    }, 100) }
  })
  // When the window loses focus, we want to show the editor as blurred
  on(window, "blur", function () { return forEachCodeMirror(onBlur); })
}
// Called when the window resizes
function onResize(cm) {
  var d = cm.display
  if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
    { return }
  // Might be a text scaling operation, clear size caches.
  d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
  d.scrollbarsClipped = false
  cm.setSize()
}

var keyNames = {
  3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
  19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
  36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
  46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
  106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
  173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
  221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
  63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
}

// Number keys
for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i) }
// Alphabetic keys
for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1) }
// Function keys
for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2 }

var keyMap = {}

keyMap.basic = {
  "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
  "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
  "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
  "Tab": "defaultTab", "Shift-Tab": "indentAuto",
  "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
  "Esc": "singleSelection"
}
// Note that the save and find-related commands aren't defined by
// default. User code or addons can define them. Unknown commands
// are simply ignored.
keyMap.pcDefault = {
  "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
  "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
  "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
  "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
  "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
  "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
  "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
  fallthrough: "basic"
}
// Very basic readline/emacs-style bindings, which are standard on Mac.
keyMap.emacsy = {
  "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
  "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
  "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
  "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
  "Ctrl-O": "openLine"
}
keyMap.macDefault = {
  "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
  "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
  "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
  "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
  "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
  "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
  "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
  fallthrough: ["basic", "emacsy"]
}
keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault

// KEYMAP DISPATCH

function normalizeKeyName(name) {
  var parts = name.split(/-(?!$)/)
  name = parts[parts.length - 1]
  var alt, ctrl, shift, cmd
  for (var i = 0; i < parts.length - 1; i++) {
    var mod = parts[i]
    if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true }
    else if (/^a(lt)?$/i.test(mod)) { alt = true }
    else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true }
    else if (/^s(hift)?$/i.test(mod)) { shift = true }
    else { throw new Error("Unrecognized modifier name: " + mod) }
  }
  if (alt) { name = "Alt-" + name }
  if (ctrl) { name = "Ctrl-" + name }
  if (cmd) { name = "Cmd-" + name }
  if (shift) { name = "Shift-" + name }
  return name
}

// This is a kludge to keep keymaps mostly working as raw objects
// (backwards compatibility) while at the same time support features
// like normalization and multi-stroke key bindings. It compiles a
// new normalized keymap, and then updates the old object to reflect
// this.
function normalizeKeyMap(keymap) {
  var copy = {}
  for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
    var value = keymap[keyname]
    if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
    if (value == "...") { delete keymap[keyname]; continue }

    var keys = map(keyname.split(" "), normalizeKeyName)
    for (var i = 0; i < keys.length; i++) {
      var val = (void 0), name = (void 0)
      if (i == keys.length - 1) {
        name = keys.join(" ")
        val = value
      } else {
        name = keys.slice(0, i + 1).join(" ")
        val = "..."
      }
      var prev = copy[name]
      if (!prev) { copy[name] = val }
      else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
    }
    delete keymap[keyname]
  } }
  for (var prop in copy) { keymap[prop] = copy[prop] }
  return keymap
}

function lookupKey(key, map, handle, context) {
  map = getKeyMap(map)
  var found = map.call ? map.call(key, context) : map[key]
  if (found === false) { return "nothing" }
  if (found === "...") { return "multi" }
  if (found != null && handle(found)) { return "handled" }

  if (map.fallthrough) {
    if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
      { return lookupKey(key, map.fallthrough, handle, context) }
    for (var i = 0; i < map.fallthrough.length; i++) {
      var result = lookupKey(key, map.fallthrough[i], handle, context)
      if (result) { return result }
    }
  }
}

// Modifier key presses don't count as 'real' key presses for the
// purpose of keymap fallthrough.
function isModifierKey(value) {
  var name = typeof value == "string" ? value : keyNames[value.keyCode]
  return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
}

// Look up the name of a key as indicated by an event object.
function keyName(event, noShift) {
  if (presto && event.keyCode == 34 && event["char"]) { return false }
  var base = keyNames[event.keyCode], name = base
  if (name == null || event.altGraphKey) { return false }
  if (event.altKey && base != "Alt") { name = "Alt-" + name }
  if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name }
  if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name }
  if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name }
  return name
}

function getKeyMap(val) {
  return typeof val == "string" ? keyMap[val] : val
}

// Helper for deleting text near the selection(s), used to implement
// backspace, delete, and similar functionality.
function deleteNearSelection(cm, compute) {
  var ranges = cm.doc.sel.ranges, kill = []
  // Build up a set of ranges to kill first, merging overlapping
  // ranges.
  for (var i = 0; i < ranges.length; i++) {
    var toKill = compute(ranges[i])
    while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
      var replaced = kill.pop()
      if (cmp(replaced.from, toKill.from) < 0) {
        toKill.from = replaced.from
        break
      }
    }
    kill.push(toKill)
  }
  // Next, remove those actual ranges.
  runInOp(cm, function () {
    for (var i = kill.length - 1; i >= 0; i--)
      { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete") }
    ensureCursorVisible(cm)
  })
}

// Commands are parameter-less actions that can be performed on an
// editor, mostly used for keybindings.
var commands = {
  selectAll: selectAll,
  singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
  killLine: function (cm) { return deleteNearSelection(cm, function (range) {
    if (range.empty()) {
      var len = getLine(cm.doc, range.head.line).text.length
      if (range.head.ch == len && range.head.line < cm.lastLine())
        { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
      else
        { return {from: range.head, to: Pos(range.head.line, len)} }
    } else {
      return {from: range.from(), to: range.to()}
    }
  }); },
  deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
    from: Pos(range.from().line, 0),
    to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
  }); }); },
  delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
    from: Pos(range.from().line, 0), to: range.from()
  }); }); },
  delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
    var top = cm.charCoords(range.head, "div").top + 5
    var leftPos = cm.coordsChar({left: 0, top: top}, "div")
    return {from: leftPos, to: range.from()}
  }); },
  delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
    var top = cm.charCoords(range.head, "div").top + 5
    var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
    return {from: range.from(), to: rightPos }
  }); },
  undo: function (cm) { return cm.undo(); },
  redo: function (cm) { return cm.redo(); },
  undoSelection: function (cm) { return cm.undoSelection(); },
  redoSelection: function (cm) { return cm.redoSelection(); },
  goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
  goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
  goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
    {origin: "+move", bias: 1}
  ); },
  goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
    {origin: "+move", bias: 1}
  ); },
  goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
    {origin: "+move", bias: -1}
  ); },
  goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
    var top = cm.charCoords(range.head, "div").top + 5
    return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
  }, sel_move); },
  goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
    var top = cm.charCoords(range.head, "div").top + 5
    return cm.coordsChar({left: 0, top: top}, "div")
  }, sel_move); },
  goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
    var top = cm.charCoords(range.head, "div").top + 5
    var pos = cm.coordsChar({left: 0, top: top}, "div")
    if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
    return pos
  }, sel_move); },
  goLineUp: function (cm) { return cm.moveV(-1, "line"); },
  goLineDown: function (cm) { return cm.moveV(1, "line"); },
  goPageUp: function (cm) { return cm.moveV(-1, "page"); },
  goPageDown: function (cm) { return cm.moveV(1, "page"); },
  goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
  goCharRight: function (cm) { return cm.moveH(1, "char"); },
  goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
  goColumnRight: function (cm) { return cm.moveH(1, "column"); },
  goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
  goGroupRight: function (cm) { return cm.moveH(1, "group"); },
  goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
  goWordRight: function (cm) { return cm.moveH(1, "word"); },
  delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
  delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
  delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
  delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
  delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
  delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
  indentAuto: function (cm) { return cm.indentSelection("smart"); },
  indentMore: function (cm) { return cm.indentSelection("add"); },
  indentLess: function (cm) { return cm.indentSelection("subtract"); },
  insertTab: function (cm) { return cm.replaceSelection("\t"); },
  insertSoftTab: function (cm) {
    var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize
    for (var i = 0; i < ranges.length; i++) {
      var pos = ranges[i].from()
      var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize)
      spaces.push(spaceStr(tabSize - col % tabSize))
    }
    cm.replaceSelections(spaces)
  },
  defaultTab: function (cm) {
    if (cm.somethingSelected()) { cm.indentSelection("add") }
    else { cm.execCommand("insertTab") }
  },
  // Swap the two chars left and right of each selection's head.
  // Move cursor behind the two swapped characters afterwards.
  //
  // Doesn't consider line feeds a character.
  // Doesn't scan more than one line above to find a character.
  // Doesn't do anything on an empty line.
  // Doesn't do anything with non-empty selections.
  transposeChars: function (cm) { return runInOp(cm, function () {
    var ranges = cm.listSelections(), newSel = []
    for (var i = 0; i < ranges.length; i++) {
      if (!ranges[i].empty()) { continue }
      var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text
      if (line) {
        if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1) }
        if (cur.ch > 0) {
          cur = new Pos(cur.line, cur.ch + 1)
          cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
                          Pos(cur.line, cur.ch - 2), cur, "+transpose")
        } else if (cur.line > cm.doc.first) {
          var prev = getLine(cm.doc, cur.line - 1).text
          if (prev) {
            cur = new Pos(cur.line, 1)
            cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
                            prev.charAt(prev.length - 1),
                            Pos(cur.line - 1, prev.length - 1), cur, "+transpose")
          }
        }
      }
      newSel.push(new Range(cur, cur))
    }
    cm.setSelections(newSel)
  }); },
  newlineAndIndent: function (cm) { return runInOp(cm, function () {
    var sels = cm.listSelections()
    for (var i = sels.length - 1; i >= 0; i--)
      { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input") }
    sels = cm.listSelections()
    for (var i$1 = 0; i$1 < sels.length; i$1++)
      { cm.indentLine(sels[i$1].from().line, null, true) }
    ensureCursorVisible(cm)
  }); },
  openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
  toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
}


function lineStart(cm, lineN) {
  var line = getLine(cm.doc, lineN)
  var visual = visualLine(line)
  if (visual != line) { lineN = lineNo(visual) }
  return endOfLine(true, cm, visual, lineN, 1)
}
function lineEnd(cm, lineN) {
  var line = getLine(cm.doc, lineN)
  var visual = visualLineEnd(line)
  if (visual != line) { lineN = lineNo(visual) }
  return endOfLine(true, cm, line, lineN, -1)
}
function lineStartSmart(cm, pos) {
  var start = lineStart(cm, pos.line)
  var line = getLine(cm.doc, start.line)
  var order = getOrder(line, cm.doc.direction)
  if (!order || order[0].level == 0) {
    var firstNonWS = Math.max(0, line.text.search(/\S/))
    var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch
    return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
  }
  return start
}

// Run a handler that was bound to a key.
function doHandleBinding(cm, bound, dropShift) {
  if (typeof bound == "string") {
    bound = commands[bound]
    if (!bound) { return false }
  }
  // Ensure previous input has been read, so that the handler sees a
  // consistent view of the document
  cm.display.input.ensurePolled()
  var prevShift = cm.display.shift, done = false
  try {
    if (cm.isReadOnly()) { cm.state.suppressEdits = true }
    if (dropShift) { cm.display.shift = false }
    done = bound(cm) != Pass
  } finally {
    cm.display.shift = prevShift
    cm.state.suppressEdits = false
  }
  return done
}

function lookupKeyForEditor(cm, name, handle) {
  for (var i = 0; i < cm.state.keyMaps.length; i++) {
    var result = lookupKey(name, cm.state.keyMaps[i], handle, cm)
    if (result) { return result }
  }
  return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
    || lookupKey(name, cm.options.keyMap, handle, cm)
}

var stopSeq = new Delayed
function dispatchKey(cm, name, e, handle) {
  var seq = cm.state.keySeq
  if (seq) {
    if (isModifierKey(name)) { return "handled" }
    stopSeq.set(50, function () {
      if (cm.state.keySeq == seq) {
        cm.state.keySeq = null
        cm.display.input.reset()
      }
    })
    name = seq + " " + name
  }
  var result = lookupKeyForEditor(cm, name, handle)

  if (result == "multi")
    { cm.state.keySeq = name }
  if (result == "handled")
    { signalLater(cm, "keyHandled", cm, name, e) }

  if (result == "handled" || result == "multi") {
    e_preventDefault(e)
    restartBlink(cm)
  }

  if (seq && !result && /\'$/.test(name)) {
    e_preventDefault(e)
    return true
  }
  return !!result
}

// Handle a key from the keydown event.
function handleKeyBinding(cm, e) {
  var name = keyName(e, true)
  if (!name) { return false }

  if (e.shiftKey && !cm.state.keySeq) {
    // First try to resolve full name (including 'Shift-'). Failing
    // that, see if there is a cursor-motion command (starting with
    // 'go') bound to the keyname without 'Shift-'.
    return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
        || dispatchKey(cm, name, e, function (b) {
             if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
               { return doHandleBinding(cm, b) }
           })
  } else {
    return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
  }
}

// Handle a key from the keypress event
function handleCharBinding(cm, e, ch) {
  return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
}

var lastStoppedKey = null
function onKeyDown(e) {
  var cm = this
  cm.curOp.focus = activeElt()
  if (signalDOMEvent(cm, e)) { return }
  // IE does strange things with escape.
  if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false }
  var code = e.keyCode
  cm.display.shift = code == 16 || e.shiftKey
  var handled = handleKeyBinding(cm, e)
  if (presto) {
    lastStoppedKey = handled ? code : null
    // Opera has no cut event... we try to at least catch the key combo
    if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
      { cm.replaceSelection("", null, "cut") }
  }

  // Turn mouse into crosshair when Alt is held on Mac.
  if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
    { showCrossHair(cm) }
}

function showCrossHair(cm) {
  var lineDiv = cm.display.lineDiv
  addClass(lineDiv, "CodeMirror-crosshair")

  function up(e) {
    if (e.keyCode == 18 || !e.altKey) {
      rmClass(lineDiv, "CodeMirror-crosshair")
      off(document, "keyup", up)
      off(document, "mouseover", up)
    }
  }
  on(document, "keyup", up)
  on(document, "mouseover", up)
}

function onKeyUp(e) {
  if (e.keyCode == 16) { this.doc.sel.shift = false }
  signalDOMEvent(this, e)
}

function onKeyPress(e) {
  var cm = this
  if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
  var keyCode = e.keyCode, charCode = e.charCode
  if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
  if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
  var ch = String.fromCharCode(charCode == null ? keyCode : charCode)
  // Some browsers fire keypress events for backspace
  if (ch == "\x08") { return }
  if (handleCharBinding(cm, e, ch)) { return }
  cm.display.input.onKeyPress(e)
}

// A mouse down can be a single click, double click, triple click,
// start of selection drag, start of text drag, new cursor
// (ctrl-click), rectangle drag (alt-drag), or xwin
// middle-click-paste. Or it might be a click on something we should
// not interfere with, such as a scrollbar or widget.
function onMouseDown(e) {
  var cm = this, display = cm.display
  if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
  display.input.ensurePolled()
  display.shift = e.shiftKey

  if (eventInWidget(display, e)) {
    if (!webkit) {
      // Briefly turn off draggability, to allow widgets to do
      // normal dragging things.
      display.scroller.draggable = false
      setTimeout(function () { return display.scroller.draggable = true; }, 100)
    }
    return
  }
  if (clickInGutter(cm, e)) { return }
  var start = posFromMouse(cm, e)
  window.focus()

  switch (e_button(e)) {
  case 1:
    // #3261: make sure, that we're not starting a second selection
    if (cm.state.selectingText)
      { cm.state.selectingText(e) }
    else if (start)
      { leftButtonDown(cm, e, start) }
    else if (e_target(e) == display.scroller)
      { e_preventDefault(e) }
    break
  case 2:
    if (webkit) { cm.state.lastMiddleDown = +new Date }
    if (start) { extendSelection(cm.doc, start) }
    setTimeout(function () { return display.input.focus(); }, 20)
    e_preventDefault(e)
    break
  case 3:
    if (captureRightClick) { onContextMenu(cm, e) }
    else { delayBlurEvent(cm) }
    break
  }
}

var lastClick;
var lastDoubleClick;
function leftButtonDown(cm, e, start) {
  if (ie) { setTimeout(bind(ensureFocus, cm), 0) }
  else { cm.curOp.focus = activeElt() }

  var now = +new Date, type
  if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) {
    type = "triple"
  } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) {
    type = "double"
    lastDoubleClick = {time: now, pos: start}
  } else {
    type = "single"
    lastClick = {time: now, pos: start}
  }

  var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained
  if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
      type == "single" && (contained = sel.contains(start)) > -1 &&
      (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) &&
      (cmp(contained.to(), start) > 0 || start.xRel < 0))
    { leftButtonStartDrag(cm, e, start, modifier) }
  else
    { leftButtonSelect(cm, e, start, type, modifier) }
}

// Start a text drag. When it ends, see if any dragging actually
// happen, and treat as a click if it didn't.
function leftButtonStartDrag(cm, e, start, modifier) {
  var display = cm.display, moved = false
  var dragEnd = operation(cm, function (e) {
    if (webkit) { display.scroller.draggable = false }
    cm.state.draggingText = false
    off(document, "mouseup", dragEnd)
    off(document, "mousemove", mouseMove)
    off(display.scroller, "dragstart", dragStart)
    off(display.scroller, "drop", dragEnd)
    if (!moved) {
      e_preventDefault(e)
      if (!modifier)
        { extendSelection(cm.doc, start) }
      // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
      if (webkit || ie && ie_version == 9)
        { setTimeout(function () {document.body.focus(); display.input.focus()}, 20) }
      else
        { display.input.focus() }
    }
  })
  var mouseMove = function(e2) {
    moved = moved || Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) >= 10
  }
  var dragStart = function () { return moved = true; }
  // Let the drag handler handle this.
  if (webkit) { display.scroller.draggable = true }
  cm.state.draggingText = dragEnd
  dragEnd.copy = mac ? e.altKey : e.ctrlKey
  // IE's approach to draggable
  if (display.scroller.dragDrop) { display.scroller.dragDrop() }
  on(document, "mouseup", dragEnd)
  on(document, "mousemove", mouseMove)
  on(display.scroller, "dragstart", dragStart)
  on(display.scroller, "drop", dragEnd)

  delayBlurEvent(cm)
  setTimeout(function () { return display.input.focus(); }, 20)
}

// Normal selection, as opposed to text dragging.
function leftButtonSelect(cm, e, start, type, addNew) {
  var display = cm.display, doc = cm.doc
  e_preventDefault(e)

  var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges
  if (addNew && !e.shiftKey) {
    ourIndex = doc.sel.contains(start)
    if (ourIndex > -1)
      { ourRange = ranges[ourIndex] }
    else
      { ourRange = new Range(start, start) }
  } else {
    ourRange = doc.sel.primary()
    ourIndex = doc.sel.primIndex
  }

  if (chromeOS ? e.shiftKey && e.metaKey : e.altKey) {
    type = "rect"
    if (!addNew) { ourRange = new Range(start, start) }
    start = posFromMouse(cm, e, true, true)
    ourIndex = -1
  } else if (type == "double") {
    var word = cm.findWordAt(start)
    if (cm.display.shift || doc.extend)
      { ourRange = extendRange(doc, ourRange, word.anchor, word.head) }
    else
      { ourRange = word }
  } else if (type == "triple") {
    var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)))
    if (cm.display.shift || doc.extend)
      { ourRange = extendRange(doc, ourRange, line.anchor, line.head) }
    else
      { ourRange = line }
  } else {
    ourRange = extendRange(doc, ourRange, start)
  }

  if (!addNew) {
    ourIndex = 0
    setSelection(doc, new Selection([ourRange], 0), sel_mouse)
    startSel = doc.sel
  } else if (ourIndex == -1) {
    ourIndex = ranges.length
    setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
                 {scroll: false, origin: "*mouse"})
  } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) {
    setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
                 {scroll: false, origin: "*mouse"})
    startSel = doc.sel
  } else {
    replaceOneSelection(doc, ourIndex, ourRange, sel_mouse)
  }

  var lastPos = start
  function extendTo(pos) {
    if (cmp(lastPos, pos) == 0) { return }
    lastPos = pos

    if (type == "rect") {
      var ranges = [], tabSize = cm.options.tabSize
      var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize)
      var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize)
      var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol)
      for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
           line <= end; line++) {
        var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize)
        if (left == right)
          { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))) }
        else if (text.length > leftPos)
          { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))) }
      }
      if (!ranges.length) { ranges.push(new Range(start, start)) }
      setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
                   {origin: "*mouse", scroll: false})
      cm.scrollIntoView(pos)
    } else {
      var oldRange = ourRange
      var anchor = oldRange.anchor, head = pos
      if (type != "single") {
        var range
        if (type == "double")
          { range = cm.findWordAt(pos) }
        else
          { range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0))) }
        if (cmp(range.anchor, anchor) > 0) {
          head = range.head
          anchor = minPos(oldRange.from(), range.anchor)
        } else {
          head = range.anchor
          anchor = maxPos(oldRange.to(), range.head)
        }
      }
      var ranges$1 = startSel.ranges.slice(0)
      ranges$1[ourIndex] = new Range(clipPos(doc, anchor), head)
      setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse)
    }
  }

  var editorSize = display.wrapper.getBoundingClientRect()
  // Used to ensure timeout re-tries don't fire when another extend
  // happened in the meantime (clearTimeout isn't reliable -- at
  // least on Chrome, the timeouts still happen even when cleared,
  // if the clear happens after their scheduled firing time).
  var counter = 0

  function extend(e) {
    var curCount = ++counter
    var cur = posFromMouse(cm, e, true, type == "rect")
    if (!cur) { return }
    if (cmp(cur, lastPos) != 0) {
      cm.curOp.focus = activeElt()
      extendTo(cur)
      var visible = visibleLines(display, doc)
      if (cur.line >= visible.to || cur.line < visible.from)
        { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e) }}), 150) }
    } else {
      var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0
      if (outside) { setTimeout(operation(cm, function () {
        if (counter != curCount) { return }
        display.scroller.scrollTop += outside
        extend(e)
      }), 50) }
    }
  }

  function done(e) {
    cm.state.selectingText = false
    counter = Infinity
    e_preventDefault(e)
    display.input.focus()
    off(document, "mousemove", move)
    off(document, "mouseup", up)
    doc.history.lastSelOrigin = null
  }

  var move = operation(cm, function (e) {
    if (!e_button(e)) { done(e) }
    else { extend(e) }
  })
  var up = operation(cm, done)
  cm.state.selectingText = up
  on(document, "mousemove", move)
  on(document, "mouseup", up)
}


// Determines whether an event happened in the gutter, and fires the
// handlers for the corresponding event.
function gutterEvent(cm, e, type, prevent) {
  var mX, mY
  try { mX = e.clientX; mY = e.clientY }
  catch(e) { return false }
  if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
  if (prevent) { e_preventDefault(e) }

  var display = cm.display
  var lineBox = display.lineDiv.getBoundingClientRect()

  if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
  mY -= lineBox.top - display.viewOffset

  for (var i = 0; i < cm.options.gutters.length; ++i) {
    var g = display.gutters.childNodes[i]
    if (g && g.getBoundingClientRect().right >= mX) {
      var line = lineAtHeight(cm.doc, mY)
      var gutter = cm.options.gutters[i]
      signal(cm, type, cm, line, gutter, e)
      return e_defaultPrevented(e)
    }
  }
}

function clickInGutter(cm, e) {
  return gutterEvent(cm, e, "gutterClick", true)
}

// CONTEXT MENU HANDLING

// To make the context menu work, we need to briefly unhide the
// textarea (making it as unobtrusive as possible) to let the
// right-click take effect on it.
function onContextMenu(cm, e) {
  if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
  if (signalDOMEvent(cm, e, "contextmenu")) { return }
  cm.display.input.onContextMenu(e)
}

function contextMenuInGutter(cm, e) {
  if (!hasHandler(cm, "gutterContextMenu")) { return false }
  return gutterEvent(cm, e, "gutterContextMenu", false)
}

function themeChanged(cm) {
  cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
    cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-")
  clearCaches(cm)
}

var Init = {toString: function(){return "CodeMirror.Init"}}

var defaults = {}
var optionHandlers = {}

function defineOptions(CodeMirror) {
  var optionHandlers = CodeMirror.optionHandlers

  function option(name, deflt, handle, notOnInit) {
    CodeMirror.defaults[name] = deflt
    if (handle) { optionHandlers[name] =
      notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old) }} : handle }
  }

  CodeMirror.defineOption = option

  // Passed to option handlers when there is no old value.
  CodeMirror.Init = Init

  // These two are, on init, called from the constructor because they
  // have to be initialized before the editor can start at all.
  option("value", "", function (cm, val) { return cm.setValue(val); }, true)
  option("mode", null, function (cm, val) {
    cm.doc.modeOption = val
    loadMode(cm)
  }, true)

  option("indentUnit", 2, loadMode, true)
  option("indentWithTabs", false)
  option("smartIndent", true)
  option("tabSize", 4, function (cm) {
    resetModeState(cm)
    clearCaches(cm)
    regChange(cm)
  }, true)
  option("lineSeparator", null, function (cm, val) {
    cm.doc.lineSep = val
    if (!val) { return }
    var newBreaks = [], lineNo = cm.doc.first
    cm.doc.iter(function (line) {
      for (var pos = 0;;) {
        var found = line.text.indexOf(val, pos)
        if (found == -1) { break }
        pos = found + val.length
        newBreaks.push(Pos(lineNo, found))
      }
      lineNo++
    })
    for (var i = newBreaks.length - 1; i >= 0; i--)
      { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) }
  })
  option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) {
    cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g")
    if (old != Init) { cm.refresh() }
  })
  option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true)
  option("electricChars", true)
  option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
    throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
  }, true)
  option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true)
  option("rtlMoveVisually", !windows)
  option("wholeLineUpdateBefore", true)

  option("theme", "default", function (cm) {
    themeChanged(cm)
    guttersChanged(cm)
  }, true)
  option("keyMap", "default", function (cm, val, old) {
    var next = getKeyMap(val)
    var prev = old != Init && getKeyMap(old)
    if (prev && prev.detach) { prev.detach(cm, next) }
    if (next.attach) { next.attach(cm, prev || null) }
  })
  option("extraKeys", null)

  option("lineWrapping", false, wrappingChanged, true)
  option("gutters", [], function (cm) {
    setGuttersForLineNumbers(cm.options)
    guttersChanged(cm)
  }, true)
  option("fixedGutter", true, function (cm, val) {
    cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"
    cm.refresh()
  }, true)
  option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true)
  option("scrollbarStyle", "native", function (cm) {
    initScrollbars(cm)
    updateScrollbars(cm)
    cm.display.scrollbars.setScrollTop(cm.doc.scrollTop)
    cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft)
  }, true)
  option("lineNumbers", false, function (cm) {
    setGuttersForLineNumbers(cm.options)
    guttersChanged(cm)
  }, true)
  option("firstLineNumber", 1, guttersChanged, true)
  option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true)
  option("showCursorWhenSelecting", false, updateSelection, true)

  option("resetSelectionOnContextMenu", true)
  option("lineWiseCopyCut", true)

  option("readOnly", false, function (cm, val) {
    if (val == "nocursor") {
      onBlur(cm)
      cm.display.input.blur()
      cm.display.disabled = true
    } else {
      cm.display.disabled = false
    }
    cm.display.input.readOnlyChanged(val)
  })
  option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset() }}, true)
  option("dragDrop", true, dragDropChanged)
  option("allowDropFileTypes", null)

  option("cursorBlinkRate", 530)
  option("cursorScrollMargin", 0)
  option("cursorHeight", 1, updateSelection, true)
  option("singleCursorHeightPerLine", true, updateSelection, true)
  option("workTime", 100)
  option("workDelay", 100)
  option("flattenSpans", true, resetModeState, true)
  option("addModeClass", false, resetModeState, true)
  option("pollInterval", 100)
  option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; })
  option("historyEventDelay", 1250)
  option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true)
  option("maxHighlightLength", 10000, resetModeState, true)
  option("moveInputWithCursor", true, function (cm, val) {
    if (!val) { cm.display.input.resetPosition() }
  })

  option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; })
  option("autofocus", null)
  option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true)
}

function guttersChanged(cm) {
  updateGutters(cm)
  regChange(cm)
  alignHorizontally(cm)
}

function dragDropChanged(cm, value, old) {
  var wasOn = old && old != Init
  if (!value != !wasOn) {
    var funcs = cm.display.dragFunctions
    var toggle = value ? on : off
    toggle(cm.display.scroller, "dragstart", funcs.start)
    toggle(cm.display.scroller, "dragenter", funcs.enter)
    toggle(cm.display.scroller, "dragover", funcs.over)
    toggle(cm.display.scroller, "dragleave", funcs.leave)
    toggle(cm.display.scroller, "drop", funcs.drop)
  }
}

function wrappingChanged(cm) {
  if (cm.options.lineWrapping) {
    addClass(cm.display.wrapper, "CodeMirror-wrap")
    cm.display.sizer.style.minWidth = ""
    cm.display.sizerWidth = null
  } else {
    rmClass(cm.display.wrapper, "CodeMirror-wrap")
    findMaxLine(cm)
  }
  estimateLineHeights(cm)
  regChange(cm)
  clearCaches(cm)
  setTimeout(function () { return updateScrollbars(cm); }, 100)
}

// A CodeMirror instance represents an editor. This is the object
// that user code is usually dealing with.

function CodeMirror(place, options) {
  var this$1 = this;

  if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }

  this.options = options = options ? copyObj(options) : {}
  // Determine effective options based on given values and defaults.
  copyObj(defaults, options, false)
  setGuttersForLineNumbers(options)

  var doc = options.value
  if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction) }
  this.doc = doc

  var input = new CodeMirror.inputStyles[options.inputStyle](this)
  var display = this.display = new Display(place, doc, input)
  display.wrapper.CodeMirror = this
  updateGutters(this)
  themeChanged(this)
  if (options.lineWrapping)
    { this.display.wrapper.className += " CodeMirror-wrap" }
  initScrollbars(this)

  this.state = {
    keyMaps: [],  // stores maps added by addKeyMap
    overlays: [], // highlighting overlays, as added by addOverlay
    modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info
    overwrite: false,
    delayingBlurEvent: false,
    focused: false,
    suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
    pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
    selectingText: false,
    draggingText: false,
    highlight: new Delayed(), // stores highlight worker timeout
    keySeq: null,  // Unfinished key sequence
    specialChars: null
  }

  if (options.autofocus && !mobile) { display.input.focus() }

  // Override magic textarea content restore that IE sometimes does
  // on our hidden textarea on reload
  if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20) }

  registerEventHandlers(this)
  ensureGlobalHandlers()

  startOperation(this)
  this.curOp.forceUpdate = true
  attachDoc(this, doc)

  if ((options.autofocus && !mobile) || this.hasFocus())
    { setTimeout(bind(onFocus, this), 20) }
  else
    { onBlur(this) }

  for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
    { optionHandlers[opt](this$1, options[opt], Init) } }
  maybeUpdateLineNumberWidth(this)
  if (options.finishInit) { options.finishInit(this) }
  for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1) }
  endOperation(this)
  // Suppress optimizelegibility in Webkit, since it breaks text
  // measuring on line wrapping boundaries.
  if (webkit && options.lineWrapping &&
      getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
    { display.lineDiv.style.textRendering = "auto" }
}

// The default configuration options.
CodeMirror.defaults = defaults
// Functions to run when options are changed.
CodeMirror.optionHandlers = optionHandlers

// Attach the necessary event handlers when initializing the editor
function registerEventHandlers(cm) {
  var d = cm.display
  on(d.scroller, "mousedown", operation(cm, onMouseDown))
  // Older IE's will not fire a second mousedown for a double click
  if (ie && ie_version < 11)
    { on(d.scroller, "dblclick", operation(cm, function (e) {
      if (signalDOMEvent(cm, e)) { return }
      var pos = posFromMouse(cm, e)
      if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
      e_preventDefault(e)
      var word = cm.findWordAt(pos)
      extendSelection(cm.doc, word.anchor, word.head)
    })) }
  else
    { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }) }
  // Some browsers fire contextmenu *after* opening the menu, at
  // which point we can't mess with it anymore. Context menu is
  // handled in onMouseDown for these browsers.
  if (!captureRightClick) { on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }) }

  // Used to suppress mouse event handling when a touch happens
  var touchFinished, prevTouch = {end: 0}
  function finishTouch() {
    if (d.activeTouch) {
      touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000)
      prevTouch = d.activeTouch
      prevTouch.end = +new Date
    }
  }
  function isMouseLikeTouchEvent(e) {
    if (e.touches.length != 1) { return false }
    var touch = e.touches[0]
    return touch.radiusX <= 1 && touch.radiusY <= 1
  }
  function farAway(touch, other) {
    if (other.left == null) { return true }
    var dx = other.left - touch.left, dy = other.top - touch.top
    return dx * dx + dy * dy > 20 * 20
  }
  on(d.scroller, "touchstart", function (e) {
    if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
      d.input.ensurePolled()
      clearTimeout(touchFinished)
      var now = +new Date
      d.activeTouch = {start: now, moved: false,
                       prev: now - prevTouch.end <= 300 ? prevTouch : null}
      if (e.touches.length == 1) {
        d.activeTouch.left = e.touches[0].pageX
        d.activeTouch.top = e.touches[0].pageY
      }
    }
  })
  on(d.scroller, "touchmove", function () {
    if (d.activeTouch) { d.activeTouch.moved = true }
  })
  on(d.scroller, "touchend", function (e) {
    var touch = d.activeTouch
    if (touch && !eventInWidget(d, e) && touch.left != null &&
        !touch.moved && new Date - touch.start < 300) {
      var pos = cm.coordsChar(d.activeTouch, "page"), range
      if (!touch.prev || farAway(touch, touch.prev)) // Single tap
        { range = new Range(pos, pos) }
      else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
        { range = cm.findWordAt(pos) }
      else // Triple tap
        { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
      cm.setSelection(range.anchor, range.head)
      cm.focus()
      e_preventDefault(e)
    }
    finishTouch()
  })
  on(d.scroller, "touchcancel", finishTouch)

  // Sync scrolling between fake scrollbars and real scrollable
  // area, ensure viewport is updated when scrolling.
  on(d.scroller, "scroll", function () {
    if (d.scroller.clientHeight) {
      setScrollTop(cm, d.scroller.scrollTop)
      setScrollLeft(cm, d.scroller.scrollLeft, true)
      signal(cm, "scroll", cm)
    }
  })

  // Listen to wheel events in order to try and update the viewport on time.
  on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); })
  on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); })

  // Prevent wrapper from ever scrolling
  on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; })

  d.dragFunctions = {
    enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e) }},
    over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e) }},
    start: function (e) { return onDragStart(cm, e); },
    drop: operation(cm, onDrop),
    leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm) }}
  }

  var inp = d.input.getField()
  on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); })
  on(inp, "keydown", operation(cm, onKeyDown))
  on(inp, "keypress", operation(cm, onKeyPress))
  on(inp, "focus", function (e) { return onFocus(cm, e); })
  on(inp, "blur", function (e) { return onBlur(cm, e); })
}

var initHooks = []
CodeMirror.defineInitHook = function (f) { return initHooks.push(f); }

// Indent the given line. The how parameter can be "smart",
// "add"/null, "subtract", or "prev". When aggressive is false
// (typically set to true for forced single-line indents), empty
// lines are not indented, and places where the mode returns Pass
// are left alone.
function indentLine(cm, n, how, aggressive) {
  var doc = cm.doc, state
  if (how == null) { how = "add" }
  if (how == "smart") {
    // Fall back to "prev" when the mode doesn't have an indentation
    // method.
    if (!doc.mode.indent) { how = "prev" }
    else { state = getStateBefore(cm, n) }
  }

  var tabSize = cm.options.tabSize
  var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize)
  if (line.stateAfter) { line.stateAfter = null }
  var curSpaceString = line.text.match(/^\s*/)[0], indentation
  if (!aggressive && !/\S/.test(line.text)) {
    indentation = 0
    how = "not"
  } else if (how == "smart") {
    indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text)
    if (indentation == Pass || indentation > 150) {
      if (!aggressive) { return }
      how = "prev"
    }
  }
  if (how == "prev") {
    if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize) }
    else { indentation = 0 }
  } else if (how == "add") {
    indentation = curSpace + cm.options.indentUnit
  } else if (how == "subtract") {
    indentation = curSpace - cm.options.indentUnit
  } else if (typeof how == "number") {
    indentation = curSpace + how
  }
  indentation = Math.max(0, indentation)

  var indentString = "", pos = 0
  if (cm.options.indentWithTabs)
    { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t"} }
  if (pos < indentation) { indentString += spaceStr(indentation - pos) }

  if (indentString != curSpaceString) {
    replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input")
    line.stateAfter = null
    return true
  } else {
    // Ensure that, if the cursor was in the whitespace at the start
    // of the line, it is moved to the end of that space.
    for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
      var range = doc.sel.ranges[i$1]
      if (range.head.line == n && range.head.ch < curSpaceString.length) {
        var pos$1 = Pos(n, curSpaceString.length)
        replaceOneSelection(doc, i$1, new Range(pos$1, pos$1))
        break
      }
    }
  }
}

// This will be set to a {lineWise: bool, text: [string]} object, so
// that, when pasting, we know what kind of selections the copied
// text was made out of.
var lastCopied = null

function setLastCopied(newLastCopied) {
  lastCopied = newLastCopied
}

function applyTextInput(cm, inserted, deleted, sel, origin) {
  var doc = cm.doc
  cm.display.shift = false
  if (!sel) { sel = doc.sel }

  var paste = cm.state.pasteIncoming || origin == "paste"
  var textLines = splitLinesAuto(inserted), multiPaste = null
  // When pasing N lines into N selections, insert one line per selection
  if (paste && sel.ranges.length > 1) {
    if (lastCopied && lastCopied.text.join("\n") == inserted) {
      if (sel.ranges.length % lastCopied.text.length == 0) {
        multiPaste = []
        for (var i = 0; i < lastCopied.text.length; i++)
          { multiPaste.push(doc.splitLines(lastCopied.text[i])) }
      }
    } else if (textLines.length == sel.ranges.length) {
      multiPaste = map(textLines, function (l) { return [l]; })
    }
  }

  var updateInput
  // Normal behavior is to insert the new text into every selection
  for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
    var range = sel.ranges[i$1]
    var from = range.from(), to = range.to()
    if (range.empty()) {
      if (deleted && deleted > 0) // Handle deletion
        { from = Pos(from.line, from.ch - deleted) }
      else if (cm.state.overwrite && !paste) // Handle overwrite
        { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)) }
      else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
        { from = to = Pos(from.line, 0) }
    }
    updateInput = cm.curOp.updateInput
    var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
                       origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")}
    makeChange(cm.doc, changeEvent)
    signalLater(cm, "inputRead", cm, changeEvent)
  }
  if (inserted && !paste)
    { triggerElectric(cm, inserted) }

  ensureCursorVisible(cm)
  cm.curOp.updateInput = updateInput
  cm.curOp.typing = true
  cm.state.pasteIncoming = cm.state.cutIncoming = false
}

function handlePaste(e, cm) {
  var pasted = e.clipboardData && e.clipboardData.getData("Text")
  if (pasted) {
    e.preventDefault()
    if (!cm.isReadOnly() && !cm.options.disableInput)
      { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }) }
    return true
  }
}

function triggerElectric(cm, inserted) {
  // When an 'electric' character is inserted, immediately trigger a reindent
  if (!cm.options.electricChars || !cm.options.smartIndent) { return }
  var sel = cm.doc.sel

  for (var i = sel.ranges.length - 1; i >= 0; i--) {
    var range = sel.ranges[i]
    if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue }
    var mode = cm.getModeAt(range.head)
    var indented = false
    if (mode.electricChars) {
      for (var j = 0; j < mode.electricChars.length; j++)
        { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
          indented = indentLine(cm, range.head.line, "smart")
          break
        } }
    } else if (mode.electricInput) {
      if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))
        { indented = indentLine(cm, range.head.line, "smart") }
    }
    if (indented) { signalLater(cm, "electricInput", cm, range.head.line) }
  }
}

function copyableRanges(cm) {
  var text = [], ranges = []
  for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
    var line = cm.doc.sel.ranges[i].head.line
    var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}
    ranges.push(lineRange)
    text.push(cm.getRange(lineRange.anchor, lineRange.head))
  }
  return {text: text, ranges: ranges}
}

function disableBrowserMagic(field, spellcheck) {
  field.setAttribute("autocorrect", "off")
  field.setAttribute("autocapitalize", "off")
  field.setAttribute("spellcheck", !!spellcheck)
}

function hiddenTextarea() {
  var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none")
  var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;")
  // The textarea is kept positioned near the cursor to prevent the
  // fact that it'll be scrolled into view on input from scrolling
  // our fake cursor out of view. On webkit, when wrap=off, paste is
  // very slow. So make the area wide instead.
  if (webkit) { te.style.width = "1000px" }
  else { te.setAttribute("wrap", "off") }
  // If border: 0; -- iOS fails to open keyboard (issue #1287)
  if (ios) { te.style.border = "1px solid black" }
  disableBrowserMagic(te)
  return div
}

// The publicly visible API. Note that methodOp(f) means
// 'wrap f in an operation, performed on its `this` parameter'.

// This is not the complete set of editor methods. Most of the
// methods defined on the Doc type are also injected into
// CodeMirror.prototype, for backwards compatibility and
// convenience.

function addEditorMethods(CodeMirror) {
  var optionHandlers = CodeMirror.optionHandlers

  var helpers = CodeMirror.helpers = {}

  CodeMirror.prototype = {
    constructor: CodeMirror,
    focus: function(){window.focus(); this.display.input.focus()},

    setOption: function(option, value) {
      var options = this.options, old = options[option]
      if (options[option] == value && option != "mode") { return }
      options[option] = value
      if (optionHandlers.hasOwnProperty(option))
        { operation(this, optionHandlers[option])(this, value, old) }
      signal(this, "optionChange", this, option)
    },

    getOption: function(option) {return this.options[option]},
    getDoc: function() {return this.doc},

    addKeyMap: function(map, bottom) {
      this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map))
    },
    removeKeyMap: function(map) {
      var maps = this.state.keyMaps
      for (var i = 0; i < maps.length; ++i)
        { if (maps[i] == map || maps[i].name == map) {
          maps.splice(i, 1)
          return true
        } }
    },

    addOverlay: methodOp(function(spec, options) {
      var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec)
      if (mode.startState) { throw new Error("Overlays may not be stateful.") }
      insertSorted(this.state.overlays,
                   {mode: mode, modeSpec: spec, opaque: options && options.opaque,
                    priority: (options && options.priority) || 0},
                   function (overlay) { return overlay.priority; })
      this.state.modeGen++
      regChange(this)
    }),
    removeOverlay: methodOp(function(spec) {
      var this$1 = this;

      var overlays = this.state.overlays
      for (var i = 0; i < overlays.length; ++i) {
        var cur = overlays[i].modeSpec
        if (cur == spec || typeof spec == "string" && cur.name == spec) {
          overlays.splice(i, 1)
          this$1.state.modeGen++
          regChange(this$1)
          return
        }
      }
    }),

    indentLine: methodOp(function(n, dir, aggressive) {
      if (typeof dir != "string" && typeof dir != "number") {
        if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev" }
        else { dir = dir ? "add" : "subtract" }
      }
      if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive) }
    }),
    indentSelection: methodOp(function(how) {
      var this$1 = this;

      var ranges = this.doc.sel.ranges, end = -1
      for (var i = 0; i < ranges.length; i++) {
        var range = ranges[i]
        if (!range.empty()) {
          var from = range.from(), to = range.to()
          var start = Math.max(end, from.line)
          end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1
          for (var j = start; j < end; ++j)
            { indentLine(this$1, j, how) }
          var newRanges = this$1.doc.sel.ranges
          if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
            { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll) }
        } else if (range.head.line > end) {
          indentLine(this$1, range.head.line, how, true)
          end = range.head.line
          if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1) }
        }
      }
    }),

    // Fetch the parser token for a given character. Useful for hacks
    // that want to inspect the mode state (say, for completion).
    getTokenAt: function(pos, precise) {
      return takeToken(this, pos, precise)
    },

    getLineTokens: function(line, precise) {
      return takeToken(this, Pos(line), precise, true)
    },

    getTokenTypeAt: function(pos) {
      pos = clipPos(this.doc, pos)
      var styles = getLineStyles(this, getLine(this.doc, pos.line))
      var before = 0, after = (styles.length - 1) / 2, ch = pos.ch
      var type
      if (ch == 0) { type = styles[2] }
      else { for (;;) {
        var mid = (before + after) >> 1
        if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid }
        else if (styles[mid * 2 + 1] < ch) { before = mid + 1 }
        else { type = styles[mid * 2 + 2]; break }
      } }
      var cut = type ? type.indexOf("overlay ") : -1
      return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
    },

    getModeAt: function(pos) {
      var mode = this.doc.mode
      if (!mode.innerMode) { return mode }
      return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
    },

    getHelper: function(pos, type) {
      return this.getHelpers(pos, type)[0]
    },

    getHelpers: function(pos, type) {
      var this$1 = this;

      var found = []
      if (!helpers.hasOwnProperty(type)) { return found }
      var help = helpers[type], mode = this.getModeAt(pos)
      if (typeof mode[type] == "string") {
        if (help[mode[type]]) { found.push(help[mode[type]]) }
      } else if (mode[type]) {
        for (var i = 0; i < mode[type].length; i++) {
          var val = help[mode[type][i]]
          if (val) { found.push(val) }
        }
      } else if (mode.helperType && help[mode.helperType]) {
        found.push(help[mode.helperType])
      } else if (help[mode.name]) {
        found.push(help[mode.name])
      }
      for (var i$1 = 0; i$1 < help._global.length; i$1++) {
        var cur = help._global[i$1]
        if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
          { found.push(cur.val) }
      }
      return found
    },

    getStateAfter: function(line, precise) {
      var doc = this.doc
      line = clipLine(doc, line == null ? doc.first + doc.size - 1: line)
      return getStateBefore(this, line + 1, precise)
    },

    cursorCoords: function(start, mode) {
      var pos, range = this.doc.sel.primary()
      if (start == null) { pos = range.head }
      else if (typeof start == "object") { pos = clipPos(this.doc, start) }
      else { pos = start ? range.from() : range.to() }
      return cursorCoords(this, pos, mode || "page")
    },

    charCoords: function(pos, mode) {
      return charCoords(this, clipPos(this.doc, pos), mode || "page")
    },

    coordsChar: function(coords, mode) {
      coords = fromCoordSystem(this, coords, mode || "page")
      return coordsChar(this, coords.left, coords.top)
    },

    lineAtHeight: function(height, mode) {
      height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top
      return lineAtHeight(this.doc, height + this.display.viewOffset)
    },
    heightAtLine: function(line, mode, includeWidgets) {
      var end = false, lineObj
      if (typeof line == "number") {
        var last = this.doc.first + this.doc.size - 1
        if (line < this.doc.first) { line = this.doc.first }
        else if (line > last) { line = last; end = true }
        lineObj = getLine(this.doc, line)
      } else {
        lineObj = line
      }
      return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
        (end ? this.doc.height - heightAtLine(lineObj) : 0)
    },

    defaultTextHeight: function() { return textHeight(this.display) },
    defaultCharWidth: function() { return charWidth(this.display) },

    getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},

    addWidget: function(pos, node, scroll, vert, horiz) {
      var display = this.display
      pos = cursorCoords(this, clipPos(this.doc, pos))
      var top = pos.bottom, left = pos.left
      node.style.position = "absolute"
      node.setAttribute("cm-ignore-events", "true")
      this.display.input.setUneditable(node)
      display.sizer.appendChild(node)
      if (vert == "over") {
        top = pos.top
      } else if (vert == "above" || vert == "near") {
        var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
        hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth)
        // Default to positioning above (if specified and possible); otherwise default to positioning below
        if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
          { top = pos.top - node.offsetHeight }
        else if (pos.bottom + node.offsetHeight <= vspace)
          { top = pos.bottom }
        if (left + node.offsetWidth > hspace)
          { left = hspace - node.offsetWidth }
      }
      node.style.top = top + "px"
      node.style.left = node.style.right = ""
      if (horiz == "right") {
        left = display.sizer.clientWidth - node.offsetWidth
        node.style.right = "0px"
      } else {
        if (horiz == "left") { left = 0 }
        else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2 }
        node.style.left = left + "px"
      }
      if (scroll)
        { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}) }
    },

    triggerOnKeyDown: methodOp(onKeyDown),
    triggerOnKeyPress: methodOp(onKeyPress),
    triggerOnKeyUp: onKeyUp,

    execCommand: function(cmd) {
      if (commands.hasOwnProperty(cmd))
        { return commands[cmd].call(null, this) }
    },

    triggerElectric: methodOp(function(text) { triggerElectric(this, text) }),

    findPosH: function(from, amount, unit, visually) {
      var this$1 = this;

      var dir = 1
      if (amount < 0) { dir = -1; amount = -amount }
      var cur = clipPos(this.doc, from)
      for (var i = 0; i < amount; ++i) {
        cur = findPosH(this$1.doc, cur, dir, unit, visually)
        if (cur.hitSide) { break }
      }
      return cur
    },

    moveH: methodOp(function(dir, unit) {
      var this$1 = this;

      this.extendSelectionsBy(function (range) {
        if (this$1.display.shift || this$1.doc.extend || range.empty())
          { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) }
        else
          { return dir < 0 ? range.from() : range.to() }
      }, sel_move)
    }),

    deleteH: methodOp(function(dir, unit) {
      var sel = this.doc.sel, doc = this.doc
      if (sel.somethingSelected())
        { doc.replaceSelection("", null, "+delete") }
      else
        { deleteNearSelection(this, function (range) {
          var other = findPosH(doc, range.head, dir, unit, false)
          return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other}
        }) }
    }),

    findPosV: function(from, amount, unit, goalColumn) {
      var this$1 = this;

      var dir = 1, x = goalColumn
      if (amount < 0) { dir = -1; amount = -amount }
      var cur = clipPos(this.doc, from)
      for (var i = 0; i < amount; ++i) {
        var coords = cursorCoords(this$1, cur, "div")
        if (x == null) { x = coords.left }
        else { coords.left = x }
        cur = findPosV(this$1, coords, dir, unit)
        if (cur.hitSide) { break }
      }
      return cur
    },

    moveV: methodOp(function(dir, unit) {
      var this$1 = this;

      var doc = this.doc, goals = []
      var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected()
      doc.extendSelectionsBy(function (range) {
        if (collapse)
          { return dir < 0 ? range.from() : range.to() }
        var headPos = cursorCoords(this$1, range.head, "div")
        if (range.goalColumn != null) { headPos.left = range.goalColumn }
        goals.push(headPos.left)
        var pos = findPosV(this$1, headPos, dir, unit)
        if (unit == "page" && range == doc.sel.primary())
          { addToScrollPos(this$1, null, charCoords(this$1, pos, "div").top - headPos.top) }
        return pos
      }, sel_move)
      if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
        { doc.sel.ranges[i].goalColumn = goals[i] } }
    }),

    // Find the word at the given position (as returned by coordsChar).
    findWordAt: function(pos) {
      var doc = this.doc, line = getLine(doc, pos.line).text
      var start = pos.ch, end = pos.ch
      if (line) {
        var helper = this.getHelper(pos, "wordChars")
        if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end }
        var startChar = line.charAt(start)
        var check = isWordChar(startChar, helper)
          ? function (ch) { return isWordChar(ch, helper); }
          : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
          : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }
        while (start > 0 && check(line.charAt(start - 1))) { --start }
        while (end < line.length && check(line.charAt(end))) { ++end }
      }
      return new Range(Pos(pos.line, start), Pos(pos.line, end))
    },

    toggleOverwrite: function(value) {
      if (value != null && value == this.state.overwrite) { return }
      if (this.state.overwrite = !this.state.overwrite)
        { addClass(this.display.cursorDiv, "CodeMirror-overwrite") }
      else
        { rmClass(this.display.cursorDiv, "CodeMirror-overwrite") }

      signal(this, "overwriteToggle", this, this.state.overwrite)
    },
    hasFocus: function() { return this.display.input.getField() == activeElt() },
    isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },

    scrollTo: methodOp(function(x, y) {
      if (x != null || y != null) { resolveScrollToPos(this) }
      if (x != null) { this.curOp.scrollLeft = x }
      if (y != null) { this.curOp.scrollTop = y }
    }),
    getScrollInfo: function() {
      var scroller = this.display.scroller
      return {left: scroller.scrollLeft, top: scroller.scrollTop,
              height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
              width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
              clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
    },

    scrollIntoView: methodOp(function(range, margin) {
      if (range == null) {
        range = {from: this.doc.sel.primary().head, to: null}
        if (margin == null) { margin = this.options.cursorScrollMargin }
      } else if (typeof range == "number") {
        range = {from: Pos(range, 0), to: null}
      } else if (range.from == null) {
        range = {from: range, to: null}
      }
      if (!range.to) { range.to = range.from }
      range.margin = margin || 0

      if (range.from.line != null) {
        resolveScrollToPos(this)
        this.curOp.scrollToPos = range
      } else {
        var sPos = calculateScrollPos(this, {
          left: Math.min(range.from.left, range.to.left),
          top: Math.min(range.from.top, range.to.top) - range.margin,
          right: Math.max(range.from.right, range.to.right),
          bottom: Math.max(range.from.bottom, range.to.bottom) + range.margin
        })
        this.scrollTo(sPos.scrollLeft, sPos.scrollTop)
      }
    }),

    setSize: methodOp(function(width, height) {
      var this$1 = this;

      var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }
      if (width != null) { this.display.wrapper.style.width = interpret(width) }
      if (height != null) { this.display.wrapper.style.height = interpret(height) }
      if (this.options.lineWrapping) { clearLineMeasurementCache(this) }
      var lineNo = this.display.viewFrom
      this.doc.iter(lineNo, this.display.viewTo, function (line) {
        if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
          { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } }
        ++lineNo
      })
      this.curOp.forceUpdate = true
      signal(this, "refresh", this)
    }),

    operation: function(f){return runInOp(this, f)},

    refresh: methodOp(function() {
      var oldHeight = this.display.cachedTextHeight
      regChange(this)
      this.curOp.forceUpdate = true
      clearCaches(this)
      this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop)
      updateGutterSpace(this)
      if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
        { estimateLineHeights(this) }
      signal(this, "refresh", this)
    }),

    swapDoc: methodOp(function(doc) {
      var old = this.doc
      old.cm = null
      attachDoc(this, doc)
      clearCaches(this)
      this.display.input.reset()
      this.scrollTo(doc.scrollLeft, doc.scrollTop)
      this.curOp.forceScroll = true
      signalLater(this, "swapDoc", this, old)
      return old
    }),

    getInputField: function(){return this.display.input.getField()},
    getWrapperElement: function(){return this.display.wrapper},
    getScrollerElement: function(){return this.display.scroller},
    getGutterElement: function(){return this.display.gutters}
  }
  eventMixin(CodeMirror)

  CodeMirror.registerHelper = function(type, name, value) {
    if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []} }
    helpers[type][name] = value
  }
  CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
    CodeMirror.registerHelper(type, name, value)
    helpers[type]._global.push({pred: predicate, val: value})
  }
}

// Used for horizontal relative motion. Dir is -1 or 1 (left or
// right), unit can be "char", "column" (like char, but doesn't
// cross line boundaries), "word" (across next word), or "group" (to
// the start of next group of word or non-word-non-whitespace
// chars). The visually param controls whether, in right-to-left
// text, direction 1 means to move towards the next index in the
// string, or towards the character to the right of the current
// position. The resulting position will have a hitSide=true
// property if it reached the end of the document.
function findPosH(doc, pos, dir, unit, visually) {
  var oldPos = pos
  var origDir = dir
  var lineObj = getLine(doc, pos.line)
  function findNextLine() {
    var l = pos.line + dir
    if (l < doc.first || l >= doc.first + doc.size) { return false }
    pos = new Pos(l, pos.ch, pos.sticky)
    return lineObj = getLine(doc, l)
  }
  function moveOnce(boundToLine) {
    var next
    if (visually) {
      next = moveVisually(doc.cm, lineObj, pos, dir)
    } else {
      next = moveLogically(lineObj, pos, dir)
    }
    if (next == null) {
      if (!boundToLine && findNextLine())
        { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir) }
      else
        { return false }
    } else {
      pos = next
    }
    return true
  }

  if (unit == "char") {
    moveOnce()
  } else if (unit == "column") {
    moveOnce(true)
  } else if (unit == "word" || unit == "group") {
    var sawType = null, group = unit == "group"
    var helper = doc.cm && doc.cm.getHelper(pos, "wordChars")
    for (var first = true;; first = false) {
      if (dir < 0 && !moveOnce(!first)) { break }
      var cur = lineObj.text.charAt(pos.ch) || "\n"
      var type = isWordChar(cur, helper) ? "w"
        : group && cur == "\n" ? "n"
        : !group || /\s/.test(cur) ? null
        : "p"
      if (group && !first && !type) { type = "s" }
      if (sawType && sawType != type) {
        if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after"}
        break
      }

      if (type) { sawType = type }
      if (dir > 0 && !moveOnce(!first)) { break }
    }
  }
  var result = skipAtomic(doc, pos, oldPos, origDir, true)
  if (equalCursorPos(oldPos, result)) { result.hitSide = true }
  return result
}

// For relative vertical movement. Dir may be -1 or 1. Unit can be
// "page" or "line". The resulting position will have a hitSide=true
// property if it reached the end of the document.
function findPosV(cm, pos, dir, unit) {
  var doc = cm.doc, x = pos.left, y
  if (unit == "page") {
    var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight)
    var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3)
    y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount

  } else if (unit == "line") {
    y = dir > 0 ? pos.bottom + 3 : pos.top - 3
  }
  var target
  for (;;) {
    target = coordsChar(cm, x, y)
    if (!target.outside) { break }
    if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
    y += dir * 5
  }
  return target
}

// CONTENTEDITABLE INPUT STYLE

var ContentEditableInput = function(cm) {
  this.cm = cm
  this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null
  this.polling = new Delayed()
  this.composing = null
  this.gracePeriod = false
  this.readDOMTimeout = null
};

ContentEditableInput.prototype.init = function (display) {
    var this$1 = this;

  var input = this, cm = input.cm
  var div = input.div = display.lineDiv
  disableBrowserMagic(div, cm.options.spellcheck)

  on(div, "paste", function (e) {
    if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
    // IE doesn't fire input events, so we schedule a read for the pasted content in this way
    if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20) }
  })

  on(div, "compositionstart", function (e) {
    this$1.composing = {data: e.data, done: false}
  })
  on(div, "compositionupdate", function (e) {
    if (!this$1.composing) { this$1.composing = {data: e.data, done: false} }
  })
  on(div, "compositionend", function (e) {
    if (this$1.composing) {
      if (e.data != this$1.composing.data) { this$1.readFromDOMSoon() }
      this$1.composing.done = true
    }
  })

  on(div, "touchstart", function () { return input.forceCompositionEnd(); })

  on(div, "input", function () {
    if (!this$1.composing) { this$1.readFromDOMSoon() }
  })

  function onCopyCut(e) {
    if (signalDOMEvent(cm, e)) { return }
    if (cm.somethingSelected()) {
      setLastCopied({lineWise: false, text: cm.getSelections()})
      if (e.type == "cut") { cm.replaceSelection("", null, "cut") }
    } else if (!cm.options.lineWiseCopyCut) {
      return
    } else {
      var ranges = copyableRanges(cm)
      setLastCopied({lineWise: true, text: ranges.text})
      if (e.type == "cut") {
        cm.operation(function () {
          cm.setSelections(ranges.ranges, 0, sel_dontScroll)
          cm.replaceSelection("", null, "cut")
        })
      }
    }
    if (e.clipboardData) {
      e.clipboardData.clearData()
      var content = lastCopied.text.join("\n")
      // iOS exposes the clipboard API, but seems to discard content inserted into it
      e.clipboardData.setData("Text", content)
      if (e.clipboardData.getData("Text") == content) {
        e.preventDefault()
        return
      }
    }
    // Old-fashioned briefly-focus-a-textarea hack
    var kludge = hiddenTextarea(), te = kludge.firstChild
    cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild)
    te.value = lastCopied.text.join("\n")
    var hadFocus = document.activeElement
    selectInput(te)
    setTimeout(function () {
      cm.display.lineSpace.removeChild(kludge)
      hadFocus.focus()
      if (hadFocus == div) { input.showPrimarySelection() }
    }, 50)
  }
  on(div, "copy", onCopyCut)
  on(div, "cut", onCopyCut)
};

ContentEditableInput.prototype.prepareSelection = function () {
  var result = prepareSelection(this.cm, false)
  result.focus = this.cm.state.focused
  return result
};

ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
  if (!info || !this.cm.display.view.length) { return }
  if (info.focus || takeFocus) { this.showPrimarySelection() }
  this.showMultipleSelections(info)
};

ContentEditableInput.prototype.showPrimarySelection = function () {
  var sel = window.getSelection(), cm = this.cm, prim = cm.doc.sel.primary()
  var from = prim.from(), to = prim.to()

  if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
    sel.removeAllRanges()
    return
  }

  var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)
  var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset)
  if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
      cmp(minPos(curAnchor, curFocus), from) == 0 &&
      cmp(maxPos(curAnchor, curFocus), to) == 0)
    { return }

  var view = cm.display.view
  var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
      {node: view[0].measure.map[2], offset: 0}
  var end = to.line < cm.display.viewTo && posToDOM(cm, to)
  if (!end) {
    var measure = view[view.length - 1].measure
    var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map
    end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}
  }

  if (!start || !end) {
    sel.removeAllRanges()
    return
  }

  var old = sel.rangeCount && sel.getRangeAt(0), rng
  try { rng = range(start.node, start.offset, end.offset, end.node) }
  catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
  if (rng) {
    if (!gecko && cm.state.focused) {
      sel.collapse(start.node, start.offset)
      if (!rng.collapsed) {
        sel.removeAllRanges()
        sel.addRange(rng)
      }
    } else {
      sel.removeAllRanges()
      sel.addRange(rng)
    }
    if (old && sel.anchorNode == null) { sel.addRange(old) }
    else if (gecko) { this.startGracePeriod() }
  }
  this.rememberSelection()
};

ContentEditableInput.prototype.startGracePeriod = function () {
    var this$1 = this;

  clearTimeout(this.gracePeriod)
  this.gracePeriod = setTimeout(function () {
    this$1.gracePeriod = false
    if (this$1.selectionChanged())
      { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }) }
  }, 20)
};

ContentEditableInput.prototype.showMultipleSelections = function (info) {
  removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors)
  removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection)
};

ContentEditableInput.prototype.rememberSelection = function () {
  var sel = window.getSelection()
  this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset
  this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset
};

ContentEditableInput.prototype.selectionInEditor = function () {
  var sel = window.getSelection()
  if (!sel.rangeCount) { return false }
  var node = sel.getRangeAt(0).commonAncestorContainer
  return contains(this.div, node)
};

ContentEditableInput.prototype.focus = function () {
  if (this.cm.options.readOnly != "nocursor") {
    if (!this.selectionInEditor())
      { this.showSelection(this.prepareSelection(), true) }
    this.div.focus()
  }
};
ContentEditableInput.prototype.blur = function () { this.div.blur() };
ContentEditableInput.prototype.getField = function () { return this.div };

ContentEditableInput.prototype.supportsTouch = function () { return true };

ContentEditableInput.prototype.receivedFocus = function () {
  var input = this
  if (this.selectionInEditor())
    { this.pollSelection() }
  else
    { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }) }

  function poll() {
    if (input.cm.state.focused) {
      input.pollSelection()
      input.polling.set(input.cm.options.pollInterval, poll)
    }
  }
  this.polling.set(this.cm.options.pollInterval, poll)
};

ContentEditableInput.prototype.selectionChanged = function () {
  var sel = window.getSelection()
  return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
    sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
};

ContentEditableInput.prototype.pollSelection = function () {
  if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
  var sel = window.getSelection(), cm = this.cm
  // On Android Chrome (version 56, at least), backspacing into an
  // uneditable block element will put the cursor in that element,
  // and then, because it's not editable, hide the virtual keyboard.
  // Because Android doesn't allow us to actually detect backspace
  // presses in a sane way, this code checks for when that happens
  // and simulates a backspace press in this case.
  if (android && chrome && this.cm.options.gutters.length && isInGutter(sel.anchorNode)) {
    this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs})
    this.blur()
    this.focus()
    return
  }
  if (this.composing) { return }
  this.rememberSelection()
  var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)
  var head = domToPos(cm, sel.focusNode, sel.focusOffset)
  if (anchor && head) { runInOp(cm, function () {
    setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll)
    if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true }
  }) }
};

ContentEditableInput.prototype.pollContent = function () {
  if (this.readDOMTimeout != null) {
    clearTimeout(this.readDOMTimeout)
    this.readDOMTimeout = null
  }

  var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary()
  var from = sel.from(), to = sel.to()
  if (from.ch == 0 && from.line > cm.firstLine())
    { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length) }
  if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
    { to = Pos(to.line + 1, 0) }
  if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }

  var fromIndex, fromLine, fromNode
  if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
    fromLine = lineNo(display.view[0].line)
    fromNode = display.view[0].node
  } else {
    fromLine = lineNo(display.view[fromIndex].line)
    fromNode = display.view[fromIndex - 1].node.nextSibling
  }
  var toIndex = findViewIndex(cm, to.line)
  var toLine, toNode
  if (toIndex == display.view.length - 1) {
    toLine = display.viewTo - 1
    toNode = display.lineDiv.lastChild
  } else {
    toLine = lineNo(display.view[toIndex + 1].line) - 1
    toNode = display.view[toIndex + 1].node.previousSibling
  }

  if (!fromNode) { return false }
  var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine))
  var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length))
  while (newText.length > 1 && oldText.length > 1) {
    if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine-- }
    else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++ }
    else { break }
  }

  var cutFront = 0, cutEnd = 0
  var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length)
  while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
    { ++cutFront }
  var newBot = lst(newText), oldBot = lst(oldText)
  var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
                           oldBot.length - (oldText.length == 1 ? cutFront : 0))
  while (cutEnd < maxCutEnd &&
         newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
    { ++cutEnd }
  // Try to move start of change to start of selection if ambiguous
  if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
    while (cutFront && cutFront > from.ch &&
           newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
      cutFront--
      cutEnd++
    }
  }

  newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "")
  newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "")

  var chFrom = Pos(fromLine, cutFront)
  var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0)
  if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
    replaceRange(cm.doc, newText, chFrom, chTo, "+input")
    return true
  }
};

ContentEditableInput.prototype.ensurePolled = function () {
  this.forceCompositionEnd()
};
ContentEditableInput.prototype.reset = function () {
  this.forceCompositionEnd()
};
ContentEditableInput.prototype.forceCompositionEnd = function () {
  if (!this.composing) { return }
  clearTimeout(this.readDOMTimeout)
  this.composing = null
  this.updateFromDOM()
  this.div.blur()
  this.div.focus()
};
ContentEditableInput.prototype.readFromDOMSoon = function () {
    var this$1 = this;

  if (this.readDOMTimeout != null) { return }
  this.readDOMTimeout = setTimeout(function () {
    this$1.readDOMTimeout = null
    if (this$1.composing) {
      if (this$1.composing.done) { this$1.composing = null }
      else { return }
    }
    this$1.updateFromDOM()
  }, 80)
};

ContentEditableInput.prototype.updateFromDOM = function () {
    var this$1 = this;

  if (this.cm.isReadOnly() || !this.pollContent())
    { runInOp(this.cm, function () { return regChange(this$1.cm); }) }
};

ContentEditableInput.prototype.setUneditable = function (node) {
  node.contentEditable = "false"
};

ContentEditableInput.prototype.onKeyPress = function (e) {
  if (e.charCode == 0) { return }
  e.preventDefault()
  if (!this.cm.isReadOnly())
    { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0) }
};

ContentEditableInput.prototype.readOnlyChanged = function (val) {
  this.div.contentEditable = String(val != "nocursor")
};

ContentEditableInput.prototype.onContextMenu = function () {};
ContentEditableInput.prototype.resetPosition = function () {};

ContentEditableInput.prototype.needsContentAttribute = true

function posToDOM(cm, pos) {
  var view = findViewForLine(cm, pos.line)
  if (!view || view.hidden) { return null }
  var line = getLine(cm.doc, pos.line)
  var info = mapFromLineView(view, line, pos.line)

  var order = getOrder(line, cm.doc.direction), side = "left"
  if (order) {
    var partPos = getBidiPartAt(order, pos.ch)
    side = partPos % 2 ? "right" : "left"
  }
  var result = nodeAndOffsetInLineMap(info.map, pos.ch, side)
  result.offset = result.collapse == "right" ? result.end : result.start
  return result
}

function isInGutter(node) {
  for (var scan = node; scan; scan = scan.parentNode)
    { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
  return false
}

function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }

function domTextBetween(cm, from, to, fromLine, toLine) {
  var text = "", closing = false, lineSep = cm.doc.lineSeparator()
  function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
  function close() {
    if (closing) {
      text += lineSep
      closing = false
    }
  }
  function addText(str) {
    if (str) {
      close()
      text += str
    }
  }
  function walk(node) {
    if (node.nodeType == 1) {
      var cmText = node.getAttribute("cm-text")
      if (cmText != null) {
        addText(cmText || node.textContent.replace(/\u200b/g, ""))
        return
      }
      var markerID = node.getAttribute("cm-marker"), range
      if (markerID) {
        var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID))
        if (found.length && (range = found[0].find()))
          { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)) }
        return
      }
      if (node.getAttribute("contenteditable") == "false") { return }
      var isBlock = /^(pre|div|p)$/i.test(node.nodeName)
      if (isBlock) { close() }
      for (var i = 0; i < node.childNodes.length; i++)
        { walk(node.childNodes[i]) }
      if (isBlock) { closing = true }
    } else if (node.nodeType == 3) {
      addText(node.nodeValue)
    }
  }
  for (;;) {
    walk(from)
    if (from == to) { break }
    from = from.nextSibling
  }
  return text
}

function domToPos(cm, node, offset) {
  var lineNode
  if (node == cm.display.lineDiv) {
    lineNode = cm.display.lineDiv.childNodes[offset]
    if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
    node = null; offset = 0
  } else {
    for (lineNode = node;; lineNode = lineNode.parentNode) {
      if (!lineNode || lineNode == cm.display.lineDiv) { return null }
      if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
    }
  }
  for (var i = 0; i < cm.display.view.length; i++) {
    var lineView = cm.display.view[i]
    if (lineView.node == lineNode)
      { return locateNodeInLineView(lineView, node, offset) }
  }
}

function locateNodeInLineView(lineView, node, offset) {
  var wrapper = lineView.text.firstChild, bad = false
  if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
  if (node == wrapper) {
    bad = true
    node = wrapper.childNodes[offset]
    offset = 0
    if (!node) {
      var line = lineView.rest ? lst(lineView.rest) : lineView.line
      return badPos(Pos(lineNo(line), line.text.length), bad)
    }
  }

  var textNode = node.nodeType == 3 ? node : null, topNode = node
  if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
    textNode = node.firstChild
    if (offset) { offset = textNode.nodeValue.length }
  }
  while (topNode.parentNode != wrapper) { topNode = topNode.parentNode }
  var measure = lineView.measure, maps = measure.maps

  function find(textNode, topNode, offset) {
    for (var i = -1; i < (maps ? maps.length : 0); i++) {
      var map = i < 0 ? measure.map : maps[i]
      for (var j = 0; j < map.length; j += 3) {
        var curNode = map[j + 2]
        if (curNode == textNode || curNode == topNode) {
          var line = lineNo(i < 0 ? lineView.line : lineView.rest[i])
          var ch = map[j] + offset
          if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)] }
          return Pos(line, ch)
        }
      }
    }
  }
  var found = find(textNode, topNode, offset)
  if (found) { return badPos(found, bad) }

  // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
  for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
    found = find(after, after.firstChild, 0)
    if (found)
      { return badPos(Pos(found.line, found.ch - dist), bad) }
    else
      { dist += after.textContent.length }
  }
  for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
    found = find(before, before.firstChild, -1)
    if (found)
      { return badPos(Pos(found.line, found.ch + dist$1), bad) }
    else
      { dist$1 += before.textContent.length }
  }
}

// TEXTAREA INPUT STYLE

var TextareaInput = function(cm) {
  this.cm = cm
  // See input.poll and input.reset
  this.prevInput = ""

  // Flag that indicates whether we expect input to appear real soon
  // now (after some event like 'keypress' or 'input') and are
  // polling intensively.
  this.pollingFast = false
  // Self-resetting timeout for the poller
  this.polling = new Delayed()
  // Tracks when input.reset has punted to just putting a short
  // string into the textarea instead of the full selection.
  this.inaccurateSelection = false
  // Used to work around IE issue with selection being forgotten when focus moves away from textarea
  this.hasSelection = false
  this.composing = null
};

TextareaInput.prototype.init = function (display) {
    var this$1 = this;

  var input = this, cm = this.cm

  // Wraps and hides input textarea
  var div = this.wrapper = hiddenTextarea()
  // The semihidden textarea that is focused when the editor is
  // focused, and receives input.
  var te = this.textarea = div.firstChild
  display.wrapper.insertBefore(div, display.wrapper.firstChild)

  // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
  if (ios) { te.style.width = "0px" }

  on(te, "input", function () {
    if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null }
    input.poll()
  })

  on(te, "paste", function (e) {
    if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }

    cm.state.pasteIncoming = true
    input.fastPoll()
  })

  function prepareCopyCut(e) {
    if (signalDOMEvent(cm, e)) { return }
    if (cm.somethingSelected()) {
      setLastCopied({lineWise: false, text: cm.getSelections()})
      if (input.inaccurateSelection) {
        input.prevInput = ""
        input.inaccurateSelection = false
        te.value = lastCopied.text.join("\n")
        selectInput(te)
      }
    } else if (!cm.options.lineWiseCopyCut) {
      return
    } else {
      var ranges = copyableRanges(cm)
      setLastCopied({lineWise: true, text: ranges.text})
      if (e.type == "cut") {
        cm.setSelections(ranges.ranges, null, sel_dontScroll)
      } else {
        input.prevInput = ""
        te.value = ranges.text.join("\n")
        selectInput(te)
      }
    }
    if (e.type == "cut") { cm.state.cutIncoming = true }
  }
  on(te, "cut", prepareCopyCut)
  on(te, "copy", prepareCopyCut)

  on(display.scroller, "paste", function (e) {
    if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
    cm.state.pasteIncoming = true
    input.focus()
  })

  // Prevent normal selection in the editor (we handle our own)
  on(display.lineSpace, "selectstart", function (e) {
    if (!eventInWidget(display, e)) { e_preventDefault(e) }
  })

  on(te, "compositionstart", function () {
    var start = cm.getCursor("from")
    if (input.composing) { input.composing.range.clear() }
    input.composing = {
      start: start,
      range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
    }
  })
  on(te, "compositionend", function () {
    if (input.composing) {
      input.poll()
      input.composing.range.clear()
      input.composing = null
    }
  })
};

TextareaInput.prototype.prepareSelection = function () {
  // Redraw the selection and/or cursor
  var cm = this.cm, display = cm.display, doc = cm.doc
  var result = prepareSelection(cm)

  // Move the hidden textarea near the cursor to prevent scrolling artifacts
  if (cm.options.moveInputWithCursor) {
    var headPos = cursorCoords(cm, doc.sel.primary().head, "div")
    var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect()
    result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
                                        headPos.top + lineOff.top - wrapOff.top))
    result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
                                         headPos.left + lineOff.left - wrapOff.left))
  }

  return result
};

TextareaInput.prototype.showSelection = function (drawn) {
  var cm = this.cm, display = cm.display
  removeChildrenAndAdd(display.cursorDiv, drawn.cursors)
  removeChildrenAndAdd(display.selectionDiv, drawn.selection)
  if (drawn.teTop != null) {
    this.wrapper.style.top = drawn.teTop + "px"
    this.wrapper.style.left = drawn.teLeft + "px"
  }
};

// Reset the input to correspond to the selection (or to be empty,
// when not typing and nothing is selected)
TextareaInput.prototype.reset = function (typing) {
  if (this.contextMenuPending) { return }
  var minimal, selected, cm = this.cm, doc = cm.doc
  if (cm.somethingSelected()) {
    this.prevInput = ""
    var range = doc.sel.primary()
    minimal = hasCopyEvent &&
      (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000)
    var content = minimal ? "-" : selected || cm.getSelection()
    this.textarea.value = content
    if (cm.state.focused) { selectInput(this.textarea) }
    if (ie && ie_version >= 9) { this.hasSelection = content }
  } else if (!typing) {
    this.prevInput = this.textarea.value = ""
    if (ie && ie_version >= 9) { this.hasSelection = null }
  }
  this.inaccurateSelection = minimal
};

TextareaInput.prototype.getField = function () { return this.textarea };

TextareaInput.prototype.supportsTouch = function () { return false };

TextareaInput.prototype.focus = function () {
  if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
    try { this.textarea.focus() }
    catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
  }
};

TextareaInput.prototype.blur = function () { this.textarea.blur() };

TextareaInput.prototype.resetPosition = function () {
  this.wrapper.style.top = this.wrapper.style.left = 0
};

TextareaInput.prototype.receivedFocus = function () { this.slowPoll() };

// Poll for input changes, using the normal rate of polling. This
// runs as long as the editor is focused.
TextareaInput.prototype.slowPoll = function () {
    var this$1 = this;

  if (this.pollingFast) { return }
  this.polling.set(this.cm.options.pollInterval, function () {
    this$1.poll()
    if (this$1.cm.state.focused) { this$1.slowPoll() }
  })
};

// When an event has just come in that is likely to add or change
// something in the input textarea, we poll faster, to ensure that
// the change appears on the screen quickly.
TextareaInput.prototype.fastPoll = function () {
  var missed = false, input = this
  input.pollingFast = true
  function p() {
    var changed = input.poll()
    if (!changed && !missed) {missed = true; input.polling.set(60, p)}
    else {input.pollingFast = false; input.slowPoll()}
  }
  input.polling.set(20, p)
};

// Read input from the textarea, and update the document to match.
// When something is selected, it is present in the textarea, and
// selected (unless it is huge, in which case a placeholder is
// used). When nothing is selected, the cursor sits after previously
// seen text (can be empty), which is stored in prevInput (we must
// not reset the textarea when typing, because that breaks IME).
TextareaInput.prototype.poll = function () {
    var this$1 = this;

  var cm = this.cm, input = this.textarea, prevInput = this.prevInput
  // Since this is called a *lot*, try to bail out as cheaply as
  // possible when it is clear that nothing happened. hasSelection
  // will be the case when there is a lot of text in the textarea,
  // in which case reading its value would be expensive.
  if (this.contextMenuPending || !cm.state.focused ||
      (hasSelection(input) && !prevInput && !this.composing) ||
      cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
    { return false }

  var text = input.value
  // If nothing changed, bail.
  if (text == prevInput && !cm.somethingSelected()) { return false }
  // Work around nonsensical selection resetting in IE9/10, and
  // inexplicable appearance of private area unicode characters on
  // some key combos in Mac (#2689).
  if (ie && ie_version >= 9 && this.hasSelection === text ||
      mac && /[\uf700-\uf7ff]/.test(text)) {
    cm.display.input.reset()
    return false
  }

  if (cm.doc.sel == cm.display.selForContextMenu) {
    var first = text.charCodeAt(0)
    if (first == 0x200b && !prevInput) { prevInput = "\u200b" }
    if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
  }
  // Find the part of the input that is actually new
  var same = 0, l = Math.min(prevInput.length, text.length)
  while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same }

  runInOp(cm, function () {
    applyTextInput(cm, text.slice(same), prevInput.length - same,
                   null, this$1.composing ? "*compose" : null)

    // Don't leave long text in the textarea, since it makes further polling slow
    if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = "" }
    else { this$1.prevInput = text }

    if (this$1.composing) {
      this$1.composing.range.clear()
      this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
                                         {className: "CodeMirror-composing"})
    }
  })
  return true
};

TextareaInput.prototype.ensurePolled = function () {
  if (this.pollingFast && this.poll()) { this.pollingFast = false }
};

TextareaInput.prototype.onKeyPress = function () {
  if (ie && ie_version >= 9) { this.hasSelection = null }
  this.fastPoll()
};

TextareaInput.prototype.onContextMenu = function (e) {
  var input = this, cm = input.cm, display = cm.display, te = input.textarea
  var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop
  if (!pos || presto) { return } // Opera is difficult.

  // Reset the current text selection only if the click is done outside of the selection
  // and 'resetSelectionOnContextMenu' option is true.
  var reset = cm.options.resetSelectionOnContextMenu
  if (reset && cm.doc.sel.contains(pos) == -1)
    { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll) }

  var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText
  input.wrapper.style.cssText = "position: absolute"
  var wrapperBox = input.wrapper.getBoundingClientRect()
  te.style.cssText = "position: absolute; width: 30px; height: 30px;\n      top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n      z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n      outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"
  var oldScrollY
  if (webkit) { oldScrollY = window.scrollY } // Work around Chrome issue (#2712)
  display.input.focus()
  if (webkit) { window.scrollTo(null, oldScrollY) }
  display.input.reset()
  // Adds "Select all" to context menu in FF
  if (!cm.somethingSelected()) { te.value = input.prevInput = " " }
  input.contextMenuPending = true
  display.selForContextMenu = cm.doc.sel
  clearTimeout(display.detectingSelectAll)

  // Select-all will be greyed out if there's nothing to select, so
  // this adds a zero-width space so that we can later check whether
  // it got selected.
  function prepareSelectAllHack() {
    if (te.selectionStart != null) {
      var selected = cm.somethingSelected()
      var extval = "\u200b" + (selected ? te.value : "")
      te.value = "\u21da" // Used to catch context-menu undo
      te.value = extval
      input.prevInput = selected ? "" : "\u200b"
      te.selectionStart = 1; te.selectionEnd = extval.length
      // Re-set this, in case some other handler touched the
      // selection in the meantime.
      display.selForContextMenu = cm.doc.sel
    }
  }
  function rehide() {
    input.contextMenuPending = false
    input.wrapper.style.cssText = oldWrapperCSS
    te.style.cssText = oldCSS
    if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos) }

    // Try to detect the user choosing select-all
    if (te.selectionStart != null) {
      if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack() }
      var i = 0, poll = function () {
        if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
            te.selectionEnd > 0 && input.prevInput == "\u200b") {
          operation(cm, selectAll)(cm)
        } else if (i++ < 10) {
          display.detectingSelectAll = setTimeout(poll, 500)
        } else {
          display.selForContextMenu = null
          display.input.reset()
        }
      }
      display.detectingSelectAll = setTimeout(poll, 200)
    }
  }

  if (ie && ie_version >= 9) { prepareSelectAllHack() }
  if (captureRightClick) {
    e_stop(e)
    var mouseup = function () {
      off(window, "mouseup", mouseup)
      setTimeout(rehide, 20)
    }
    on(window, "mouseup", mouseup)
  } else {
    setTimeout(rehide, 50)
  }
};

TextareaInput.prototype.readOnlyChanged = function (val) {
  if (!val) { this.reset() }
};

TextareaInput.prototype.setUneditable = function () {};

TextareaInput.prototype.needsContentAttribute = false

function fromTextArea(textarea, options) {
  options = options ? copyObj(options) : {}
  options.value = textarea.value
  if (!options.tabindex && textarea.tabIndex)
    { options.tabindex = textarea.tabIndex }
  if (!options.placeholder && textarea.placeholder)
    { options.placeholder = textarea.placeholder }
  // Set autofocus to true if this textarea is focused, or if it has
  // autofocus and no other element is focused.
  if (options.autofocus == null) {
    var hasFocus = activeElt()
    options.autofocus = hasFocus == textarea ||
      textarea.getAttribute("autofocus") != null && hasFocus == document.body
  }

  function save() {textarea.value = cm.getValue()}

  var realSubmit
  if (textarea.form) {
    on(textarea.form, "submit", save)
    // Deplorable hack to make the submit method do the right thing.
    if (!options.leaveSubmitMethodAlone) {
      var form = textarea.form
      realSubmit = form.submit
      try {
        var wrappedSubmit = form.submit = function () {
          save()
          form.submit = realSubmit
          form.submit()
          form.submit = wrappedSubmit
        }
      } catch(e) {}
    }
  }

  options.finishInit = function (cm) {
    cm.save = save
    cm.getTextArea = function () { return textarea; }
    cm.toTextArea = function () {
      cm.toTextArea = isNaN // Prevent this from being ran twice
      save()
      textarea.parentNode.removeChild(cm.getWrapperElement())
      textarea.style.display = ""
      if (textarea.form) {
        off(textarea.form, "submit", save)
        if (typeof textarea.form.submit == "function")
          { textarea.form.submit = realSubmit }
      }
    }
  }

  textarea.style.display = "none"
  var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
    options)
  return cm
}

function addLegacyProps(CodeMirror) {
  CodeMirror.off = off
  CodeMirror.on = on
  CodeMirror.wheelEventPixels = wheelEventPixels
  CodeMirror.Doc = Doc
  CodeMirror.splitLines = splitLinesAuto
  CodeMirror.countColumn = countColumn
  CodeMirror.findColumn = findColumn
  CodeMirror.isWordChar = isWordCharBasic
  CodeMirror.Pass = Pass
  CodeMirror.signal = signal
  CodeMirror.Line = Line
  CodeMirror.changeEnd = changeEnd
  CodeMirror.scrollbarModel = scrollbarModel
  CodeMirror.Pos = Pos
  CodeMirror.cmpPos = cmp
  CodeMirror.modes = modes
  CodeMirror.mimeModes = mimeModes
  CodeMirror.resolveMode = resolveMode
  CodeMirror.getMode = getMode
  CodeMirror.modeExtensions = modeExtensions
  CodeMirror.extendMode = extendMode
  CodeMirror.copyState = copyState
  CodeMirror.startState = startState
  CodeMirror.innerMode = innerMode
  CodeMirror.commands = commands
  CodeMirror.keyMap = keyMap
  CodeMirror.keyName = keyName
  CodeMirror.isModifierKey = isModifierKey
  CodeMirror.lookupKey = lookupKey
  CodeMirror.normalizeKeyMap = normalizeKeyMap
  CodeMirror.StringStream = StringStream
  CodeMirror.SharedTextMarker = SharedTextMarker
  CodeMirror.TextMarker = TextMarker
  CodeMirror.LineWidget = LineWidget
  CodeMirror.e_preventDefault = e_preventDefault
  CodeMirror.e_stopPropagation = e_stopPropagation
  CodeMirror.e_stop = e_stop
  CodeMirror.addClass = addClass
  CodeMirror.contains = contains
  CodeMirror.rmClass = rmClass
  CodeMirror.keyNames = keyNames
}

// EDITOR CONSTRUCTOR

defineOptions(CodeMirror)

addEditorMethods(CodeMirror)

// Set up methods on CodeMirror's prototype to redirect to the editor's document.
var dontDelegate = "iter insert remove copy getEditor constructor".split(" ")
for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
  { CodeMirror.prototype[prop] = (function(method) {
    return function() {return method.apply(this.doc, arguments)}
  })(Doc.prototype[prop]) } }

eventMixin(Doc)

// INPUT HANDLING

CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}

// MODE DEFINITION AND QUERYING

// Extra arguments are stored as the mode's dependencies, which is
// used by (legacy) mechanisms like loadmode.js to automatically
// load a mode. (Preferred mechanism is the require/define calls.)
CodeMirror.defineMode = function(name/*, mode, …*/) {
  if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name }
  defineMode.apply(this, arguments)
}

CodeMirror.defineMIME = defineMIME

// Minimal default mode.
CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); })
CodeMirror.defineMIME("text/plain", "null")

// EXTENSIONS

CodeMirror.defineExtension = function (name, func) {
  CodeMirror.prototype[name] = func
}
CodeMirror.defineDocExtension = function (name, func) {
  Doc.prototype[name] = func
}

CodeMirror.fromTextArea = fromTextArea

addLegacyProps(CodeMirror)

CodeMirror.version = "5.25.2"

return CodeMirror;

})));
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  "use strict";

  var HINT_ELEMENT_CLASS        = "CodeMirror-hint";
  var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";

  // This is the old interface, kept around for now to stay
  // backwards-compatible.
  CodeMirror.showHint = function(cm, getHints, options) {
    if (!getHints) return cm.showHint(options);
    if (options && options.async) getHints.async = true;
    var newOpts = {hint: getHints};
    if (options) for (var prop in options) newOpts[prop] = options[prop];
    return cm.showHint(newOpts);
  };

  CodeMirror.defineExtension("showHint", function(options) {
    options = parseOptions(this, this.getCursor("start"), options);
    var selections = this.listSelections()
    if (selections.length > 1) return;
    // By default, don't allow completion when something is selected.
    // A hint function can have a `supportsSelection` property to
    // indicate that it can handle selections.
    if (this.somethingSelected()) {
      if (!options.hint.supportsSelection) return;
      // Don't try with cross-line selections
      for (var i = 0; i < selections.length; i++)
        if (selections[i].head.line != selections[i].anchor.line) return;
    }

    if (this.state.completionActive) this.state.completionActive.close();
    var completion = this.state.completionActive = new Completion(this, options);
    if (!completion.options.hint) return;

    CodeMirror.signal(this, "startCompletion", this);
    completion.update(true);
  });

  function Completion(cm, options) {
    this.cm = cm;
    this.options = options;
    this.widget = null;
    this.debounce = 0;
    this.tick = 0;
    this.startPos = this.cm.getCursor("start");
    this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length;

    var self = this;
    cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); });
  }

  var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
    return setTimeout(fn, 1000/60);
  };
  var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;

  Completion.prototype = {
    close: function() {
      if (!this.active()) return;
      this.cm.state.completionActive = null;
      this.tick = null;
      this.cm.off("cursorActivity", this.activityFunc);

      if (this.widget && this.data) CodeMirror.signal(this.data, "close");
      if (this.widget) this.widget.close();
      CodeMirror.signal(this.cm, "endCompletion", this.cm);
    },

    active: function() {
      return this.cm.state.completionActive == this;
    },

    pick: function(data, i) {
      var completion = data.list[i];
      if (completion.hint) completion.hint(this.cm, data, completion);
      else this.cm.replaceRange(getText(completion), completion.from || data.from,
                                completion.to || data.to, "complete");
      CodeMirror.signal(data, "pick", completion);
      this.close();
    },

    cursorActivity: function() {
      if (this.debounce) {
        cancelAnimationFrame(this.debounce);
        this.debounce = 0;
      }

      var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);
      if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||
          pos.ch < this.startPos.ch || this.cm.somethingSelected() ||
          (pos.ch && this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {
        this.close();
      } else {
        var self = this;
        this.debounce = requestAnimationFrame(function() {self.update();});
        if (this.widget) this.widget.disable();
      }
    },

    update: function(first) {
      if (this.tick == null) return
      var self = this, myTick = ++this.tick
      fetchHints(this.options.hint, this.cm, this.options, function(data) {
        if (self.tick == myTick) self.finishUpdate(data, first)
      })
    },

    finishUpdate: function(data, first) {
      if (this.data) CodeMirror.signal(this.data, "update");

      var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);
      if (this.widget) this.widget.close();

      if (data && this.data && isNewCompletion(this.data, data)) return;
      this.data = data;

      if (data && data.list.length) {
        if (picked && data.list.length == 1) {
          this.pick(data, 0);
        } else {
          this.widget = new Widget(this, data);
          CodeMirror.signal(data, "shown");
        }
      }
    }
  };

  function isNewCompletion(old, nw) {
    var moved = CodeMirror.cmpPos(nw.from, old.from)
    return moved > 0 && old.to.ch - old.from.ch != nw.to.ch - nw.from.ch
  }

  function parseOptions(cm, pos, options) {
    var editor = cm.options.hintOptions;
    var out = {};
    for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
    if (editor) for (var prop in editor)
      if (editor[prop] !== undefined) out[prop] = editor[prop];
    if (options) for (var prop in options)
      if (options[prop] !== undefined) out[prop] = options[prop];
    if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos)
    return out;
  }

  function getText(completion) {
    if (typeof completion == "string") return completion;
    else return completion.text;
  }

  function buildKeyMap(completion, handle) {
    var baseMap = {
      Up: function() {handle.moveFocus(-1);},
      Down: function() {handle.moveFocus(1);},
      PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},
      PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},
      Home: function() {handle.setFocus(0);},
      End: function() {handle.setFocus(handle.length - 1);},
      Enter: handle.pick,
      Tab: handle.pick,
      Esc: handle.close
    };
    var custom = completion.options.customKeys;
    var ourMap = custom ? {} : baseMap;
    function addBinding(key, val) {
      var bound;
      if (typeof val != "string")
        bound = function(cm) { return val(cm, handle); };
      // This mechanism is deprecated
      else if (baseMap.hasOwnProperty(val))
        bound = baseMap[val];
      else
        bound = val;
      ourMap[key] = bound;
    }
    if (custom)
      for (var key in custom) if (custom.hasOwnProperty(key))
        addBinding(key, custom[key]);
    var extra = completion.options.extraKeys;
    if (extra)
      for (var key in extra) if (extra.hasOwnProperty(key))
        addBinding(key, extra[key]);
    return ourMap;
  }

  function getHintElement(hintsElement, el) {
    while (el && el != hintsElement) {
      if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el;
      el = el.parentNode;
    }
  }

  function Widget(completion, data) {
    this.completion = completion;
    this.data = data;
    this.picked = false;
    var widget = this, cm = completion.cm;

    var hints = this.hints = document.createElement("ul");
    hints.className = "CodeMirror-hints";
    this.selectedHint = data.selectedHint || 0;

    var completions = data.list;
    for (var i = 0; i < completions.length; ++i) {
      var elt = hints.appendChild(document.createElement("li")), cur = completions[i];
      var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS);
      if (cur.className != null) className = cur.className + " " + className;
      elt.className = className;
      if (cur.render) cur.render(elt, data, cur);
      else elt.appendChild(document.createTextNode(cur.displayText || getText(cur)));
      elt.hintId = i;
    }

    var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);
    var left = pos.left, top = pos.bottom, below = true;
    hints.style.left = left + "px";
    hints.style.top = top + "px";
    // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
    var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
    var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
    (completion.options.container || document.body).appendChild(hints);
    var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
    var scrolls = hints.scrollHeight > hints.clientHeight + 1
    var startScroll = cm.getScrollInfo();

    if (overlapY > 0) {
      var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
      if (curTop - height > 0) { // Fits above cursor
        hints.style.top = (top = pos.top - height) + "px";
        below = false;
      } else if (height > winH) {
        hints.style.height = (winH - 5) + "px";
        hints.style.top = (top = pos.bottom - box.top) + "px";
        var cursor = cm.getCursor();
        if (data.from.ch != cursor.ch) {
          pos = cm.cursorCoords(cursor);
          hints.style.left = (left = pos.left) + "px";
          box = hints.getBoundingClientRect();
        }
      }
    }
    var overlapX = box.right - winW;
    if (overlapX > 0) {
      if (box.right - box.left > winW) {
        hints.style.width = (winW - 5) + "px";
        overlapX -= (box.right - box.left) - winW;
      }
      hints.style.left = (left = pos.left - overlapX) + "px";
    }
    if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling)
      node.style.paddingRight = cm.display.nativeBarWidth + "px"

    cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
      moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
      setFocus: function(n) { widget.changeActive(n); },
      menuSize: function() { return widget.screenAmount(); },
      length: completions.length,
      close: function() { completion.close(); },
      pick: function() { widget.pick(); },
      data: data
    }));

    if (completion.options.closeOnUnfocus) {
      var closingOnBlur;
      cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
      cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
    }

    cm.on("scroll", this.onScroll = function() {
      var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
      var newTop = top + startScroll.top - curScroll.top;
      var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop);
      if (!below) point += hints.offsetHeight;
      if (point <= editor.top || point >= editor.bottom) return completion.close();
      hints.style.top = newTop + "px";
      hints.style.left = (left + startScroll.left - curScroll.left) + "px";
    });

    CodeMirror.on(hints, "dblclick", function(e) {
      var t = getHintElement(hints, e.target || e.srcElement);
      if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
    });

    CodeMirror.on(hints, "click", function(e) {
      var t = getHintElement(hints, e.target || e.srcElement);
      if (t && t.hintId != null) {
        widget.changeActive(t.hintId);
        if (completion.options.completeOnSingleClick) widget.pick();
      }
    });

    CodeMirror.on(hints, "mousedown", function() {
      setTimeout(function(){cm.focus();}, 20);
    });

    CodeMirror.signal(data, "select", completions[0], hints.firstChild);
    return true;
  }

  Widget.prototype = {
    close: function() {
      if (this.completion.widget != this) return;
      this.completion.widget = null;
      this.hints.parentNode.removeChild(this.hints);
      this.completion.cm.removeKeyMap(this.keyMap);

      var cm = this.completion.cm;
      if (this.completion.options.closeOnUnfocus) {
        cm.off("blur", this.onBlur);
        cm.off("focus", this.onFocus);
      }
      cm.off("scroll", this.onScroll);
    },

    disable: function() {
      this.completion.cm.removeKeyMap(this.keyMap);
      var widget = this;
      this.keyMap = {Enter: function() { widget.picked = true; }};
      this.completion.cm.addKeyMap(this.keyMap);
    },

    pick: function() {
      this.completion.pick(this.data, this.selectedHint);
    },

    changeActive: function(i, avoidWrap) {
      if (i >= this.data.list.length)
        i = avoidWrap ? this.data.list.length - 1 : 0;
      else if (i < 0)
        i = avoidWrap ? 0  : this.data.list.length - 1;
      if (this.selectedHint == i) return;
      var node = this.hints.childNodes[this.selectedHint];
      node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
      node = this.hints.childNodes[this.selectedHint = i];
      node.className += " " + ACTIVE_HINT_ELEMENT_CLASS;
      if (node.offsetTop < this.hints.scrollTop)
        this.hints.scrollTop = node.offsetTop - 3;
      else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
        this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3;
      CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node);
    },

    screenAmount: function() {
      return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;
    }
  };

  function applicableHelpers(cm, helpers) {
    if (!cm.somethingSelected()) return helpers
    var result = []
    for (var i = 0; i < helpers.length; i++)
      if (helpers[i].supportsSelection) result.push(helpers[i])
    return result
  }

  function fetchHints(hint, cm, options, callback) {
    if (hint.async) {
      hint(cm, callback, options)
    } else {
      var result = hint(cm, options)
      if (result && result.then) result.then(callback)
      else callback(result)
    }
  }

  function resolveAutoHints(cm, pos) {
    var helpers = cm.getHelpers(pos, "hint"), words
    if (helpers.length) {
      var resolved = function(cm, callback, options) {
        var app = applicableHelpers(cm, helpers);
        function run(i) {
          if (i == app.length) return callback(null)
          fetchHints(app[i], cm, options, function(result) {
            if (result && result.list.length > 0) callback(result)
            else run(i + 1)
          })
        }
        run(0)
      }
      resolved.async = true
      resolved.supportsSelection = true
      return resolved
    } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) {
      return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) }
    } else if (CodeMirror.hint.anyword) {
      return function(cm, options) { return CodeMirror.hint.anyword(cm, options) }
    } else {
      return function() {}
    }
  }

  CodeMirror.registerHelper("hint", "auto", {
    resolve: resolveAutoHints
  });

  CodeMirror.registerHelper("hint", "fromList", function(cm, options) {
    var cur = cm.getCursor(), token = cm.getTokenAt(cur);
    var to = CodeMirror.Pos(cur.line, token.end);
    if (token.string && /\w/.test(token.string[token.string.length - 1])) {
      var term = token.string, from = CodeMirror.Pos(cur.line, token.start);
    } else {
      var term = "", from = to;
    }
    var found = [];
    for (var i = 0; i < options.words.length; i++) {
      var word = options.words[i];
      if (word.slice(0, term.length) == term)
        found.push(word);
    }

    if (found.length) return {list: found, from: from, to: to};
  });

  CodeMirror.commands.autocomplete = CodeMirror.showHint;

  var defaultOptions = {
    hint: CodeMirror.hint.auto,
    completeSingle: true,
    alignWithWord: true,
    closeCharacters: /[\s()\[\]{};:>,]/,
    closeOnUnfocus: true,
    completeOnSingleClick: true,
    container: null,
    customKeys: null,
    extraKeys: null
  };

  CodeMirror.defineOption("hintOptions", null);
});

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  var Pos = CodeMirror.Pos;

  function forEach(arr, f) {
    for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
  }

  function arrayContains(arr, item) {
    if (!Array.prototype.indexOf) {
      var i = arr.length;
      while (i--) {
        if (arr[i] === item) {
          return true;
        }
      }
      return false;
    }
    return arr.indexOf(item) != -1;
  }

  function scriptHint(editor, keywords, getToken, options) {
    // Find the token at the cursor
    var cur = editor.getCursor(), token = getToken(editor, cur);
    if (/\b(?:string|comment)\b/.test(token.type)) return;
    token.state = CodeMirror.innerMode(editor.getMode(), token.state).state;

    // If it's not a 'word-style' token, ignore the token.
    if (!/^[\w$_]*$/.test(token.string)) {
      token = {start: cur.ch, end: cur.ch, string: "", state: token.state,
               type: token.string == "." ? "property" : null};
    } else if (token.end > cur.ch) {
      token.end = cur.ch;
      token.string = token.string.slice(0, cur.ch - token.start);
    }

    var tprop = token;
    // If it is a property, find out what it is a property of.
    while (tprop.type == "property") {
      tprop = getToken(editor, Pos(cur.line, tprop.start));
      if (tprop.string != ".") return;
      tprop = getToken(editor, Pos(cur.line, tprop.start));
      if (!context) var context = [];
      context.push(tprop);
    }
    return {list: getCompletions(token, context, keywords, options),
            from: Pos(cur.line, token.start),
            to: Pos(cur.line, token.end)};
  }

  function javascriptHint(editor, options) {
    return scriptHint(editor, javascriptKeywords,
                      function (e, cur) {return e.getTokenAt(cur);},
                      options);
  };
  CodeMirror.registerHelper("hint", "javascript", javascriptHint);

  function getCoffeeScriptToken(editor, cur) {
  // This getToken, it is for coffeescript, imitates the behavior of
  // getTokenAt method in javascript.js, that is, returning "property"
  // type and treat "." as indepenent token.
    var token = editor.getTokenAt(cur);
    if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {
      token.end = token.start;
      token.string = '.';
      token.type = "property";
    }
    else if (/^\.[\w$_]*$/.test(token.string)) {
      token.type = "property";
      token.start++;
      token.string = token.string.replace(/\./, '');
    }
    return token;
  }

  function coffeescriptHint(editor, options) {
    return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options);
  }
  CodeMirror.registerHelper("hint", "coffeescript", coffeescriptHint);

  var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
                     "toUpperCase toLowerCase split concat match replace search").split(" ");
  var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
                    "lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
  var funcProps = "prototype apply call bind".split(" ");
  var javascriptKeywords = ("break case catch continue debugger default delete do else false finally for function " +
                  "if in instanceof new null return switch throw true try typeof var void while with").split(" ");
  var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " +
                  "if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");

  function forAllProps(obj, callback) {
    if (!Object.getOwnPropertyNames || !Object.getPrototypeOf) {
      for (var name in obj) callback(name)
    } else {
      for (var o = obj; o; o = Object.getPrototypeOf(o))
        Object.getOwnPropertyNames(o).forEach(callback)
    }
  }

  function getCompletions(token, context, keywords, options) {
    var found = [], start = token.string, global = options && options.globalScope || window;
    function maybeAdd(str) {
      if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str);
    }
    function gatherCompletions(obj) {
      if (typeof obj == "string") forEach(stringProps, maybeAdd);
      else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
      else if (obj instanceof Function) forEach(funcProps, maybeAdd);
      forAllProps(obj, maybeAdd)
    }

    if (context && context.length) {
      // If this is a property, see if it belongs to some object we can
      // find in the current environment.
      var obj = context.pop(), base;
      if (obj.type && obj.type.indexOf("variable") === 0) {
        if (options && options.additionalContext)
          base = options.additionalContext[obj.string];
        if (!options || options.useGlobalScope !== false)
          base = base || global[obj.string];
      } else if (obj.type == "string") {
        base = "";
      } else if (obj.type == "atom") {
        base = 1;
      } else if (obj.type == "function") {
        if (global.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
            (typeof global.jQuery == 'function'))
          base = global.jQuery();
        else if (global._ != null && (obj.string == '_') && (typeof global._ == 'function'))
          base = global._();
      }
      while (base != null && context.length)
        base = base[context.pop().string];
      if (base != null) gatherCompletions(base);
    } else {
      // If not, just look in the global object and any local scope
      // (reading into JS mode internals to get at the local and global variables)
      for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
      for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name);
      if (!options || options.useGlobalScope !== false)
        gatherCompletions(global);
      forEach(keywords, maybeAdd);
    }
    return found;
  }
});

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"), require("../../mode/sql/sql"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror", "../../mode/sql/sql"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  "use strict";

  var tables;
  var defaultTable;
  var keywords;
  var identifierQuote;
  var CONS = {
    QUERY_DIV: ";",
    ALIAS_KEYWORD: "AS"
  };
  var Pos = CodeMirror.Pos, cmpPos = CodeMirror.cmpPos;

  function isArray(val) { return Object.prototype.toString.call(val) == "[object Array]" }

  function getKeywords(editor) {
    var mode = editor.doc.modeOption;
    if (mode === "sql") mode = "text/x-sql";
    return CodeMirror.resolveMode(mode).keywords;
  }

  function getIdentifierQuote(editor) {
    var mode = editor.doc.modeOption;
    if (mode === "sql") mode = "text/x-sql";
    return CodeMirror.resolveMode(mode).identifierQuote || "`";
  }

  function getText(item) {
    return typeof item == "string" ? item : item.text;
  }

  function wrapTable(name, value) {
    if (isArray(value)) value = {columns: value}
    if (!value.text) value.text = name
    return value
  }

  function parseTables(input) {
    var result = {}
    if (isArray(input)) {
      for (var i = input.length - 1; i >= 0; i--) {
        var item = input[i]
        result[getText(item).toUpperCase()] = wrapTable(getText(item), item)
      }
    } else if (input) {
      for (var name in input)
        result[name.toUpperCase()] = wrapTable(name, input[name])
    }
    return result
  }

  function getTable(name) {
    return tables[name.toUpperCase()]
  }

  function shallowClone(object) {
    var result = {};
    for (var key in object) if (object.hasOwnProperty(key))
      result[key] = object[key];
    return result;
  }

  function match(string, word) {
    var len = string.length;
    var sub = getText(word).substr(0, len);
    return string.toUpperCase() === sub.toUpperCase();
  }

  function addMatches(result, search, wordlist, formatter) {
    if (isArray(wordlist)) {
      for (var i = 0; i < wordlist.length; i++)
        if (match(search, wordlist[i])) result.push(formatter(wordlist[i]))
    } else {
      for (var word in wordlist) if (wordlist.hasOwnProperty(word)) {
        var val = wordlist[word]
        if (!val || val === true)
          val = word
        else
          val = val.displayText ? {text: val.text, displayText: val.displayText} : val.text
        if (match(search, val)) result.push(formatter(val))
      }
    }
  }

  function cleanName(name) {
    // Get rid name from identifierQuote and preceding dot(.)
    if (name.charAt(0) == ".") {
      name = name.substr(1);
    }
    // replace doublicated identifierQuotes with single identifierQuotes
    // and remove single identifierQuotes
    var nameParts = name.split(identifierQuote+identifierQuote);
    for (var i = 0; i < nameParts.length; i++)
      nameParts[i] = nameParts[i].replace(new RegExp(identifierQuote,"g"), "");
    return nameParts.join(identifierQuote);
  }

  function insertIdentifierQuotes(name) {
    var nameParts = getText(name).split(".");
    for (var i = 0; i < nameParts.length; i++)
      nameParts[i] = identifierQuote +
        // doublicate identifierQuotes
        nameParts[i].replace(new RegExp(identifierQuote,"g"), identifierQuote+identifierQuote) +
        identifierQuote;
    var escaped = nameParts.join(".");
    if (typeof name == "string") return escaped;
    name = shallowClone(name);
    name.text = escaped;
    return name;
  }

  function nameCompletion(cur, token, result, editor) {
    // Try to complete table, column names and return start position of completion
    var useIdentifierQuotes = false;
    var nameParts = [];
    var start = token.start;
    var cont = true;
    while (cont) {
      cont = (token.string.charAt(0) == ".");
      useIdentifierQuotes = useIdentifierQuotes || (token.string.charAt(0) == identifierQuote);

      start = token.start;
      nameParts.unshift(cleanName(token.string));

      token = editor.getTokenAt(Pos(cur.line, token.start));
      if (token.string == ".") {
        cont = true;
        token = editor.getTokenAt(Pos(cur.line, token.start));
      }
    }

    // Try to complete table names
    var string = nameParts.join(".");
    addMatches(result, string, tables, function(w) {
      return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
    });

    // Try to complete columns from defaultTable
    addMatches(result, string, defaultTable, function(w) {
      return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
    });

    // Try to complete columns
    string = nameParts.pop();
    var table = nameParts.join(".");

    var alias = false;
    var aliasTable = table;
    // Check if table is available. If not, find table by Alias
    if (!getTable(table)) {
      var oldTable = table;
      table = findTableByAlias(table, editor);
      if (table !== oldTable) alias = true;
    }

    var columns = getTable(table);
    if (columns && columns.columns)
      columns = columns.columns;

    if (columns) {
      addMatches(result, string, columns, function(w) {
        var tableInsert = table;
        if (alias == true) tableInsert = aliasTable;
        if (typeof w == "string") {
          w = tableInsert + "." + w;
        } else {
          w = shallowClone(w);
          w.text = tableInsert + "." + w.text;
        }
        return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
      });
    }

    return start;
  }

  function eachWord(lineText, f) {
    if (!lineText) return;
    var excepted = /[,;]/g;
    var words = lineText.split(" ");
    for (var i = 0; i < words.length; i++) {
      f(words[i]?words[i].replace(excepted, '') : '');
    }
  }

  function findTableByAlias(alias, editor) {
    var doc = editor.doc;
    var fullQuery = doc.getValue();
    var aliasUpperCase = alias.toUpperCase();
    var previousWord = "";
    var table = "";
    var separator = [];
    var validRange = {
      start: Pos(0, 0),
      end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length)
    };

    //add separator
    var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV);
    while(indexOfSeparator != -1) {
      separator.push(doc.posFromIndex(indexOfSeparator));
      indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1);
    }
    separator.unshift(Pos(0, 0));
    separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length));

    //find valid range
    var prevItem = null;
    var current = editor.getCursor()
    for (var i = 0; i < separator.length; i++) {
      if ((prevItem == null || cmpPos(current, prevItem) > 0) && cmpPos(current, separator[i]) <= 0) {
        validRange = {start: prevItem, end: separator[i]};
        break;
      }
      prevItem = separator[i];
    }

    var query = doc.getRange(validRange.start, validRange.end, false);

    for (var i = 0; i < query.length; i++) {
      var lineText = query[i];
      eachWord(lineText, function(word) {
        var wordUpperCase = word.toUpperCase();
        if (wordUpperCase === aliasUpperCase && getTable(previousWord))
          table = previousWord;
        if (wordUpperCase !== CONS.ALIAS_KEYWORD)
          previousWord = word;
      });
      if (table) break;
    }
    return table;
  }

  CodeMirror.registerHelper("hint", "sql", function(editor, options) {
    tables = parseTables(options && options.tables)
    var defaultTableName = options && options.defaultTable;
    var disableKeywords = options && options.disableKeywords;
    defaultTable = defaultTableName && getTable(defaultTableName);
    keywords = getKeywords(editor);
    identifierQuote = getIdentifierQuote(editor);

    if (defaultTableName && !defaultTable)
      defaultTable = findTableByAlias(defaultTableName, editor);

    defaultTable = defaultTable || [];

    if (defaultTable.columns)
      defaultTable = defaultTable.columns;

    var cur = editor.getCursor();
    var result = [];
    var token = editor.getTokenAt(cur), start, end, search;
    if (token.end > cur.ch) {
      token.end = cur.ch;
      token.string = token.string.slice(0, cur.ch - token.start);
    }

    if (token.string.match(/^[.`"\w@]\w*$/)) {
      search = token.string;
      start = token.start;
      end = token.end;
    } else {
      start = end = cur.ch;
      search = "";
    }
    if (search.charAt(0) == "." || search.charAt(0) == identifierQuote) {
      start = nameCompletion(cur, token, result, editor);
    } else {
      addMatches(result, search, tables, function(w) {return w;});
      addMatches(result, search, defaultTable, function(w) {return w;});
      if (!disableKeywords)
        addMatches(result, search, keywords, function(w) {return w.toUpperCase();});
    }

    return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)};
  });
});

(function () {
  var Pos = CodeMirror.Pos;

  function forEach(arr, f) {
    for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
  }
  
  function arrayContains(arr, item) {
    if (!Array.prototype.indexOf) {
      var i = arr.length;
      while (i--) {
        if (arr[i] === item) {
          return true;
        }
      }
      return false;
    }
    return arr.indexOf(item) != -1;
  }

  function scriptHint(editor, keywords, getToken, options) {
    // Find the token at the cursor
    var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
    var sToken = token.string.trim();

    if ( sToken == "(") {
  token = tprop = getToken(editor, Pos(cur.line, tprop.start));
        return {list: getCompletions(token.string, keywords, options),
                from: Pos(cur.line, token.start),
                to: Pos(cur.line, token.end + 1)};
    }
    if ( sToken == "=") {
        return {list: getCompletions(token.string, keywords, options),
                from: Pos(cur.line, token.start + 1),
                to: Pos(cur.line, token.end)};
    }
    return {list: getCompletions(token.string, keywords, options),
            from: Pos(cur.line, token.start),
            to: Pos(cur.line, token.end)};
  }

  CodeMirror.phpHint = function(editor, options) {
    return scriptHint(editor, phpPMFunctions, function (e, cur) {return e.getTokenAt(cur);}, options);
  };

  var SPACE = " ";
  var arrayFunctions = [];
  
  var formatDate = "formatDate";
  var formatDateFunction = [formatDate+"($date,$format,$language);",formatDate+"($date,$format);"];
  arrayFunctions[formatDate] = formatDateFunction;
  
  var getCurrentDate = "getCurrentDate";
  var getCurrentDateFunction = [getCurrentDate+"()"];
  arrayFunctions[getCurrentDate] = getCurrentDateFunction;
  
  var getCurrentTime = "getCurrentTime";
  var getCurrentTimeFunction = [getCurrentTime+"()"];
  arrayFunctions[getCurrentTime] = getCurrentTimeFunction;
  
  var literalDate = "literalDate";
  var literalDateFunction = [literalDate+"($date,$Language)",literalDate+"($date)"];
  arrayFunctions[literalDate] = literalDateFunction;
  
  var capitalize = "capitalize";
  var capitalizeFunction = [capitalize+"($textToConvert)"];
  arrayFunctions[capitalize] = capitalizeFunction;
  
  var lowerCase = "lowerCase";
  var lowerCaseFunction = [lowerCase+"($textToConvert)"];
  arrayFunctions[lowerCase] = lowerCaseFunction;
  
  var upperCase = "upperCase";
  var upperCaseFunction = [upperCase+"($textToConvert)"];
  arrayFunctions[upperCase] = upperCaseFunction;
  
  var userInfo = "userInfo";
  var userInfoFunction = [userInfo+"($USER_ID)"];
  arrayFunctions[userInfo] = userInfoFunction;
  
  var executeQuery = "executeQuery";
  var executeQueryFunction = [executeQuery+"($sqlStatement,$DBConnectionUID)",executeQuery+"($sqlStatement)"];
  arrayFunctions[executeQuery] = executeQueryFunction;
  
  var orderGrid = "orderGrid";
  var orderGridFunction = ("orderGrid($gridName,$field,$criteria) orderGrid($gridName,$field)").split(SPACE);
  arrayFunctions[orderGrid] = orderGridFunction;
  
  var evaluateFunction = "evaluateFunction";
  var evaluateFunctionFunction = [evaluateFunction+"($gridName,$Expression)"];
  arrayFunctions[evaluateFunction] = evaluateFunctionFunction;
  
  var PMFTotalCalculation = "PMFTotalCalculation";
  var PMFTotalCalculationFunction = [PMFTotalCalculation + "($gridName, $field, 'function')"];
  arrayFunctions[PMFTotalCalculation] = PMFTotalCalculationFunction;

  var PMFTaskCase = "PMFTaskCase";
  var PMFTaskCaseFunction = [PMFTaskCase+"($caseId)"];
  arrayFunctions[PMFTaskCase] = PMFTaskCaseFunction;
  
  var PMFTaskList = "PMFTaskList";
  var PMFTaskListFunction = [PMFTaskList+"($userId)"];
  arrayFunctions[PMFTaskList] = PMFTaskListFunction;
  
  var PMFUserList = "PMFUserList";
  var PMFUserListFunction = [PMFUserList+"()"];
  arrayFunctions[PMFUserList] = PMFUserListFunction;
  
  var PMFGroupList = "PMFGroupList";
  var PMFGroupListFunction = [PMFGroupList+"()"];
  arrayFunctions[PMFGroupList] = PMFGroupListFunction;
  
  var PMFRoleList = "PMFRoleList";
  var PMFRoleListFunction = [PMFRoleList+"()"];
  arrayFunctions[PMFRoleList] = PMFRoleListFunction;
  
  var PMFCaseList = "PMFCaseList";
  var PMFCaseListFunction = [PMFCaseList+"($userId)",PMFCaseList+"()"];
  arrayFunctions[PMFCaseList] = PMFCaseListFunction;
  
  var PMFProcessList = "PMFProcessList";
  var PMFProcessListFunction = [PMFProcessList+"()"];
  arrayFunctions[PMFProcessList] = PMFProcessListFunction;
  
  var PMFSendVariables = "PMFSendVariables";
  var PMFSendVariablesFunction = [PMFSendVariables+"($caseId,$variables)"];
  arrayFunctions[PMFSendVariables] = PMFSendVariablesFunction;
  
  var PMFDerivateCase = "PMFDerivateCase";
  var PMFDerivateCaseFunction = [PMFDerivateCase+"($caseId,$delegation,$executeTriggersBeforeAssigment)",PMFDerivateCase+"($caseId,$delegation)"];
  arrayFunctions[PMFDerivateCase] = PMFDerivateCaseFunction;
  
  var PMFNewCaseImpersonate = "PMFNewCaseImpersonate";
  var PMFNewCaseImpersonateFunction = [PMFNewCaseImpersonate+"($processId,$userId,$variables)"];
  arrayFunctions[PMFNewCaseImpersonate] = PMFNewCaseImpersonateFunction;
  
  var PMFNewCase = "PMFNewCase";
  var PMFNewCaseFunction = [PMFNewCase+"($processId,$userId,$taskId,$variables)"];
  arrayFunctions[PMFNewCase] = PMFNewCaseFunction;
  
  var PMFPauseCase = "PMFPauseCase";
  var PMFPauseCaseFunction = [PMFPauseCase+"($caseUid,$delIndex,$userUid,$unpauseDate)",PMFPauseCase+"($caseUid,$delIndex,$userUid)"];
  arrayFunctions[PMFPauseCase] = PMFPauseCaseFunction;
  
  var PMFAssignUserToGroup = "PMFAssignUserToGroup";
  var PMFAssignUserToGroupFunction = [PMFAssignUserToGroup+"($userId,$groupId)"];
  arrayFunctions[PMFAssignUserToGroup] = PMFAssignUserToGroupFunction;
  
  var PMFCreateUser = "PMFCreateUser";
  var PMFCreateUserFunction = [PMFCreateUser+"($userId,$password,$firstname,$lastname,$email,$role)"];
  arrayFunctions[PMFCreateUser] = PMFCreateUserFunction;
  
  var PMFUpdateUser = "PMFUpdateUser";
  var PMFUpdateUserFunction = [PMFUpdateUser+"($userUid,$userName,$firstName,$lastName,$email,$dueDate,$status,$role,$password)"];
  arrayFunctions[PMFUpdateUser] = PMFUpdateUserFunction;
  
  var PMFInformationUser = "PMFInformationUser";
  var PMFInformationUserFunction = [PMFInformationUser+"($userUid)"];
  arrayFunctions[PMFInformationUser] = PMFInformationUserFunction;
  
  var generateCode = "generateCode";
  var generateCodeFunction = [generateCode+"($size,$type)"];
  arrayFunctions[generateCode] = generateCodeFunction;
  
  var setCaseTrackerCode = "setCaseTrackerCode";
  var setCaseTrackerCodeFunction = [setCaseTrackerCode+"($caseId,$code,$pin)"];
  arrayFunctions[setCaseTrackerCode] = setCaseTrackerCodeFunction;
  
  var jumping = "jumping";
  var jumpingFunction = [jumping+"($caseId,$delegation)"];
  arrayFunctions[jumping] = jumpingFunction;
   
  var PMFRedirectToStep = "PMFRedirectToStep";
  var PMFRedirectToStepFunction = [PMFRedirectToStep+"($caseId,$delegation,$stepType,$stepId)"];
  arrayFunctions[PMFRedirectToStep] = PMFRedirectToStepFunction;
  
  var pauseCase = "pauseCase";
  var pauseCaseFunction = [pauseCase+"($caseId,$delegation,$userId,$unpauseDate)",pauseCase+"($caseId,$delegation,$userId)"];
  arrayFunctions[pauseCase] = pauseCaseFunction;
  
  var PMFUnpauseCase = "PMFUnpauseCase";
  var PMFUnpauseCaseFunction = [PMFUnpauseCase+"($caseId,$delegation,$userId,$unpauseDate)",PMFUnpauseCase+"($caseId,$delegation,$userId)"];
  arrayFunctions[PMFUnpauseCase] = PMFUnpauseCaseFunction;

  var PMFSendMessage = "PMFSendMessage";
  var PMFSendMessageFunction = [PMFSendMessage+"($caseId,$from,$to,$cc,$bcc,$subject,$template,$fields,$attachments)",PMFSendMessage+"($caseId,$from,$to,$cc,$bcc,$subject,$template,$fields)",PMFSendMessage+"($caseId,$from,$to,$cc,$bcc,$subject,$template)"];
  arrayFunctions[PMFSendMessage] = PMFSendMessageFunction;
  
  var PMFgetLabelOption = "PMFgetLabelOption";
  var PMFgetLabelOptionFunction = [PMFgetLabelOption+"($processId,$dynaformId,$fieldName,$optionId)"];
  arrayFunctions[PMFgetLabelOption] = PMFgetLabelOptionFunction;
  
  var PMFGenerateOutputDocument = "PMFGenerateOutputDocument";
  var PMFGenerateOutputDocumentFunction = [PMFGenerateOutputDocument+"($outputID)"];
  arrayFunctions[PMFGenerateOutputDocument] = PMFGenerateOutputDocumentFunction;
  
  var PMFGetUserEmailAddress = "PMFGetUserEmailAddress";
  var PMFGetUserEmailAddressFunction = [PMFGetUserEmailAddress+"($id,$APP_UID,$prefix)",PMFGetUserEmailAddress+"($id,$APP_UID)",PMFGetUserEmailAddress+"($id)"];
  arrayFunctions[PMFGetUserEmailAddress] = PMFGetUserEmailAddressFunction;
  
  var PMFGetNextAssignedUser = "PMFGetNextAssignedUser";
  var PMFGetNextAssignedUserFunction = (PMFGetNextAssignedUser+"($application,$task)").split(SPACE);
  arrayFunctions[PMFGetNextAssignedUser] = PMFGetNextAssignedUserFunction;
  
  var PMFDeleteCase = "PMFDeleteCase";
  var PMFDeleteCaseFunction = ("PMFDeleteCase($caseId)").split(SPACE);
  arrayFunctions[PMFDeleteCase] = PMFDeleteCaseFunction;
  
  var PMFCancelCase = "PMFCancelCase";
  var PMFCancelCaseFunction = [PMFCancelCase+"($caseUid,$delIndex,$userUid)"];
  arrayFunctions[PMFCancelCase] = PMFCancelCaseFunction;
  
  var PMFAddInputDocument = "PMFAddInputDocument";
  var PMFAddInputDocumentFunction = [PMFAddInputDocument+"($inputDocumentUid,$appDocUid,$docVersion,$appDocType,$appDocComment,$inputDocumentAction,$caseUid,$delIndex,$taskUid,$userUid,$option,$file)",PMFAddInputDocument+"($inputDocumentUid,$appDocUid,$docVersion,$appDocType,$appDocComment,$inputDocumentAction,$caseUid,$delIndex,$taskUid,$userUid,$option)",PMFAddInputDocument+"($inputDocumentUid,$appDocUid,$docVersion,$appDocType,$appDocComment,$inputDocumentAction,$caseUid,$delIndex,$taskUid,$userUid)"];
  arrayFunctions[PMFAddInputDocument] = PMFAddInputDocumentFunction;
  
  var PMFAddCaseNote = "PMFAddCaseNote";
  var PMFAddCaseNoteFunction = [PMFAddCaseNote+"($caseUid,$processUid,$taskUid,$userUid,$note,$sendMail)"];
  arrayFunctions[PMFAddCaseNote] = PMFAddCaseNoteFunction;
  
  var PMFGetCaseNotes = "PMFGetCaseNotes";
  var PMFGetCaseNotesFunction = [PMFGetCaseNotes+"($applicationID,$type,$userUid);",PMFGetCaseNotes+"($applicationID,$type)",PMFGetCaseNotes+"($applicationID)"];
  arrayFunctions[PMFGetCaseNotes] = PMFGetCaseNotesFunction;
  
  var phpPMFunctions = [formatDate,getCurrentDate,getCurrentTime,literalDate,capitalize,lowerCase,upperCase,userInfo,executeQuery,orderGrid,
  evaluateFunction, PMFTotalCalculation, PMFTaskCase,PMFTaskList,PMFUserList,PMFGroupList,PMFRoleList,PMFCaseList,PMFProcessList,PMFSendVariables,PMFDerivateCase,
  PMFNewCaseImpersonate,PMFNewCase,PMFPauseCase,PMFUnpauseCase,PMFAssignUserToGroup,PMFCreateUser,PMFUpdateUser,PMFInformationUser,        
  generateCode,setCaseTrackerCode,jumping,PMFRedirectToStep,pauseCase,PMFSendMessage,PMFgetLabelOption,PMFGenerateOutputDocument,   
  PMFGetUserEmailAddress,PMFGetNextAssignedUser,PMFDeleteCase,PMFCancelCase,PMFAddInputDocument,PMFAddCaseNote,PMFGetCaseNotes];

  var phpKeywords = ("break case catch continue default do else false for function " +
                  "if new return switch throw true try var while").split(SPACE);

  function getCompletions(functionName, keywords, options) {
    
    var found = [];

    function maybeAdd(str) {// for keywords ?
      if ( str.indexOf(functionName) == 0 && !arrayContains(found, str)) {
         found.push(str);
      }
    }

    function yesAdd(str) {
      if ( !arrayContains(found, str)) {
         found.push(str);
      }
    }
    
    arrayFunction = arrayFunctions[functionName];
  
    if (arrayFunction != undefined) {
      forEach( arrayFunction, yesAdd);
    } else {
      if (functionName.trim() == "") {
      forEach (phpKeywords, yesAdd);
    forEach (keywords, yesAdd);
      } else if (functionName == "=") {
    forEach (phpPMFunctions, yesAdd);
      } else {
    for (index = 0; index < phpKeywords.length; index++) {
        if ( phpKeywords[index].indexOf(functionName) == 0 ) {
            found.push(phpKeywords[index]);
        }
    } 
    forEach(keywords, maybeAdd);
      }
    }
    return found;
  }
})();

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"), require("./xml-hint"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror", "./xml-hint"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  "use strict";

  var langs = "ab aa af ak sq am ar an hy as av ae ay az bm ba eu be bn bh bi bs br bg my ca ch ce ny zh cv kw co cr hr cs da dv nl dz en eo et ee fo fj fi fr ff gl ka de el gn gu ht ha he hz hi ho hu ia id ie ga ig ik io is it iu ja jv kl kn kr ks kk km ki rw ky kv kg ko ku kj la lb lg li ln lo lt lu lv gv mk mg ms ml mt mi mr mh mn na nv nb nd ne ng nn no ii nr oc oj cu om or os pa pi fa pl ps pt qu rm rn ro ru sa sc sd se sm sg sr gd sn si sk sl so st es su sw ss sv ta te tg th ti bo tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa cy wo fy xh yi yo za zu".split(" ");
  var targets = ["_blank", "_self", "_top", "_parent"];
  var charsets = ["ascii", "utf-8", "utf-16", "latin1", "latin1"];
  var methods = ["get", "post", "put", "delete"];
  var encs = ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"];
  var media = ["all", "screen", "print", "embossed", "braille", "handheld", "print", "projection", "screen", "tty", "tv", "speech",
               "3d-glasses", "resolution [>][<][=] [X]", "device-aspect-ratio: X/Y", "orientation:portrait",
               "orientation:landscape", "device-height: [X]", "device-width: [X]"];
  var s = { attrs: {} }; // Simple tag, reused for a whole lot of tags

  var data = {
    a: {
      attrs: {
        href: null, ping: null, type: null,
        media: media,
        target: targets,
        hreflang: langs
      }
    },
    abbr: s,
    acronym: s,
    address: s,
    applet: s,
    area: {
      attrs: {
        alt: null, coords: null, href: null, target: null, ping: null,
        media: media, hreflang: langs, type: null,
        shape: ["default", "rect", "circle", "poly"]
      }
    },
    article: s,
    aside: s,
    audio: {
      attrs: {
        src: null, mediagroup: null,
        crossorigin: ["anonymous", "use-credentials"],
        preload: ["none", "metadata", "auto"],
        autoplay: ["", "autoplay"],
        loop: ["", "loop"],
        controls: ["", "controls"]
      }
    },
    b: s,
    base: { attrs: { href: null, target: targets } },
    basefont: s,
    bdi: s,
    bdo: s,
    big: s,
    blockquote: { attrs: { cite: null } },
    body: s,
    br: s,
    button: {
      attrs: {
        form: null, formaction: null, name: null, value: null,
        autofocus: ["", "autofocus"],
        disabled: ["", "autofocus"],
        formenctype: encs,
        formmethod: methods,
        formnovalidate: ["", "novalidate"],
        formtarget: targets,
        type: ["submit", "reset", "button"]
      }
    },
    canvas: { attrs: { width: null, height: null } },
    caption: s,
    center: s,
    cite: s,
    code: s,
    col: { attrs: { span: null } },
    colgroup: { attrs: { span: null } },
    command: {
      attrs: {
        type: ["command", "checkbox", "radio"],
        label: null, icon: null, radiogroup: null, command: null, title: null,
        disabled: ["", "disabled"],
        checked: ["", "checked"]
      }
    },
    data: { attrs: { value: null } },
    datagrid: { attrs: { disabled: ["", "disabled"], multiple: ["", "multiple"] } },
    datalist: { attrs: { data: null } },
    dd: s,
    del: { attrs: { cite: null, datetime: null } },
    details: { attrs: { open: ["", "open"] } },
    dfn: s,
    dir: s,
    div: s,
    dl: s,
    dt: s,
    em: s,
    embed: { attrs: { src: null, type: null, width: null, height: null } },
    eventsource: { attrs: { src: null } },
    fieldset: { attrs: { disabled: ["", "disabled"], form: null, name: null } },
    figcaption: s,
    figure: s,
    font: s,
    footer: s,
    form: {
      attrs: {
        action: null, name: null,
        "accept-charset": charsets,
        autocomplete: ["on", "off"],
        enctype: encs,
        method: methods,
        novalidate: ["", "novalidate"],
        target: targets
      }
    },
    frame: s,
    frameset: s,
    h1: s, h2: s, h3: s, h4: s, h5: s, h6: s,
    head: {
      attrs: {},
      children: ["title", "base", "link", "style", "meta", "script", "noscript", "command"]
    },
    header: s,
    hgroup: s,
    hr: s,
    html: {
      attrs: { manifest: null },
      children: ["head", "body"]
    },
    i: s,
    iframe: {
      attrs: {
        src: null, srcdoc: null, name: null, width: null, height: null,
        sandbox: ["allow-top-navigation", "allow-same-origin", "allow-forms", "allow-scripts"],
        seamless: ["", "seamless"]
      }
    },
    img: {
      attrs: {
        alt: null, src: null, ismap: null, usemap: null, width: null, height: null,
        crossorigin: ["anonymous", "use-credentials"]
      }
    },
    input: {
      attrs: {
        alt: null, dirname: null, form: null, formaction: null,
        height: null, list: null, max: null, maxlength: null, min: null,
        name: null, pattern: null, placeholder: null, size: null, src: null,
        step: null, value: null, width: null,
        accept: ["audio/*", "video/*", "image/*"],
        autocomplete: ["on", "off"],
        autofocus: ["", "autofocus"],
        checked: ["", "checked"],
        disabled: ["", "disabled"],
        formenctype: encs,
        formmethod: methods,
        formnovalidate: ["", "novalidate"],
        formtarget: targets,
        multiple: ["", "multiple"],
        readonly: ["", "readonly"],
        required: ["", "required"],
        type: ["hidden", "text", "search", "tel", "url", "email", "password", "datetime", "date", "month",
               "week", "time", "datetime-local", "number", "range", "color", "checkbox", "radio",
               "file", "submit", "image", "reset", "button"]
      }
    },
    ins: { attrs: { cite: null, datetime: null } },
    kbd: s,
    keygen: {
      attrs: {
        challenge: null, form: null, name: null,
        autofocus: ["", "autofocus"],
        disabled: ["", "disabled"],
        keytype: ["RSA"]
      }
    },
    label: { attrs: { "for": null, form: null } },
    legend: s,
    li: { attrs: { value: null } },
    link: {
      attrs: {
        href: null, type: null,
        hreflang: langs,
        media: media,
        sizes: ["all", "16x16", "16x16 32x32", "16x16 32x32 64x64"]
      }
    },
    map: { attrs: { name: null } },
    mark: s,
    menu: { attrs: { label: null, type: ["list", "context", "toolbar"] } },
    meta: {
      attrs: {
        content: null,
        charset: charsets,
        name: ["viewport", "application-name", "author", "description", "generator", "keywords"],
        "http-equiv": ["content-language", "content-type", "default-style", "refresh"]
      }
    },
    meter: { attrs: { value: null, min: null, low: null, high: null, max: null, optimum: null } },
    nav: s,
    noframes: s,
    noscript: s,
    object: {
      attrs: {
        data: null, type: null, name: null, usemap: null, form: null, width: null, height: null,
        typemustmatch: ["", "typemustmatch"]
      }
    },
    ol: { attrs: { reversed: ["", "reversed"], start: null, type: ["1", "a", "A", "i", "I"] } },
    optgroup: { attrs: { disabled: ["", "disabled"], label: null } },
    option: { attrs: { disabled: ["", "disabled"], label: null, selected: ["", "selected"], value: null } },
    output: { attrs: { "for": null, form: null, name: null } },
    p: s,
    param: { attrs: { name: null, value: null } },
    pre: s,
    progress: { attrs: { value: null, max: null } },
    q: { attrs: { cite: null } },
    rp: s,
    rt: s,
    ruby: s,
    s: s,
    samp: s,
    script: {
      attrs: {
        type: ["text/javascript"],
        src: null,
        async: ["", "async"],
        defer: ["", "defer"],
        charset: charsets
      }
    },
    section: s,
    select: {
      attrs: {
        form: null, name: null, size: null,
        autofocus: ["", "autofocus"],
        disabled: ["", "disabled"],
        multiple: ["", "multiple"]
      }
    },
    small: s,
    source: { attrs: { src: null, type: null, media: null } },
    span: s,
    strike: s,
    strong: s,
    style: {
      attrs: {
        type: ["text/css"],
        media: media,
        scoped: null
      }
    },
    sub: s,
    summary: s,
    sup: s,
    table: s,
    tbody: s,
    td: { attrs: { colspan: null, rowspan: null, headers: null } },
    textarea: {
      attrs: {
        dirname: null, form: null, maxlength: null, name: null, placeholder: null,
        rows: null, cols: null,
        autofocus: ["", "autofocus"],
        disabled: ["", "disabled"],
        readonly: ["", "readonly"],
        required: ["", "required"],
        wrap: ["soft", "hard"]
      }
    },
    tfoot: s,
    th: { attrs: { colspan: null, rowspan: null, headers: null, scope: ["row", "col", "rowgroup", "colgroup"] } },
    thead: s,
    time: { attrs: { datetime: null } },
    title: s,
    tr: s,
    track: {
      attrs: {
        src: null, label: null, "default": null,
        kind: ["subtitles", "captions", "descriptions", "chapters", "metadata"],
        srclang: langs
      }
    },
    tt: s,
    u: s,
    ul: s,
    "var": s,
    video: {
      attrs: {
        src: null, poster: null, width: null, height: null,
        crossorigin: ["anonymous", "use-credentials"],
        preload: ["auto", "metadata", "none"],
        autoplay: ["", "autoplay"],
        mediagroup: ["movie"],
        muted: ["", "muted"],
        controls: ["", "controls"]
      }
    },
    wbr: s
  };

  var globalAttrs = {
    accesskey: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
    "class": null,
    contenteditable: ["true", "false"],
    contextmenu: null,
    dir: ["ltr", "rtl", "auto"],
    draggable: ["true", "false", "auto"],
    dropzone: ["copy", "move", "link", "string:", "file:"],
    hidden: ["hidden"],
    id: null,
    inert: ["inert"],
    itemid: null,
    itemprop: null,
    itemref: null,
    itemscope: ["itemscope"],
    itemtype: null,
    lang: ["en", "es"],
    spellcheck: ["true", "false"],
    style: null,
    tabindex: ["1", "2", "3", "4", "5", "6", "7", "8", "9"],
    title: null,
    translate: ["yes", "no"],
    onclick: null,
    rel: ["stylesheet", "alternate", "author", "bookmark", "help", "license", "next", "nofollow", "noreferrer", "prefetch", "prev", "search", "tag"]
  };
  function populate(obj) {
    for (var attr in globalAttrs) if (globalAttrs.hasOwnProperty(attr))
      obj.attrs[attr] = globalAttrs[attr];
  }

  populate(s);
  for (var tag in data) if (data.hasOwnProperty(tag) && data[tag] != s)
    populate(data[tag]);

  CodeMirror.htmlSchema = data;
  function htmlHint(cm, options) {
    var local = {schemaInfo: data};
    if (options) for (var opt in options) local[opt] = options[opt];
    return CodeMirror.hint.xml(cm, local);
  }
  CodeMirror.registerHelper("hint", "html", htmlHint);
});

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
"use strict";

function expressionAllowed(stream, state, backUp) {
  return /^(?:operator|sof|keyword c|case|new|export|default|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
    (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
}

CodeMirror.defineMode("javascript", function(config, parserConfig) {
  var indentUnit = config.indentUnit;
  var statementIndent = parserConfig.statementIndent;
  var jsonldMode = parserConfig.jsonld;
  var jsonMode = parserConfig.json || jsonldMode;
  var isTS = parserConfig.typescript;
  var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;

  // Tokenizer

  var keywords = function(){
    function kw(type) {return {type: type, style: "keyword"};}
    var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
    var operator = kw("operator"), atom = {type: "atom", style: "atom"};

    var jsKeywords = {
      "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
      "return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C,
      "var": kw("var"), "const": kw("var"), "let": kw("var"),
      "function": kw("function"), "catch": kw("catch"),
      "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
      "in": operator, "typeof": operator, "instanceof": operator,
      "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
      "this": kw("this"), "class": kw("class"), "super": kw("atom"),
      "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
      "await": C, "async": kw("async")
    };

    // Extend the 'normal' keywords with the TypeScript language extensions
    if (isTS) {
      var type = {type: "variable", style: "variable-3"};
      var tsKeywords = {
        // object-like things
        "interface": kw("class"),
        "implements": C,
        "namespace": C,
        "module": kw("module"),
        "enum": kw("module"),
        "type": kw("type"),

        // scope modifiers
        "public": kw("modifier"),
        "private": kw("modifier"),
        "protected": kw("modifier"),
        "abstract": kw("modifier"),

        // operators
        "as": operator,

        // types
        "string": type, "number": type, "boolean": type, "any": type
      };

      for (var attr in tsKeywords) {
        jsKeywords[attr] = tsKeywords[attr];
      }
    }

    return jsKeywords;
  }();

  var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
  var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;

  function readRegexp(stream) {
    var escaped = false, next, inSet = false;
    while ((next = stream.next()) != null) {
      if (!escaped) {
        if (next == "/" && !inSet) return;
        if (next == "[") inSet = true;
        else if (inSet && next == "]") inSet = false;
      }
      escaped = !escaped && next == "\\";
    }
  }

  // Used as scratch variables to communicate multiple values without
  // consing up tons of objects.
  var type, content;
  function ret(tp, style, cont) {
    type = tp; content = cont;
    return style;
  }
  function tokenBase(stream, state) {
    var ch = stream.next();
    if (ch == '"' || ch == "'") {
      state.tokenize = tokenString(ch);
      return state.tokenize(stream, state);
    } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
      return ret("number", "number");
    } else if (ch == "." && stream.match("..")) {
      return ret("spread", "meta");
    } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
      return ret(ch);
    } else if (ch == "=" && stream.eat(">")) {
      return ret("=>", "operator");
    } else if (ch == "0" && stream.eat(/x/i)) {
      stream.eatWhile(/[\da-f]/i);
      return ret("number", "number");
    } else if (ch == "0" && stream.eat(/o/i)) {
      stream.eatWhile(/[0-7]/i);
      return ret("number", "number");
    } else if (ch == "0" && stream.eat(/b/i)) {
      stream.eatWhile(/[01]/i);
      return ret("number", "number");
    } else if (/\d/.test(ch)) {
      stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
      return ret("number", "number");
    } else if (ch == "/") {
      if (stream.eat("*")) {
        state.tokenize = tokenComment;
        return tokenComment(stream, state);
      } else if (stream.eat("/")) {
        stream.skipToEnd();
        return ret("comment", "comment");
      } else if (expressionAllowed(stream, state, 1)) {
        readRegexp(stream);
        stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
        return ret("regexp", "string-2");
      } else {
        stream.eatWhile(isOperatorChar);
        return ret("operator", "operator", stream.current());
      }
    } else if (ch == "`") {
      state.tokenize = tokenQuasi;
      return tokenQuasi(stream, state);
    } else if (ch == "#") {
      stream.skipToEnd();
      return ret("error", "error");
    } else if (isOperatorChar.test(ch)) {
      if (ch != ">" || !state.lexical || state.lexical.type != ">")
        stream.eatWhile(isOperatorChar);
      return ret("operator", "operator", stream.current());
    } else if (wordRE.test(ch)) {
      stream.eatWhile(wordRE);
      var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
      return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
                     ret("variable", "variable", word);
    }
  }

  function tokenString(quote) {
    return function(stream, state) {
      var escaped = false, next;
      if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
        state.tokenize = tokenBase;
        return ret("jsonld-keyword", "meta");
      }
      while ((next = stream.next()) != null) {
        if (next == quote && !escaped) break;
        escaped = !escaped && next == "\\";
      }
      if (!escaped) state.tokenize = tokenBase;
      return ret("string", "string");
    };
  }

  function tokenComment(stream, state) {
    var maybeEnd = false, ch;
    while (ch = stream.next()) {
      if (ch == "/" && maybeEnd) {
        state.tokenize = tokenBase;
        break;
      }
      maybeEnd = (ch == "*");
    }
    return ret("comment", "comment");
  }

  function tokenQuasi(stream, state) {
    var escaped = false, next;
    while ((next = stream.next()) != null) {
      if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
        state.tokenize = tokenBase;
        break;
      }
      escaped = !escaped && next == "\\";
    }
    return ret("quasi", "string-2", stream.current());
  }

  var brackets = "([{}])";
  // This is a crude lookahead trick to try and notice that we're
  // parsing the argument patterns for a fat-arrow function before we
  // actually hit the arrow token. It only works if the arrow is on
  // the same line as the arguments and there's no strange noise
  // (comments) in between. Fallback is to only notice when we hit the
  // arrow, and not declare the arguments as locals for the arrow
  // body.
  function findFatArrow(stream, state) {
    if (state.fatArrowAt) state.fatArrowAt = null;
    var arrow = stream.string.indexOf("=>", stream.start);
    if (arrow < 0) return;

    if (isTS) { // Try to skip TypeScript return type declarations after the arguments
      var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
      if (m) arrow = m.index
    }

    var depth = 0, sawSomething = false;
    for (var pos = arrow - 1; pos >= 0; --pos) {
      var ch = stream.string.charAt(pos);
      var bracket = brackets.indexOf(ch);
      if (bracket >= 0 && bracket < 3) {
        if (!depth) { ++pos; break; }
        if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
      } else if (bracket >= 3 && bracket < 6) {
        ++depth;
      } else if (wordRE.test(ch)) {
        sawSomething = true;
      } else if (/["'\/]/.test(ch)) {
        return;
      } else if (sawSomething && !depth) {
        ++pos;
        break;
      }
    }
    if (sawSomething && !depth) state.fatArrowAt = pos;
  }

  // Parser

  var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};

  function JSLexical(indented, column, type, align, prev, info) {
    this.indented = indented;
    this.column = column;
    this.type = type;
    this.prev = prev;
    this.info = info;
    if (align != null) this.align = align;
  }

  function inScope(state, varname) {
    for (var v = state.localVars; v; v = v.next)
      if (v.name == varname) return true;
    for (var cx = state.context; cx; cx = cx.prev) {
      for (var v = cx.vars; v; v = v.next)
        if (v.name == varname) return true;
    }
  }

  function parseJS(state, style, type, content, stream) {
    var cc = state.cc;
    // Communicate our context to the combinators.
    // (Less wasteful than consing up a hundred closures on every call.)
    cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;

    if (!state.lexical.hasOwnProperty("align"))
      state.lexical.align = true;

    while(true) {
      var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
      if (combinator(type, content)) {
        while(cc.length && cc[cc.length - 1].lex)
          cc.pop()();
        if (cx.marked) return cx.marked;
        if (type == "variable" && inScope(state, content)) return "variable-2";
        return style;
      }
    }
  }

  // Combinator utils

  var cx = {state: null, column: null, marked: null, cc: null};
  function pass() {
    for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
  }
  function cont() {
    pass.apply(null, arguments);
    return true;
  }
  function register(varname) {
    function inList(list) {
      for (var v = list; v; v = v.next)
        if (v.name == varname) return true;
      return false;
    }
    var state = cx.state;
    cx.marked = "def";
    if (state.context) {
      if (inList(state.localVars)) return;
      state.localVars = {name: varname, next: state.localVars};
    } else {
      if (inList(state.globalVars)) return;
      if (parserConfig.globalVars)
        state.globalVars = {name: varname, next: state.globalVars};
    }
  }

  // Combinators

  var defaultVars = {name: "this", next: {name: "arguments"}};
  function pushcontext() {
    cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
    cx.state.localVars = defaultVars;
  }
  function popcontext() {
    cx.state.localVars = cx.state.context.vars;
    cx.state.context = cx.state.context.prev;
  }
  function pushlex(type, info) {
    var result = function() {
      var state = cx.state, indent = state.indented;
      if (state.lexical.type == "stat") indent = state.lexical.indented;
      else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
        indent = outer.indented;
      state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
    };
    result.lex = true;
    return result;
  }
  function poplex() {
    var state = cx.state;
    if (state.lexical.prev) {
      if (state.lexical.type == ")")
        state.indented = state.lexical.indented;
      state.lexical = state.lexical.prev;
    }
  }
  poplex.lex = true;

  function expect(wanted) {
    function exp(type) {
      if (type == wanted) return cont();
      else if (wanted == ";") return pass();
      else return cont(exp);
    };
    return exp;
  }

  function statement(type, value) {
    if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
    if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
    if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
    if (type == "{") return cont(pushlex("}"), block, poplex);
    if (type == ";") return cont();
    if (type == "if") {
      if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
        cx.state.cc.pop()();
      return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
    }
    if (type == "function") return cont(functiondef);
    if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
    if (type == "variable") return cont(pushlex("stat"), maybelabel);
    if (type == "switch") return cont(pushlex("form"), parenExpr, pushlex("}", "switch"), expect("{"),
                                      block, poplex, poplex);
    if (type == "case") return cont(expression, expect(":"));
    if (type == "default") return cont(expect(":"));
    if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
                                     statement, poplex, popcontext);
    if (type == "class") return cont(pushlex("form"), className, poplex);
    if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
    if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
    if (type == "module") return cont(pushlex("form"), pattern, pushlex("}"), expect("{"), block, poplex, poplex)
    if (type == "type") return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
    if (type == "async") return cont(statement)
    if (value == "@") return cont(expression, statement)
    return pass(pushlex("stat"), expression, expect(";"), poplex);
  }
  function expression(type) {
    return expressionInner(type, false);
  }
  function expressionNoComma(type) {
    return expressionInner(type, true);
  }
  function parenExpr(type) {
    if (type != "(") return pass()
    return cont(pushlex(")"), expression, expect(")"), poplex)
  }
  function expressionInner(type, noComma) {
    if (cx.state.fatArrowAt == cx.stream.start) {
      var body = noComma ? arrowBodyNoComma : arrowBody;
      if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
      else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
    }

    var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
    if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
    if (type == "function") return cont(functiondef, maybeop);
    if (type == "class") return cont(pushlex("form"), classExpression, poplex);
    if (type == "keyword c" || type == "async") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
    if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
    if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
    if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
    if (type == "{") return contCommasep(objprop, "}", null, maybeop);
    if (type == "quasi") return pass(quasi, maybeop);
    if (type == "new") return cont(maybeTarget(noComma));
    return cont();
  }
  function maybeexpression(type) {
    if (type.match(/[;\}\)\],]/)) return pass();
    return pass(expression);
  }
  function maybeexpressionNoComma(type) {
    if (type.match(/[;\}\)\],]/)) return pass();
    return pass(expressionNoComma);
  }

  function maybeoperatorComma(type, value) {
    if (type == ",") return cont(expression);
    return maybeoperatorNoComma(type, value, false);
  }
  function maybeoperatorNoComma(type, value, noComma) {
    var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
    var expr = noComma == false ? expression : expressionNoComma;
    if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
    if (type == "operator") {
      if (/\+\+|--/.test(value)) return cont(me);
      if (value == "?") return cont(expression, expect(":"), expr);
      return cont(expr);
    }
    if (type == "quasi") { return pass(quasi, me); }
    if (type == ";") return;
    if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
    if (type == ".") return cont(property, me);
    if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
  }
  function quasi(type, value) {
    if (type != "quasi") return pass();
    if (value.slice(value.length - 2) != "${") return cont(quasi);
    return cont(expression, continueQuasi);
  }
  function continueQuasi(type) {
    if (type == "}") {
      cx.marked = "string-2";
      cx.state.tokenize = tokenQuasi;
      return cont(quasi);
    }
  }
  function arrowBody(type) {
    findFatArrow(cx.stream, cx.state);
    return pass(type == "{" ? statement : expression);
  }
  function arrowBodyNoComma(type) {
    findFatArrow(cx.stream, cx.state);
    return pass(type == "{" ? statement : expressionNoComma);
  }
  function maybeTarget(noComma) {
    return function(type) {
      if (type == ".") return cont(noComma ? targetNoComma : target);
      else return pass(noComma ? expressionNoComma : expression);
    };
  }
  function target(_, value) {
    if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
  }
  function targetNoComma(_, value) {
    if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
  }
  function maybelabel(type) {
    if (type == ":") return cont(poplex, statement);
    return pass(maybeoperatorComma, expect(";"), poplex);
  }
  function property(type) {
    if (type == "variable") {cx.marked = "property"; return cont();}
  }
  function objprop(type, value) {
    if (type == "async") {
      cx.marked = "property";
      return cont(objprop);
    } else if (type == "variable" || cx.style == "keyword") {
      cx.marked = "property";
      if (value == "get" || value == "set") return cont(getterSetter);
      return cont(afterprop);
    } else if (type == "number" || type == "string") {
      cx.marked = jsonldMode ? "property" : (cx.style + " property");
      return cont(afterprop);
    } else if (type == "jsonld-keyword") {
      return cont(afterprop);
    } else if (type == "modifier") {
      return cont(objprop)
    } else if (type == "[") {
      return cont(expression, expect("]"), afterprop);
    } else if (type == "spread") {
      return cont(expression);
    } else if (type == ":") {
      return pass(afterprop)
    }
  }
  function getterSetter(type) {
    if (type != "variable") return pass(afterprop);
    cx.marked = "property";
    return cont(functiondef);
  }
  function afterprop(type) {
    if (type == ":") return cont(expressionNoComma);
    if (type == "(") return pass(functiondef);
  }
  function commasep(what, end, sep) {
    function proceed(type, value) {
      if (sep ? sep.indexOf(type) > -1 : type == ",") {
        var lex = cx.state.lexical;
        if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
        return cont(function(type, value) {
          if (type == end || value == end) return pass()
          return pass(what)
        }, proceed);
      }
      if (type == end || value == end) return cont();
      return cont(expect(end));
    }
    return function(type, value) {
      if (type == end || value == end) return cont();
      return pass(what, proceed);
    };
  }
  function contCommasep(what, end, info) {
    for (var i = 3; i < arguments.length; i++)
      cx.cc.push(arguments[i]);
    return cont(pushlex(end, info), commasep(what, end), poplex);
  }
  function block(type) {
    if (type == "}") return cont();
    return pass(statement, block);
  }
  function maybetype(type, value) {
    if (isTS) {
      if (type == ":") return cont(typeexpr);
      if (value == "?") return cont(maybetype);
    }
  }
  function typeexpr(type) {
    if (type == "variable") {cx.marked = "variable-3"; return cont(afterType);}
    if (type == "string" || type == "number" || type == "atom") return cont(afterType);
    if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex)
    if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType)
  }
  function maybeReturnType(type) {
    if (type == "=>") return cont(typeexpr)
  }
  function typeprop(type, value) {
    if (type == "variable" || cx.style == "keyword") {
      cx.marked = "property"
      return cont(typeprop)
    } else if (value == "?") {
      return cont(typeprop)
    } else if (type == ":") {
      return cont(typeexpr)
    }
  }
  function typearg(type) {
    if (type == "variable") return cont(typearg)
    else if (type == ":") return cont(typeexpr)
  }
  function afterType(type, value) {
    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
    if (value == "|" || type == ".") return cont(typeexpr)
    if (type == "[") return cont(expect("]"), afterType)
  }
  function vardef() {
    return pass(pattern, maybetype, maybeAssign, vardefCont);
  }
  function pattern(type, value) {
    if (type == "modifier") return cont(pattern)
    if (type == "variable") { register(value); return cont(); }
    if (type == "spread") return cont(pattern);
    if (type == "[") return contCommasep(pattern, "]");
    if (type == "{") return contCommasep(proppattern, "}");
  }
  function proppattern(type, value) {
    if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
      register(value);
      return cont(maybeAssign);
    }
    if (type == "variable") cx.marked = "property";
    if (type == "spread") return cont(pattern);
    if (type == "}") return pass();
    return cont(expect(":"), pattern, maybeAssign);
  }
  function maybeAssign(_type, value) {
    if (value == "=") return cont(expressionNoComma);
  }
  function vardefCont(type) {
    if (type == ",") return cont(vardef);
  }
  function maybeelse(type, value) {
    if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
  }
  function forspec(type) {
    if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
  }
  function forspec1(type) {
    if (type == "var") return cont(vardef, expect(";"), forspec2);
    if (type == ";") return cont(forspec2);
    if (type == "variable") return cont(formaybeinof);
    return pass(expression, expect(";"), forspec2);
  }
  function formaybeinof(_type, value) {
    if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
    return cont(maybeoperatorComma, forspec2);
  }
  function forspec2(type, value) {
    if (type == ";") return cont(forspec3);
    if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
    return pass(expression, expect(";"), forspec3);
  }
  function forspec3(type) {
    if (type != ")") cont(expression);
  }
  function functiondef(type, value) {
    if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
    if (type == "variable") {register(value); return cont(functiondef);}
    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext);
    if (isTS && value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, functiondef)
  }
  function funarg(type) {
    if (type == "spread") return cont(funarg);
    return pass(pattern, maybetype, maybeAssign);
  }
  function classExpression(type, value) {
    // Class expressions may have an optional name.
    if (type == "variable") return className(type, value);
    return classNameAfter(type, value);
  }
  function className(type, value) {
    if (type == "variable") {register(value); return cont(classNameAfter);}
  }
  function classNameAfter(type, value) {
    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, classNameAfter)
    if (value == "extends" || value == "implements" || (isTS && type == ","))
      return cont(isTS ? typeexpr : expression, classNameAfter);
    if (type == "{") return cont(pushlex("}"), classBody, poplex);
  }
  function classBody(type, value) {
    if (type == "variable" || cx.style == "keyword") {
      if ((value == "async" || value == "static" || value == "get" || value == "set" ||
           (isTS && (value == "public" || value == "private" || value == "protected" || value == "readonly" || value == "abstract"))) &&
          cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false)) {
        cx.marked = "keyword";
        return cont(classBody);
      }
      cx.marked = "property";
      return cont(isTS ? classfield : functiondef, classBody);
    }
    if (type == "[")
      return cont(expression, expect("]"), isTS ? classfield : functiondef, classBody)
    if (value == "*") {
      cx.marked = "keyword";
      return cont(classBody);
    }
    if (type == ";") return cont(classBody);
    if (type == "}") return cont();
    if (value == "@") return cont(expression, classBody)
  }
  function classfield(type, value) {
    if (value == "?") return cont(classfield)
    if (type == ":") return cont(typeexpr, maybeAssign)
    if (value == "=") return cont(expressionNoComma)
    return pass(functiondef)
  }
  function afterExport(type, value) {
    if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
    if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
    if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
    return pass(statement);
  }
  function exportField(type, value) {
    if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
    if (type == "variable") return pass(expressionNoComma, exportField);
  }
  function afterImport(type) {
    if (type == "string") return cont();
    return pass(importSpec, maybeMoreImports, maybeFrom);
  }
  function importSpec(type, value) {
    if (type == "{") return contCommasep(importSpec, "}");
    if (type == "variable") register(value);
    if (value == "*") cx.marked = "keyword";
    return cont(maybeAs);
  }
  function maybeMoreImports(type) {
    if (type == ",") return cont(importSpec, maybeMoreImports)
  }
  function maybeAs(_type, value) {
    if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
  }
  function maybeFrom(_type, value) {
    if (value == "from") { cx.marked = "keyword"; return cont(expression); }
  }
  function arrayLiteral(type) {
    if (type == "]") return cont();
    return pass(commasep(expressionNoComma, "]"));
  }

  function isContinuedStatement(state, textAfter) {
    return state.lastType == "operator" || state.lastType == "," ||
      isOperatorChar.test(textAfter.charAt(0)) ||
      /[,.]/.test(textAfter.charAt(0));
  }

  // Interface

  return {
    startState: function(basecolumn) {
      var state = {
        tokenize: tokenBase,
        lastType: "sof",
        cc: [],
        lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
        localVars: parserConfig.localVars,
        context: parserConfig.localVars && {vars: parserConfig.localVars},
        indented: basecolumn || 0
      };
      if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
        state.globalVars = parserConfig.globalVars;
      return state;
    },

    token: function(stream, state) {
      if (stream.sol()) {
        if (!state.lexical.hasOwnProperty("align"))
          state.lexical.align = false;
        state.indented = stream.indentation();
        findFatArrow(stream, state);
      }
      if (state.tokenize != tokenComment && stream.eatSpace()) return null;
      var style = state.tokenize(stream, state);
      if (type == "comment") return style;
      state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
      return parseJS(state, style, type, content, stream);
    },

    indent: function(state, textAfter) {
      if (state.tokenize == tokenComment) return CodeMirror.Pass;
      if (state.tokenize != tokenBase) return 0;
      var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
      // Kludge to prevent 'maybelse' from blocking lexical scope pops
      if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
        var c = state.cc[i];
        if (c == poplex) lexical = lexical.prev;
        else if (c != maybeelse) break;
      }
      while ((lexical.type == "stat" || lexical.type == "form") &&
             (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
                                   (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
                                   !/^[,\.=+\-*:?[\(]/.test(textAfter))))
        lexical = lexical.prev;
      if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
        lexical = lexical.prev;
      var type = lexical.type, closing = firstChar == type;

      if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
      else if (type == "form" && firstChar == "{") return lexical.indented;
      else if (type == "form") return lexical.indented + indentUnit;
      else if (type == "stat")
        return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
      else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
        return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
      else if (lexical.align) return lexical.column + (closing ? 0 : 1);
      else return lexical.indented + (closing ? 0 : indentUnit);
    },

    electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
    blockCommentStart: jsonMode ? null : "/*",
    blockCommentEnd: jsonMode ? null : "*/",
    lineComment: jsonMode ? null : "//",
    fold: "brace",
    closeBrackets: "()[]{}''\"\"``",

    helperType: jsonMode ? "json" : "javascript",
    jsonldMode: jsonldMode,
    jsonMode: jsonMode,

    expressionAllowed: expressionAllowed,
    skipExpression: function(state) {
      var top = state.cc[state.cc.length - 1]
      if (top == expression || top == expressionNoComma) state.cc.pop()
    }
  };
});

CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);

CodeMirror.defineMIME("text/javascript", "javascript");
CodeMirror.defineMIME("text/ecmascript", "javascript");
CodeMirror.defineMIME("application/javascript", "javascript");
CodeMirror.defineMIME("application/x-javascript", "javascript");
CodeMirror.defineMIME("application/ecmascript", "javascript");
CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });

});

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
    (document.documentMode == null || document.documentMode < 8);

  var Pos = CodeMirror.Pos;

  var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};

  function findMatchingBracket(cm, where, strict, config) {
    var line = cm.getLineHandle(where.line), pos = where.ch - 1;
    var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
    if (!match) return null;
    var dir = match.charAt(1) == ">" ? 1 : -1;
    if (strict && (dir > 0) != (pos == where.ch)) return null;
    var style = cm.getTokenTypeAt(Pos(where.line, pos + 1));

    var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config);
    if (found == null) return null;
    return {from: Pos(where.line, pos), to: found && found.pos,
            match: found && found.ch == match.charAt(0), forward: dir > 0};
  }

  // bracketRegex is used to specify which type of bracket to scan
  // should be a regexp, e.g. /[[\]]/
  //
  // Note: If "where" is on an open bracket, then this bracket is ignored.
  //
  // Returns false when no bracket was found, null when it reached
  // maxScanLines and gave up
  function scanForBracket(cm, where, dir, style, config) {
    var maxScanLen = (config && config.maxScanLineLength) || 10000;
    var maxScanLines = (config && config.maxScanLines) || 1000;

    var stack = [];
    var re = config && config.bracketRegex ? config.bracketRegex : /[(){}[\]]/;
    var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1)
                          : Math.max(cm.firstLine() - 1, where.line - maxScanLines);
    for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) {
      var line = cm.getLine(lineNo);
      if (!line) continue;
      var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1;
      if (line.length > maxScanLen) continue;
      if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0);
      for (; pos != end; pos += dir) {
        var ch = line.charAt(pos);
        if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) {
          var match = matching[ch];
          if ((match.charAt(1) == ">") == (dir > 0)) stack.push(ch);
          else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch};
          else stack.pop();
        }
      }
    }
    return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null;
  }

  function matchBrackets(cm, autoclear, config) {
    // Disable brace matching in long lines, since it'll cause hugely slow updates
    var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000;
    var marks = [], ranges = cm.listSelections();
    for (var i = 0; i < ranges.length; i++) {
      var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, false, config);
      if (match && cm.getLine(match.from.line).length <= maxHighlightLen) {
        var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
        marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style}));
        if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen)
          marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style}));
      }
    }

    if (marks.length) {
      // Kludge to work around the IE bug from issue #1193, where text
      // input stops going to the textare whever this fires.
      if (ie_lt8 && cm.state.focused) cm.focus();

      var clear = function() {
        cm.operation(function() {
          for (var i = 0; i < marks.length; i++) marks[i].clear();
        });
      };
      if (autoclear) setTimeout(clear, 800);
      else return clear;
    }
  }

  var currentlyHighlighted = null;
  function doMatchBrackets(cm) {
    cm.operation(function() {
      if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
      currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
    });
  }

  CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
    if (old && old != CodeMirror.Init) {
      cm.off("cursorActivity", doMatchBrackets);
      if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
    }
    if (val) {
      cm.state.matchBrackets = typeof val == "object" ? val : {};
      cm.on("cursorActivity", doMatchBrackets);
    }
  });

  CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
  CodeMirror.defineExtension("findMatchingBracket", function(pos, strict, config){
    return findMatchingBracket(this, pos, strict, config);
  });
  CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){
    return scanForBracket(this, pos, dir, style, config);
  });
});

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  "use strict";

  var defaultTags = {
    script: [
      ["lang", /(javascript|babel)/i, "javascript"],
      ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
      ["type", /./, "text/plain"],
      [null, null, "javascript"]
    ],
    style:  [
      ["lang", /^css$/i, "css"],
      ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
      ["type", /./, "text/plain"],
      [null, null, "css"]
    ]
  };

  function maybeBackup(stream, pat, style) {
    var cur = stream.current(), close = cur.search(pat);
    if (close > -1) {
      stream.backUp(cur.length - close);
    } else if (cur.match(/<\/?$/)) {
      stream.backUp(cur.length);
      if (!stream.match(pat, false)) stream.match(cur);
    }
    return style;
  }

  var attrRegexpCache = {};
  function getAttrRegexp(attr) {
    var regexp = attrRegexpCache[attr];
    if (regexp) return regexp;
    return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
  }

  function getAttrValue(text, attr) {
    var match = text.match(getAttrRegexp(attr))
    return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
  }

  function getTagRegexp(tagName, anchored) {
    return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i");
  }

  function addTags(from, to) {
    for (var tag in from) {
      var dest = to[tag] || (to[tag] = []);
      var source = from[tag];
      for (var i = source.length - 1; i >= 0; i--)
        dest.unshift(source[i])
    }
  }

  function findMatchingMode(tagInfo, tagText) {
    for (var i = 0; i < tagInfo.length; i++) {
      var spec = tagInfo[i];
      if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
    }
  }

  CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
    var htmlMode = CodeMirror.getMode(config, {
      name: "xml",
      htmlMode: true,
      multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
      multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag
    });

    var tags = {};
    var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
    addTags(defaultTags, tags);
    if (configTags) addTags(configTags, tags);
    if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
      tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])

    function html(stream, state) {
      var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
      if (tag && !/[<>\s\/]/.test(stream.current()) &&
          (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
          tags.hasOwnProperty(tagName)) {
        state.inTag = tagName + " "
      } else if (state.inTag && tag && />$/.test(stream.current())) {
        var inTag = /^([\S]+) (.*)/.exec(state.inTag)
        state.inTag = null
        var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
        var mode = CodeMirror.getMode(config, modeSpec)
        var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
        state.token = function (stream, state) {
          if (stream.match(endTagA, false)) {
            state.token = html;
            state.localState = state.localMode = null;
            return null;
          }
          return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
        };
        state.localMode = mode;
        state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, ""));
      } else if (state.inTag) {
        state.inTag += stream.current()
        if (stream.eol()) state.inTag += " "
      }
      return style;
    };

    return {
      startState: function () {
        var state = CodeMirror.startState(htmlMode);
        return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
      },

      copyState: function (state) {
        var local;
        if (state.localState) {
          local = CodeMirror.copyState(state.localMode, state.localState);
        }
        return {token: state.token, inTag: state.inTag,
                localMode: state.localMode, localState: local,
                htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
      },

      token: function (stream, state) {
        return state.token(stream, state);
      },

      indent: function (state, textAfter) {
        if (!state.localMode || /^\s*<\//.test(textAfter))
          return htmlMode.indent(state.htmlState, textAfter);
        else if (state.localMode.indent)
          return state.localMode.indent(state.localState, textAfter);
        else
          return CodeMirror.Pass;
      },

      innerMode: function (state) {
        return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
      }
    };
  }, "xml", "javascript", "css");

  CodeMirror.defineMIME("text/html", "htmlmixed");
});

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
"use strict";

var htmlConfig = {
  autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
                    'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
                    'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
                    'track': true, 'wbr': true, 'menuitem': true},
  implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
                     'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
                     'th': true, 'tr': true},
  contextGrabbers: {
    'dd': {'dd': true, 'dt': true},
    'dt': {'dd': true, 'dt': true},
    'li': {'li': true},
    'option': {'option': true, 'optgroup': true},
    'optgroup': {'optgroup': true},
    'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
          'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
          'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
          'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
          'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
    'rp': {'rp': true, 'rt': true},
    'rt': {'rp': true, 'rt': true},
    'tbody': {'tbody': true, 'tfoot': true},
    'td': {'td': true, 'th': true},
    'tfoot': {'tbody': true},
    'th': {'td': true, 'th': true},
    'thead': {'tbody': true, 'tfoot': true},
    'tr': {'tr': true}
  },
  doNotIndent: {"pre": true},
  allowUnquoted: true,
  allowMissing: true,
  caseFold: true
}

var xmlConfig = {
  autoSelfClosers: {},
  implicitlyClosed: {},
  contextGrabbers: {},
  doNotIndent: {},
  allowUnquoted: false,
  allowMissing: false,
  caseFold: false
}

CodeMirror.defineMode("xml", function(editorConf, config_) {
  var indentUnit = editorConf.indentUnit
  var config = {}
  var defaults = config_.htmlMode ? htmlConfig : xmlConfig
  for (var prop in defaults) config[prop] = defaults[prop]
  for (var prop in config_) config[prop] = config_[prop]

  // Return variables for tokenizers
  var type, setStyle;

  function inText(stream, state) {
    function chain(parser) {
      state.tokenize = parser;
      return parser(stream, state);
    }

    var ch = stream.next();
    if (ch == "<") {
      if (stream.eat("!")) {
        if (stream.eat("[")) {
          if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
          else return null;
        } else if (stream.match("--")) {
          return chain(inBlock("comment", "-->"));
        } else if (stream.match("DOCTYPE", true, true)) {
          stream.eatWhile(/[\w\._\-]/);
          return chain(doctype(1));
        } else {
          return null;
        }
      } else if (stream.eat("?")) {
        stream.eatWhile(/[\w\._\-]/);
        state.tokenize = inBlock("meta", "?>");
        return "meta";
      } else {
        type = stream.eat("/") ? "closeTag" : "openTag";
        state.tokenize = inTag;
        return "tag bracket";
      }
    } else if (ch == "&") {
      var ok;
      if (stream.eat("#")) {
        if (stream.eat("x")) {
          ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
        } else {
          ok = stream.eatWhile(/[\d]/) && stream.eat(";");
        }
      } else {
        ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
      }
      return ok ? "atom" : "error";
    } else {
      stream.eatWhile(/[^&<]/);
      return null;
    }
  }
  inText.isInText = true;

  function inTag(stream, state) {
    var ch = stream.next();
    if (ch == ">" || (ch == "/" && stream.eat(">"))) {
      state.tokenize = inText;
      type = ch == ">" ? "endTag" : "selfcloseTag";
      return "tag bracket";
    } else if (ch == "=") {
      type = "equals";
      return null;
    } else if (ch == "<") {
      state.tokenize = inText;
      state.state = baseState;
      state.tagName = state.tagStart = null;
      var next = state.tokenize(stream, state);
      return next ? next + " tag error" : "tag error";
    } else if (/[\'\"]/.test(ch)) {
      state.tokenize = inAttribute(ch);
      state.stringStartCol = stream.column();
      return state.tokenize(stream, state);
    } else {
      stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
      return "word";
    }
  }

  function inAttribute(quote) {
    var closure = function(stream, state) {
      while (!stream.eol()) {
        if (stream.next() == quote) {
          state.tokenize = inTag;
          break;
        }
      }
      return "string";
    };
    closure.isInAttribute = true;
    return closure;
  }

  function inBlock(style, terminator) {
    return function(stream, state) {
      while (!stream.eol()) {
        if (stream.match(terminator)) {
          state.tokenize = inText;
          break;
        }
        stream.next();
      }
      return style;
    };
  }
  function doctype(depth) {
    return function(stream, state) {
      var ch;
      while ((ch = stream.next()) != null) {
        if (ch == "<") {
          state.tokenize = doctype(depth + 1);
          return state.tokenize(stream, state);
        } else if (ch == ">") {
          if (depth == 1) {
            state.tokenize = inText;
            break;
          } else {
            state.tokenize = doctype(depth - 1);
            return state.tokenize(stream, state);
          }
        }
      }
      return "meta";
    };
  }

  function Context(state, tagName, startOfLine) {
    this.prev = state.context;
    this.tagName = tagName;
    this.indent = state.indented;
    this.startOfLine = startOfLine;
    if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
      this.noIndent = true;
  }
  function popContext(state) {
    if (state.context) state.context = state.context.prev;
  }
  function maybePopContext(state, nextTagName) {
    var parentTagName;
    while (true) {
      if (!state.context) {
        return;
      }
      parentTagName = state.context.tagName;
      if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
          !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
        return;
      }
      popContext(state);
    }
  }

  function baseState(type, stream, state) {
    if (type == "openTag") {
      state.tagStart = stream.column();
      return tagNameState;
    } else if (type == "closeTag") {
      return closeTagNameState;
    } else {
      return baseState;
    }
  }
  function tagNameState(type, stream, state) {
    if (type == "word") {
      state.tagName = stream.current();
      setStyle = "tag";
      return attrState;
    } else {
      setStyle = "error";
      return tagNameState;
    }
  }
  function closeTagNameState(type, stream, state) {
    if (type == "word") {
      var tagName = stream.current();
      if (state.context && state.context.tagName != tagName &&
          config.implicitlyClosed.hasOwnProperty(state.context.tagName))
        popContext(state);
      if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
        setStyle = "tag";
        return closeState;
      } else {
        setStyle = "tag error";
        return closeStateErr;
      }
    } else {
      setStyle = "error";
      return closeStateErr;
    }
  }

  function closeState(type, _stream, state) {
    if (type != "endTag") {
      setStyle = "error";
      return closeState;
    }
    popContext(state);
    return baseState;
  }
  function closeStateErr(type, stream, state) {
    setStyle = "error";
    return closeState(type, stream, state);
  }

  function attrState(type, _stream, state) {
    if (type == "word") {
      setStyle = "attribute";
      return attrEqState;
    } else if (type == "endTag" || type == "selfcloseTag") {
      var tagName = state.tagName, tagStart = state.tagStart;
      state.tagName = state.tagStart = null;
      if (type == "selfcloseTag" ||
          config.autoSelfClosers.hasOwnProperty(tagName)) {
        maybePopContext(state, tagName);
      } else {
        maybePopContext(state, tagName);
        state.context = new Context(state, tagName, tagStart == state.indented);
      }
      return baseState;
    }
    setStyle = "error";
    return attrState;
  }
  function attrEqState(type, stream, state) {
    if (type == "equals") return attrValueState;
    if (!config.allowMissing) setStyle = "error";
    return attrState(type, stream, state);
  }
  function attrValueState(type, stream, state) {
    if (type == "string") return attrContinuedState;
    if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
    setStyle = "error";
    return attrState(type, stream, state);
  }
  function attrContinuedState(type, stream, state) {
    if (type == "string") return attrContinuedState;
    return attrState(type, stream, state);
  }

  return {
    startState: function(baseIndent) {
      var state = {tokenize: inText,
                   state: baseState,
                   indented: baseIndent || 0,
                   tagName: null, tagStart: null,
                   context: null}
      if (baseIndent != null) state.baseIndent = baseIndent
      return state
    },

    token: function(stream, state) {
      if (!state.tagName && stream.sol())
        state.indented = stream.indentation();

      if (stream.eatSpace()) return null;
      type = null;
      var style = state.tokenize(stream, state);
      if ((style || type) && style != "comment") {
        setStyle = null;
        state.state = state.state(type || style, stream, state);
        if (setStyle)
          style = setStyle == "error" ? style + " error" : setStyle;
      }
      return style;
    },

    indent: function(state, textAfter, fullLine) {
      var context = state.context;
      // Indent multi-line strings (e.g. css).
      if (state.tokenize.isInAttribute) {
        if (state.tagStart == state.indented)
          return state.stringStartCol + 1;
        else
          return state.indented + indentUnit;
      }
      if (context && context.noIndent) return CodeMirror.Pass;
      if (state.tokenize != inTag && state.tokenize != inText)
        return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
      // Indent the starts of attribute names.
      if (state.tagName) {
        if (config.multilineTagIndentPastTag !== false)
          return state.tagStart + state.tagName.length + 2;
        else
          return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
      }
      if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
      var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
      if (tagAfter && tagAfter[1]) { // Closing tag spotted
        while (context) {
          if (context.tagName == tagAfter[2]) {
            context = context.prev;
            break;
          } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
            context = context.prev;
          } else {
            break;
          }
        }
      } else if (tagAfter) { // Opening tag spotted
        while (context) {
          var grabbers = config.contextGrabbers[context.tagName];
          if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
            context = context.prev;
          else
            break;
        }
      }
      while (context && context.prev && !context.startOfLine)
        context = context.prev;
      if (context) return context.indent + indentUnit;
      else return state.baseIndent || 0;
    },

    electricInput: /<\/[\s\w:]+>$/,
    blockCommentStart: "<!--",
    blockCommentEnd: "-->",

    configuration: config.htmlMode ? "html" : "xml",
    helperType: config.htmlMode ? "html" : "xml",

    skipAttribute: function(state) {
      if (state.state == attrValueState)
        state.state = attrState
    }
  };
});

CodeMirror.defineMIME("text/xml", "xml");
CodeMirror.defineMIME("application/xml", "xml");
if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
  CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});

});

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
"use strict";

CodeMirror.defineMode("css", function(config, parserConfig) {
  var inline = parserConfig.inline
  if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");

  var indentUnit = config.indentUnit,
      tokenHooks = parserConfig.tokenHooks,
      documentTypes = parserConfig.documentTypes || {},
      mediaTypes = parserConfig.mediaTypes || {},
      mediaFeatures = parserConfig.mediaFeatures || {},
      mediaValueKeywords = parserConfig.mediaValueKeywords || {},
      propertyKeywords = parserConfig.propertyKeywords || {},
      nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
      fontProperties = parserConfig.fontProperties || {},
      counterDescriptors = parserConfig.counterDescriptors || {},
      colorKeywords = parserConfig.colorKeywords || {},
      valueKeywords = parserConfig.valueKeywords || {},
      allowNested = parserConfig.allowNested,
      lineComment = parserConfig.lineComment,
      supportsAtComponent = parserConfig.supportsAtComponent === true;

  var type, override;
  function ret(style, tp) { type = tp; return style; }

  // Tokenizers

  function tokenBase(stream, state) {
    var ch = stream.next();
    if (tokenHooks[ch]) {
      var result = tokenHooks[ch](stream, state);
      if (result !== false) return result;
    }
    if (ch == "@") {
      stream.eatWhile(/[\w\\\-]/);
      return ret("def", stream.current());
    } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
      return ret(null, "compare");
    } else if (ch == "\"" || ch == "'") {
      state.tokenize = tokenString(ch);
      return state.tokenize(stream, state);
    } else if (ch == "#") {
      stream.eatWhile(/[\w\\\-]/);
      return ret("atom", "hash");
    } else if (ch == "!") {
      stream.match(/^\s*\w*/);
      return ret("keyword", "important");
    } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
      stream.eatWhile(/[\w.%]/);
      return ret("number", "unit");
    } else if (ch === "-") {
      if (/[\d.]/.test(stream.peek())) {
        stream.eatWhile(/[\w.%]/);
        return ret("number", "unit");
      } else if (stream.match(/^-[\w\\\-]+/)) {
        stream.eatWhile(/[\w\\\-]/);
        if (stream.match(/^\s*:/, false))
          return ret("variable-2", "variable-definition");
        return ret("variable-2", "variable");
      } else if (stream.match(/^\w+-/)) {
        return ret("meta", "meta");
      }
    } else if (/[,+>*\/]/.test(ch)) {
      return ret(null, "select-op");
    } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
      return ret("qualifier", "qualifier");
    } else if (/[:;{}\[\]\(\)]/.test(ch)) {
      return ret(null, ch);
    } else if ((ch == "u" && stream.match(/rl(-prefix)?\(/)) ||
               (ch == "d" && stream.match("omain(")) ||
               (ch == "r" && stream.match("egexp("))) {
      stream.backUp(1);
      state.tokenize = tokenParenthesized;
      return ret("property", "word");
    } else if (/[\w\\\-]/.test(ch)) {
      stream.eatWhile(/[\w\\\-]/);
      return ret("property", "word");
    } else {
      return ret(null, null);
    }
  }

  function tokenString(quote) {
    return function(stream, state) {
      var escaped = false, ch;
      while ((ch = stream.next()) != null) {
        if (ch == quote && !escaped) {
          if (quote == ")") stream.backUp(1);
          break;
        }
        escaped = !escaped && ch == "\\";
      }
      if (ch == quote || !escaped && quote != ")") state.tokenize = null;
      return ret("string", "string");
    };
  }

  function tokenParenthesized(stream, state) {
    stream.next(); // Must be '('
    if (!stream.match(/\s*[\"\')]/, false))
      state.tokenize = tokenString(")");
    else
      state.tokenize = null;
    return ret(null, "(");
  }

  // Context management

  function Context(type, indent, prev) {
    this.type = type;
    this.indent = indent;
    this.prev = prev;
  }

  function pushContext(state, stream, type, indent) {
    state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
    return type;
  }

  function popContext(state) {
    if (state.context.prev)
      state.context = state.context.prev;
    return state.context.type;
  }

  function pass(type, stream, state) {
    return states[state.context.type](type, stream, state);
  }
  function popAndPass(type, stream, state, n) {
    for (var i = n || 1; i > 0; i--)
      state.context = state.context.prev;
    return pass(type, stream, state);
  }

  // Parser

  function wordAsValue(stream) {
    var word = stream.current().toLowerCase();
    if (valueKeywords.hasOwnProperty(word))
      override = "atom";
    else if (colorKeywords.hasOwnProperty(word))
      override = "keyword";
    else
      override = "variable";
  }

  var states = {};

  states.top = function(type, stream, state) {
    if (type == "{") {
      return pushContext(state, stream, "block");
    } else if (type == "}" && state.context.prev) {
      return popContext(state);
    } else if (supportsAtComponent && /@component/.test(type)) {
      return pushContext(state, stream, "atComponentBlock");
    } else if (/^@(-moz-)?document$/.test(type)) {
      return pushContext(state, stream, "documentTypes");
    } else if (/^@(media|supports|(-moz-)?document|import)$/.test(type)) {
      return pushContext(state, stream, "atBlock");
    } else if (/^@(font-face|counter-style)/.test(type)) {
      state.stateArg = type;
      return "restricted_atBlock_before";
    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {
      return "keyframes";
    } else if (type && type.charAt(0) == "@") {
      return pushContext(state, stream, "at");
    } else if (type == "hash") {
      override = "builtin";
    } else if (type == "word") {
      override = "tag";
    } else if (type == "variable-definition") {
      return "maybeprop";
    } else if (type == "interpolation") {
      return pushContext(state, stream, "interpolation");
    } else if (type == ":") {
      return "pseudo";
    } else if (allowNested && type == "(") {
      return pushContext(state, stream, "parens");
    }
    return state.context.type;
  };

  states.block = function(type, stream, state) {
    if (type == "word") {
      var word = stream.current().toLowerCase();
      if (propertyKeywords.hasOwnProperty(word)) {
        override = "property";
        return "maybeprop";
      } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
        override = "string-2";
        return "maybeprop";
      } else if (allowNested) {
        override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
        return "block";
      } else {
        override += " error";
        return "maybeprop";
      }
    } else if (type == "meta") {
      return "block";
    } else if (!allowNested && (type == "hash" || type == "qualifier")) {
      override = "error";
      return "block";
    } else {
      return states.top(type, stream, state);
    }
  };

  states.maybeprop = function(type, stream, state) {
    if (type == ":") return pushContext(state, stream, "prop");
    return pass(type, stream, state);
  };

  states.prop = function(type, stream, state) {
    if (type == ";") return popContext(state);
    if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
    if (type == "}" || type == "{") return popAndPass(type, stream, state);
    if (type == "(") return pushContext(state, stream, "parens");

    if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) {
      override += " error";
    } else if (type == "word") {
      wordAsValue(stream);
    } else if (type == "interpolation") {
      return pushContext(state, stream, "interpolation");
    }
    return "prop";
  };

  states.propBlock = function(type, _stream, state) {
    if (type == "}") return popContext(state);
    if (type == "word") { override = "property"; return "maybeprop"; }
    return state.context.type;
  };

  states.parens = function(type, stream, state) {
    if (type == "{" || type == "}") return popAndPass(type, stream, state);
    if (type == ")") return popContext(state);
    if (type == "(") return pushContext(state, stream, "parens");
    if (type == "interpolation") return pushContext(state, stream, "interpolation");
    if (type == "word") wordAsValue(stream);
    return "parens";
  };

  states.pseudo = function(type, stream, state) {
    if (type == "meta") return "pseudo";

    if (type == "word") {
      override = "variable-3";
      return state.context.type;
    }
    return pass(type, stream, state);
  };

  states.documentTypes = function(type, stream, state) {
    if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
      override = "tag";
      return state.context.type;
    } else {
      return states.atBlock(type, stream, state);
    }
  };

  states.atBlock = function(type, stream, state) {
    if (type == "(") return pushContext(state, stream, "atBlock_parens");
    if (type == "}" || type == ";") return popAndPass(type, stream, state);
    if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");

    if (type == "interpolation") return pushContext(state, stream, "interpolation");

    if (type == "word") {
      var word = stream.current().toLowerCase();
      if (word == "only" || word == "not" || word == "and" || word == "or")
        override = "keyword";
      else if (mediaTypes.hasOwnProperty(word))
        override = "attribute";
      else if (mediaFeatures.hasOwnProperty(word))
        override = "property";
      else if (mediaValueKeywords.hasOwnProperty(word))
        override = "keyword";
      else if (propertyKeywords.hasOwnProperty(word))
        override = "property";
      else if (nonStandardPropertyKeywords.hasOwnProperty(word))
        override = "string-2";
      else if (valueKeywords.hasOwnProperty(word))
        override = "atom";
      else if (colorKeywords.hasOwnProperty(word))
        override = "keyword";
      else
        override = "error";
    }
    return state.context.type;
  };

  states.atComponentBlock = function(type, stream, state) {
    if (type == "}")
      return popAndPass(type, stream, state);
    if (type == "{")
      return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
    if (type == "word")
      override = "error";
    return state.context.type;
  };

  states.atBlock_parens = function(type, stream, state) {
    if (type == ")") return popContext(state);
    if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
    return states.atBlock(type, stream, state);
  };

  states.restricted_atBlock_before = function(type, stream, state) {
    if (type == "{")
      return pushContext(state, stream, "restricted_atBlock");
    if (type == "word" && state.stateArg == "@counter-style") {
      override = "variable";
      return "restricted_atBlock_before";
    }
    return pass(type, stream, state);
  };

  states.restricted_atBlock = function(type, stream, state) {
    if (type == "}") {
      state.stateArg = null;
      return popContext(state);
    }
    if (type == "word") {
      if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
          (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
        override = "error";
      else
        override = "property";
      return "maybeprop";
    }
    return "restricted_atBlock";
  };

  states.keyframes = function(type, stream, state) {
    if (type == "word") { override = "variable"; return "keyframes"; }
    if (type == "{") return pushContext(state, stream, "top");
    return pass(type, stream, state);
  };

  states.at = function(type, stream, state) {
    if (type == ";") return popContext(state);
    if (type == "{" || type == "}") return popAndPass(type, stream, state);
    if (type == "word") override = "tag";
    else if (type == "hash") override = "builtin";
    return "at";
  };

  states.interpolation = function(type, stream, state) {
    if (type == "}") return popContext(state);
    if (type == "{" || type == ";") return popAndPass(type, stream, state);
    if (type == "word") override = "variable";
    else if (type != "variable" && type != "(" && type != ")") override = "error";
    return "interpolation";
  };

  return {
    startState: function(base) {
      return {tokenize: null,
              state: inline ? "block" : "top",
              stateArg: null,
              context: new Context(inline ? "block" : "top", base || 0, null)};
    },

    token: function(stream, state) {
      if (!state.tokenize && stream.eatSpace()) return null;
      var style = (state.tokenize || tokenBase)(stream, state);
      if (style && typeof style == "object") {
        type = style[1];
        style = style[0];
      }
      override = style;
      state.state = states[state.state](type, stream, state);
      return override;
    },

    indent: function(state, textAfter) {
      var cx = state.context, ch = textAfter && textAfter.charAt(0);
      var indent = cx.indent;
      if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
      if (cx.prev) {
        if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
                          cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
          // Resume indentation from parent context.
          cx = cx.prev;
          indent = cx.indent;
        } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
            ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
          // Dedent relative to current context.
          indent = Math.max(0, cx.indent - indentUnit);
          cx = cx.prev;
        }
      }
      return indent;
    },

    electricChars: "}",
    blockCommentStart: "/*",
    blockCommentEnd: "*/",
    lineComment: lineComment,
    fold: "brace"
  };
});

  function keySet(array) {
    var keys = {};
    for (var i = 0; i < array.length; ++i) {
      keys[array[i].toLowerCase()] = true;
    }
    return keys;
  }

  var documentTypes_ = [
    "domain", "regexp", "url", "url-prefix"
  ], documentTypes = keySet(documentTypes_);

  var mediaTypes_ = [
    "all", "aural", "braille", "handheld", "print", "projection", "screen",
    "tty", "tv", "embossed"
  ], mediaTypes = keySet(mediaTypes_);

  var mediaFeatures_ = [
    "width", "min-width", "max-width", "height", "min-height", "max-height",
    "device-width", "min-device-width", "max-device-width", "device-height",
    "min-device-height", "max-device-height", "aspect-ratio",
    "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
    "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
    "max-color", "color-index", "min-color-index", "max-color-index",
    "monochrome", "min-monochrome", "max-monochrome", "resolution",
    "min-resolution", "max-resolution", "scan", "grid", "orientation",
    "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
    "pointer", "any-pointer", "hover", "any-hover"
  ], mediaFeatures = keySet(mediaFeatures_);

  var mediaValueKeywords_ = [
    "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
    "interlace", "progressive"
  ], mediaValueKeywords = keySet(mediaValueKeywords_);

  var propertyKeywords_ = [
    "align-content", "align-items", "align-self", "alignment-adjust",
    "alignment-baseline", "anchor-point", "animation", "animation-delay",
    "animation-direction", "animation-duration", "animation-fill-mode",
    "animation-iteration-count", "animation-name", "animation-play-state",
    "animation-timing-function", "appearance", "azimuth", "backface-visibility",
    "background", "background-attachment", "background-blend-mode", "background-clip",
    "background-color", "background-image", "background-origin", "background-position",
    "background-repeat", "background-size", "baseline-shift", "binding",
    "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
    "bookmark-target", "border", "border-bottom", "border-bottom-color",
    "border-bottom-left-radius", "border-bottom-right-radius",
    "border-bottom-style", "border-bottom-width", "border-collapse",
    "border-color", "border-image", "border-image-outset",
    "border-image-repeat", "border-image-slice", "border-image-source",
    "border-image-width", "border-left", "border-left-color",
    "border-left-style", "border-left-width", "border-radius", "border-right",
    "border-right-color", "border-right-style", "border-right-width",
    "border-spacing", "border-style", "border-top", "border-top-color",
    "border-top-left-radius", "border-top-right-radius", "border-top-style",
    "border-top-width", "border-width", "bottom", "box-decoration-break",
    "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
    "caption-side", "clear", "clip", "color", "color-profile", "column-count",
    "column-fill", "column-gap", "column-rule", "column-rule-color",
    "column-rule-style", "column-rule-width", "column-span", "column-width",
    "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
    "cue-after", "cue-before", "cursor", "direction", "display",
    "dominant-baseline", "drop-initial-after-adjust",
    "drop-initial-after-align", "drop-initial-before-adjust",
    "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
    "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
    "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
    "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings",
    "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust",
    "font-stretch", "font-style", "font-synthesis", "font-variant",
    "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
    "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
    "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow",
    "grid-auto-rows", "grid-column", "grid-column-end", "grid-column-gap",
    "grid-column-start", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap",
    "grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns",
    "grid-template-rows", "hanging-punctuation", "height", "hyphens",
    "icon", "image-orientation", "image-rendering", "image-resolution",
    "inline-box-align", "justify-content", "left", "letter-spacing",
    "line-break", "line-height", "line-stacking", "line-stacking-ruby",
    "line-stacking-shift", "line-stacking-strategy", "list-style",
    "list-style-image", "list-style-position", "list-style-type", "margin",
    "margin-bottom", "margin-left", "margin-right", "margin-top",
    "marks", "marquee-direction", "marquee-loop",
    "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
    "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
    "nav-left", "nav-right", "nav-up", "object-fit", "object-position",
    "opacity", "order", "orphans", "outline",
    "outline-color", "outline-offset", "outline-style", "outline-width",
    "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
    "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
    "page", "page-break-after", "page-break-before", "page-break-inside",
    "page-policy", "pause", "pause-after", "pause-before", "perspective",
    "perspective-origin", "pitch", "pitch-range", "play-during", "position",
    "presentation-level", "punctuation-trim", "quotes", "region-break-after",
    "region-break-before", "region-break-inside", "region-fragment",
    "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
    "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
    "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin",
    "shape-outside", "size", "speak", "speak-as", "speak-header",
    "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
    "tab-size", "table-layout", "target", "target-name", "target-new",
    "target-position", "text-align", "text-align-last", "text-decoration",
    "text-decoration-color", "text-decoration-line", "text-decoration-skip",
    "text-decoration-style", "text-emphasis", "text-emphasis-color",
    "text-emphasis-position", "text-emphasis-style", "text-height",
    "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
    "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
    "text-wrap", "top", "transform", "transform-origin", "transform-style",
    "transition", "transition-delay", "transition-duration",
    "transition-property", "transition-timing-function", "unicode-bidi",
    "user-select", "vertical-align", "visibility", "voice-balance", "voice-duration",
    "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
    "voice-volume", "volume", "white-space", "widows", "width", "will-change", "word-break",
    "word-spacing", "word-wrap", "z-index",
    // SVG-specific
    "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
    "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
    "color-interpolation", "color-interpolation-filters",
    "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
    "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
    "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
    "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
    "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
    "glyph-orientation-vertical", "text-anchor", "writing-mode"
  ], propertyKeywords = keySet(propertyKeywords_);

  var nonStandardPropertyKeywords_ = [
    "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
    "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
    "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside",
    "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
    "searchfield-results-decoration", "zoom"
  ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);

  var fontProperties_ = [
    "font-family", "src", "unicode-range", "font-variant", "font-feature-settings",
    "font-stretch", "font-weight", "font-style"
  ], fontProperties = keySet(fontProperties_);

  var counterDescriptors_ = [
    "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
    "speak-as", "suffix", "symbols", "system"
  ], counterDescriptors = keySet(counterDescriptors_);

  var colorKeywords_ = [
    "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
    "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
    "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
    "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
    "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
    "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
    "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
    "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
    "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
    "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
    "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
    "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
    "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
    "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
    "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
    "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
    "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
    "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
    "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
    "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
    "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
    "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
    "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
    "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
    "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
    "whitesmoke", "yellow", "yellowgreen"
  ], colorKeywords = keySet(colorKeywords_);

  var valueKeywords_ = [
    "above", "absolute", "activeborder", "additive", "activecaption", "afar",
    "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
    "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
    "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page",
    "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
    "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
    "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
    "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
    "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
    "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
    "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
    "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
    "compact", "condensed", "contain", "content", "contents",
    "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
    "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
    "decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
    "destination-in", "destination-out", "destination-over", "devanagari", "difference",
    "disc", "discard", "disclosure-closed", "disclosure-open", "document",
    "dot-dash", "dot-dot-dash",
    "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
    "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
    "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
    "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
    "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
    "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
    "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
    "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
    "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
    "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
    "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove",
    "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
    "help", "hidden", "hide", "higher", "highlight", "highlighttext",
    "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
    "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
    "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
    "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert",
    "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
    "katakana", "katakana-iroha", "keep-all", "khmer",
    "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
    "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
    "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
    "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
    "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
    "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d",
    "media-controls-background", "media-current-time-display",
    "media-fullscreen-button", "media-mute-button", "media-play-button",
    "media-return-to-realtime-button", "media-rewind-button",
    "media-seek-back-button", "media-seek-forward-button", "media-slider",
    "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
    "media-volume-slider-container", "media-volume-sliderthumb", "medium",
    "menu", "menulist", "menulist-button", "menulist-text",
    "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
    "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
    "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
    "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
    "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote",
    "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
    "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
    "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
    "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
    "progress", "push-button", "radial-gradient", "radio", "read-only",
    "read-write", "read-write-plaintext-only", "rectangle", "region",
    "relative", "repeat", "repeating-linear-gradient",
    "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
    "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
    "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
    "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
    "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield",
    "searchfield-cancel-button", "searchfield-decoration",
    "searchfield-results-button", "searchfield-results-decoration",
    "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
    "simp-chinese-formal", "simp-chinese-informal", "single",
    "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
    "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
    "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
    "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "spell-out", "square",
    "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
    "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table",
    "table-caption", "table-cell", "table-column", "table-column-group",
    "table-footer-group", "table-header-group", "table-row", "table-row-group",
    "tamil",
    "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
    "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
    "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
    "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
    "trad-chinese-formal", "trad-chinese-informal", "transform",
    "translate", "translate3d", "translateX", "translateY", "translateZ",
    "transparent", "ultra-condensed", "ultra-expanded", "underline", "unset", "up",
    "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
    "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
    "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
    "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
    "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
    "xx-large", "xx-small"
  ], valueKeywords = keySet(valueKeywords_);

  var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
    .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
    .concat(valueKeywords_);
  CodeMirror.registerHelper("hintWords", "css", allWords);

  function tokenCComment(stream, state) {
    var maybeEnd = false, ch;
    while ((ch = stream.next()) != null) {
      if (maybeEnd && ch == "/") {
        state.tokenize = null;
        break;
      }
      maybeEnd = (ch == "*");
    }
    return ["comment", "comment"];
  }

  CodeMirror.defineMIME("text/css", {
    documentTypes: documentTypes,
    mediaTypes: mediaTypes,
    mediaFeatures: mediaFeatures,
    mediaValueKeywords: mediaValueKeywords,
    propertyKeywords: propertyKeywords,
    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
    fontProperties: fontProperties,
    counterDescriptors: counterDescriptors,
    colorKeywords: colorKeywords,
    valueKeywords: valueKeywords,
    tokenHooks: {
      "/": function(stream, state) {
        if (!stream.eat("*")) return false;
        state.tokenize = tokenCComment;
        return tokenCComment(stream, state);
      }
    },
    name: "css"
  });

  CodeMirror.defineMIME("text/x-scss", {
    mediaTypes: mediaTypes,
    mediaFeatures: mediaFeatures,
    mediaValueKeywords: mediaValueKeywords,
    propertyKeywords: propertyKeywords,
    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
    colorKeywords: colorKeywords,
    valueKeywords: valueKeywords,
    fontProperties: fontProperties,
    allowNested: true,
    lineComment: "//",
    tokenHooks: {
      "/": function(stream, state) {
        if (stream.eat("/")) {
          stream.skipToEnd();
          return ["comment", "comment"];
        } else if (stream.eat("*")) {
          state.tokenize = tokenCComment;
          return tokenCComment(stream, state);
        } else {
          return ["operator", "operator"];
        }
      },
      ":": function(stream) {
        if (stream.match(/\s*\{/, false))
          return [null, null]
        return false;
      },
      "$": function(stream) {
        stream.match(/^[\w-]+/);
        if (stream.match(/^\s*:/, false))
          return ["variable-2", "variable-definition"];
        return ["variable-2", "variable"];
      },
      "#": function(stream) {
        if (!stream.eat("{")) return false;
        return [null, "interpolation"];
      }
    },
    name: "css",
    helperType: "scss"
  });

  CodeMirror.defineMIME("text/x-less", {
    mediaTypes: mediaTypes,
    mediaFeatures: mediaFeatures,
    mediaValueKeywords: mediaValueKeywords,
    propertyKeywords: propertyKeywords,
    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
    colorKeywords: colorKeywords,
    valueKeywords: valueKeywords,
    fontProperties: fontProperties,
    allowNested: true,
    lineComment: "//",
    tokenHooks: {
      "/": function(stream, state) {
        if (stream.eat("/")) {
          stream.skipToEnd();
          return ["comment", "comment"];
        } else if (stream.eat("*")) {
          state.tokenize = tokenCComment;
          return tokenCComment(stream, state);
        } else {
          return ["operator", "operator"];
        }
      },
      "@": function(stream) {
        if (stream.eat("{")) return [null, "interpolation"];
        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false;
        stream.eatWhile(/[\w\\\-]/);
        if (stream.match(/^\s*:/, false))
          return ["variable-2", "variable-definition"];
        return ["variable-2", "variable"];
      },
      "&": function() {
        return ["atom", "atom"];
      }
    },
    name: "css",
    helperType: "less"
  });

  CodeMirror.defineMIME("text/x-gss", {
    documentTypes: documentTypes,
    mediaTypes: mediaTypes,
    mediaFeatures: mediaFeatures,
    propertyKeywords: propertyKeywords,
    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
    fontProperties: fontProperties,
    counterDescriptors: counterDescriptors,
    colorKeywords: colorKeywords,
    valueKeywords: valueKeywords,
    supportsAtComponent: true,
    tokenHooks: {
      "/": function(stream, state) {
        if (!stream.eat("*")) return false;
        state.tokenize = tokenCComment;
        return tokenCComment(stream, state);
      }
    },
    name: "css",
    helperType: "gss"
  });

});

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
"use strict";

function Context(indented, column, type, info, align, prev) {
  this.indented = indented;
  this.column = column;
  this.type = type;
  this.info = info;
  this.align = align;
  this.prev = prev;
}
function pushContext(state, col, type, info) {
  var indent = state.indented;
  if (state.context && state.context.type == "statement" && type != "statement")
    indent = state.context.indented;
  return state.context = new Context(indent, col, type, info, null, state.context);
}
function popContext(state) {
  var t = state.context.type;
  if (t == ")" || t == "]" || t == "}")
    state.indented = state.context.indented;
  return state.context = state.context.prev;
}

function typeBefore(stream, state, pos) {
  if (state.prevToken == "variable" || state.prevToken == "variable-3") return true;
  if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, pos))) return true;
  if (state.typeAtEndOfLine && stream.column() == stream.indentation()) return true;
}

function isTopScope(context) {
  for (;;) {
    if (!context || context.type == "top") return true;
    if (context.type == "}" && context.prev.info != "namespace") return false;
    context = context.prev;
  }
}

CodeMirror.defineMode("clike", function(config, parserConfig) {
  var indentUnit = config.indentUnit,
      statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
      dontAlignCalls = parserConfig.dontAlignCalls,
      keywords = parserConfig.keywords || {},
      types = parserConfig.types || {},
      builtin = parserConfig.builtin || {},
      blockKeywords = parserConfig.blockKeywords || {},
      defKeywords = parserConfig.defKeywords || {},
      atoms = parserConfig.atoms || {},
      hooks = parserConfig.hooks || {},
      multiLineStrings = parserConfig.multiLineStrings,
      indentStatements = parserConfig.indentStatements !== false,
      indentSwitch = parserConfig.indentSwitch !== false,
      namespaceSeparator = parserConfig.namespaceSeparator,
      isPunctuationChar = parserConfig.isPunctuationChar || /[\[\]{}\(\),;\:\.]/,
      numberStart = parserConfig.numberStart || /[\d\.]/,
      number = parserConfig.number || /^(?:0x[a-f\d]+|0b[01]+|(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)(u|ll?|l|f)?/i,
      isOperatorChar = parserConfig.isOperatorChar || /[+\-*&%=<>!?|\/]/,
      isIdentifierChar = parserConfig.isIdentifierChar || /[\w\$_\xa1-\uffff]/;

  var curPunc, isDefKeyword;

  function tokenBase(stream, state) {
    var ch = stream.next();
    if (hooks[ch]) {
      var result = hooks[ch](stream, state);
      if (result !== false) return result;
    }
    if (ch == '"' || ch == "'") {
      state.tokenize = tokenString(ch);
      return state.tokenize(stream, state);
    }
    if (isPunctuationChar.test(ch)) {
      curPunc = ch;
      return null;
    }
    if (numberStart.test(ch)) {
      stream.backUp(1)
      if (stream.match(number)) return "number"
      stream.next()
    }
    if (ch == "/") {
      if (stream.eat("*")) {
        state.tokenize = tokenComment;
        return tokenComment(stream, state);
      }
      if (stream.eat("/")) {
        stream.skipToEnd();
        return "comment";
      }
    }
    if (isOperatorChar.test(ch)) {
      while (!stream.match(/^\/[\/*]/, false) && stream.eat(isOperatorChar)) {}
      return "operator";
    }
    stream.eatWhile(isIdentifierChar);
    if (namespaceSeparator) while (stream.match(namespaceSeparator))
      stream.eatWhile(isIdentifierChar);

    var cur = stream.current();
    if (contains(keywords, cur)) {
      if (contains(blockKeywords, cur)) curPunc = "newstatement";
      if (contains(defKeywords, cur)) isDefKeyword = true;
      return "keyword";
    }
    if (contains(types, cur)) return "variable-3";
    if (contains(builtin, cur)) {
      if (contains(blockKeywords, cur)) curPunc = "newstatement";
      return "builtin";
    }
    if (contains(atoms, cur)) return "atom";
    return "variable";
  }

  function tokenString(quote) {
    return function(stream, state) {
      var escaped = false, next, end = false;
      while ((next = stream.next()) != null) {
        if (next == quote && !escaped) {end = true; break;}
        escaped = !escaped && next == "\\";
      }
      if (end || !(escaped || multiLineStrings))
        state.tokenize = null;
      return "string";
    };
  }

  function tokenComment(stream, state) {
    var maybeEnd = false, ch;
    while (ch = stream.next()) {
      if (ch == "/" && maybeEnd) {
        state.tokenize = null;
        break;
      }
      maybeEnd = (ch == "*");
    }
    return "comment";
  }

  function maybeEOL(stream, state) {
    if (parserConfig.typeFirstDefinitions && stream.eol() && isTopScope(state.context))
      state.typeAtEndOfLine = typeBefore(stream, state, stream.pos)
  }

  // Interface

  return {
    startState: function(basecolumn) {
      return {
        tokenize: null,
        context: new Context((basecolumn || 0) - indentUnit, 0, "top", null, false),
        indented: 0,
        startOfLine: true,
        prevToken: null
      };
    },

    token: function(stream, state) {
      var ctx = state.context;
      if (stream.sol()) {
        if (ctx.align == null) ctx.align = false;
        state.indented = stream.indentation();
        state.startOfLine = true;
      }
      if (stream.eatSpace()) { maybeEOL(stream, state); return null; }
      curPunc = isDefKeyword = null;
      var style = (state.tokenize || tokenBase)(stream, state);
      if (style == "comment" || style == "meta") return style;
      if (ctx.align == null) ctx.align = true;

      if (curPunc == ";" || curPunc == ":" || (curPunc == "," && stream.match(/^\s*(?:\/\/.*)?$/, false)))
        while (state.context.type == "statement") popContext(state);
      else if (curPunc == "{") pushContext(state, stream.column(), "}");
      else if (curPunc == "[") pushContext(state, stream.column(), "]");
      else if (curPunc == "(") pushContext(state, stream.column(), ")");
      else if (curPunc == "}") {
        while (ctx.type == "statement") ctx = popContext(state);
        if (ctx.type == "}") ctx = popContext(state);
        while (ctx.type == "statement") ctx = popContext(state);
      }
      else if (curPunc == ctx.type) popContext(state);
      else if (indentStatements &&
               (((ctx.type == "}" || ctx.type == "top") && curPunc != ";") ||
                (ctx.type == "statement" && curPunc == "newstatement"))) {
        pushContext(state, stream.column(), "statement", stream.current());
      }

      if (style == "variable" &&
          ((state.prevToken == "def" ||
            (parserConfig.typeFirstDefinitions && typeBefore(stream, state, stream.start) &&
             isTopScope(state.context) && stream.match(/^\s*\(/, false)))))
        style = "def";

      if (hooks.token) {
        var result = hooks.token(stream, state, style);
        if (result !== undefined) style = result;
      }

      if (style == "def" && parserConfig.styleDefs === false) style = "variable";

      state.startOfLine = false;
      state.prevToken = isDefKeyword ? "def" : style || curPunc;
      maybeEOL(stream, state);
      return style;
    },

    indent: function(state, textAfter) {
      if (state.tokenize != tokenBase && state.tokenize != null || state.typeAtEndOfLine) return CodeMirror.Pass;
      var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
      if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
      if (parserConfig.dontIndentStatements)
        while (ctx.type == "statement" && parserConfig.dontIndentStatements.test(ctx.info))
          ctx = ctx.prev
      if (hooks.indent) {
        var hook = hooks.indent(state, ctx, textAfter);
        if (typeof hook == "number") return hook
      }
      var closing = firstChar == ctx.type;
      var switchBlock = ctx.prev && ctx.prev.info == "switch";
      if (parserConfig.allmanIndentation && /[{(]/.test(firstChar)) {
        while (ctx.type != "top" && ctx.type != "}") ctx = ctx.prev
        return ctx.indented
      }
      if (ctx.type == "statement")
        return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
      if (ctx.align && (!dontAlignCalls || ctx.type != ")"))
        return ctx.column + (closing ? 0 : 1);
      if (ctx.type == ")" && !closing)
        return ctx.indented + statementIndentUnit;

      return ctx.indented + (closing ? 0 : indentUnit) +
        (!closing && switchBlock && !/^(?:case|default)\b/.test(textAfter) ? indentUnit : 0);
    },

    electricInput: indentSwitch ? /^\s*(?:case .*?:|default:|\{\}?|\})$/ : /^\s*[{}]$/,
    blockCommentStart: "/*",
    blockCommentEnd: "*/",
    lineComment: "//",
    fold: "brace"
  };
});

  function words(str) {
    var obj = {}, words = str.split(" ");
    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
    return obj;
  }
  function contains(words, word) {
    if (typeof words === "function") {
      return words(word);
    } else {
      return words.propertyIsEnumerable(word);
    }
  }
  var cKeywords = "auto if break case register continue return default do sizeof " +
    "static else struct switch extern typedef union for goto while enum const volatile";
  var cTypes = "int long char short double float unsigned signed void size_t ptrdiff_t";

  function cppHook(stream, state) {
    if (!state.startOfLine) return false
    for (var ch, next = null; ch = stream.peek();) {
      if (ch == "\\" && stream.match(/^.$/)) {
        next = cppHook
        break
      } else if (ch == "/" && stream.match(/^\/[\/\*]/, false)) {
        break
      }
      stream.next()
    }
    state.tokenize = next
    return "meta"
  }

  function pointerHook(_stream, state) {
    if (state.prevToken == "variable-3") return "variable-3";
    return false;
  }

  function cpp14Literal(stream) {
    stream.eatWhile(/[\w\.']/);
    return "number";
  }

  function cpp11StringHook(stream, state) {
    stream.backUp(1);
    // Raw strings.
    if (stream.match(/(R|u8R|uR|UR|LR)/)) {
      var match = stream.match(/"([^\s\\()]{0,16})\(/);
      if (!match) {
        return false;
      }
      state.cpp11RawStringDelim = match[1];
      state.tokenize = tokenRawString;
      return tokenRawString(stream, state);
    }
    // Unicode strings/chars.
    if (stream.match(/(u8|u|U|L)/)) {
      if (stream.match(/["']/, /* eat */ false)) {
        return "string";
      }
      return false;
    }
    // Ignore this hook.
    stream.next();
    return false;
  }

  function cppLooksLikeConstructor(word) {
    var lastTwo = /(\w+)::~?(\w+)$/.exec(word);
    return lastTwo && lastTwo[1] == lastTwo[2];
  }

  // C#-style strings where "" escapes a quote.
  function tokenAtString(stream, state) {
    var next;
    while ((next = stream.next()) != null) {
      if (next == '"' && !stream.eat('"')) {
        state.tokenize = null;
        break;
      }
    }
    return "string";
  }

  // C++11 raw string literal is <prefix>"<delim>( anything )<delim>", where
  // <delim> can be a string up to 16 characters long.
  function tokenRawString(stream, state) {
    // Escape characters that have special regex meanings.
    var delim = state.cpp11RawStringDelim.replace(/[^\w\s]/g, '\\$&');
    var match = stream.match(new RegExp(".*?\\)" + delim + '"'));
    if (match)
      state.tokenize = null;
    else
      stream.skipToEnd();
    return "string";
  }

  function def(mimes, mode) {
    if (typeof mimes == "string") mimes = [mimes];
    var words = [];
    function add(obj) {
      if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop))
        words.push(prop);
    }
    add(mode.keywords);
    add(mode.types);
    add(mode.builtin);
    add(mode.atoms);
    if (words.length) {
      mode.helperType = mimes[0];
      CodeMirror.registerHelper("hintWords", mimes[0], words);
    }

    for (var i = 0; i < mimes.length; ++i)
      CodeMirror.defineMIME(mimes[i], mode);
  }

  def(["text/x-csrc", "text/x-c", "text/x-chdr"], {
    name: "clike",
    keywords: words(cKeywords),
    types: words(cTypes + " bool _Complex _Bool float_t double_t intptr_t intmax_t " +
                 "int8_t int16_t int32_t int64_t uintptr_t uintmax_t uint8_t uint16_t " +
                 "uint32_t uint64_t"),
    blockKeywords: words("case do else for if switch while struct"),
    defKeywords: words("struct"),
    typeFirstDefinitions: true,
    atoms: words("null true false"),
    hooks: {"#": cppHook, "*": pointerHook},
    modeProps: {fold: ["brace", "include"]}
  });

  def(["text/x-c++src", "text/x-c++hdr"], {
    name: "clike",
    keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try explicit new " +
                    "static_cast typeid catch operator template typename class friend private " +
                    "this using const_cast inline public throw virtual delete mutable protected " +
                    "alignas alignof constexpr decltype nullptr noexcept thread_local final " +
                    "static_assert override"),
    types: words(cTypes + " bool wchar_t"),
    blockKeywords: words("catch class do else finally for if struct switch try while"),
    defKeywords: words("class namespace struct enum union"),
    typeFirstDefinitions: true,
    atoms: words("true false null"),
    dontIndentStatements: /^template$/,
    isIdentifierChar: /[\w\$_~\xa1-\uffff]/,
    hooks: {
      "#": cppHook,
      "*": pointerHook,
      "u": cpp11StringHook,
      "U": cpp11StringHook,
      "L": cpp11StringHook,
      "R": cpp11StringHook,
      "0": cpp14Literal,
      "1": cpp14Literal,
      "2": cpp14Literal,
      "3": cpp14Literal,
      "4": cpp14Literal,
      "5": cpp14Literal,
      "6": cpp14Literal,
      "7": cpp14Literal,
      "8": cpp14Literal,
      "9": cpp14Literal,
      token: function(stream, state, style) {
        if (style == "variable" && stream.peek() == "(" &&
            (state.prevToken == ";" || state.prevToken == null ||
             state.prevToken == "}") &&
            cppLooksLikeConstructor(stream.current()))
          return "def";
      }
    },
    namespaceSeparator: "::",
    modeProps: {fold: ["brace", "include"]}
  });

  def("text/x-java", {
    name: "clike",
    keywords: words("abstract assert break case catch class const continue default " +
                    "do else enum extends final finally float for goto if implements import " +
                    "instanceof interface native new package private protected public " +
                    "return static strictfp super switch synchronized this throw throws transient " +
                    "try volatile while @interface"),
    types: words("byte short int long float double boolean char void Boolean Byte Character Double Float " +
                 "Integer Long Number Object Short String StringBuffer StringBuilder Void"),
    blockKeywords: words("catch class do else finally for if switch try while"),
    defKeywords: words("class interface package enum @interface"),
    typeFirstDefinitions: true,
    atoms: words("true false null"),
    number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
    hooks: {
      "@": function(stream) {
        // Don't match the @interface keyword.
        if (stream.match('interface', false)) return false;

        stream.eatWhile(/[\w\$_]/);
        return "meta";
      }
    },
    modeProps: {fold: ["brace", "import"]}
  });

  def("text/x-csharp", {
    name: "clike",
    keywords: words("abstract as async await base break case catch checked class const continue" +
                    " default delegate do else enum event explicit extern finally fixed for" +
                    " foreach goto if implicit in interface internal is lock namespace new" +
                    " operator out override params private protected public readonly ref return sealed" +
                    " sizeof stackalloc static struct switch this throw try typeof unchecked" +
                    " unsafe using virtual void volatile while add alias ascending descending dynamic from get" +
                    " global group into join let orderby partial remove select set value var yield"),
    types: words("Action Boolean Byte Char DateTime DateTimeOffset Decimal Double Func" +
                 " Guid Int16 Int32 Int64 Object SByte Single String Task TimeSpan UInt16 UInt32" +
                 " UInt64 bool byte char decimal double short int long object"  +
                 " sbyte float string ushort uint ulong"),
    blockKeywords: words("catch class do else finally for foreach if struct switch try while"),
    defKeywords: words("class interface namespace struct var"),
    typeFirstDefinitions: true,
    atoms: words("true false null"),
    hooks: {
      "@": function(stream, state) {
        if (stream.eat('"')) {
          state.tokenize = tokenAtString;
          return tokenAtString(stream, state);
        }
        stream.eatWhile(/[\w\$_]/);
        return "meta";
      }
    }
  });

  function tokenTripleString(stream, state) {
    var escaped = false;
    while (!stream.eol()) {
      if (!escaped && stream.match('"""')) {
        state.tokenize = null;
        break;
      }
      escaped = stream.next() == "\\" && !escaped;
    }
    return "string";
  }

  def("text/x-scala", {
    name: "clike",
    keywords: words(

      /* scala */
      "abstract case catch class def do else extends final finally for forSome if " +
      "implicit import lazy match new null object override package private protected return " +
      "sealed super this throw trait try type val var while with yield _ " +

      /* package scala */
      "assert assume require print println printf readLine readBoolean readByte readShort " +
      "readChar readInt readLong readFloat readDouble"
    ),
    types: words(
      "AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " +
      "Enumeration Equiv Error Exception Fractional Function IndexedSeq Int Integral Iterable " +
      "Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " +
      "Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " +
      "StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector " +

      /* package java.lang */
      "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
      "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
      "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
      "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
    ),
    multiLineStrings: true,
    blockKeywords: words("catch class do else finally for forSome if match switch try while"),
    defKeywords: words("class def object package trait type val var"),
    atoms: words("true false null"),
    indentStatements: false,
    indentSwitch: false,
    isOperatorChar: /[+\-*&%=<>!?|\/#:@]/,
    hooks: {
      "@": function(stream) {
        stream.eatWhile(/[\w\$_]/);
        return "meta";
      },
      '"': function(stream, state) {
        if (!stream.match('""')) return false;
        state.tokenize = tokenTripleString;
        return state.tokenize(stream, state);
      },
      "'": function(stream) {
        stream.eatWhile(/[\w\$_\xa1-\uffff]/);
        return "atom";
      },
      "=": function(stream, state) {
        var cx = state.context
        if (cx.type == "}" && cx.align && stream.eat(">")) {
          state.context = new Context(cx.indented, cx.column, cx.type, cx.info, null, cx.prev)
          return "operator"
        } else {
          return false
        }
      }
    },
    modeProps: {closeBrackets: {triples: '"'}}
  });

  function tokenKotlinString(tripleString){
    return function (stream, state) {
      var escaped = false, next, end = false;
      while (!stream.eol()) {
        if (!tripleString && !escaped && stream.match('"') ) {end = true; break;}
        if (tripleString && stream.match('"""')) {end = true; break;}
        next = stream.next();
        if(!escaped && next == "$" && stream.match('{'))
          stream.skipTo("}");
        escaped = !escaped && next == "\\" && !tripleString;
      }
      if (end || !tripleString)
        state.tokenize = null;
      return "string";
    }
  }

  def("text/x-kotlin", {
    name: "clike",
    keywords: words(
      /*keywords*/
      "package as typealias class interface this super val " +
      "var fun for is in This throw return " +
      "break continue object if else while do try when !in !is as? " +

      /*soft keywords*/
      "file import where by get set abstract enum open inner override private public internal " +
      "protected catch finally out final vararg reified dynamic companion constructor init " +
      "sealed field property receiver param sparam lateinit data inline noinline tailrec " +
      "external annotation crossinline const operator infix suspend"
    ),
    types: words(
      /* package java.lang */
      "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
      "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
      "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
      "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
    ),
    intendSwitch: false,
    indentStatements: false,
    multiLineStrings: true,
    number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
    blockKeywords: words("catch class do else finally for if where try while enum"),
    defKeywords: words("class val var object package interface fun"),
    atoms: words("true false null this"),
    hooks: {
      '"': function(stream, state) {
        state.tokenize = tokenKotlinString(stream.match('""'));
        return state.tokenize(stream, state);
      }
    },
    modeProps: {closeBrackets: {triples: '"'}}
  });

  def(["x-shader/x-vertex", "x-shader/x-fragment"], {
    name: "clike",
    keywords: words("sampler1D sampler2D sampler3D samplerCube " +
                    "sampler1DShadow sampler2DShadow " +
                    "const attribute uniform varying " +
                    "break continue discard return " +
                    "for while do if else struct " +
                    "in out inout"),
    types: words("float int bool void " +
                 "vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " +
                 "mat2 mat3 mat4"),
    blockKeywords: words("for while do if else struct"),
    builtin: words("radians degrees sin cos tan asin acos atan " +
                    "pow exp log exp2 sqrt inversesqrt " +
                    "abs sign floor ceil fract mod min max clamp mix step smoothstep " +
                    "length distance dot cross normalize ftransform faceforward " +
                    "reflect refract matrixCompMult " +
                    "lessThan lessThanEqual greaterThan greaterThanEqual " +
                    "equal notEqual any all not " +
                    "texture1D texture1DProj texture1DLod texture1DProjLod " +
                    "texture2D texture2DProj texture2DLod texture2DProjLod " +
                    "texture3D texture3DProj texture3DLod texture3DProjLod " +
                    "textureCube textureCubeLod " +
                    "shadow1D shadow2D shadow1DProj shadow2DProj " +
                    "shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod " +
                    "dFdx dFdy fwidth " +
                    "noise1 noise2 noise3 noise4"),
    atoms: words("true false " +
                "gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex " +
                "gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 " +
                "gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 " +
                "gl_FogCoord gl_PointCoord " +
                "gl_Position gl_PointSize gl_ClipVertex " +
                "gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor " +
                "gl_TexCoord gl_FogFragCoord " +
                "gl_FragCoord gl_FrontFacing " +
                "gl_FragData gl_FragDepth " +
                "gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix " +
                "gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse " +
                "gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse " +
                "gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose " +
                "gl_ProjectionMatrixInverseTranspose " +
                "gl_ModelViewProjectionMatrixInverseTranspose " +
                "gl_TextureMatrixInverseTranspose " +
                "gl_NormalScale gl_DepthRange gl_ClipPlane " +
                "gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel " +
                "gl_FrontLightModelProduct gl_BackLightModelProduct " +
                "gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ " +
                "gl_FogParameters " +
                "gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords " +
                "gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats " +
                "gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " +
                "gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " +
                "gl_MaxDrawBuffers"),
    indentSwitch: false,
    hooks: {"#": cppHook},
    modeProps: {fold: ["brace", "include"]}
  });

  def("text/x-nesc", {
    name: "clike",
    keywords: words(cKeywords + "as atomic async call command component components configuration event generic " +
                    "implementation includes interface module new norace nx_struct nx_union post provides " +
                    "signal task uses abstract extends"),
    types: words(cTypes),
    blockKeywords: words("case do else for if switch while struct"),
    atoms: words("null true false"),
    hooks: {"#": cppHook},
    modeProps: {fold: ["brace", "include"]}
  });

  def("text/x-objectivec", {
    name: "clike",
    keywords: words(cKeywords + "inline restrict _Bool _Complex _Imaginary BOOL Class bycopy byref id IMP in " +
                    "inout nil oneway out Protocol SEL self super atomic nonatomic retain copy readwrite readonly"),
    types: words(cTypes),
    atoms: words("YES NO NULL NILL ON OFF true false"),
    hooks: {
      "@": function(stream) {
        stream.eatWhile(/[\w\$]/);
        return "keyword";
      },
      "#": cppHook,
      indent: function(_state, ctx, textAfter) {
        if (ctx.type == "statement" && /^@\w/.test(textAfter)) return ctx.indented
      }
    },
    modeProps: {fold: "brace"}
  });

  def("text/x-squirrel", {
    name: "clike",
    keywords: words("base break clone continue const default delete enum extends function in class" +
                    " foreach local resume return this throw typeof yield constructor instanceof static"),
    types: words(cTypes),
    blockKeywords: words("case catch class else for foreach if switch try while"),
    defKeywords: words("function local class"),
    typeFirstDefinitions: true,
    atoms: words("true false null"),
    hooks: {"#": cppHook},
    modeProps: {fold: ["brace", "include"]}
  });

  // Ceylon Strings need to deal with interpolation
  var stringTokenizer = null;
  function tokenCeylonString(type) {
    return function(stream, state) {
      var escaped = false, next, end = false;
      while (!stream.eol()) {
        if (!escaped && stream.match('"') &&
              (type == "single" || stream.match('""'))) {
          end = true;
          break;
        }
        if (!escaped && stream.match('``')) {
          stringTokenizer = tokenCeylonString(type);
          end = true;
          break;
        }
        next = stream.next();
        escaped = type == "single" && !escaped && next == "\\";
      }
      if (end)
          state.tokenize = null;
      return "string";
    }
  }

  def("text/x-ceylon", {
    name: "clike",
    keywords: words("abstracts alias assembly assert assign break case catch class continue dynamic else" +
                    " exists extends finally for function given if import in interface is let module new" +
                    " nonempty object of out outer package return satisfies super switch then this throw" +
                    " try value void while"),
    types: function(word) {
        // In Ceylon all identifiers that start with an uppercase are types
        var first = word.charAt(0);
        return (first === first.toUpperCase() && first !== first.toLowerCase());
    },
    blockKeywords: words("case catch class dynamic else finally for function if interface module new object switch try while"),
    defKeywords: words("class dynamic function interface module object package value"),
    builtin: words("abstract actual aliased annotation by default deprecated doc final formal late license" +
                   " native optional sealed see serializable shared suppressWarnings tagged throws variable"),
    isPunctuationChar: /[\[\]{}\(\),;\:\.`]/,
    isOperatorChar: /[+\-*&%=<>!?|^~:\/]/,
    numberStart: /[\d#$]/,
    number: /^(?:#[\da-fA-F_]+|\$[01_]+|[\d_]+[kMGTPmunpf]?|[\d_]+\.[\d_]+(?:[eE][-+]?\d+|[kMGTPmunpf]|)|)/i,
    multiLineStrings: true,
    typeFirstDefinitions: true,
    atoms: words("true false null larger smaller equal empty finished"),
    indentSwitch: false,
    styleDefs: false,
    hooks: {
      "@": function(stream) {
        stream.eatWhile(/[\w\$_]/);
        return "meta";
      },
      '"': function(stream, state) {
          state.tokenize = tokenCeylonString(stream.match('""') ? "triple" : "single");
          return state.tokenize(stream, state);
        },
      '`': function(stream, state) {
          if (!stringTokenizer || !stream.match('`')) return false;
          state.tokenize = stringTokenizer;
          stringTokenizer = null;
          return state.tokenize(stream, state);
        },
      "'": function(stream) {
        stream.eatWhile(/[\w\$_\xa1-\uffff]/);
        return "atom";
      },
      token: function(_stream, state, style) {
          if ((style == "variable" || style == "variable-3") &&
              state.prevToken == ".") {
            return "variable-2";
          }
        }
    },
    modeProps: {
        fold: ["brace", "import"],
        closeBrackets: {triples: '"'}
    }
  });

});

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../clike/clike"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../clike/clike"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  "use strict";

  function keywords(str) {
    var obj = {}, words = str.split(" ");
    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
    return obj;
  }

  // Helper for phpString
  function matchSequence(list, end, escapes) {
    if (list.length == 0) return phpString(end);
    return function (stream, state) {
      var patterns = list[0];
      for (var i = 0; i < patterns.length; i++) if (stream.match(patterns[i][0])) {
        state.tokenize = matchSequence(list.slice(1), end);
        return patterns[i][1];
      }
      state.tokenize = phpString(end, escapes);
      return "string";
    };
  }
  function phpString(closing, escapes) {
    return function(stream, state) { return phpString_(stream, state, closing, escapes); };
  }
  function phpString_(stream, state, closing, escapes) {
    // "Complex" syntax
    if (escapes !== false && stream.match("${", false) || stream.match("{$", false)) {
      state.tokenize = null;
      return "string";
    }

    // Simple syntax
    if (escapes !== false && stream.match(/^\$[a-zA-Z_][a-zA-Z0-9_]*/)) {
      // After the variable name there may appear array or object operator.
      if (stream.match("[", false)) {
        // Match array operator
        state.tokenize = matchSequence([
          [["[", null]],
          [[/\d[\w\.]*/, "number"],
           [/\$[a-zA-Z_][a-zA-Z0-9_]*/, "variable-2"],
           [/[\w\$]+/, "variable"]],
          [["]", null]]
        ], closing, escapes);
      }
      if (stream.match(/\-\>\w/, false)) {
        // Match object operator
        state.tokenize = matchSequence([
          [["->", null]],
          [[/[\w]+/, "variable"]]
        ], closing, escapes);
      }
      return "variable-2";
    }

    var escaped = false;
    // Normal string
    while (!stream.eol() &&
           (escaped || escapes === false ||
            (!stream.match("{$", false) &&
             !stream.match(/^(\$[a-zA-Z_][a-zA-Z0-9_]*|\$\{)/, false)))) {
      if (!escaped && stream.match(closing)) {
        state.tokenize = null;
        state.tokStack.pop(); state.tokStack.pop();
        break;
      }
      escaped = stream.next() == "\\" && !escaped;
    }
    return "string";
  }

  var phpKeywords = "abstract and array as break case catch class clone const continue declare default " +
    "do else elseif enddeclare endfor endforeach endif endswitch endwhile extends final " +
    "for foreach function global goto if implements interface instanceof namespace " +
    "new or private protected public static switch throw trait try use var while xor " +
    "die echo empty exit eval include include_once isset list require require_once return " +
    "print unset __halt_compiler self static parent yield insteadof finally";
  var phpAtoms = "true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__ __TRAIT__";
  var phpBuiltin = "func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex hex2bin sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents file_put_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists array_intersect_key array_combine array_column pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport http_response_code get_declared_traits getimagesizefromstring socket_import_stream stream_set_chunk_size trait_exists header_register_callback class_uses session_status session_register_shutdown echo print global static exit array empty eval isset unset die include require include_once require_once json_decode json_encode json_last_error json_last_error_msg curl_close curl_copy_handle curl_errno curl_error curl_escape curl_exec curl_file_create curl_getinfo curl_init curl_multi_add_handle curl_multi_close curl_multi_exec curl_multi_getcontent curl_multi_info_read curl_multi_init curl_multi_remove_handle curl_multi_select curl_multi_setopt curl_multi_strerror curl_pause curl_reset curl_setopt_array curl_setopt curl_share_close curl_share_init curl_share_setopt curl_strerror curl_unescape curl_version mysqli_affected_rows mysqli_autocommit mysqli_change_user mysqli_character_set_name mysqli_close mysqli_commit mysqli_connect_errno mysqli_connect_error mysqli_connect mysqli_data_seek mysqli_debug mysqli_dump_debug_info mysqli_errno mysqli_error_list mysqli_error mysqli_fetch_all mysqli_fetch_array mysqli_fetch_assoc mysqli_fetch_field_direct mysqli_fetch_field mysqli_fetch_fields mysqli_fetch_lengths mysqli_fetch_object mysqli_fetch_row mysqli_field_count mysqli_field_seek mysqli_field_tell mysqli_free_result mysqli_get_charset mysqli_get_client_info mysqli_get_client_stats mysqli_get_client_version mysqli_get_connection_stats mysqli_get_host_info mysqli_get_proto_info mysqli_get_server_info mysqli_get_server_version mysqli_info mysqli_init mysqli_insert_id mysqli_kill mysqli_more_results mysqli_multi_query mysqli_next_result mysqli_num_fields mysqli_num_rows mysqli_options mysqli_ping mysqli_prepare mysqli_query mysqli_real_connect mysqli_real_escape_string mysqli_real_query mysqli_reap_async_query mysqli_refresh mysqli_rollback mysqli_select_db mysqli_set_charset mysqli_set_local_infile_default mysqli_set_local_infile_handler mysqli_sqlstate mysqli_ssl_set mysqli_stat mysqli_stmt_init mysqli_store_result mysqli_thread_id mysqli_thread_safe mysqli_use_result mysqli_warning_count";
  CodeMirror.registerHelper("hintWords", "php", [phpKeywords, phpAtoms, phpBuiltin].join(" ").split(" "));
  CodeMirror.registerHelper("wordChars", "php", /[\w$]/);

  var phpConfig = {
    name: "clike",
    helperType: "php",
    keywords: keywords(phpKeywords),
    blockKeywords: keywords("catch do else elseif for foreach if switch try while finally"),
    defKeywords: keywords("class function interface namespace trait"),
    atoms: keywords(phpAtoms),
    builtin: keywords(phpBuiltin),
    multiLineStrings: true,
    hooks: {
      "$": function(stream) {
        stream.eatWhile(/[\w\$_]/);
        return "variable-2";
      },
      "<": function(stream, state) {
        var before;
        if (before = stream.match(/<<\s*/)) {
          var quoted = stream.eat(/['"]/);
          stream.eatWhile(/[\w\.]/);
          var delim = stream.current().slice(before[0].length + (quoted ? 2 : 1));
          if (quoted) stream.eat(quoted);
          if (delim) {
            (state.tokStack || (state.tokStack = [])).push(delim, 0);
            state.tokenize = phpString(delim, quoted != "'");
            return "string";
          }
        }
        return false;
      },
      "#": function(stream) {
        while (!stream.eol() && !stream.match("?>", false)) stream.next();
        return "comment";
      },
      "/": function(stream) {
        if (stream.eat("/")) {
          while (!stream.eol() && !stream.match("?>", false)) stream.next();
          return "comment";
        }
        return false;
      },
      '"': function(_stream, state) {
        (state.tokStack || (state.tokStack = [])).push('"', 0);
        state.tokenize = phpString('"');
        return "string";
      },
      "{": function(_stream, state) {
        if (state.tokStack && state.tokStack.length)
          state.tokStack[state.tokStack.length - 1]++;
        return false;
      },
      "}": function(_stream, state) {
        if (state.tokStack && state.tokStack.length > 0 &&
            !--state.tokStack[state.tokStack.length - 1]) {
          state.tokenize = phpString(state.tokStack[state.tokStack.length - 2]);
        }
        return false;
      }
    }
  };

  CodeMirror.defineMode("php", function(config, parserConfig) {
    var htmlMode = CodeMirror.getMode(config, "text/html");
    var phpMode = CodeMirror.getMode(config, phpConfig);

    function dispatch(stream, state) {
      var isPHP = state.curMode == phpMode;
      if (stream.sol() && state.pending && state.pending != '"' && state.pending != "'") state.pending = null;
      if (!isPHP) {
        if (stream.match(/^<\?\w*/)) {
          state.curMode = phpMode;
          if (!state.php) state.php = CodeMirror.startState(phpMode, htmlMode.indent(state.html, ""))
          state.curState = state.php;
          return "meta";
        }
        if (state.pending == '"' || state.pending == "'") {
          while (!stream.eol() && stream.next() != state.pending) {}
          var style = "string";
        } else if (state.pending && stream.pos < state.pending.end) {
          stream.pos = state.pending.end;
          var style = state.pending.style;
        } else {
          var style = htmlMode.token(stream, state.curState);
        }
        if (state.pending) state.pending = null;
        var cur = stream.current(), openPHP = cur.search(/<\?/), m;
        if (openPHP != -1) {
          if (style == "string" && (m = cur.match(/[\'\"]$/)) && !/\?>/.test(cur)) state.pending = m[0];
          else state.pending = {end: stream.pos, style: style};
          stream.backUp(cur.length - openPHP);
        }
        return style;
      } else if (isPHP && state.php.tokenize == null && stream.match("?>")) {
        state.curMode = htmlMode;
        state.curState = state.html;
        if (!state.php.context.prev) state.php = null;
        return "meta";
      } else {
        return phpMode.token(stream, state.curState);
      }
    }

    return {
      startState: function() {
        var html = CodeMirror.startState(htmlMode)
        var php = parserConfig.startOpen ? CodeMirror.startState(phpMode) : null
        return {html: html,
                php: php,
                curMode: parserConfig.startOpen ? phpMode : htmlMode,
                curState: parserConfig.startOpen ? php : html,
                pending: null};
      },

      copyState: function(state) {
        var html = state.html, htmlNew = CodeMirror.copyState(htmlMode, html),
            php = state.php, phpNew = php && CodeMirror.copyState(phpMode, php), cur;
        if (state.curMode == htmlMode) cur = htmlNew;
        else cur = phpNew;
        return {html: htmlNew, php: phpNew, curMode: state.curMode, curState: cur,
                pending: state.pending};
      },

      token: dispatch,

      indent: function(state, textAfter) {
        if ((state.curMode != phpMode && /^\s*<\//.test(textAfter)) ||
            (state.curMode == phpMode && /^\?>/.test(textAfter)))
          return htmlMode.indent(state.html, textAfter);
        return state.curMode.indent(state.curState, textAfter);
      },

      blockCommentStart: "/*",
      blockCommentEnd: "*/",
      lineComment: "//",

      innerMode: function(state) { return {state: state.curState, mode: state.curMode}; }
    };
  }, "htmlmixed", "clike");

  CodeMirror.defineMIME("application/x-httpd-php", "php");
  CodeMirror.defineMIME("application/x-httpd-php-open", {name: "php", startOpen: true});
  CodeMirror.defineMIME("text/x-php", phpConfig);
});

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
"use strict";

CodeMirror.defineMode("sql", function(config, parserConfig) {
  "use strict";

  var client         = parserConfig.client || {},
      atoms          = parserConfig.atoms || {"false": true, "true": true, "null": true},
      builtin        = parserConfig.builtin || {},
      keywords       = parserConfig.keywords || {},
      operatorChars  = parserConfig.operatorChars || /^[*+\-%<>!=&|~^]/,
      support        = parserConfig.support || {},
      hooks          = parserConfig.hooks || {},
      dateSQL        = parserConfig.dateSQL || {"date" : true, "time" : true, "timestamp" : true};

  function tokenBase(stream, state) {
    var ch = stream.next();

    // call hooks from the mime type
    if (hooks[ch]) {
      var result = hooks[ch](stream, state);
      if (result !== false) return result;
    }

    if (support.hexNumber &&
      ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/))
      || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/))) {
      // hex
      // ref: http://dev.mysql.com/doc/refman/5.5/en/hexadecimal-literals.html
      return "number";
    } else if (support.binaryNumber &&
      (((ch == "b" || ch == "B") && stream.match(/^'[01]+'/))
      || (ch == "0" && stream.match(/^b[01]+/)))) {
      // bitstring
      // ref: http://dev.mysql.com/doc/refman/5.5/en/bit-field-literals.html
      return "number";
    } else if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) {
      // numbers
      // ref: http://dev.mysql.com/doc/refman/5.5/en/number-literals.html
          stream.match(/^[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/);
      support.decimallessFloat && stream.eat('.');
      return "number";
    } else if (ch == "?" && (stream.eatSpace() || stream.eol() || stream.eat(";"))) {
      // placeholders
      return "variable-3";
    } else if (ch == "'" || (ch == '"' && support.doubleQuote)) {
      // strings
      // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
      state.tokenize = tokenLiteral(ch);
      return state.tokenize(stream, state);
    } else if ((((support.nCharCast && (ch == "n" || ch == "N"))
        || (support.charsetCast && ch == "_" && stream.match(/[a-z][a-z0-9]*/i)))
        && (stream.peek() == "'" || stream.peek() == '"'))) {
      // charset casting: _utf8'str', N'str', n'str'
      // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
      return "keyword";
    } else if (/^[\(\),\;\[\]]/.test(ch)) {
      // no highlighting
      return null;
    } else if (support.commentSlashSlash && ch == "/" && stream.eat("/")) {
      // 1-line comment
      stream.skipToEnd();
      return "comment";
    } else if ((support.commentHash && ch == "#")
        || (ch == "-" && stream.eat("-") && (!support.commentSpaceRequired || stream.eat(" ")))) {
      // 1-line comments
      // ref: https://kb.askmonty.org/en/comment-syntax/
      stream.skipToEnd();
      return "comment";
    } else if (ch == "/" && stream.eat("*")) {
      // multi-line comments
      // ref: https://kb.askmonty.org/en/comment-syntax/
      state.tokenize = tokenComment;
      return state.tokenize(stream, state);
    } else if (ch == ".") {
      // .1 for 0.1
      if (support.zerolessFloat && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i)) {
        return "number";
      }
      // .table_name (ODBC)
      // // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
      if (support.ODBCdotTable && stream.match(/^[a-zA-Z_]+/)) {
        return "variable-2";
      }
    } else if (operatorChars.test(ch)) {
      // operators
      stream.eatWhile(operatorChars);
      return null;
    } else if (ch == '{' &&
        (stream.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/) || stream.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/))) {
      // dates (weird ODBC syntax)
      // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html
      return "number";
    } else {
      stream.eatWhile(/^[_\w\d]/);
      var word = stream.current().toLowerCase();
      // dates (standard SQL syntax)
      // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html
      if (dateSQL.hasOwnProperty(word) && (stream.match(/^( )+'[^']*'/) || stream.match(/^( )+"[^"]*"/)))
        return "number";
      if (atoms.hasOwnProperty(word)) return "atom";
      if (builtin.hasOwnProperty(word)) return "builtin";
      if (keywords.hasOwnProperty(word)) return "keyword";
      if (client.hasOwnProperty(word)) return "string-2";
      return null;
    }
  }

  // 'string', with char specified in quote escaped by '\'
  function tokenLiteral(quote) {
    return function(stream, state) {
      var escaped = false, ch;
      while ((ch = stream.next()) != null) {
        if (ch == quote && !escaped) {
          state.tokenize = tokenBase;
          break;
        }
        escaped = !escaped && ch == "\\";
      }
      return "string";
    };
  }
  function tokenComment(stream, state) {
    while (true) {
      if (stream.skipTo("*")) {
        stream.next();
        if (stream.eat("/")) {
          state.tokenize = tokenBase;
          break;
        }
      } else {
        stream.skipToEnd();
        break;
      }
    }
    return "comment";
  }

  function pushContext(stream, state, type) {
    state.context = {
      prev: state.context,
      indent: stream.indentation(),
      col: stream.column(),
      type: type
    };
  }

  function popContext(state) {
    state.indent = state.context.indent;
    state.context = state.context.prev;
  }

  return {
    startState: function() {
      return {tokenize: tokenBase, context: null};
    },

    token: function(stream, state) {
      if (stream.sol()) {
        if (state.context && state.context.align == null)
          state.context.align = false;
      }
      if (stream.eatSpace()) return null;

      var style = state.tokenize(stream, state);
      if (style == "comment") return style;

      if (state.context && state.context.align == null)
        state.context.align = true;

      var tok = stream.current();
      if (tok == "(")
        pushContext(stream, state, ")");
      else if (tok == "[")
        pushContext(stream, state, "]");
      else if (state.context && state.context.type == tok)
        popContext(state);
      return style;
    },

    indent: function(state, textAfter) {
      var cx = state.context;
      if (!cx) return CodeMirror.Pass;
      var closing = textAfter.charAt(0) == cx.type;
      if (cx.align) return cx.col + (closing ? 0 : 1);
      else return cx.indent + (closing ? 0 : config.indentUnit);
    },

    blockCommentStart: "/*",
    blockCommentEnd: "*/",
    lineComment: support.commentSlashSlash ? "//" : support.commentHash ? "#" : null
  };
});

(function() {
  "use strict";

  // `identifier`
  function hookIdentifier(stream) {
    // MySQL/MariaDB identifiers
    // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
    var ch;
    while ((ch = stream.next()) != null) {
      if (ch == "`" && !stream.eat("`")) return "variable-2";
    }
    stream.backUp(stream.current().length - 1);
    return stream.eatWhile(/\w/) ? "variable-2" : null;
  }

  // "identifier"
  function hookIdentifierDoublequote(stream) {
    // Standard SQL /SQLite identifiers
    // ref: http://web.archive.org/web/20160813185132/http://savage.net.au/SQL/sql-99.bnf.html#delimited%20identifier
    // ref: http://sqlite.org/lang_keywords.html
    var ch;
    while ((ch = stream.next()) != null) {
      if (ch == "\"" && !stream.eat("\"")) return "variable-2";
    }
    stream.backUp(stream.current().length - 1);
    return stream.eatWhile(/\w/) ? "variable-2" : null;
  }

  // variable token
  function hookVar(stream) {
    // variables
    // @@prefix.varName @varName
    // varName can be quoted with ` or ' or "
    // ref: http://dev.mysql.com/doc/refman/5.5/en/user-variables.html
    if (stream.eat("@")) {
      stream.match(/^session\./);
      stream.match(/^local\./);
      stream.match(/^global\./);
    }

    if (stream.eat("'")) {
      stream.match(/^.*'/);
      return "variable-2";
    } else if (stream.eat('"')) {
      stream.match(/^.*"/);
      return "variable-2";
    } else if (stream.eat("`")) {
      stream.match(/^.*`/);
      return "variable-2";
    } else if (stream.match(/^[0-9a-zA-Z$\.\_]+/)) {
      return "variable-2";
    }
    return null;
  };

  // short client keyword token
  function hookClient(stream) {
    // \N means NULL
    // ref: http://dev.mysql.com/doc/refman/5.5/en/null-values.html
    if (stream.eat("N")) {
        return "atom";
    }
    // \g, etc
    // ref: http://dev.mysql.com/doc/refman/5.5/en/mysql-commands.html
    return stream.match(/^[a-zA-Z.#!?]/) ? "variable-2" : null;
  }

  // these keywords are used by all SQL dialects (however, a mode can still overwrite it)
  var sqlKeywords = "alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit ";

  // turn a space-separated list into an array
  function set(str) {
    var obj = {}, words = str.split(" ");
    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
    return obj;
  }

  // A generic SQL Mode. It's not a standard, it just try to support what is generally supported
  CodeMirror.defineMIME("text/x-sql", {
    name: "sql",
    keywords: set(sqlKeywords + "begin"),
    builtin: set("bool boolean bit blob enum long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision real date datetime year unsigned signed decimal numeric"),
    atoms: set("false true null unknown"),
    operatorChars: /^[*+\-%<>!=]/,
    dateSQL: set("date time timestamp"),
    support: set("ODBCdotTable doubleQuote binaryNumber hexNumber")
  });

  CodeMirror.defineMIME("text/x-mssql", {
    name: "sql",
    client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),
    keywords: set(sqlKeywords + "begin trigger proc view index for add constraint key primary foreign collate clustered nonclustered declare exec"),
    builtin: set("bigint numeric bit smallint decimal smallmoney int tinyint money float real char varchar text nchar nvarchar ntext binary varbinary image cursor timestamp hierarchyid uniqueidentifier sql_variant xml table "),
    atoms: set("false true null unknown"),
    operatorChars: /^[*+\-%<>!=]/,
    dateSQL: set("date datetimeoffset datetime2 smalldatetime datetime time"),
    hooks: {
      "@":   hookVar
    }
  });

  CodeMirror.defineMIME("text/x-mysql", {
    name: "sql",
    client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),
    keywords: set(sqlKeywords + "accessible action add after algorithm all analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general get global grant grants group group_concat handler hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"),
    builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"),
    atoms: set("false true null unknown"),
    operatorChars: /^[*+\-%<>!=&|^]/,
    dateSQL: set("date time timestamp"),
    support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),
    hooks: {
      "@":   hookVar,
      "`":   hookIdentifier,
      "\\":  hookClient
    }
  });

  CodeMirror.defineMIME("text/x-mariadb", {
    name: "sql",
    client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),
    keywords: set(sqlKeywords + "accessible action add after algorithm all always analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general generated get global grant grants group groupby_concat handler hard hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password persistent phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show shutdown signal slave slow smallint snapshot soft soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views virtual warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"),
    builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"),
    atoms: set("false true null unknown"),
    operatorChars: /^[*+\-%<>!=&|^]/,
    dateSQL: set("date time timestamp"),
    support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),
    hooks: {
      "@":   hookVar,
      "`":   hookIdentifier,
      "\\":  hookClient
    }
  });

  // provided by the phpLiteAdmin project - phpliteadmin.org
  CodeMirror.defineMIME("text/x-sqlite", {
    name: "sql",
    // commands of the official SQLite client, ref: https://www.sqlite.org/cli.html#dotcmd
    client: set("auth backup bail binary changes check clone databases dbinfo dump echo eqp exit explain fullschema headers help import imposter indexes iotrace limit lint load log mode nullvalue once open output print prompt quit read restore save scanstats schema separator session shell show stats system tables testcase timeout timer trace vfsinfo vfslist vfsname width"),
    // ref: http://sqlite.org/lang_keywords.html
    keywords: set(sqlKeywords + "abort action add after all analyze attach autoincrement before begin cascade case cast check collate column commit conflict constraint cross current_date current_time current_timestamp database default deferrable deferred detach each else end escape except exclusive exists explain fail for foreign full glob if ignore immediate index indexed initially inner instead intersect isnull key left limit match natural no notnull null of offset outer plan pragma primary query raise recursive references regexp reindex release rename replace restrict right rollback row savepoint temp temporary then to transaction trigger unique using vacuum view virtual when with without"),
    // SQLite is weakly typed, ref: http://sqlite.org/datatype3.html. This is just a list of some common types.
    builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text clob bigint int int2 int8 integer float double char varchar date datetime year unsigned signed numeric real"),
    // ref: http://sqlite.org/syntax/literal-value.html
    atoms: set("null current_date current_time current_timestamp"),
    // ref: http://sqlite.org/lang_expr.html#binaryops
    operatorChars: /^[*+\-%<>!=&|/~]/,
    // SQLite is weakly typed, ref: http://sqlite.org/datatype3.html. This is just a list of some common types.
    dateSQL: set("date time timestamp datetime"),
    support: set("decimallessFloat zerolessFloat"),
    identifierQuote: "\"",  //ref: http://sqlite.org/lang_keywords.html
    hooks: {
      // bind-parameters ref:http://sqlite.org/lang_expr.html#varparam
      "@":   hookVar,
      ":":   hookVar,
      "?":   hookVar,
      "$":   hookVar,
      // The preferred way to escape Identifiers is using double quotes, ref: http://sqlite.org/lang_keywords.html
      "\"":   hookIdentifierDoublequote,
      // there is also support for backtics, ref: http://sqlite.org/lang_keywords.html
      "`":   hookIdentifier
    }
  });

  // the query language used by Apache Cassandra is called CQL, but this mime type
  // is called Cassandra to avoid confusion with Contextual Query Language
  CodeMirror.defineMIME("text/x-cassandra", {
    name: "sql",
    client: { },
    keywords: set("add all allow alter and any apply as asc authorize batch begin by clustering columnfamily compact consistency count create custom delete desc distinct drop each_quorum exists filtering from grant if in index insert into key keyspace keyspaces level limit local_one local_quorum modify nan norecursive nosuperuser not of on one order password permission permissions primary quorum rename revoke schema select set storage superuser table three to token truncate ttl two type unlogged update use user users using values where with writetime"),
    builtin: set("ascii bigint blob boolean counter decimal double float frozen inet int list map static text timestamp timeuuid tuple uuid varchar varint"),
    atoms: set("false true infinity NaN"),
    operatorChars: /^[<>=]/,
    dateSQL: { },
    support: set("commentSlashSlash decimallessFloat"),
    hooks: { }
  });

  // this is based on Peter Raganitsch's 'plsql' mode
  CodeMirror.defineMIME("text/x-plsql", {
    name:       "sql",
    client:     set("appinfo arraysize autocommit autoprint autorecovery autotrace blockterminator break btitle cmdsep colsep compatibility compute concat copycommit copytypecheck define describe echo editfile embedded escape exec execute feedback flagger flush heading headsep instance linesize lno loboffset logsource long longchunksize markup native newpage numformat numwidth pagesize pause pno recsep recsepchar release repfooter repheader serveroutput shiftinout show showmode size spool sqlblanklines sqlcase sqlcode sqlcontinue sqlnumber sqlpluscompatibility sqlprefix sqlprompt sqlterminator suffix tab term termout time timing trimout trimspool ttitle underline verify version wrap"),
    keywords:   set("abort accept access add all alter and any array arraylen as asc assert assign at attributes audit authorization avg base_table begin between binary_integer body boolean by case cast char char_base check close cluster clusters colauth column comment commit compress connect connected constant constraint crash create current currval cursor data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete desc digits dispose distinct do drop else elseif elsif enable end entry escape exception exception_init exchange exclusive exists exit external fast fetch file for force form from function generic goto grant group having identified if immediate in increment index indexes indicator initial initrans insert interface intersect into is key level library like limited local lock log logging long loop master maxextents maxtrans member minextents minus mislabel mode modify multiset new next no noaudit nocompress nologging noparallel not nowait number_base object of off offline on online only open option or order out package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior private privileges procedure public raise range raw read rebuild record ref references refresh release rename replace resource restrict return returning returns reverse revoke rollback row rowid rowlabel rownum rows run savepoint schema segment select separate session set share snapshot some space split sql start statement storage subtype successful synonym tabauth table tables tablespace task terminate then to trigger truncate type union unique unlimited unrecoverable unusable update use using validate value values variable view views when whenever where while with work"),
    builtin:    set("abs acos add_months ascii asin atan atan2 average bfile bfilename bigserial bit blob ceil character chartorowid chr clob concat convert cos cosh count dec decode deref dual dump dup_val_on_index empty error exp false float floor found glb greatest hextoraw initcap instr instrb int integer isopen last_day least length lengthb ln lower lpad ltrim lub make_ref max min mlslabel mod months_between natural naturaln nchar nclob new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null number numeric nvarchar2 nvl others power rawtohex real reftohex round rowcount rowidtochar rowtype rpad rtrim serial sign signtype sin sinh smallint soundex sqlcode sqlerrm sqrt stddev string substr substrb sum sysdate tan tanh to_char text to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid unlogged upper user userenv varchar varchar2 variance varying vsize xml"),
    operatorChars: /^[*+\-%<>!=~]/,
    dateSQL:    set("date time timestamp"),
    support:    set("doubleQuote nCharCast zerolessFloat binaryNumber hexNumber")
  });

  // Created to support specific hive keywords
  CodeMirror.defineMIME("text/x-hive", {
    name: "sql",
    keywords: set("select alter $elem$ $key$ $value$ add after all analyze and archive as asc before between binary both bucket buckets by cascade case cast change cluster clustered clusterstatus collection column columns comment compute concatenate continue create cross cursor data database databases dbproperties deferred delete delimited desc describe directory disable distinct distribute drop else enable end escaped exclusive exists explain export extended external false fetch fields fileformat first format formatted from full function functions grant group having hold_ddltime idxproperties if import in index indexes inpath inputdriver inputformat insert intersect into is items join keys lateral left like limit lines load local location lock locks mapjoin materialized minus msck no_drop nocompress not of offline on option or order out outer outputdriver outputformat overwrite partition partitioned partitions percent plus preserve procedure purge range rcfile read readonly reads rebuild recordreader recordwriter recover reduce regexp rename repair replace restrict revoke right rlike row schema schemas semi sequencefile serde serdeproperties set shared show show_database sort sorted ssl statistics stored streamtable table tables tablesample tblproperties temporary terminated textfile then tmp to touch transform trigger true unarchive undo union uniquejoin unlock update use using utc utc_tmestamp view when where while with"),
    builtin: set("bool boolean long timestamp tinyint smallint bigint int float double date datetime unsigned string array struct map uniontype"),
    atoms: set("false true null unknown"),
    operatorChars: /^[*+\-%<>!=]/,
    dateSQL: set("date timestamp"),
    support: set("ODBCdotTable doubleQuote binaryNumber hexNumber")
  });

  CodeMirror.defineMIME("text/x-pgsql", {
    name: "sql",
    client: set("source"),
    // http://www.postgresql.org/docs/9.5/static/sql-keywords-appendix.html
    keywords: set(sqlKeywords + "a abort abs absent absolute access according action ada add admin after aggregate all allocate also always analyse analyze any are array array_agg array_max_cardinality asensitive assertion assignment asymmetric at atomic attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli binary bit_length blob blocked bom both breadth c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain characteristics characters character_length character_set_catalog character_set_name character_set_schema char_length check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column columns column_name command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constraint constraints constraint_catalog constraint_name constraint_schema constructor contains content continue control conversion convert copy corr corresponding cost covar_pop covar_samp cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datetime_interval_code datetime_interval_precision day db deallocate dec declare default defaults deferrable deferred defined definer degree delimiter delimiters dense_rank depth deref derived describe descriptor deterministic diagnostics dictionary disable discard disconnect dispatch dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain dynamic dynamic_function dynamic_function_code each element else empty enable encoding encrypted end end-exec end_frame end_partition enforced enum equals escape event every except exception exclude excluding exclusive exec execute exists exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreign fortran forward found frame_row free freeze fs full function functions fusion g general generated get global go goto grant granted greatest grouping groups handler header hex hierarchy hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import including increment indent index indexes indicator inherit inherits initially inline inner inout input insensitive instance instantiable instead integrity intersect intersection invoker isnull isolation k key key_member key_type label lag language large last last_value lateral lead leading leakproof least left length level library like_regex link listen ln load local localtime localtimestamp location locator lock locked logged lower m map mapping match matched materialized max maxvalue max_cardinality member merge message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized nothing notify notnull nowait nth_value ntile null nullable nullif nulls number object occurrences_regex octets octet_length of off offset oids old only open operator option options ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password percent percentile_cont percentile_disc percent_rank period permission placing plans pli policy portion position position_regex power precedes preceding prepare prepared preserve primary prior privileges procedural procedure program public quote range rank read reads reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict result return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns revoke right role rollback rollup routine routine_catalog routine_name routine_schema row rows row_count row_number rule savepoint scale schema schema_name scope scope_catalog scope_name scope_schema scroll search second section security selective self sensitive sequence sequences serializable server server_name session session_user setof sets share show similar simple size skip snapshot some source space specific specifictype specific_name sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset substring substring_regex succeeds sum symmetric sysid system system_time system_user t tables tablesample tablespace table_name temp template temporary then ties timezone_hour timezone_minute to token top_level_count trailing transaction transactions_committed transactions_rolled_back transaction_active transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted unique unknown unlink unlisten unlogged unnamed unnest until untyped upper uri usage user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of varbinary variadic var_pop var_samp verbose version versioning view views volatile when whenever whitespace width_bucket window within work wrapper write xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes loop repeat"),
    // http://www.postgresql.org/docs/9.5/static/datatype.html
    builtin: set("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float8 inet integer int int4 interval json jsonb line lseg macaddr money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"),
    atoms: set("false true null unknown"),
    operatorChars: /^[*+\-%<>!=&|^\/#@?~]/,
    dateSQL: set("date time timestamp"),
    support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast")
  });

  // Google's SQL-like query language, GQL
  CodeMirror.defineMIME("text/x-gql", {
    name: "sql",
    keywords: set("ancestor and asc by contains desc descendant distinct from group has in is limit offset on order select superset where"),
    atoms: set("false true"),
    builtin: set("blob datetime first key __key__ string integer double boolean null"),
    operatorChars: /^[*+\-%<>!=]/
  });
}());

});

/*
  How Properties of Mime Types are used by SQL Mode
  =================================================

  keywords:
    A list of keywords you want to be highlighted.
  builtin:
    A list of builtin types you want to be highlighted (if you want types to be of class "builtin" instead of "keyword").
  operatorChars:
    All characters that must be handled as operators.
  client:
    Commands parsed and executed by the client (not the server).
  support:
    A list of supported syntaxes which are not common, but are supported by more than 1 DBMS.
    * ODBCdotTable: .tableName
    * zerolessFloat: .1
    * doubleQuote
    * nCharCast: N'string'
    * charsetCast: _utf8'string'
    * commentHash: use # char for comments
    * commentSlashSlash: use // for comments
    * commentSpaceRequired: require a space after -- for comments
  atoms:
    Keywords that must be highlighted as atoms,. Some DBMS's support more atoms than others:
    UNKNOWN, INFINITY, UNDERFLOW, NaN...
  dateSQL:
    Used for date/time SQL standard syntax, because not all DBMS's support same temporal types.
*/

