// Вспомогательные функции
function remClassName(n, cn)
{
	n.className = n.className.replace(new RegExp('(^|\\s)' + cn + '(?=\\s|$)', 'g'), '')
	return cn
}

function noBubble(e)
{
	if (window.event)
		window.event.cancelBubble = true
	else
		e.stopPropagation()
	return false
}

function eTarget(e)
{
	return e.target || e.srcElement
}



isSafari = navigator.userAgent.toLowerCase().indexOf('safari') != -1
isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') != -1

Textareas =
{
	last_val: null,
	save_timer	: 0,
	save_timeout: 0,
	font_timer	: 0,
	save	: true,
	
	rx_link: function()
	{
		var rex = '((([a-z]{3,5}:\/\/)|(www\\.))([\\w-]+\\.)+[\\w-]+(/[\\w-\\+./?%:;&=#\\[\\]]*)?)|(\\w+([-+.\’]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*)'
		this.rx_link = (/*@cc_on !@*/0) ? new RegExp(rex) : new RegExp(rex, 'g')
	},
	
	fontInc	: function(textarea)
	{
		//console.log('inc', textarea.fs, textarea.scrollHeight)
		//opera.postError('scroll: '+textarea.scrollHeight+', height'+textarea.clientHeight)
		// чтоб не прыгало Opera
		if (textarea.fs == 1)
			return clearInterval(this.font_timer)
		
		if (textarea.scrollHeight > 185)
		{
			textarea.style.fontSize = --textarea.fs+'%'
			if (window.opera)
				textarea.scrollTop = 0
		}
		else
			clearInterval(this.font_timer)
	},
	
	fontDec	: function(textarea)
	{
		// чтоб не прыгало
		//console.log('dec', textarea.fs, textarea.scrollHeight)
		//opera.postError(textarea.scrollHeight)
		
		if (textarea.fs != 100 && (textarea.scrollHeight < 180 || isFirefox || isSafari) )
		{
			textarea.style.fontSize = ++textarea.fs+'%'
		}
		else
			clearInterval(this.font_timer)
		
		if (textarea.scrollHeight > 185)
		{
			clearInterval(this.font_timer)
			textarea.style.fontSize = --textarea.fs+'%'
		}
	},
	
	// редактирование стикера
	font_size : function(textarea)
	{
		// textarea.clientHeight = 185
		// шрифт взял из расчета 100%, получилось гибче и красивей
		var l = textarea.value.length, ll = textarea.last_length, interval, t = this
		
		if (textarea.scrollHeight > 185/*l > ll && l > 9*/)
		{
			clearInterval(this.font_timer)
			t.font_timer = setInterval(function()
			{
				t.fontInc(textarea)
			}, 1) // 1 для ie
		}
		
		if (l < ll && textarea.scrollHeight <= 185 && textarea.fs != 100)
		{
			clearInterval(this.font_timer)
			t.font_timer = setInterval(function()
			{
				t.fontDec(textarea)
			}, 1)
		}
		
		textarea.last_length = l
	},
	
	// синхронизация с остальными элементами (сохранение и отображение)
	sync : function(textarea, previewer)
	{
		previewer.style.fontSize = textarea.style.fontSize
		
		if (/*@cc_on !@*/0) //ie
		{
			previewer.innerText = ' '
			
			if (!textarea.firstChild)
				previewer.firstChild.nodeValue = ' '
			else
				previewer.firstChild.nodeValue = textarea.firstChild.nodeValue
			
			var text_node, href, link_node = document.createElement('a')
			
			while (f = this.rx_link.exec(previewer.lastChild.nodeValue))
			{
				switch(f[2])
				{
					case 'www.'	: href = 'http://';	break
					case ''		: href = 'mailto:';	break
					default		: href = ''
				}
				text_node = previewer.lastChild.splitText(f.index)
				text_node.deleteData(0,((link_node = link_node.cloneNode(false)).innerText = f[0]).length)
				link_node.setAttribute('href',href+f[0])
				link_node.ondrag	= function(){ return false }
				link_node.onclick	= Drag.onclick
				text_node.parentNode.insertBefore(link_node, text_node)
			}
		}
		else
		{
			previewer.innerHTML = ' '
			previewer.replaceChild(document.createTextNode(textarea.value), previewer.firstChild)
			
			previewer.innerHTML = previewer.innerHTML.replace(this.rx_link, function(str,str2, protocol)
			{
				switch(protocol)
				{
					case 'www.'	: href = 'http://';	break
					case ''		: href = 'mailto:';	break
					default		: href = ''
				}
				return '<a href="'+href+str+'">'+str+'</a>'
			})
		}
	},
	
	// переход в режим редактирования
	editor : function(stick)
	{
		Drag.clear()
		
		var t = this, tx = stick.textarea
		
		if (stick.node.style.zIndex != Drag.zindex) // усли выделили стикеры, и редактируем
			stick.node.style.zIndex = ++Drag.zindex
		
		t.last_val = tx.value
		
		Drag.add(stick)
		tx.parentNode.className = 'stick-edit mark-out'
		
		tx.focus()
		
		t.save_timer = setInterval(function()
		{
			var save_ary = []
			if (t.save && t.last_val != tx.value)
			{
				t.last_val = tx.value
				stick.text = tx.value
				save_ary.push('text')
			}
			
			if (stick.z != stick.node.style.zIndex)
			{
				stick.z = +stick.node.style.zIndex
				save_ary.push('z')
			}
			
			if (save_ary.length)
				stick.save(save_ary)
			
			t.font_size(tx, stick) // вставка мышкой из буфера
		}, 1000)
	},
	
	onblur	: function(stick)
	{
		var t = this, tx = stick.textarea
		tx.value = tx.value.replace(/\r/g, '')
		
		clearInterval(t.save_timer)
		
		remClassName(stick.node.firstChild, 'stick-edit')
		
		t.sync(tx, stick.previewer)
		
		if (t.last_val != tx.value)
		{
			stick.text = tx.value
			stick.save(['text'])
		}
		
	},
	
	keyup	: function(e, stick)
	{
		var t = this
		t.save = false
		
		clearTimeout(t.save_timeout)
		t.save_timeout = setTimeout(function(){ t.save = true }, 500)
		
		Textareas.font_size(stick.textarea)
		
		noBubble(e)
		
		if (e.keyCode == 27) //esc
		{
			stick.textarea.blur()
		}
	}
}

Textareas.rx_link() // компилируем RegExp для разных браузеров


