EGGPalette = {
	swatches: [ 
			'white,black', '#80d6ff,black', '#7efed6,black', '#2cfd94,black', '#91fc19,black', 
			'#d3fd7c,black', '#fbfd1d,black', '#fcd67c,black', '#fb9312,black', '#fb0d0c,black', 	
			'#fd1893,black', '#fc7a7a,black', '#fd83d7,black', '#d67afe,black', '#8079fe,white'
	],
	/* str : "#FFFFFF, #000000" */
	toColorHash: function(str) {
		var ret = str.split('/');
		if (ret.length!=2) {
			ret = str.split(',');
		}
		return (ret.length>1) ? {'color':ret[1], 'backgroundColor':ret[0]} : {'color':'black', 'backgroundColor':'white'};
	},
	setCSSColorStyle: function(target, str) {
		target.setStyle(this.toColorHash(str));
	}
};
EGGRequest = {
	_toQueryString: function(params,rootKey) {
	    // handle nulls
	    if (params == null) {
	      	return rootKey + '=';
	    // handle arrays
	    } else if (params instanceof Array) {
	      	var ret = [] ;
	      	for(var loc=0;loc<params.length;loc++) {
	        	var key = (rootKey) ? (rootKey + '['+loc+']') : loc ;
				//var key = (rootKey) ? (rootKey + '[]') : loc ;	
	        	ret.push(this._toQueryString(params[loc],key)) ;
	      	}
	      	return ret.join('&') ;
	    // handle objects
	    } else if (typeof(params) == "object") {
	      	var ret = [];
	      	for(var cur in params) {
	        	var key = (rootKey) ? (rootKey + '['+cur+']') : cur ;
	        	ret.push(this._toQueryString(params[cur],key)) ;
	      	}
	      	return ret.join('&') ;
	    // handle other values
	    } else 
			//return [encodeURIComponent(rootKey),encodeURIComponent(params)].join('=') ;
			return [rootKey,encodeURIComponent(params)].join('=') ;
	}
};

EGGMetrics = {
	scaleRectByHeight: function(f, height) {
		f = Object.clone(f);
		f.width = Math.floor( (height / f.height) * f.width );
		f.height = height;
		return f;
	},
	scaleRectByWidth: function(f, width) {
		f = Object.clone(f);
		f.height = Math.floor( (width / f.width) * f.height );
		f.width = width;
		
		return f;
	}
};