/* «Стикер» */
Stick = function(stick_data)
{
	var t = this
	
	t.node	= document.getElementById('stick-template').getElementsByTagName('div')[0].cloneNode(true)
	t.mode	= ''
	t.hided	= false
	
	t.previewer	= t.node.getElementsByTagName('p')[0]
	t.textarea	= t.node.getElementsByTagName('textarea')[0]
	
	t.previewer.ondblclick	= function(e){ t.ondblclick(e) }
	t.previewer.onclick = Drag.onclick
	
	t.node.onmousedown	= function(e) { t.shadowMousedown(e || event, this) } // кликаем по теньке, для выбора стикера снизу
	
	t.node.firstChild.onmouseout	= function(){ cmenu.currentTarget = null }
	t.node.firstChild.onmousedown	= function(e){ t.mousedown(e || event) }
	
	t.textarea.onblur = function(){ Textareas.onblur(t) }
	t.textarea.onkeyup = function(e){ Textareas.keyup(e || event, t) }
	
	// load
	// TODO, можно с оптимизировать и при создании стикера передавать часть данных
	if (stick_data && typeof stick_data.note_id != 'undefined')
	{
		t.text	= stick_data.text
		t.note_id= stick_data.note_id
		t.theme	= stick_data.theme
		t.date	= stick_data.date
		t.x	= stick_data.x
		t.y	= stick_data.y
		t.z	= stick_data.z
		
		if (Drag.zindex < t.z)
			Drag.zindex = stick_data.z
		
		if (sticks.max_id < t.note_id)
			sticks.max_id = t.note_id
		
		t.node.className	+= ' ' + t.theme
		t.node.style.left	= stick_data.x + '%'
		t.node.style.top	= stick_data.y + '%'
		t.node.style.zIndex	= stick_data.z
		
		stick_container.appendChild(t.node)
		
		t.textarea.fs = 100
		t.textarea.last_length = 0
		t.textarea.value = t.text
		
		while (t.textarea.scrollHeight > 185)
		{
			t.textarea.style.fontSize = --t.textarea.fs+'%'
			if (t.textarea.fs == 1)
				break
		}
			
		Textareas.sync(t.textarea, t.previewer)
	}
	else // new stick
	{
		t.theme					= (stick_data && stick_data.is_sms) ? sticks.settings.sms_note_color : sticks.settings.note_color
		t.date					= +(new Date())
		t.node.className		+= ' ' + t.theme
		t.text					= ''
		t.textarea.fs			= 100
		t.textarea.last_length	= 0
		t.z						= ++Drag.zindex
		t.node.style.zIndex		= t.z
		t.note_id				= 'new_1'
		
		window.stick_container.appendChild(t.node)
	}
	stick_data = null // clear for event function
	return t
}

Stick.prototype =
{
	ondblclick	: function(e)
	{
		if (Textareas.last != this.textarea)
			Textareas.editor(this)
		
		noBubble(e)
	},
	
	shadowMousedown: function(e, node)
	{
		if (node != eTarget(e))
			return
		
		var t = this, x = e.clientX, y = e.clientY, z = +t.node.style.zIndex, i = sticks.length, top, left
		
		sticks.zSort()
		
		while (sticks[--i])
		{
			if (+sticks[i].node.style.zIndex <= z) // «+» для приведения к Integer
			{
				// Вычисления: sticks[i].node.offsetTop + stick_container.offsetTop + parseItn(stick[i].node.paddingTop,10)
				if(
					y >= (top = sticks[i].node.offsetTop) + 24 && y <= top + 240 &&
					x >= (left = sticks[i].node.offsetLeft) + 24 && x <= left + 240
				)
				{
					sticks[i].mousedown(e)
					/*document.body.innerHTML += '<div style="z-index:100000000;position:absolute;top:'+(sticks[i].node.offsetTop+2+22)+'px;left:0;width:100%;border-top:1px solid red;"></div> \
					<div style="z-index:100000000;position:absolute;top:'+(sticks[i].node.offsetTop+218+22)+'px;left:0;width:100%;border-top:1px solid red;"></div> \
					<div style="z-index:100000000;position:absolute;left:'+(sticks[i].node.offsetLeft+18+6)+'px;top:0;height:200%;border-left:1px solid red;"></div> \
					<div style="z-index:100000000;position:absolute;left:'+(sticks[i].node.offsetLeft+234+6)+'px;top:0;height:200%;border-left:1px solid red;"></div> \
					<div style="z-index:100000000;position:absolute;left:'+(x-1)+'px;top:'+(y-1)+'px;width:3px;height:3px;background:green"></div> \
					'*/
					
					return e.cancelBubble = true // for Drag.docSel
				}
			}
		}
	},
	
	mousedown	: function(e)
	{
		var target = eTarget(e), t = this, invert = ~t.node.firstChild.className.indexOf('mark-out')
		
		cmenu.lastTarget = t
		cmenu.currentTarget = t
		
		if (target.tagName == 'TEXTAREA')
			return
		
		if (t.node.style.zIndex != Drag.zindex)
			t.node.style.zIndex = ++Drag.zindex
		
		Drag.stick = t
		
		if (e.button !== /*@cc_on 1 && e.button !== @*/0) // не левый клик
		{
			if (!invert)
			{
				if (!e.shiftKey || Mode.mode == 'date')
					Drag.clear()
				Drag.add(t)
			}
			return
		}
		
		
		if (e.shiftKey && Mode.mode != 'date')
			return Drag.shiftKey(e, t, invert)
		
		if (!invert)
		{
			Drag.clear()
			Drag.add(t)
		}
		
		Drag.drag(e)
	},
	
	remove		: function(not_send)
	{
		stick_container.removeChild(this.node)
		sticks.without(this)
		this.deleted = 1
		Events.start('stick_remove', this)
		return this.save(['deleted'], 0, not_send)
	},
	
	save		: function(items, func, not_send)
	{
		if (!items) // грузим все прописанные тут настройки
			items = ['text','x','y','z','theme','date']
		
		var props = [], i, v
		
		for (i = items.length - 1; i >= 0; i--)
		{
			if (typeof this[items[i]] == 'string')
			{
				v = '"' + encodeURIComponent(this[items[i]].replace(/(\x5c|\x2F|\x22|[\x0c-\x0d]|[\x08-\x0a])/g, function(i,d){return "\\".concat({"\b":"b","\t":"t","\n":"n","\f":"f","\r":"r",'"':'"',"\\":"\\","/":"/"}[d])}).replace(/([\x00-\x07]|\x0b|[\x0e-\x1f])/g, function(i,d){
							var	n=d.charCodeAt(0).toString(16);
							return "\\u".concat(["","000","00","0",""][n.length],n)
						})) + '"'
			}
			else
				v = this[items[i]]
			
			props.push('"'+items[i]+'":'+v)
		}
		
		if (not_send) // ex. multi
			return '{"note_id":"'+this.note_id+'",'+props.join(',')+'}'
		else
			req('updates/', 'notes={"notes":{"note":[{"note_id":"'+this.note_id+'",'+props.join(',')+'}]}}', func)
		
	},
	
	// перевод в проценты
	percantage	: function()
	{
		var n = this.node, top = n.offsetTop, left = n.offsetLeft, h = stick_container.offsetHeight || 1, w = stick_container.offsetWidth || 1
		
		// min нужен для надежности. Например при onresize
		n.style.top		= Math.min(Math.round(top*1000000/h)/10000, 100)+'%'
		//браузреные ошибки округления
		if (n.offsetTop != top)
			n.style.top	= Math.min(Math.round(++top*1000000/h)/10000, 100)+'%'
	
		n.style.left		= Math.min(Math.round(left*1000000/w)/10000, 100)+'%'
		if (n.offsetLeft	!= left)
			n.style.left	= Math.min(Math.round(++left*1000000/w)/10000, 100)+'%'
	},
	
	changeTheme	: function(theme, not_send)
	{
		remClassName(this.node, this.theme)
		this.theme = theme
		this.node.className += ' ' + theme
		return this.save(['theme'], 0, not_send)
	}
}



/* массив со стикерами */
var sticks = new Array()

sticks.create = function(stick_data)
{
	var stick = new Stick(stick_data)
	this.push(stick)
	return stick
}

sticks.createFromPosition = function(e, x, y)
{
	var stick = sticks.create(), n = stick.node
	
	n.style.left	= Math.max(0, Math.min(x, stick_container.offsetWidth)) + 'px'
	n.style.top	= Math.max(0, Math.min(y, stick_container.offsetHeight)) + 'px'
	
	stick.percantage()
	
	stick.x = parseFloat(n.style.left)
	stick.y = parseFloat(n.style.top)
	
	stick.save(['text','z','x','y','theme'], function(r)
	{
		eval('r='+r)
		stick.date		= r.savedNotes[0].date
		stick.note_id	= r.savedNotes[0].new_id
		if (sticks.max_id < stick.note_id)
			sticks.max_id = stick.note_id
	})
	
	Textareas.editor(stick)
}

sticks.sortColor = function()
{
	var t = this, j = 0, l = t.length, i, theme_obj = {}, theme_len = Themes.length - 1
	
	t.zNormilize()
	
	// создаем объект с темами
	for (i = theme_len; i >= 0; i--)
		theme_obj[Themes[i]] = []
	
	// пихаем в темы порядковые номера стикеров
	for (i=0; i < l; i++)
	{
		if (!t[i].hided)
			theme_obj[t[i].theme].push(i)
	}
	
	var length, max = 0
	
	// удаляем темы которых нет
	for (i = theme_len; i >= 0; i--)
	{
		length = theme_obj[Themes[i]].length
		if (!length)
			delete theme_obj[Themes[i]]
		else if (length > max)
			max = length
	}
	
	// считаем колличество оставшихся тем
	i = 0
	for (var obj in theme_obj)
		i++
		
	var a = new Animator({
		onComplete: function()
		{
			var a, a_c_s = [], an, n, d, i, th, th_l, x, y, k, r
			
			for (var obj in theme_obj)
			{
				th = theme_obj[obj]
				d = Math.round(500 / th.length)
				an = []
				r = 20
				th_l = th.length
				p = 3*Math.PI/th_l
				// высчитываем множитель aa от радиуса, значение должно быть в районе двух
				aa = Math.log(r / Math.cos(th_l*p)) / Math.log(th_l*p)
				
				aa = th.opt.aa
				
				// no need to animate he first element
				for (i=1; i < th_l; i++)
				{
					k = th_l-i
					
					x = th.opt.l + Math.exp(aa*Math.log(k*p)) * Math.cos(k*p)
					y = th.opt.t - Math.exp(aa*Math.log(k*p)) * Math.sin(k*p)
					
					n = sticks[th[i]].node
					
					an.unshift(new Animator({transition: function(){return 1}, duration: d})
					 .addSubject(new NumericalStyleSubject(n, 'left', th.opt.l, x))
					 .addSubject(new NumericalStyleSubject(n, 'top', th.opt.t, y))
					)
				}
				
				if (an.length)
				{
					a = new AnimatorChain(an)
					a.seekTo(1)
					a_c_s.push(a)
				}
			}
			
			sticks.sortColor.a = a_c_s
		}
	})
	
	// Тестируем колличество доступных тем
/*	delete theme_obj.green
	//delete theme_obj.white
	//delete theme_obj.yellow
	//delete theme_obj.blue
	i = 4*/
	var w_top = (window.stick_container.offsetWidth + sticks.w)/(Math.floor(i/2) || 1)
	var w_bot = (window.stick_container.offsetWidth + sticks.w)/Math.ceil(i/2)
	var h = Math.round((window.stick_container.offsetHeight + sticks.h)/((i == 1) ? 1 : 2))
	var html = '', is_top = (i == 1) ? false : true, k = 0, j = 0, w, l, t, st_l, st_t, n, aa, p, th, th_l, search_h = document.getElementById('search').offsetHeight
	
	for (var obj in theme_obj)
	{
		is_top = !is_top
		w = is_top ? w_top : w_bot
		t = is_top ? 0 : h
		l = (is_top ? k : j)*w
		
		st_l = (is_top ? k : j)*w + w/2 - sticks.w/2
		st_l = (st_l < 0) ? 0 : st_l
		st_l = (st_l > window.stick_container.offsetWidth) ? window.stick_container.offsetWidth : st_l
		
		st_t = (is_top ? 0 : h) + h/2 - sticks.h/2
		st_t = (st_t < 0) ? 0 : st_t
		st_t = (st_t > window.stick_container.offsetHeight) ? window.stick_container.offsetHeight : st_t
		st_t = (is_top && st_t < search_h && (i == 1 || k+1 == Math.floor(i/2))) ? search_h : st_t
		
		//html += '<div class=stick style="background-color:blue;left:'+st_l+'px;top:'+st_t+'px; z-index:20010">'+(is_top?'top':'bot')+'</div>'
		//html += '<div style="outline:2px solid red;position: absolute; z-index:20000;height:' + h + 'px;width:'+w+'px;top:'+t+'px;left:'+l+'px">'+obj+'</div>'
		
		th_l = theme_obj[obj].length
		
		p = 3*Math.PI // 360ø + 180ø
		aa = Math.log(Math.max((w - sticks.w)/2, 1.1) / -Math.cos(p)) / Math.log(p) // x
		
		p = 2.5*Math.PI // 360ø + 90ø
		aa = Math.min(aa, Math.log(Math.max((h - sticks.h)/2, 1.1) / Math.sin(p)) / Math.log(p))
		
		theme_obj[obj].opt = { l : st_l, t : st_t, aa: aa }
		
		for (th = th_l - 1; th >= 0; th--)
		{
			n = sticks[theme_obj[obj][th]].node
			a.addSubject(new NumericalStyleSubject(n, 'left', n.offsetLeft, st_l))
			 .addSubject(new NumericalStyleSubject(n, 'top', n.offsetTop, st_t))
		}
		
		is_top ? k++ : j++
	}
	a.play()
	sticks.sortColor.a = a
	
	//window.stick_container.innerHTML += html
}