Popegg = {
	Cookie: {
		getHashCookie: function(n) {
			var v = this.getCookie(n);
			var h = {};

			if (v) {
				v.split('&').each(function(t) {
					t = t.split('=');
					h[unescape(t[0])] = unescape(t[1]);
				}.bind(this));
			}
			return h;
		},
		/**
			* Sets a hashtable name/value object to a cookie.
			*
			* @param {String} n Name of the cookie.
			* @param {Object} v Hashtable object to set as cookie.
			* @param {Date} d Optional date object for the expiration of the cookie.
			* @param {String} p Optional path to restrict the cookie to.
			* @param {String} d Optional domain to restrict the cookie to.
			* @param {String} s Is the cookie secure or not.
		*/
		setHashCookie: function(n, v, e, p, d, s) {
			var o = '';
			v = $H(v);
			v.each(function(item) {
				//console.log("%@=%@".fmt(item.key, item.value));
				o += (!o ? '' : '&') + escape(item.key) + '=' + escape(item.value);
			}.bind(this));
			//console.log(o);
			this.setCookie(n, o, e, p, d, s);
		},
		/**
			* Gets the raw data of a cookie by name.
			*
			* @param {String} n Name of cookie to retrive.
			* @return {String} Cookie data string.
			*/
		getCookie: function(n) {
			var c = document.cookie, e, p = n + "=", b;

			// Strict mode
			if (!c) return;

			b = c.indexOf("; " + p);

			if (b == -1) {
				b = c.indexOf(p);
				if (b != 0) return null;
			} else b += 2;

			e = c.indexOf(";", b);

			if (e == -1) e = c.length;

			return unescape(c.substring(b + p.length, e));
		},

		/**
		 * Sets a raw cookie string.
		 *
		 * @param {String} n Name of the cookie.
		 * @param {String} v Raw cookie data.
		 * @param {Date} d Optional date object for the expiration of the cookie.
		 * @param {String} p Optional path to restrict the cookie to.
		 * @param {String} d Optional domain to restrict the cookie to.
		 * @param {String} s Is the cookie secure or not.
		 */
		setCookie : function(n, v, e, p, d, s) {
			if (!e) e = new Date(new Date().getTime()+(3650*24*60*60*1000));
			document.cookie = n + "=" + escape(v) +
				((e) ? "; expires=" + e.toGMTString() : "") +
				((p) ? "; path=" + escape(p) : "") +
				((d) ? "; domain=" + d : "") +
				((s) ? "; secure" : "");
		},

		/**
		 * Removes/deletes a cookie by name.
		 *
		 * @param {String} n Cookie name to remove/delete.
		 * @param {Strong} p Optional path to remove the cookie from.
		 */
		removeCookie : function(n, p) {
			//var d = new Date();
			var d=new Date(new Date().getTime()-(24*60*60*1000));
			//d.setTime(d.getTime() - 1000);
			
			//this.setCookie(n, '', d, p, d);
			//console.log(n+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT;");
			document.cookie=(n+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT;");
		}
	}


};
Popegg.RESTSource = {
	property_names: [],
	
	recentError: null,
	trackable: false,
	
	//TODO: Collage.Canvas class에서 override되지 않아 일단 지워봄
	//collection_url: function() { return "/"; },
	member_url: function() { return "%@/%@".fmt(this.collection_url(), this.id); },
	
	_toQueryString: function(params,rootKey) {
	    // handle nulls
	    if (params == null) {
	      return rootKey + '=';

	    // handle arrays
	    } else if (params instanceof Array) {
	      var ret = [] ;
	      for(var loc=0;loc<params.length;loc++) {
	        var key = (rootKey) ? (rootKey + '['+loc+']') : loc ;
	        ret.push(this._toQueryString(params[loc],key)) ;
	      }
	      return ret.join('&') ;

	    // handle objects
	    } else if (typeof(params) == "object") {
	      var ret = [];
	      for(var cur in params) {
	        var key = (rootKey) ? (rootKey + '['+cur+']') : cur ;
	        ret.push(this._toQueryString(params[cur],key)) ;
	      }
	      return ret.join('&') ;

	    // handle other values
	    } else {
			return [encodeURIComponent(rootKey),encodeURIComponent(params)].join('=') ;
		}
			
	  },
	properties: function() {
		var h = {};
		this.property_names.each(function(name) { 
			h[name] = this[name]; 
		}.bind(this));
		return h;
	},
	rCreate: function() {
		this.recentError = '';
		new Ajax.Request(this.collection_url(), {
			method:'POST',
			requestHeaders: { Accept: 'application/json' },
			parameters: this._toQueryString(this.properties()),
			onSuccess: this._afterCreate.bind(this)
		});
		if (this.trackable) pageTracker._trackPageview(this.collection_url()+"/create");
	},
	rDelete: function() {
		if (this.id==null || this.id==0) {
			console.log("Item id is null or zero:"+this.member_url());
			return;
		}
		new Ajax.Request(this.member_url(), {
			method:'POST',
			requestHeaders: { Accept: 'application/json' },
			parameters: {_method:'DELETE'}
		});
		//if (this.trackable) pageTracker._trackPageview(this.collection_url()+"/delete");
	},
	rRefresh: function(params) {
	 	new Ajax.Request(this.member_url(), {
			method:'GET',
			requestHeaders: { Accept: 'application/json, text/javascript, text/html, application/xml, text/xml, */*' },
			//parameters: {id:this.get('id')},
			onSuccess: this._afterRefresh.bind(this)
		});
		if (this.trackable) pageTracker._trackPageview(this.member_url());
	},
	rUpdate: function(params) {
		if (this.id==null || this.id==0) {
			console.log("Item id is null or zero:"+this.member_url());
			return;
		}
		var p = params || this.property_names;
		
		var h = {};
		p.each(function(name) { h[name] = this[name]; }.bind(this));
		
		console.log(this._toQueryString(h));
		
		new Ajax.Request(this.member_url(), {
			method:'PUT',
			requestHeaders: { Accept: 'application/json' },
			//parameters: h,
			parameters: this._toQueryString(h),
			onSuccess: this._afterUpdate.bind(this)
		});
		
		//if (this.trackable) pageTracker._trackPageview(this.member_url()+"/update");
	},
	_afterRefresh: function(transport) {
		try {
			var data = eval('data='+transport.responseText);
			if (data) {
				if (data.error) {
					if (this._errorRefresh) this._errorRefresh(data.error);
				} else {
					data = $H(data);
					data.each(function(i) { this[i.key] = i.value; }.bind(this));
				}
			}
			if (this._successRefresh) this._successRefresh(this);
		} catch(e) { console.log(e); }
	},
 	_afterCreate: function(transport) {
		try {
			var data = eval('data='+transport.responseText);
			if (data) {
				if (data.error) {				
					console.log("ERROR:"+data.error);	
					this._errorCreate(data.error);
				} else {
					// 에러가 아니면 가져온 것으로 property들 갱신해준다.
					data = $H(data);
					//console.log(this);
					data.each(function(i) { 
						this[i.key] = i.value;
						//if (this.setIfChange) this.setIfChange(i.key, i.value); 
						//else if (this.set) this.set(i.key, i.value);
						//else console.log("this is not OBJECT");
					}.bind(this));
					if (this._successCreate) this._successCreate(this);
				}
			}
		} catch(e) { console.log(e); }
		this._successCreate = null;
	},
	_afterUpdate: function(transport) {
		try {
			var data = eval('data='+transport.responseText);
			if (data) {
				if (data.error) {
					console.log("ERROR:"+data.error);
				} else {
					// 에러가 아니면 가져온 것으로 property들 갱신해준다.
					data = $H(data);
					data.each(function(i) { 
						this[i.key] = i.value;
					}.bind(this));
					
					if (this._successUpdate) this._successUpdate(this);
				}
			}
		} catch(e) { console.log(e); }
		this._successUpdate = null;
	},
	_errorCreate: function(error) {
		
		
	},
	rList: function(collection_url, callback) {
		var header = {Accept: 'application/json'};
		if (callback['timestamp']) {
			header['X-POPEGG-Timestamp'] = callback['timestamp'];
		}
		new Ajax.Request(collection_url, {
			method:'GET', requestHeaders: header,
			onSuccess: this._afterList.bind(callback)
		});
		if (this.trackable) pageTracker._trackPageview(collection_url);
	},
	_afterList: function(transport) {
		try {
			var timestamp = new Date(transport.getResponseHeader('Date')).toUTCString();
			//console.log(timestamp);
			var records = eval('records='+transport.responseText) ;
			if (!records) { console.log('invalid json!'); return; }
			//if($type(records)!=T_ARRAY) {
			if ( !(records instanceof Array)) {
				return null;
			} else {
				// 여기서 this는 [caller, collectionname, class of item]의 쌍이다. map을 쓰면 속도가 느려질 수 있으므로 for로 바꾸는 것도 생각해 보자
				// 새로 들어온 것은 추가 해주고 변경된 것은 반영해주고.
				//console.log(this);
				var collectionOwner = this['owner'];
				var collectionName = this['collectionName'];
				var collection = collectionOwner[collectionName];
				var itemClass = this['itemClass'];
				var appendFlag = this['append'];
				var changeLastModified = this['changeLastModified'];
				
				
				if (records.length>0 && changeLastModified == true)
					collectionOwner.lastModified = timestamp;
										
				if (!collection) collection = [];
				
				if (appendFlag == undefined || appendFlag == false) {
					collectionOwner[collectionName] = records.map(function(rec) 
						{ 
							//var item = itemClass.create(rec);
							var item = Object.extend(new itemClass(), rec);
							if (item.rebuildProperties) item.rebuildProperties();
							return item; 
						}.bind(this) 
					); 
					//store.replace(0, arr.length, arr); <-- 이 구문은 매우 이상한 동작을 한다.
				} else {
					var idset = {};
					var idx = collection.length;
					// collection 순환하면서 id, idx 쌍을 해쉬로 만든다.
					while(--idx>=0) idset[collection[idx].id] = idx;
					//console.log("%@ vs. %@".fmt(collection.length, idset.keys.length))
					// 그다음 레코드 순환하면서 교체해주고
					idx = records.length;
					var newRecords = [];
					while(--idx>=0) {
						var itemIdx = idset[records[idx].id];
						if (itemIdx!=undefined) {
							var exist = collection[itemIdx];
							var h = $H(records[idx]);
							//console.log(exist);
							exist.beginPropertyChanges();
							h.each(function(item) {
								exist.set(item.key,item.value);
								//console.log("%@->%@".fmt(exist.get('value'), item.value));
							}.bind(this) );
							if (exist.rebuildProperties) exist.rebuildProperties();
							exist.endPropertyChanges();
							//console.log("METRIC:%@,%@".fmt(exist.get('metric').x, exist.get('metric').y));							
							// 여기는 덮어쓰면 안되고 update하자
							//collection[itemIdx] = itemClass.create(records[idx]);
						} else {
							var item = itemClass.create(records[idx]);
							if (item.rebuildProperties) item.rebuildProperties();
							newRecords.push(item); // 순서가 뒤집어지는 것에 유의
						}
					}
					// 새 레코드들 추가해 준다.

					//if (newRecords.length>0) collection.replace(0,0 , newRecords);
					if (newRecords.length>0) collection.replace(collection.length,0 , newRecords);
					collection.replace(0,0,[]); //property change
				}
				if (collectionOwner && collectionOwner._successListFor) collectionOwner._successListFor();
			}
			//console.log("Change to recent time: %@".fmt(collectionOwner.get('lastModified')));
		} catch(e) {  console.log(e); }
	}
};

EGGListener = Class.create({
	bindUrl: "/stacks/converting",
	registry: {},
	pollingTimer: null,
	interval: 2000,
	
	start_poller: function(intv) {
		this.interval = intv;
		if (!this.pollingTimer) {
			this.pollingTimer = setTimeout("Popegg.DocItemListener.polling()", intv);
		} else {
			clearTimeout(this.pollingTimer);
			this.pollingTimer  = null;
			this.pollingTimer = setTimeout("Popegg.DocItemListener.polling()", intv);
		}
	},
	boost_poller: function() {
		this.start_poller(2000);
	},
	stop_poller: function() {
		if (this.pollingTimer) {
			clearTimeout(this.pollingTimer);
			this.pollingTimer = null;
		}
	},
	polling: function() {
		// timer 멈춰놓고 
		console.log("poiing");
		this.stop_poller();
		//console.log(this.pollingTimer.fireTime - new Date().getTime());
		// registry 순환하면서, max_age 넘은놈은 빼고 나머지만으로 parameter 구성
		var params = [];
		var now = new Date().getTime();
		for (var k in this.registry) {
			var ctx = this.registry[k];
			if (ctx.max_age < now) {
				delete this.registry[k];
				console.log("TIME OUT for :" + ctx);
			} else {
				// params 만든다
				console.log(ctx.object_condition);
				params.push(ctx.object_condition);
			}
		}		
		if (params.length > 0) {
			var param_str = EGGRequest._toQueryString(params, "P");
			//console.log(param_str);
			new Ajax.Request(this.bindUrl, {
				method:'POST',
				requestHeaders: { Accept: 'application/json' },
				parameters: param_str,
				onComplete: this._afterListen.bind(this)
			});
			
			//Popegg.DocItemListener.listen("id=? and convert_progress='complete'", null);
			// 구성된 parameter로 ajax 콜
			//this._afterListen();		
		} else {
			console.log("EMPTY_QUEUE");
		}
	},
	/* object, condition, callback */
	listen: function(object_condition, callback) {
		// 등록, TODO atomic operation이 되도록 하자
		var now = new Date().getTime();
		var context = {
			id: object_condition.id,
			start_time: now,
			max_age: now + 600 * 1000,//+ 60000,
			object_condition: object_condition,
			callback: callback
		};
		this.registry[context.id] = context;
		// Boost
		this.boost_poller();
	},
	_afterListen: function(transport) {
		try {
			console.log("_afterListen");
			try {
				var rtn = eval('rtn='+transport.responseText);
				if (rtn) {
					if (rtn.error) {
						console.log(rtn.error);
					} else {
						var idx=rtn.length;
						while(--idx>=0) {
							var id = parseInt(rtn[idx].id, 10);
							//console.log("id="+id);
							var obj = this.registry[id];
							if (obj) {
								if (obj.callback) {
									console.log("COMPLETE Listening:");
									//console.log("CALLBACK");
									obj.callback(rtn[idx]);
								}
								delete this.registry[id];
							}
						
						}
						//console.log(this.registry);
					}
				}
			} catch(e) { 
				console.log(e); 
			}
			// interval 바꿔서 timer 재기동
			// 리턴값 확인

			var intv = Math.min(this.interval*2, 20000);
			this.start_poller(intv);
			//TODO: request 끝나는 시간
		} catch(ge) {
			console.log(ge);
		}
		// condition 제거
		// timer 정상화
		// callback 호출
	}
});
// EGGListener.listen("(eq a b)", callback);

Collage = {};


Collage.Canvas = Class.create(Popegg.RESTSource, {	
	/////////////////////////////////////////////////
	//
	collection_url: function() { return "/canvases"; },
	/////////////////////////////////////////////////
	// Properties  
	//
	title: "",
	editable: false,  // 정의 : 제목 수정가능
	isTop: false,   // 아이템을 추가할 수 있는지 결정하는데 쓰임
	thumbnail: "/images/app/blank.gif",
	
	isLoaded: function() {
		return this.title != '로드 중...';
	},
	
	resource_type: "Canvas",
	getResourceType: function() { 
		//console.log("FIX THIS:canvas.resource_type");
		return "Canvas"; 
	},
	getResourceId: function() { 
		//console.log("FIX THIS:canvas.resource_id");
		return this.id; 
	},
	
	property_names: ['id', 'title', 'description'],
	
	_successCreate: function(transport) {
	}
});

Collage.CanvasItemStyle = Class.create({
	
	vshadow: true,
	vborder: false,
	vshow_title: false
});

Collage.canvasItemRenderHelper = new Class.create({
	
});

Collage.CanvasItem = Class.create(Popegg.RESTSource, {
	
	initialize: function() {
		return this;
	},
	initWithObject: function(obj) {
		Object.extend(this, obj);
		return this;
	},

	outlets:['title'],

	property_names: ['canvas_id', 'id', 'metric_str', 'resource_type', 'resource_id', 'isbn13', 'title', 'description', 'author', 'publisher', /*'thumbnail',*/ 'content_body', 'color', 'new_attach_item_id', 'rect_size', 'visual_style'],
	

	visual_style: {
		vshadow: true,
		vborder: false,
		vshow_title: false
	},
	unread: true,
	
	collection_url: function() {		
		return "/canvases/%@/canvas_items".fmt(this.canvas_id);
	},
	
	rDelete: function() {
		if (this.id==null || this.id==0) {
			console.log("Item id is null or zero:"+this.member_url());
			return;
		}
		new Ajax.Request(this.member_url(), {
			method:'POST',
			requestHeaders: { Accept: 'application/json' },
			parameters: {_method:'DELETE', type:this.type, feed_id:this.feed_id }
		});
		//if (this.trackable) pageTracker._trackPageview(this.collection_url()+"/delete");
	},
	
	saveStyleChanges: function() {
		this.rUpdate(['visual_style']);
	},

	updateThumbnail: function(h) {
		//console.log("update thumbnail for ");
		//console.log(h);
		if (h) {
			//TODO;
			//this.beginPropertyChanges();
			
			if (h.title && this.resource_type=='WebItem') this.title = h.title;
			//if (h.thumbnail) this.set('thumbnail', '/images/app/blank.gif');
			if (h.thumbnail) this.thumbnail = h.thumbnail;
			if (h.large_thumb) this.large_thumb = h.large_thumb;
			if (h.original_thumb) this.original_thumb = h.original_thumb;
			if (h.original_width) this.original_width = h.original_width;
			if (h.original_height) this.original_height = h.original_height;
			
			//this.endPropertyChanges();
			
			this._temporaryMetric = true;
			this.removeImage();
			Collage.mainController.updateItem(this, true);
		} else {
			this._temporaryMetric = true;
			this.removeImage();
			Collage.mainController.updateItem(this, true);
		}
	},
	
	_createSuccess: function(transport) {
		try {
			var data = eval('data='+transport.responseText);
			data = $H(data);
			if (data) {
				data.unset('metric');
				data.unset('metric_str');
				data.each(function(i) {
					//console.log("%@,%@".fmt(i.key,i.value));
					this.set(i.key, i.value);
				}.bind(this));
			}
			
		} catch(e) { console.log(e); }
	},
	_errorCreate: function(error) {
		this.recentError = error;
		if (this._by_drop) {
			SC.page.msgbox.showMessage(error);
			Collage.mainController.removeItem(this);
		} else {
			if (this._failCreate) this._failCreate(error);
		}
		this._failCreate = null;
	},

	get_metric_str: function() {
		var m = this.metric;
		return "%@,%@,%@,%@".fmt(Math.ceil(m.x), Math.ceil(m.y), Math.ceil(m.width||0), Math.ceil(m.height||0) );
	},

    activeFrame: function() {
        var m = this.metric;
        var ib = this.inner_bound;
        return {
            x: m.x + ib.x/10,
            y: m.y + ib.y/10,
            width: ib.width/10,
            height: ib.height/10
        };
    },
    offset: function() {
        var m = this.metric;
        var ib = this.inner_bound;
        return {
            left: Math.floor(ib.x/10),
            top: Math.floor(ib.y/10)
        };
    },
	/////////////////////////////
	
	//
	_visible: true,
	_initialRendered: true, //	style 바꾸기 할때 style observer가 불필요하기 노출되지 않게 하기 위해
	_thumbnailReady: 0,  // 0: not ready, 1: success, 2: don's need, 2: error
	
	loadImage: function() {
		this._thumbnailReady = 0;
		var m = this.metric;
		/*
		if (m.width>200 || m.height>200)
			imageCache.loadImage(this, this.large_thumb);
		else
		    imageCache.loadImage(this, this.thumbnail);
    	*/	
		
		//this.img = new Image();
		//this.img = document.createElement("img");
		this.img = new Image();
		this.img.onload = this._thumbnailLoaded.bind(this);
		this.img.onerror = this._thumbnailLoadError.bind(this);
		this.img.onabort = this._thumbnailLoadError.bind(this);
		
		if (m.width>400 || m.height>400)
			this.img.src = this.large_thumb;
		else
    		this.img.src = this.thumbnail;
    	
	},
	removeImage: function() {
		this._thumbnailReady = 0;
		this.img = null;
	},
	unbindImageHandler: function() {
	    this.img.onload = null;
	    this.img.onerror = null;
	    this.img.onabort = null;
	},
	prepareRendering: function(ctx, invalidateRect) {
		if (!this._visible) {
			Collage.mainController._renderingQueue_readyItem(this);
			return;
		}
		
		var m = this.metric;
	
		var min_size = this.rect_size || 100;
		
		if (m.width < 20 || m.height < 20) {
			m.width = min_size;
			m.height = min_size;
		}
	
		/*
		ctx.save();
		ctx.beginPath();
		ctx.rect(invalidateRect.x,invalidateRect.y,invalidateRect.width, invalidateRect.height);
		ctx.clip();
		//ctx.fillStyle="rgba(0,0,0,0.2)";
		//ctx.fillRect(m.x, m.y, m.width, m.height);
		ctx.restore();
		*/
		
		if (this.resource_type == "LabelItem") {
			this._thumbnailReady = 2;
			Collage.mainController._renderingQueue_readyItem(this);
		} else if (this.img == null) {	
			this.loadImage();		
		} else {
			// imagekㅏ 로드 되어 있다는 뜻
			Collage.mainController._renderingQueue_readyItem(this);
		}
	},
	render2d: function(ctx, invalidateRect, fUnreadFilter) {	
		if (!this._visible) return;
		
		var m = this.metric;

		//this._targetContext = ctx;
		//this._invalidateRect = invalidateRect;
		/*
		var tempCanvas = document.createElement("canvas");
		tempCanvas.width = invalidateRect.width;
		tempCanvas.height = invalidateRect.height;
		var ctx = tempCanvas.getContext("2d");
		*/
		
		//ctx.translate(-invalidateRect.x, -invalidateRect.y);
		
		
		if (this.resource_type == "LabelItem") {
			ctx.save();
			//ctx.clearRect();
			ctx.beginPath();
			ctx.rect(invalidateRect.x,invalidateRect.y,invalidateRect.width, invalidateRect.height);
			ctx.clip();
			ctx.clearRect(m.x, m.y, m.width, m.height);
			
			this._renderLabel(ctx);
			this._renderBadge(ctx);
			this._renderBaloon(ctx);
			this._renderTitle(ctx);
			ctx.restore();
			
			//this._targetContext.drawImage(ctx.canvas, invalidateRect.x, invalidateRect.y);
			//this._targetContext = null; //TT시간차	
		} else {
			this._renderThumbnail(ctx, invalidateRect, fUnreadFilter);
		}
	},
	_renderThumbnail: function(ctx, invalidateRect, fUnreadFilter) {
		var m = this.metric;
		
		if (this._thumbnailReady!=1) {
			console.log("ITEM ERROR:"+this._thumbnailReady);
			return;		
		} else {
			try {
				ctx.save();	
				
				ctx.beginPath();
				ctx.rect(invalidateRect.x,invalidateRect.y,invalidateRect.width, invalidateRect.height);
				ctx.clip();
				
				if (fUnreadFilter===true)
					if (this.unread) ctx.globalAlpha = 1.0;
					else ctx.globalAlpha = 0.2;
				
				this._renderBorder(ctx);
				
				if (this.resource_type=="Canvas")
				{
				    ctx.save();
				    ctx.fillStyle = "rgba(0,0,0,0.15)";
				    ctx.fillRect(
				        m.x + this.inner_bound.x/10, 
				        m.y + this.inner_bound.y/10, 
				        this.inner_bound.width/10, 
				        this.inner_bound.height/10
				    );
				    ctx.restore();
				} else {
				    if (Prototype.Browser.Chrome && Prototype.Browser.OS.windows) {
    					if (this.visual_style.vshadow && !this.visual_style.vborder) {
    						// 강제 그림자
							if (!fUnreadFilter || this.unread)
							{
								ctx.save();
		    					ctx.shadowOffsetX = 2;
								ctx.shadowOffsetY = 2;
								ctx.shadowColor = "rgba(1,1,1,0.8)";
								ctx.shadowBlur = 7;

								ctx.fillStyle="rgba(0,0,0,0)";
								ctx.fillRect(m.x, m.y, m.width, m.height);
								ctx.restore();
							}							
	    					//ctx.clearRect(m.x, m.y, m.width, m.height);
    					}
    				}
				}
				ctx.save();
				if (!this.visual_style.vborder) this._setShadow(ctx);
				ctx.drawImage(this.img,m.x,m.y, m.width, m.height);				
				ctx.restore();
				
				/**
				if (this.resource_type=="BookItem") {
					ctx.save();
					ctx.drawImage(Collage.canvasItemRenderHelper.bookHardCover,m.x,m.y, m.width, m.height);
					ctx.restore();
				}
				***/
				this._renderBadge(ctx);
				this._renderBaloon(ctx);
				this._renderTitle(ctx);
				
				ctx.restore();

			} catch(e) {
				console.log("Drawing exception:");
				console.log(e);
				console.log(this.img);
				console.log(this.metric);
			}

		}
	},
	
	_setShadow: function(ctx) {
		if (this.visual_style.vshadow) {
			ctx.shadowOffsetX = 2;
			ctx.shadowOffsetY = 2;
			//ctx.shadowColor = "#222222";
			//ctx.shadowBlur = 10;
			ctx.shadowColor = "rgba(1,1,1,0.8)";
			ctx.shadowBlur = 7;
		}
	},
	_renderLabel: function(ctx) {
			var m = this.metric;
			var ch = EGGPalette.toColorHash( this.color );
			// 배경
			ctx.save();
			ctx.translate(m.x, m.y);
			
			this._setShadow(ctx);
			ctx.fillStyle=ch['backgroundColor'];
			ctx.fillRect(0,0,m.width,m.height);
			ctx.restore();

			ctx.save();
			ctx.translate(m.x, m.y);
			ctx.fillStyle = ch['color'];
			ctx.textAlign = 'center';
			ctx.textBaseline = 'middle';
			ctx.font = '26px Arial';
	
			ctx.beginPath();
			ctx.rect(0,0,m.width, m.height);
			ctx.clip();
	
			ctx.fillText(this.title, m.width/2, m.height/2);//, m.width);
			ctx.restore();

	},
	
	_thumbnailLoadError: function() {
	    this.unbindImageHandler();
		this._thumbnailReady = 2;
		Collage.mainController._renderingQueue_readyItem(this);
		//imageCache.releaseWorker();
		//Collage.mainController._renderingQueue_commit();
	},
	_thumbnailLoaded: function() {
	    this.unbindImageHandler();
		try {
			
		this._thumbnailReady = 1;
		//imageCache.releaseWorker();

		var m = this.metric;
		var min_size = 100;
		
		this.imageWidth = this.img.width;
		this.imageHeight = this.img.height;
		
		if (this._temporaryMetric===true) {
			m.height = m.width * this.imageHeight / this.imageWidth;

			this.metric_str = this.get_metric_str();
			this.rUpdate(['metric_str']);
			this.inner_bound.height = m.height*10;
    		this.inner_bound.width = m.width*10;
			this._temporaryMetric = false;
		} else if (m.width < 20 || m.height < 20) {
			if (this.img.width > this.img.height) {
				m.width = min_size;
				m.height = Math.round( this.imageHeight * min_size / this.imageWidth  );
			} else {
				m.height = min_size;
				m.width = Math.round( this.imageWidth * min_size / this.imageHeight  );
			}
			this.inner_bound.height = m.height*10;
    		this.inner_bound.width = m.width*10;
		}
		this.metric = m;
		} catch(e) { console.log(e); }
		
		Collage.mainController._renderingQueue_readyItem(this);
		
		//try {
		    //Collage.mainController._renderingQueue_commit();
			Collage.mainController.selectedItem_reposition();
		//} catch(e) {
		//	console.log("=====================>"+e);
		//}		
	},
	_renderBorder: function(ctx) {
		if (this.visual_style.vborder) {
			var m = this.metric;
			ctx.save();
			/*
			ctx.strokeStyle = "white";
			ctx.lineWidth = 4;
			ctx.strokeRect(m.x-2,m.y-2,m.width+4,m.height+4);
			*/
			this._setShadow(ctx);
			ctx.fillStyle = "white";
			ctx.fillRect(m.x-5,m.y-5,m.width+10,m.height+10);
			
			ctx.restore();
		}
	},
	
	_renderBadge: function(ctx) {
		if (this.attach_count > 0) {
			var m = this.metric;
			ctx.save();
			ctx.translate(m.x - 10, m.y - 10);
			ctx.drawImage(Collage.canvasItemRenderHelper.badgeImage, 0, 0);
			ctx.fillStyle = "white";
			ctx.textAlign = 'center';
			ctx.textBaseline = 'middle';
			ctx.font = '16px Arial';
			ctx.fillText(this.attach_count, 18, 13, 36);
			ctx.restore();
		}
	},
	_renderBaloon: function(ctx) {
		var now = parseInt(new Date().getTime()/1000, 10);
		
		if (this.resource_type!="Canvas") {
			if (!!this.latest_comment_date) {
				var m = this.metric;
				ctx.save();
				ctx.translate(m.x + m.width - 10, m.y - 10);
				if (this.latest_comment_date>now-60*60*24) {
					ctx.drawImage(Collage.canvasItemRenderHelper.baloonActiveImage, 0, 0);
				} else {
					ctx.drawImage(Collage.canvasItemRenderHelper.baloonInactiveImage, 0, 0);
				}
				ctx.restore();
			}
		}	
	},
	//////////////////////////////////////////////////////////
	// Drag & Drops : Link and Unlink
	//
	_renderTitle: function(ctx) {
	    //if (this.resource_type=="Canvas") return;
	    
		if (this.resource_type != 'LabelItem' && (this.resource_type=="Canvas" || this.visual_style.vshow_title)) {
			var m = this.metric;
			
			ctx.save();
			ctx.font = '20px Arial';
			var textmetrics = ctx.measureText(this.title);
			var textwidth = Math.min(m.width, textmetrics.width);
			var gap = (m.width - textwidth)/2;
			ctx.restore();
			
            
            /*
			ctx.save();
			if (this.resource_type=="Canvas")
			{
			    ctx.translate(m.x, m.y + this.inner_bound.y/10 + this.inner_bound.height/10 + 16);
			    //ctx.strokeStyle = "rgba(255,255,255,0.8)";
			}
			else
			{
			    ctx.translate(m.x, m.y + m.height + 18);
			}
			ctx.strokeStyle = "rgba(0,0,0,0.5)";
			ctx.lineWidth = 22;
			ctx.lineCap = "round";
			ctx.beginPath();			
			ctx.moveTo(gap,0);
			ctx.lineTo(m.width-gap, 0);
			ctx.stroke();
			ctx.closePath();
			ctx.restore();
	        */
	        
			ctx.save();
			if (this.resource_type=="Canvas")
			{
			    ctx.translate(m.x, m.y + this.inner_bound.y/10 + this.inner_bound.height/10 + (16-9));
			    //ctx.fillStyle = "black";
			}
			else
			{
			    ctx.translate(m.x, m.y + m.height+9);
			    
			}
			ctx.fillStyle = "white";
			ctx.textBaseline = 'middle';
			ctx.font = '18px Arial';
	
			ctx.beginPath();
			ctx.rect(0,0,m.width, 20);
			ctx.clip();

			if (textmetrics.width > m.width) {
				ctx.textAlign = 'left';
				ctx.fillText(this.title, 0, 8);//, m.width);
			} else {
				ctx.textAlign = 'center';
				ctx.fillText(this.title, m.width/2, 8);//, m.width);
			}
			ctx.restore();
		}
		
	},
	isDropTarget: function() {
	 	return this.resource_type!='LabelItem';	
	},
	isDraggable: function() {
		return this.editable;
	},
	isStackable: function() {
		return this.resource_type!='LabelItem' && this.resource_type!='NoteItem' && this.editable && this.resource_type!='Canvas';
	},
	isResizable: function() {
		return this.editable;
	},
	//////////////////////////////////////////////////////////
	// Upload
	//
	uploadRatio: 0,
	uploadStatusMessage: "업로드 중이 아닙니다.",
	uploadStatus: null,
	_uploadXHR: null,
	_file: null,
	
	_uploadError: function() {
		console.log("upload error");
		this.uploadStatusMessage = "업로드가 실패하였습니다.";
		this.uploadStatus = "ERROR";
		this._uploadXHR = null;
		app.sendMessage("canvasitem", {action:"update_upload_status", canvas_item_id:this.id});
	},
	_uploadComplete: function() {
		console.log("success");
		this.uploadStatusMessage = "업로드 완료. 변환 중";
		this.uploadStatus = "SUCCESS";
		this._uploadXHR = null;
		app.sendMessage("canvasitem", {action:"update_upload_status", canvas_item_id:this.id});
	},
	_uploadCanceled: function() {
		console.log("uploadCanceled");
		this.uploadStatusMessage = "업로드 취소";
		this.uploadStatus = "CANCELED";
		this._uploadXHR = null;
		app.sendMessage("canvasitem", {action:"update_upload_status", canvas_item_id:this.id});
	},
	_uploadProgress: function(progress) {
		var percent = 0;
		
		if (progress.totalSize == this._file.fileSize) {
			if (progress.totalSize > 0)
				percent = Math.floor(progress.position*100/progress.totalSize);

			console.log("%@/%@, %@%".fmt(progress.position, progress.totalSize, percent));
			this.uploadRatio = percent;
		} else {
			console.log("abnormal progress: %@/%@".fmt(progress.position, progress.totalSize));
			this.cancelUpload();
			this.startUpload();
		}
		this.uploadStatus = "UPLOADING";
		this.uploadStatusMessage = "%@/%@".fmt(progress.position, progress.totalSize);
		app.sendMessage("canvasitem", {action:"update_upload_status", canvas_item_id:this.id});
	},
	startUpload: function() {	
		var xhr = new XMLHttpRequest();
		
		this._uploadXHR = xhr;
		
		if (this.resource_type=="ImageItem") {
			xhr.open("POST", "/collage/image_drag_upload", true);
		} else {
			xhr.open("POST", "/collage/hdfs_drag_upload?filename="+this._file.fileName, true);
			//alert('not supported');
		}
		
		//xhr.open("POST", "/collage/dummy?ticket="+item.ticket, true);
		xhr.setRequestHeader("Cache-Control", "no-cache");
		xhr.setRequestHeader('Content-Type', 'application/octet-stream');

		xhr.upload.addEventListener("progress", this._uploadProgress.bind(this), false);
		xhr.upload.addEventListener("load", this._uploadComplete.bind(this), false);
		xhr.upload.addEventListener("error", this._uploadError.bind(this), false);  
		xhr.upload.addEventListener("abort", this._uploadCanceled.bind(this), false);

	    xhr.addEventListener("load", this.requestComplete.bind(this), false);
		
		if (this._file.getAsBinary)
			xhr.sendAsBinary(this._file.getAsBinary());
		else
			xhr.send(this._file);		
	},
	cancelUpload: function() {
		if (this._uploadXHR) this._uploadXHR.abort();
	},
	requestComplete: function(evt) {		
		console.log(evt.target.responseText);
		var data = eval('data='+evt.target.responseText);   //에러났을때는 parse error가 난다.
		if (data) {
			if (data.error) {
				console.log(error);
				return;
			} else {
				this.resource_type = data.resource_type;
				this.resource_id = data.resource_id;			
				this.thumbnail = data.thumbnail;		
				this.large_thumb = data.large_thumb;		
				
				//SC.page.mainView.canvasView.updateItem(this, true);
				this.uploadStatusMessage = "변환 중";
				this.uploadStatus = "CONVERTING";
				app.sendMessage("canvasitem", {action:"update_upload_status", canvas_item_id:this.id});
				
				if (data.resource_type=="ImageItem") {
					this._successCreate = this.uploadAndCreateComplete.bind(this);
				}
				this.rCreate();
				if (data.resource_type!="ImageItem") {
					Popegg.DocItemListener.listen({id:parseInt(this.resource_id, 10), convert_progress:'complete'}, this.uploadAndCreateComplete.bind(this));
				}
			}
		}
	},
	uploadAndCreateComplete: function(h) {
		//console.log("uploadAndCreateComplete");
		this.uploadStatusMessage = "완료";
		this.uploadStatus = "COMPLETE";
		//$egg("#comment-bar #canvasitem-%@".fmt(this.temp_id)).attr('id', "canvasitem-%@".fmt(this.id));
		this.temp_id = null;
		app.sendMessage("canvasitem", {action:"update", canvas_item_id:this.id});
		this._temporaryMetric = true;
		
		this.updateThumbnail(h);
		this._file = null;
		app.sendMessage("canvasitem", {action:"update_upload_status", canvas_item_id:this.id});
	}
});


CanvasLog = {};

var TableViewController = Class.create({
	collection: null,
	view: null,
	headerColView: null,
	headerRowView: null,
	dataView:null,
	initialize: function() {
		return this;
	},
	attachView: function(path) {
		this.view = $egg(path.view);
		this.headerColView = $egg(path.view + " .header-col-container");
		this.headerRowView = $egg(path.view + " .header-row-container .header-row");
		this.dataView = $egg(path.view + " .data-rows-container .data-rows");
		
		this.view.find(".data-rows-container").bind('scroll', function() {
			console.log("scroll");
			this.headerColView.scrollTop(this.view.find(".data-rows-container").scrollTop());
			this.view.find(".header-row-container").scrollLeft(this.view.find(".data-rows-container").scrollLeft());
		}.bind(this));
		var self = this;
		this.view.live('click dblclick mousedown', function(evt) {
		    var row = $egg(evt.target).parents('.table-viewer .row');
		    if (evt.type=="dblclick") 
		    {
		        if (row.length>0) {
		            self.editEntry();
		        }
		    }
		    else if (evt.type=="mousedown")
		    {
		        if (row.length>0) {
		            self.selectedRowId = row.attr('id');
		        }
		    }
		    else if (evt.type=="click")
		    {
		        if ($egg(evt.target).is('.new_entry span')) {
		            console.log("new entry");
		            self.newEntry();
		        }
		    }
		});
		return this;
	},
	setCollection: function(arr) {
		this.collection = arr;
		
		this.view.children(".collection-item").unbind('click');
		this.view.empty();
		
		var html = '<div class="frame collection-item"><img src="%@" class="thumbnail"/></div>';
		var len = this.collection.length;
		for (var i=0;i<len;i++) {
			var item = this.collection[i];
			this.view.append(this.renderItem(item, i));
		}
	},
	renderItem: function(item, index) {
		return '<div class="frame collection-item"><img src="%@" class="thumbnail"/></div>'.fmt(item.thumbnail);
	},
	
	open: function(item) {
		this.content = item;
			
		$egg.getJSON("/table/header_row/%@".fmt(item.resource_uid), this.setHeaderRow.bind(this));
		$egg.getJSON("/table/header_col/%@".fmt(item.resource_uid), this.setHeaderCol.bind(this));
		$egg.getJSON("/table/cells/%@".fmt(item.resource_uid), this.setData.bind(this));	
	},
	///////////////////////////////////////////////
	// Table View Delegation
	//
	selectedRowId: null,
	data_rows: [], 
	
	setHeaderRow: function(data) {
		
		this.headerRowView.empty();
		var len = data.length;
		for(var i=0;i<len;i++) {
			this.headerRowView.append('<div class="cell" style="top:0px; left:%@px; width:%@px;" title="">%@</div>'.fmt(data[i].left, data[i].width-10, data[i].label));
		}		
	},
	setHeaderCol: function(data) {
		this.headerColView.empty();
		var len = data.length;
		for(var i=0;i<len;i++) {
			this.headerColView.append('<div class="cell" title=""><span class="label">%@</span><a class="delete">삭제</a></div>'.fmt(data[i].label));
		}
	},
	setData: function(data) {
		this.dataView.empty();
		var rows = data.length;
		for(var row=0;row<rows;row++) {	
			var html = [];		

			if (data[row].cells) {
				var len = data[row].cells.length;
				html.push('<div class="row" id="%@" style="width:%@px; height:%@px; top:%@px;">'.fmt(data[row].id, len*150, 30, 0));
				for (var i=0; i<len; i++) {
					html.push('<div class="cell" style="left:%@px">%@</div>'.fmt(150*i, data[row].cells[i]));
				}
			} else {
				html.push('<div class="row" id="%@">'.fmt(data[row]).id);
			}
			html.push('</div>');
			this.dataView.append(html.join(''));
		}
		
	},
	refreshTable: function() {
		$egg.getJSON("/table/header_col/%@".fmt(this.content.resource_uid), this.setHeaderCol.bind(this));
		$egg.getJSON("/table/cells/%@".fmt(this.content.resource_uid), this.setData.bind(this));
	},
	newEntry: function() {
	    /*
		$egg('#dialog_content')
		.dialog(PLAIN_DIALOG_OPTIONS)
		.load("/table/new_entry/%@".fmt(this.content.resource_uid));
		*/
		app.openDialog('%@: 새 Sheet'.fmt(this.content.title), "/table/new_entry/%@".fmt(this.content.resource_uid));
	},
	editEntry: function() {
        app.openDialog(
            "%@: Sheet 수정".fmt(this.content.title),
            "/table/edit_entry/%@/%@".fmt(this.content.resource_uid, this.selectedRowId)
        );
	},
	deleteEntry: function(row_id) {
		new Ajax.Request('/table/delete_entry/%@/%@'.fmt(this.content.resource_uid, row_id), {
			method: 'POST',
			onSuccess: function() { this.refreshTable(); }.bind(this)
		});
	},
	clearAnswers: function(e) {
		try {  
			Selector.findChildElements(e.parentNode, ["input[type='radio']"]).each(function(r) { r.checked=false; });
		} catch(e) {}
	}
});


var PreviewController = Class.create({
	//////////////////////////////////////////////////////////
	// View
	//
	view: null,
	tabView: null,
	imageView: null,
	movieView: null,
	listView: null,
	tableView: null,
	
	initialize: function() {	
	    /**
		this.listView = new CollectionViewController().initWithObject({
			removeItem: function(index) {
				this.view.children("DIV:nth-child(%@).collection-item".fmt(index+1)).unbind('click').remove();
				this.collection.removeAt(index);
				if (index < this.collection.length) this.view.children("DIV:nth-child(%@).collection-item".fmt(index+1)).click();
				else this.view.children("DIV:nth-child(%@).collection-item".fmt(this.collection.length)).click();
			}
		});
		**/
		//this.bookView = new BookViewController();
		this.tableView = new TableViewController();
		return this;	
	},
	attachView: function(path) {
		this.view = $egg(path.view);
		this.tabView = $egg(path.tabView);
		
		var self = this;

        $egg('#preview-panel .main-pane .list-scroll-view').listview({
            updateItem: function(item, view) {
			    view.children(".thumbnail").attr('src', item.thumbnail);
		    },
		    template: function(item, index) {
		        return '<div class="frame egg-list-item" style="position:absolute; top:%@px;"><img src="/images/app/blank.gif" class="thumbnail"/></div>'.fmt(index*100);
		    }
		});
		
		$egg('#preview-panel .book-viewer').listview({
		    rowHeight: 'auto',
		    updateItem: function(item, view, position) {
		        
		        if (!item) return;
		        
    			if (position.end == position.index) {
    			    $egg('#preview-panel .main-pane .list-scroll-view .egg-list-item.sel').removeClass('sel');
            		$egg('#preview-panel .main-pane .list-scroll-view .egg-list-item:nth-child(%@)'.fmt(position.start+1)).addClass('sel');
    			}
    				 
		        if (view.hasClass('loaded')) return;
		               
		        view.removeClass('blank').addClass('loaded');
		        
		        if (item.resource_type=="NoteItem") 
		        {
		            view.addClass("NoteItem");
		            view.children("img.page_image").hide();
		            view.append('<div class="notepad"><div class="editor" contenteditable="true"></div></div>');
		            var notepad = view.find(".notepad");
		            notepad.width(item.screen.width).height(item.screen.height);
		            notepad.find(".editor").html(item.data);
		        } 
		        else 
		        {
		            view.children("img.page_image")
        				.width(item.screen.width)
        				//.height(this.collection[i].screen.height)
        				.attr('src', item.large_thumb);

        			if (item.resource_type=="MovieItem") view.addClass("MovieItem"); 
		        }
    			
    			
		    },
		    template: function(item, index) {
		        return '<div class="page-item egg-list-item blank" style="position:absolute;display:block;margin:auto;top:%@px;height:%@px;"><img src="/images/app/blank.gif" class="thumbnail sc-image-view page_image" /></div>'.fmt(item.screen.top, item.screen.height);
		    }
		});
        
		//this.bookView.attachView(path.bookviewer);
		this.tableView.attachView(path.tableviewer);
		
		this.tabView.children('.button').click(function() {
			console.log("TAB CLICKED");
			var others = $egg.makeArray( 
				$egg(this).addClass('sel').siblings(".button").removeClass('sel')
			).map(function(e) { 
				return e.className.match(/\w+_btn/)[0].gsub(/_btn/,''); 
			}).join(' ');
			
			var sel = this.className.match(/\w+_btn/)[0].gsub(/_btn/,'');
			//Collage.previewController.set('viewMode', sel); 
			$egg(this).closest('.toolbar_segment').removeClass(others).addClass(sel).trigger('tabchange');
		});		
		this.tabView.bind('tabchange', this.tabChanged.bind(this));
		
		//$egg('#preview-panel .comments-bubble').draggable({handle:'.header'});
		$egg('#preview-panel').live('click dblclick change keydown', this.eventHandler.bind(this));
		
		return this;
	},
	selectTab: function(tabName) {
		console.log(this.tabView.children("."+tabName+'_btn').length);
		this.tabView.children("."+tabName+'_btn').click();
	},

	//////////////////////////////////////////////////////////
	// Content
	content: [],
	selectedItem: null,
	preview: function(item) {					
		this.view.css({right:$egg('#canvas_main').css("right")});
		if (this.view.css('display')=='none') this.view.fadeIn(400);
		this.content = [item];
		
		if (item.resource_type=="ImageItem" && item.original_width > this.view.width()) {
			this.selectTab('fitToScreen');
		} else this.selectTab('originalSize');
		
		/*if (item.attach_count && item.attach_count>0) this.view.addClass('has_stack');
		else this.view.removeClass('has_stack');
		
		this.view.addClass('has_stack');*/
		
		this.selectItem(item);
		this.listStack();
	},
	_preview: function(item) {
		console.log("____preview");
		if (this.selectedItem == item) return;

        /**
		this.content = [item];
		
		if (item.resource_type=="ImageItem" && item.original_width > this.view.width()) {
			this.selectTab('fitToScreen');
		} else this.selectTab('originalSize');
		
		if (item.attach_count && item.attach_count>0) this.view.addClass('has_stack');
		else this.view.removeClass('has_stack');
		**/
		
		this.selectItem(item);
		this.listStack();
	},
	close: function() {
		if (this.view.css('display')!='none') {
		    var self = this;
		    $egg("#preview-panel .page-item .notepad").each(function() {
		        var idx = $egg(this).parents('.page-item').index();
		        console.log("노트입니다.:%@".fmt(idx));
		        
		        var item = self.content[idx];
		        
		        if (item)
		        {
		            //var r = document.createRange();
                    //r.selectNodeContents($egg('#notepad .editor')[0]);
    	            //window.getSelection().removeAllRanges();
                    //window.getSelection().addRange(r);
                    
                    $egg(this).find(".editor").focus();
                    document.execCommand('selectAll', false, null);
    	            document.execCommand('removeFormat', false, null);

                    var value = $egg(this).find(".editor").html();

                    if (value != self.content[idx].data) {
                        console.log("변경되었습니다.");
                        item.title = $egg(this).find(".editor").lbText().split('\n')[0].substr(0, 20)+"...";
        	            item.description = value;
        	            item.rUpdate(['title', 'description']);
                    }
                    else
                    {
                        console.log("변경되지 않았습니다.");
                    }
		        }
		        
	            return true;
		    });
			//this.viewer.movieViewer.update('');
			//this.viewer.imageViewer.imageView.set('value', null);//rootElement.src="/images/app/blank.gif";
			//Collage.itemController.setCurrentItem(Collage.mainController.currentItem);
			$egg('#preview-panel .main-pane .list-scroll-view').listview('collection', []);
			$egg('#preview-panel .book-viewer').listview('collection', []);
			
			//this.bookView.clear();
			this.view.fadeOut(400);
			this.content = null;
			this.selectedItem = null;
		}
	},
	getViewMode: function() {
		return this.tabView.children('.button.sel').attr('class').match(/\w+_btn/)[0].gsub(/_btn/,'');
	},
	tabChanged: function() {
		this.layoutPages();
		//this.bookView.updateListView(true);
		//var collection = $egg('#preview-panel .book-viewer').listview('collection');
		//console.log(collection);

		$egg('#preview-panel .book-viewer').listview_prepareAllItem();
		$egg('#preview-panel .book-viewer').listview_update();

	},

    layoutPages: function() {
		var top = 10;
		var viewmode = this.getViewMode();
		
		var width = $egg('#preview-panel .book-viewer').width() -160;
        var height = $egg('#preview-panel .book-viewer').height() -20;
        
		var len = this.content.length;
		for (var i=0;i<len;i++) {
			var item = this.content[i];
			item.screen = {
			    top: top,
			    width: item.original_width,
			    height: item.original_height
			};

            if (item.resource_type=="ImageItem" && viewmode=="fitToScreen") {			    

                if (item.screen.height > height) {
                    item.screen.width = Math.floor( (height / item.screen.height) * item.screen.width );
                    item.screen.height = height;
                }
                if (item.screen.width > width) {
                    item.screen.height = Math.floor( (width / item.screen.width) * item.screen.height );
                    item.screen.width = width;
                }
            } else if (item.resource_type=="MovieItem") { 
                item.screen.height = Math.floor( (480 / item.screen.width) * item.screen.height );
                item.screen.width = 480;
            }
			top += item.screen.height+10;
		}
    },
	//////////////////////////////////////////////////
	//
	//
	listStack: function() {
		this.content = [];
				
		
		if (this.selectedItem.resource_type=="TableItem") {
		    this.view.removeClass('has_stack');
		    return;
		}
		
		var self = this;
		
			$egg.getJSON("/canvas_item/pages/%@".fmt(this.selectedItem.id), function(data) {
			    //console.log(data);
				var len = data.length;
				
				if (len > 0) this.view.addClass('has_stack');
				else this.view.removeClass('has_stack');
				//var top = this.content.first().screen.top+this.content.first().screen.height+10;
				var top = 10;
				for (var i=0;i<len;i++) {
					var item = new Collage.CanvasItem();
					item = Object.extend(item, data[i]);
					this.content.push(item);
				}
				this.layoutPages();
				//var self = this;
				
				$egg('#preview-panel .main-pane .list-scroll-view').listview('collection', this.content);
				//this.bookView.setCollection(this.content);
				$egg('#preview-panel .book-viewer').listview('collection', this.content);
				
				$egg('#preview-panel').setClass('has_stack', this.content && this.content.length > 1);
				
				//this.listView.view.children().first().click();
			}.bind(this));
		//}
	},
	unlinkItem: function() {
		var item = this.selectedItem;
		Collage.itemController.unlinkItem(item);
		
		var index = this.listView.collection.indexOf(this.selectedItem);
		this.listView.removeItem(index);
		
		/*
		var newitem = this.listView.collection[index];
		if (!newitem) newitem = this.listView.collection.last();
		this.selectItem(newitem);
		*/
	},
	selectItemByIndex: function(idx) {
		console.log("Selected by index");
		this.selectItem(this.content[idx]);
	},
	selectItem: function(item) {
		console.log("Changed:"+item.id);
		if (item) {			
		    
			if (this.selectedItem === item) {
				console.log("Same object!");
				return;
			}
			if (Collage.itemController) Collage.itemController.setCurrentItem(item);
			this.selectedItem = item;

			this.view.removeClass("ImageItem MovieItem WebItem BookItem DocItem LabelItem TableItem");
			
			if (this.selectedItem.resource_type=="TableItem")
			    this.view.addClass("TableItem");
			else
			    this.view.addClass("DocItem");
			
			$egg('#preview-panel .comments-bubble').draggable('destroy').remove();
			$egg('#preview-panel .comments-threads').remove();
			
			
			var resType = item.resource_type;
			//this.imageView.open(item, this.getViewMode());
			//$egg('#preview-panel .image-viewer').append('<div class="comments-threads"></div>');
			//this.movieView.open(item);
			//$egg('#preview-panel .movie-viewer').append('<div class="comments-threads"></div>');
			//this.bookView.rowHeight = item.original_height + 20;
			//this.bookView.colWidth = item.original_width;				
				
			if (resType=="TableItem") {
				this.tableView.open(item);
			} else {
				//if (resType=="BookItem") this.bookView.rowHeight = 900;
				//this.bookView.open(item);
				$egg('#preview-panel .book-viewer').append('<div class="comments-threads"></div>');
			
			    var self = this;
			    $egg('#preview-panel .book-viewer .comments-threads').load('/comment/threads', {resource_id:item.resource_id, canvas_item_uid:item.uid}, 
			        function() {
				        $egg(this).find('.comments-bubble')
					    .draggable({axis: 'y', scroll:true, handle:'.header'})
					    .resizable({handles:'se'})
					    .bind({
						    resizestop: function() {
							    if (!canvas.loggedIn) return;
							    $egg.post('/comment/meta', { 
								    top: ($egg(this).frame().y),
								    width: $egg(this).width(),
								    thread_id: $egg(this).idattr('thread-'),
									resource_id: self.selectedItem.resource_id,
								    canvas_item_uid: self.selectedItem.uid
							    });
						    },
                            dragstop: function() {
                                if (!canvas.loggedIn) return;
                                $egg.post('/comment/meta', { 
                                    top: ($egg(this).frame().y),
                                    width: $egg(this).width(),
                                    thread_id: $egg(this).idattr('thread-'),
									resource_id: self.selectedItem.resource_id,
                                    canvas_item_uid: self.selectedItem.uid
                                });
								// console.log();
								
                            }
					    });  // bind
			    });  // callback, load()
			}
		}
  	},
	cancelEditing: function(editor) {
		if (editor===undefined) editor = $egg('#inplace-editor');
		
	},
	beginEditing: function(body) {

	},
	eventHandler: function(evt) {
		//console.log(evt);
		//console.log(evt.target);
		
		var target = $egg(evt.target);
		
		var thread = target.parents(".comments-bubble");
		
		
		if (thread.length==0) 
		{
		    if (evt.type == "click") {
		        if (target.is('.list-view .egg-list-item .thumbnail')) 
                {
                    $egg(target).parent().addClass('sel').siblings().removeClass('sel');
    				$egg('#preview-panel .book-viewer').scrollTop(this.content[$egg(target).parent().index()].screen.top);
                }
                else if (target.is('.page-item.MovieItem img'))
                {
                    var itemview = $egg(target).parent();
                    var item = this.content[itemview.index()];
                    itemview.html('<img class="activity-indicator" src="/images/spinner32.gif" style="position:absolute; left:48%; top:50px;"/>')
                    .load('/collage/stack_preview', 
                        {
                            resource_type: item.resource_type, 
            				resource_id: item.resource_id
                        }
                    );
                }
		    }
		    else if (evt.type == "dblclick")
		    {
		        
		        
		    }
		    return;
		} 
		////////////////////////
		// COMMENTS
		else 
		{
		    var threadId = thread.idattr('thread-');

    		var itemView = target.parents(".comment-item");
    		if (itemView.length>0) itemId = itemView.idattr('comment-');
    		var self = this;

    		if (evt.type == "click") 
    		{
    			if (target.is('.body')) 
    			{  
    				if ($egg('#inplace-editor').length>0) 
    				{
    					$egg('#inplace-editor')
    						.unbind('blur keydown').hide()
    						.siblings(".body").show()
    						.parents('.comments-bubble .comment-item.editing').removeClass('editing');;

    					var bubble = $egg('#inplace-editor').parents('.comments-bubble');
    					$egg('#inplace-editor').remove();		
    					bubble.find('#comment-temporary').remove();
    					if (bubble.find('.comment-item').length == 0) {

    						$egg.post('/comment/remove_meta', {
    							thread_id: bubble.idattr('thread-'),
    							canvas_item_id: self.selectedItem.uid
    						});
    						bubble.remove();
    					}
    				}
    				target.before('<span contenteditable="true" id="inplace-editor" style="background-color:white;">%@</span>'.fmt(target.html()));


    				$egg("#inplace-editor").bind({
    					blur: function(evt) {
    						console.log("BLUR...............");
    					},
    					keydown: function(evt) {
    						if (evt.keyCode == '13') {
    						} else if (evt.keyCode == '27') {
    							$egg('#inplace-editor').unbind('blur keydown').hide().siblings(".body").show();
    							$egg('#inplace-editor').remove();
    							target.parents('.comments-bubble .comment-item.editing').removeClass('editing');
    						}
    					}
    				}).val(target.text()).focus();

    				target.hide();
    				thread.addClass("writing");
    				itemView.addClass('editing');
    				if (document.getSelection) 
    				    if (document.getSelection().collapseToEnd) document.getSelection().collapseToEnd();
    				else if (document.selection)
    				{
    				    var r = document.selection.createRange();
    				    r.collapse(true);
    				    r.select();
    				}
    				$egg("#inplace-editor").focus();
    				    

    			} 
    			else if (target.is('.delete-comment .icon')) 
    			{ 
    			    //delete comment
    				var comment = target.parents(".comment-item");

    				comment.load('/comment/destroy', {id:comment.attr('id').replace('comment-','')}, function() {
    					var comments = $egg(this).siblings();
    					var bubble = $egg(this).parents('.comments-bubble');
    					$egg(this).remove();

    					if (comments.length==0) {
    						$egg.post('/comment/remove_meta', {
    							thread_id: bubble.idattr('thread-'),
    							canvas_item_id: self.selectedItem.uid
    						});
    						bubble.remove();
    					}
    				});

    			} 
    			else if (target.is('.comment_reply .label')) 
    			{
    				target.parents('.comments-bubble').addClass("writing");
    				target.parents('.comments-bubble').find('.canvasitem-comments').append(
    					'<div class="list-item comment-item" id="comment-temporary"><span class="member">%@</span>						<span class="body">&nbsp;</span></div>'.fmt(canvas.member_name)
    				);
    				target.parents('.comments-bubble').find("#comment-temporary .body").click();
    				target.parents('.comments-bubble #comment-temporary').addClass('editing');

    			} 
    			else if (target.is('.comment_submit .label')) 
    			{
    				target.parents('.comments-bubble .comment-item.editing').removeClass('editing');

    				target.parents('.comments-bubble').removeClass("writing");

    				var v = $egg('#inplace-editor').lbText();
    				//$egg('#inplace-editor').text();
    				var bodyElem = $egg('#inplace-editor').siblings(".body");				
    				bodyElem.show().lbHtml(v);

    				$egg('#inplace-editor').unbind('blur keydown').hide().siblings(".body").show();
    				$egg('#inplace-editor').remove();

    				//console.log(bodyElem.parents('comment-item')[0]);
    				var id = bodyElem.parents('.comment-item').removeClass('editing').attr('id').replace('comment-','');

    				if (id == "temporary") {
    					bodyElem.parents('.comment-item').ajaxReplace('/comment/create', {
    						body: v, canvas_item_id:this.selectedItem.id, thread_id: threadId
    					});
    				} else {
    					$egg.post('/comment/update', {id:id, body:v});
    				}
    			} 
    			else if (target.is('.comment_cancel span')) 
    			{
    				var bubble = target.parents('.comments-bubble');
    				bubble.removeClass("writing");
    				$egg('#inplace-editor').unbind('blur keydown').hide().siblings(".body").show().parents('.comments-bubble .comment-item').removeClass('editing');;
    				$egg('#inplace-editor').remove();		
    				bubble.find('#comment-temporary').remove();
    				if (bubble.find('.comment-item').length == 0) {

    					$egg.post('/comment/remove_meta', {
    						thread_id: bubble.idattr('thread-'),
    						canvas_item_id: self.selectedItem.uid
    					});
    					bubble.remove();
    				}
    			}

    		} 
		}
		
	}
		
});