sticks.without = function(n)
{
	for (var i = this.length - 1; i >= 0; i--)
	{
		if (this[i] == n)
		{
			return this.splice(i, 1)
		}
	}
}

// сортировка по дате
// drag - то что держим
// to_drag - куда перетащили
// save_center - когда центральный стикер остается на месте
// sides - переместить вправо или влево
sticks.sortDate = function(drag, to_drag, save_center, sides)
{
	var has_center = true /* разбиваем на два потока */, t = Search.sticks || this, l = t.length, n, last_central = sticks.sortDate.last_central, i
	
	if (!l)
		return
	
	if (((to_drag || save_center) && (!drag || drag == t[0].node || drag == t[l-1].node)) || sides) // при крайней левой позиции все работает правильно
	{
		var to = to_drag || sides
		n = drag || last_central.node
		
		if ((to == 'right' && n == t[0].node) || (to == 'left' && n == t[l-1].node)
		)
		{
			return
		}
	}
	
	var central = save_center ? sticks.sortDate.last_central : false
	if ((!drag && !sides) || (!to_drag && !save_center && !sides))
	{
		t.zNormilize()
		central = t[Drag.zindex] // как центральный, возьмем самый высокий
	}
	
	function compareNumbers(a, b){ return a.date - b.date }
	
	t.sort(compareNumbers)
	
	var a = sticks.sortDate.a, r_ary = [], l_ary = []
	a.clearSubjects()
	
	for (i=0; i < l; i++)
	{
		if (drag && to_drag && to_drag != 'center' && drag === t[i].node)
			continue
		
		if (has_center)
		{
			//console.log(!!sides , '((',sides == 'left' , !!this[i+1] , sticks.sortDate.last_central == this[i+1],') || (',sides == 'right' ,'&&', !!this[i-1] ,'&&', sticks.sortDate.last_central == this[i-1])
			if ((t[i] == central) || // простая сортировка
				(drag && (to_drag == 'right' && t[i+1] && drag == t[i+1].node) || (to_drag == 'left' && t[i-1] && drag == t[i-1].node) || (to_drag == 'center' && drag == t[i].node)) || // сортировка при драгании
				(sides && ((sides == 'right' && t[i+1] && last_central == t[i+1]) || (sides == 'left' && t[i-1] && last_central == t[i-1]))) // сортировка вправо влево
			)
			{
				//central
				has_center = false
				sticks.sortDate.last_central = t[i]
				
				document.getElementById('stick-view-date').innerHTML = (new Date(t[i].date)).sortDate()
				
				// замер растояния между стикерами
				//setTimeout(function(i){ return function(){
				//	console.log(t[i].node.offsetLeft-t[i-1].node.offsetLeft+252,t[i-1].node,t[i].node)
				//}}(i), 2000)
			}
			else //right
			{
				r_ary.push(i)
			}
		}
		else //left
		{
			l_ary.push(i)
		}
	}
	
	var i, j, l_r = r_ary.length, l_l = l_ary.length
	
	var width	= Math.floor((window.stick_container.offsetWidth+252)/2)
	var width_r	= Math.floor((width-252*2)/l_r)
	var width_l	= Math.floor((width-252*2)/l_l) // на самом деле, это право
	var top		= Math.round((window.stick_container.offsetHeight+216)/2 - 110)
	var rem_width_r = (width-252*2)-width_r*l_r || 0 // остатки
	var rem_width_l = (width-252*2)-width_l*l_l || 0
	
	for (j=0; j < l_l; j++)
	{
		i = l_ary[j]
		n = t[i].node
		n.style.zIndex = (l - j - 2)
		a.addSubject(new NumericalStyleSubject(n, 'left', n.offsetLeft, width+rem_width_l+252+width_l*j - (rem_width_l-j)*((rem_width_l-j)>0)))
		 .addSubject(new NumericalStyleSubject(n, 'top', n.offsetTop, top + (i&1)*5))
	}
	
	for (j = l_r - 1; j >= 0; j--)
	{
		i = r_ary[j]
		n = t[i].node
		n.style.zIndex = j
		a.addSubject(new NumericalStyleSubject(n, 'left', n.offsetLeft, width_r*j + (rem_width_r>0)*(rem_width_r--)))
		 .addSubject(new NumericalStyleSubject(n, 'top', n.offsetTop, top + (i&1)*5))
	}
	
	
	if (!drag || to_drag != 'center')
	{
		n = sticks.sortDate.last_central.node
		a.addSubject(new NumericalStyleSubject(n, 'left', n.offsetLeft, width-252/2))
		 .addSubject(new NumericalStyleSubject(n, 'top', n.offsetTop, top))
		
		n.style.zIndex = l - 1
		if (!to_drag)
		{
			Drag.clear()
			Drag.add(sticks.sortDate.last_central)
		}
	}
	
	a.play()
	
	// направление движения, нужно при удалении центрального стикера (навигация с клавиатуры)
	var to = sides || ((sticks.sortDate.last_i.right > l_r || sticks.sortDate.last_i.left < l_l) ? 'right' : 'left')
	
	sticks.sortDate.last_i = {right: l_r, left: l_l, to: to}
	//setTimeout(function(){ window.stick_container.innerHTML += '<div style="position:absolute;top:50px;left:0;background:red;width:'+width+'px;z-index:100">половина</div><div style="position:absolute;top:0;left;0;top:40;background:green;width:'+
	//(width_r*l_r)+'px;">ширина справа</div><div style="position:absolute;top:45px;right:-252px;background:yellow;width:'+(width_l*l_l)+'px">ширина справа</div>'}, 1000)
}

sticks.sortDate.a = new Animator()


// сортируем массив со стикерами по порядку z-index'а
Array.prototype.zSort = function ()
{
	this.sort(this.zSort.func)
}

Array.prototype.zSort.func = function (a, b)
{
	return a.node.style.zIndex - b.node.style.zIndex
}

// делаем z-index по порядку
Array.prototype.zNormilize = function(save)
{
	this.sort(this.zSort.func)
	
	for (var i=0; i < this.length; i++)
		this[i].node.style.zIndex = i
	
	Drag.zindex = i - 1
	
	if (save)
	{
		var props = []
		
		for (i = 0; i < this.length; i++)
		{
			this[i].z = this[i].node.style.zIndex
			props.push('{"note_id":"'+this[i].note_id+'","z":"'+this[i].z+'"} ')
		}
		
		req('updates/', 'notes={"notes":{"note":['+props.join(',')+']}}')
	}
}

sticks.setInterval = function()
{
	this.interval = setInterval(function(){
		req('notes/?last_id='+sticks.max_id,'',function(v){
			if (!v) return
			
			try
			{
				v = eval(v)
				for (var i = v.length - 1; i >= 0; i--)
				{
					v[i].is_sms = true
					sticks.create(v[i])
				}
			}
			catch(e){}
		})
	}, 15000)
}

sticks.settings = {note_color:'blue', sms_note_color:'green'}
sticks.max_id = 0 // для запроса стикеров из смс
sticks.w = 252 // ширина
sticks.h = 236 // высота


/* Режим отображения */
var Mode =
{
	a 		: new Animator(),
	mode	: 'standart',
	date	:
	{
		to		: function()
		{
			var s = window.sticks, t = this
			sticks.sortDate.a.options.onComplete = function(){}
			
			s.sortDate.last_i = {right: 0, left: 0}
			t.generate()
			
			Events.add('esc', 'Mode', function(){ Mode.to('standart') }, true)
			Events.add('left', 'Mode', function(){ s.sortDate(null,null,false,'left') })
			Events.add('right', 'Mode', function(){ s.sortDate(null,null,false,'right') })
			Events.add('del', 'Mode', function()
			{
				cmenu.lastTarget = sticks.sortDate.last_central
				cmenu.del_stick()
			})
			Events.add('stick_remove', 'Mode_pause', function()
			{
				var l = s.sortDate.last_i, r = l.right, to = (l.to == 'right') ? r-1 : r
				
				if(s[to])
					s[to].node.style.zIndex = s.length
				
				t.generate()
			})
			Events.add('stick_search', 'Mode', function()
			{
				if (sticks.sortDate.last_central && !sticks.sortDate.last_central.hided)
					sticks.sortDate.last_central.node.style.zIndex = sticks.length
				
				t.date_container.style.display = ((Search.sticks || sticks).length) ? 'block' : 'none'
				
				t.generate()
			})
			
			if ((Search.sticks || sticks).length)
				t.date_container.style.display = 'block'
		},
		
		cancel	: function(m)
		{
			sticks.sortDate.a.clearSubjects()
			
			this.date_container.style.display = 'none'
			
			Events.del('esc', 'Mode')
			Events.del('left', 'Mode')
			Events.del('right', 'Mode')
			Events.del('del', 'Mode')
			Events.del('stick_remove', 'Mode_pause')
			Events.del('stick_search', 'Mode')
		},
		
		generate: function()
		{
			sticks.sortDate()
		}
	},
	
	color	:
	{
		to		: function()
		{
			var t = this
			t.generate()
			Events.add('esc', 'Mode', function(){ Mode.to('standart') }, true)
			Events.add('stick_remove', 'Mode', function()
			{
				t.generate()
			})
		},
		
		cancel	: function(m)
		{
			var a = sticks.sortColor.a, i
			if (a.options)
			{
				a.options.onComplete = function(){}
				a.clearSubjects()
			}
			else // AnimatorChain
			{
				for (i = a.length - 1; i >= 0; i--)
				{
					a[i].clearSubjects()
				}
			}
			
			Events.del('esc', 'Mode')
			Events.del('stick_remove', 'Mode')
		},
		
		generate: function()
		{
			sticks.sortColor()
		}
	},
	
	standart:
	{
		to		: function()
		{
			this.generate()
		},
		
		cancel	: function()
		{
			Mode.a.clearSubjects()
		},
		
		generate: function()
		{
			var a = Mode.a, s = sticks, n, c = stick_container, w = c.offsetWidth/100, h = c.offsetHeight/100, i = s.length, j = i
			
			a.options.onComplete = function()
			{
				while (j--)
				{
					if (!Drag.stick || !inArray(s[j], Drag.ary, 'stick'))
					{
						n = s[j].node.style
						n.left = s[j].x + '%'
						n.top = s[j].y + '%'
					}
				}
			}
			
			while (i--)
			{
				if (!Drag.stick || !inArray(s[i], Drag.ary, 'stick'))
				{
					n = s[i].node
					a.addSubject(new NumericalStyleSubject(n, 'left', n.offsetLeft, Math.round(s[i].x*w)))
					 .addSubject(new NumericalStyleSubject(n, 'top', n.offsetTop, Math.round(s[i].y*h)))
				}
			}
			
			a.play()
		}
	},
	
	to		: function(m)
	{
		if (this.mode == m)
			return
		
		this[this.mode].cancel(m)
		this.mode = m
		this[m].to()
		cmenu_body.changeMode(m)
		return false
	},
	
	generate: function()
	{
		this[this.mode].generate()
	}
}



/* «Фильтр» */
Search =
{
	a: new Animator({ onComplete: function(){
		var s = this.subjects, i = s.length, j, ignore = []
		
		// удаляем из анимации потовряющиеся элементы.
		function without(i)
		{
			for (var k=0; k < ignore.length; k++)
			{
				for (var j = s[i].els.length - 1; j >= 0; j--)
				{
					if (ignore[k] == s[i].els[j])
					{
						s[i].els.splice(j, 1)
					}
				}
			}
		}
		
		while (i--)
		{
			without(i)
			for (j = s[i].els.length - 1; j >= 0; j--)
			{
				if (s[i].to == 0)
				{
					s[i].els[j].style.display = 'none'
				}
				else
				{
					/*@cc_on s[i].els[j].style.cssText = s[i].els[j].style.cssText.replace(/FILTER:.*?;/,'') @*/
				}
			}
			ignore = ignore.concat(s[i].els)
		}
	}}),
	
	last_text	: '',
	sticks		: null,
	
	text	: function(node, e)
	{
		noBubble(e)
		
		if (e && e.keyCode == 27) //esc
		{
			if (node.className == '') // Opera
				node.value = '' // FF2
			return this.showAll()
		}
		
		var text = node.value, t = this
		
		if (t.last_text == text)
			return
		
		t.last_text = text
		text = text.trim().makeSafeRegExp()
		
		// показываем все
		if (!text.length)
			return t.showAll()
		
		t.sticks = []
		
		var len	= sticks.length, a = t.a, b_ary = [], a_ary = [], i, rx	= new RegExp('([,./ !?:)(;-]' + text + ')|(^' + text + ')', 'im')
		
		for (i=0; i < len; i++)
		{
			if (rx.test(sticks[i].textarea.value))
			{
				t.sticks.push(sticks[i])
				if (sticks[i].hided)
				{
					sticks[i].node.style.display = 'block'
					a_ary.push(sticks[i].node)
				}
				sticks[i].hided = false
			}
			else if (!sticks[i].hided)
			{
				sticks[i].hided = true
				sticks[i].node.style.display = 'block'
				b_ary.push(sticks[i].node)
			}
		}
		
		if (a.state == 1)
			a.clearSubjects()
		
		if (a_ary.length)
		{
			a.addSubject(new NumericalStyleSubject(a_ary, 'opacity', 0, 1))
		}
		
		if(b_ary.length)
		{
			a.addSubject(new NumericalStyleSubject(b_ary, 'opacity', 1, 0))
		}
		
		// для быстрого набора
		if (a.state == 1)
			a.play()
		else
			a.seekTo(1)
		
		Events.start('stick_search')
	},
	
	showAll	: function()
	{
		if (this.sticks === null)
			return
		
		var a = this.a, a_ary = [], i = sticks.length
		
		while (i--)
		{
			if (sticks[i].hided)
			{
				sticks[i].hided = false
				sticks[i].node.style.display = 'block'
				a_ary.push(sticks[i].node)
			}
		}
		
		if (a_ary.length)
		{
			a.addSubject(new NumericalStyleSubject(a_ary, 'opacity', 0, 1))
			a.play()
		}
		
		this.last_text = ''
		this.sticks = null
		
		Events.start('stick_search')
	},
	
	focus	: function(node)
	{
		var t = this
		node.onsearch	= function(){ t.text(this) }
		node.ondblclick	= noBubble
		node.onblur		= t.blur
		node.onkeypress = function(e) // Opera
		{
			if ((e || event).keyCode == 27) //esc
			{
				if (node.className == '')
					node.value = ''
				return t.showAll()
			}
		}
		node.onkeyup	= function(e){ t.text(this, e) }
		
		t.focus = function()
		{
			Drag.clear()
			
			if (node.className == 'empty')
			{
				node.value = ''
				node.className = ''
				Events.add('stick_remove', 'Search', function(stick)
				{
					if (Search.sticks)
						sticks.without.call(Search.sticks, stick)
				})
			}
		}
		
		t.focus()
	},
	
	blur	: function()
	{
		if (!this.value)
		{
			this.className = 'empty'
			this.value = this.defaultValue
			Events.del('stick_remove', 'Search')
		}
	}
}



/* «Ajax» */
function req(u, p, func, when)
{
	if (!when) // для смены языка when = 2
		when = 4
	
	var r=[	function(){ return new XMLHttpRequest() },
			function(){ return new ActiveXObject('Msxml2.XMLHTTP') },
			function(){ return new ActiveXObject('Microsoft.XMLHTTP') }]
	
	for(var i in r)
	{
		try {
			var q = r[i]()
			
			q.onreadystatechange = func ? function()
			{
				try {
					if(q.readyState == when && (when != 4 || q.status==200))
						func(when != 4 || q.responseText )
				} catch(e){}
			} : function(){}
			
			if (p)
			{
				q.open('POST', u, true)
				q.setRequestHeader('Content-type', 'application/x-www-form-urlencoded') // ; charset=utf-8
				q.setRequestHeader('Content-length', p.length)
				q.setRequestHeader('Connection', 'close')
				q.send(p)
			}
			else
			{
				q.open('GET', u, true)
				q.send(null)
			}
			break
		 } catch(e){}
	}
}



/* «Настройки» */
var Settings =
{
	note_color	: {cur:null, actual:null},
	sms_note_color: {cur:null, actual:null},
	
	open		: function()
	{
		Events.add('esc', 'Settings', function()
		{
			Settings.close()
		}, true)
		
		Events.pause('Mode')
		
		var lang = document.getElementById('s-lang'), i = lang.options.length, phone = document.getElementById('m-phone')
		
		if (lang.value != sticks.settings.lang)
		{
			while (i--)
			{
				if (lang.options[i].value == sticks.settings.lang)
					lang.selectedIndex = i
			}
		}
		
		if (phone)
		{
			document.getElementById('m-phone').value = sticks.settings.mphone_number
			phone.onkeyup()
		}
		
		ContextMenu.forbid = true
		this.container.style.display = 'block'
		return false
	},
	
	close		: function()
	{
		this.container.style.display = 'none'
		
		if (this.note_color.cur != this.note_color.actual)
		{
			remClassName(this.note_color.cur, 'cur')
			this.note_color.actual.className += ' cur'
			this.note_color.cur = this.note_color.actual
		}
		
		if (this.sms_note_color.cur != this.sms_note_color.actual)
		{
			remClassName(this.sms_note_color.cur, 'cur')
			this.sms_note_color.actual.className += ' cur'
			this.sms_note_color.cur = this.sms_note_color.actual
		}
		
		Events.del('esc', 'Settings')
		Events.play('Mode')
		
		return ContextMenu.forbid = false
	},
	
	changeTheme	: function(node, theme, opt)
	{
		if (this[opt].cur == node)
			return
		
		remClassName(this[opt].cur, 'cur')
		this[opt].cur = node
		node.className += ' cur'
	},
	
	save		: function()
	{
		var params = [], theme, is_change = false, phone = document.getElementById('m-phone'), lang = document.getElementById('s-lang').value
		if (this.note_color.cur != this.note_color.actual)
		{
			theme = this.note_color.cur.className.split(' ')[0]
			sticks.settings.note_color = theme
			params.push('note_color='+theme)
			this.note_color.actual = this.note_color.cur
		}
		
		if (this.sms_note_color.cur != this.sms_note_color.actual)
		{
			theme = this.sms_note_color.cur.className.split(' ')[0]
			sticks.settings.sms_note_color = theme
			params.push('sms_note_color='+theme)
			this.sms_note_color.actual = this.sms_note_color.cur
		}
		
		if (phone)
		{
			phone.onkeyup()
			if (sticks.settings.mphone_number != phone.value)
			{
				sticks.settings.mphone_number = phone.value
				params.push('mphone_number='+phone.value)
			}
		}
		
		if (sticks.settings.lang != lang)
		{
			params.push('lang='+lang)
			document.cookie = 'langpref='+lang+';path=/'
		}
		
		if (params.length)
		{
			var bt = document.getElementById('bt-save'), click = bt.firstChild.onclick
			bt.firstChild.onclick = 'return false'
			bt.className += ' bt-dis'
			
			req('options/', params.join('&'), function(r)
			{
				if (sticks.settings.lang != lang)
					return location.reload()
				
				eval('r='+r)
				var s = r[1]
				
				if (phone && sticks.settings.mphone_confirm_code != s.mphone_confirm_code)
				{
					document.getElementById('m-code').innerHTML = s.mphone_confirm_code
				}
				
				sticks.settings = s
				
				if (phone)
					phone.onkeyup()
				
				bt.firstChild.onclick = click
				remClassName(bt, 'bt-dis')
			}, sticks.settings.lang != lang ? 2 : null)
		}
		
		return false
	}
}



/* Регистрация и взаимодействие внутренних событий */
Events =
{
	list	: {},
	now		: '', // сейчас отрабатывает, для контроля над событиями
	paused	: [],
	
	add		: function(e, name, func, single)
	{
		if (!this.list[e])
			this.list[e] = {}
		
		this.list[e][name] = func
		if (single)
			this.list[e][name].single = single
	},
	
	del		: function(e, name)
	{
		delete this.list[e][name]
	},
	
	start	: function(e, prms)
	{
		var l = this.list[e], last
		/*	Как это все задумано:
			если есть single, то последовательность оборвали (чтоб что-то закрывать не сразу)
			вся надежда на естественную сортировку
		*/
		for (var i in l)
		{
			if (l[i].paused)
				continue
			
			if (l[i].single)
				last = l[i]
			else
				l[i](prms)
		}
		if (last)
			last(prms)
	},
	
	/* Приостанавливаем события, например в Настройках */
	pause	: function(name)
	{
		for (var e in this.list)
		{
			if (this.list[e][name])
				this.list[e][name].paused = true
		}
	},
	
	play	: function(name)
	{
		for (var e in this.list)
		{
			if (this.list[e][name])
				delete this.list[e][name].paused
		}
	}
}



/* Расширение стандартных объектов */
// http://blog.stevenlevithan.com/archives/faster-trim-javascript
String.prototype.trim = function()
{
	return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '')
}

String.prototype.makeSafeRegExp = function()
{
	//FIXME: we should used definitely more complex escaping system
	return this.replace(/(\+)/g,'\\\$1')
		.replace(/(\?)/g,'\\\$1')
		.replace(/(\*)/g,'\\\$1')
		.replace(/(\\)(?!d)/g,'\\\$1')
		.replace(/(\()/g,'\\\$1')
		.replace(/(\))/g,'\\\$1')
		.replace(/(\[)/g,'\\\$1')
		.replace(/(\.)/g,'\\\$1')
}

Date.prototype.sortDate = function()
{
	var m = ['january','february','march','april','may','june','july','august','september','october','november','december']
	//var padded = function(n){ return (n<10) ? '0'+n : n }
	var th = (sticks.settings.lang == 'EN') ? '<sup>th</sup>' : ''
	return '<span class="day">'+this.getDate()+th+'</span><span class="mon">'+lang['month_'+m[this.getMonth()]]+'</span><span class="year">'+this.getFullYear()+'</span>'
}



Drag =
{
	ary: [],
	stick: null,
	zindex	: 1,
	capture: false, // если сейчас ноды активны (захвачены)
	draged	: false, // для контроля нажатия на ссылку
	container: null,
	
	shiftKey: function(e, stick, stick_is_invert)
	{
		var node = stick.node.firstChild, t = this
		
		t.add(stick)
		t.drag(e)
		
		document.onmouseup = function()
		{
			t.stop()
			
			if (!t.draged && stick_is_invert)
			{
				remClassName(node, 'mark-out')
				var i = -1
				while (t.ary[++i])
				{
					if (t.ary[i].stick == stick)
					{
						t.ary.splice(i, 1)
						break
					}
				}
			}
		}
	},
	
	clear: function()
	{
		var i = -1
		while (this.ary[++i])
		{
			remClassName(this.ary[i].stick.node.firstChild, 'mark-out')
		}
		this.ary = []
	},
	
	add: function(stick)
	{
		var node = stick.node.firstChild, stick_is_invert = !!~node.className.indexOf('mark-out')
		
		if (!stick_is_invert)
		{
			this.ary.push({stick:stick})
			node.className = 'mark-out'
			
			return true
		}
		
		return false
	},
	
	del: function(stick)
	{
		var node = stick.node.firstChild, stick_is_invert = !!~node.className.indexOf('mark-out'), i = -1
		
		if (stick_is_invert)
		{
			remClassName(node, 'mark-out')
			
			while(this.ary[++i])
			{
				if (this.ary[i].stick == stick)
				{
					this.ary.splice(i, 1)
					break
				}
			}
		}
	},
	
	docSel: function(e)
	{
		var target = eTarget(e), t = this, div, i = -1, old_ary = false
		
		if (e.button !== /*@cc_on 1 && e.button !== @*/0) // не левый клик
			return
		
		if (Mode.mode == 'date')
		{
			if (e.target)
			{
				if (window.focusedElement && window.focusedElement.tagName)
					window.focusedElement.blur()
			}
			window.getSelection().removeAllRanges() // снимаем выделение
			return
		}
		
		if (target != document.body && target != document.documentElement && target != stick_container && target.parentNode.id != 'stick-container')
			return
		
		if (!e.shiftKey)
			t.clear()
		else
		{
			old_ary = []
			while(t.ary[++i])
				old_ary.push(t.ary[i].stick)
		}
		
		var startX = e.clientX, startY = e.clientY
		
		if (e.target)
		{
			if (window.focusedElement && window.focusedElement.tagName)
				window.focusedElement.blur()
			
			e.preventDefault() // не даем выделять текст, и курсор нормальный становиться в Safari3
			window.getSelection().removeAllRanges() // снимаем выделение
		}
		else
			document.body.onselectstart = function(){ e.returnValue = false } //ie
		
		document.onmousemove	= function(e)
		{
			e = e || window.event
			
			if (Math.abs(startX - e.clientX) > 3 || Math.abs(startY - e.clientY) > 3)
			{
				div = document.createElement('div')
				
				div.style.left	= startX + 'px'
				div.style.top	= startY + 'px'
				div.style.width	= 0
				div.style.height= 0
				
				div.className = 'selection-box'
				
				document.body.appendChild(div)
				
				document.onmousemove	= function(e)
				{
					e = e || window.event
					var x = startX - e.clientX, y = startY - e.clientY
					if (x < 0)
					{
						div.style.left	= startX + 'px'
						div.style.right	= 'auto'
					}
					else
					{
						div.style.left	= 'auto'
						div.style.right = document.body.offsetWidth - startX + 'px'
					}
					
					if (y < 0)
					{
						div.style.top	= startY + 'px'
						div.style.bottom= 'auto'
					}
					else
					{
						div.style.top	= 'auto'
						div.style.bottom= document.body.offsetHeight - startY + 'px'
					}
					
					div.style.width = Math.abs(startX - e.clientX) + 'px'
					div.style.height = Math.abs(startY - e.clientY) + 'px'
					
					var sel_rect = {x:div.offsetLeft, w:div.offsetWidth, y:div.offsetTop, h:div.offsetHeight}, n, i = -1
					
					while (sticks[++i])
					{
						n = sticks[i].node
						if(intersectRect(
							sel_rect,
							{x:n.offsetLeft+18+6, w:216, y:n.offsetTop+2+22, h:216}
						))
						{
							if (old_ary && inArray(sticks[i], old_ary)) // shiftKey
								t.del(sticks[i])
							else
								t.add(sticks[i])
						}
						else
						{
							if (old_ary && inArray(sticks[i], old_ary))
								t.add(sticks[i])
							else
								t.del(sticks[i])
						}
					}
					
					return false
				}
			}
		}
		
		document.onmouseup = function()
		{
			if (div)
				document.body.removeChild(div)
			
			document.onmousemove = ''
			document.onmouseup = ''
		}
	},
	
	
	drag: function(e)
	{
		if (e.target)
		{
			if (window.focusedElement && window.focusedElement.tagName)
				window.focusedElement.blur()
			
			e.preventDefault() // не даем выделять текст, и курсор нормальный становиться в Safari3
			window.getSelection().removeAllRanges() // снимаем выделение
		}
		else
			document.body.onselectstart = function(){ e.returnValue = false} //ie
		
		var t = this,
			startX = e.clientX, startY = e.clientY,
			cont_w = stick_container.offsetWidth, cont_h = stick_container.offsetHeight
		
		t.capture = true
		
		if (Mode.mode != 'date')
		{
			document.onmousemove = bind(t, t.move, startX, startY, cont_w, cont_h)
			document.onmouseup = bind(t, t.stop)
		}
		else
		{
			document.onmousemove = bind(t, t.moveDate, startX, startY, cont_w, cont_h)
			Events.add('esc', 'Drag', function()
			{
				document.onmousemove = bind(t, t.move, startX, startY, cont_w, cont_h)
				document.onmouseup = bind(t, t.stop)
				Events.del('esc', 'Drag')
			})
			document.onmouseup = bind(t, t.stopDate)
		}
		
		var n, i = -1
		while (t.ary[++i])
		{
			n = t.ary[i].stick.node
			var left = n.offsetLeft, top = n.offsetTop
			
			t.ary[i].minleft= e.clientX - left
			t.ary[i].mintop	= e.clientY - top
			t.ary[i].maxleft= e.clientX + (cont_w - left)
			t.ary[i].maxtop	= e.clientY + (cont_h - top)
			t.ary[i].nodeX	= left
			t.ary[i].nodeY	= top
		}
	},
	
	move: function(startX, startY, cont_w, cont_h, e)
	{
		e = e || event
		var item, i = 0, n, s
		
		while (item = this.ary[i++])
		{
			n = item.stick.node
			s = n.style
			
			if (e.clientX > item.minleft && e.clientX < item.maxleft)
				s.left = item.nodeX + e.clientX - startX + 'px'
			else
				s.left = e.clientX > item.minleft ? cont_w + 'px' : 0
			
			if (e.clientY > item.mintop && e.clientY < item.maxtop)
				s.top = item.nodeY + e.clientY - startY + 'px'
			else
				s.top = e.clientY > item.mintop ? cont_h + 'px' : 0
		}
	},
	
	stop: function()
	{
		var i = -1, stick, n, save_data = []
		
		if (this.ary.length)
		{
			if (this.ary[0].stick.node.offsetLeft == this.ary[0].nodeX && this.ary[0].stick.node.offsetTop == this.ary[0].nodeY)
			{
				this.draged = false
				while (this.ary[++i])
				{
					stick = this.ary[i].stick
					if (stick.z != stick.node.style.zIndex)
					{
						stick.z = +stick.node.style.zIndex
						save_data.push(stick.save(['z'], '', true))
					}
				}
			}
			else
			{
				this.draged = true
				while (this.ary[++i])
				{
					stick = this.ary[i].stick
					n = stick.node
					stick.percantage()
					stick.x = parseFloat(n.style.left)
					stick.y = parseFloat(n.style.top)
					stick.z = +n.style.zIndex
					save_data.push(stick.save(['x','y','z'], '', true))
				}
			}
		
			if (save_data.length)
				req('updates/', 'notes={"notes":{"note":[' + save_data.join(',') + ']}}')
		}
		
		this.stick = null
	
		document.onmousemove = ''
		document.onmouseup = ''
	},
	
	moveDate: function(startX, startY, cont_w, cont_h, e)
	{
		e = e || event
		
		var t = this, n = t.stick.node
		t.move(startX, startY, cont_w, cont_h, e)
		if ((cont_w/2 - 252/2) > n.offsetLeft) // с права
		{
			if (t.moveDate.last === 0)
				return
			
			sticks.sortDate(n, 'left')
			t.moveDate.last = 0
		}
		else if ((cont_w/2 + 252/2) < n.offsetLeft) // с лева
		{
			if (t.moveDate.last === 1)
				return
			
			sticks.sortDate(n, 'right')
			t.moveDate.last = 1
		}
		else if (t.moveDate.last !== 2) // из центра
		{
			sticks.sortDate(n, 'center')
			t.moveDate.last = 2
		}
	},
	
	stopDate: function()
	{
		var t = this, n = t.stick.node
		
		// Если не изменялась позицию
		t.draged = !(n.offsetLeft == t.ary[0].nodeX && n.offsetTop == t.ary[0].nodeY)
		
		sticks.sortDate(n, null, (t.moveDate.last != 2 && t.draged == true))
		t.moveDate.last = null
		
		Events.del('esc', 'Drag')
		
		t.stick = null
		
		document.onmouseup = ''
		document.onmousemove = ''
		/*@cc_on document.body.onselectstart = '' @*/
	},
	
	// this == node
	onclick	: function()
	{
		return !Drag.draged
	}
}


function intersectRect(rectA, rectB)
{
	var x = Math.max(rectA.x, rectB.x)
	var y = Math.max(rectA.y, rectB.y)
	var w = Math.min(rectA.x + rectA.w, rectB.x + rectB.w) - x
	var h = Math.min(rectA.y + rectA.h, rectB.y + rectB.h) - y
	
	return (x < x + w && y < y + h)
}

function bind()
{
	var args = Array.prototype.slice.call(arguments), object = args.shift(), fn = args.shift()
	return function()
	{
		return fn.apply(object, args.concat(Array.prototype.slice.call(arguments)))
	}
}

function inArray(elem, array, prop)
{
	if (prop)
		for (var i = array.length - 1; i >= 0; i--)
		{
			// Use === because on IE, window == document
			if (array[i][prop] === elem)
				return true
		}
	else
		for (var i = array.length - 1; i >= 0; i--)
		{
			if (array[i] === elem)
				return true
		}
	
	return false
}

/*@cc_on @if(0) @*/
	document.addEventListener('focus', function(e){ window.focusedElement = e.target }, true);
	document.addEventListener('blur', function(e){ window.focusedElement = null }, true);
/*@cc_on @end @*/