1 /**
  2 	@class
  3 	@extends rio.Attr
  4 	
  5 	Page is used to create page classes in a rio application.  It handles css dependencies, rendering, templating, 
  6 	layout management, and popup management.
  7 	
  8 	@author Jason Tillery
  9 	@copyright 2008-2009 Thinklink LLC
 10 */
 11 rio.Page = {
 12 	create: function() {
 13 		var args = $A(arguments);
 14 		if (args.length > 0 && args.last() != undefined && !args.last().ATTR) {
 15 			args[args.size() - 1] = Object.extend({ noExtend: true }, args.last());
 16 		}
 17 		var page = rio.Attr.create.apply(this, args);
 18 
 19 		Object.extend(page, {
 20 			requireCss: function(name) {
 21 				rio.Application.includeCss("pages/" + name);
 22 			},
 23 			template: function(name) {
 24 				rio.boot.loadedTemplates.push(rio.boot.appRoot + "pages/" + name);
 25 
 26 				this._templateName = name;
 27 			},
 28 			applyTemplate: function() {
 29 				if (this._templateName && !this._templateApplied) {
 30 					var template = rio.JsTemplate.build("pages/" + this._templateName);
 31 					page.addMethods(template.mixinMethods());
 32 
 33 					this._templateApplied = true;
 34 				}
 35 			},
 36 			manageLayout: function() {
 37 				this.__manageLayout = true;
 38 			},
 39 			manageUndo: function() {
 40 				this.__manageUndo = true;
 41 			}
 42 		});
 43 		
 44 		page.addMethods({
 45 			html: function() {
 46 				if (!this._html) { this._html = this.buildHtml(); }
 47 				return this._html;
 48 			},
 49 			
 50 			popup: function() {
 51 				if (!this.__popup) { 
 52 					this.__popup = new rio.components.Popup({
 53 						content: this.html()
 54 					});
 55 				}
 56 				this.__popup.activate();
 57 				this.render();
 58 			},
 59 			
 60 			resizePopup: function() {
 61 				this.__popup.resize();
 62 			},
 63 			
 64 			closePopup: function(skipFade) {
 65 				this.__popup.deactivate(skipFade);
 66 			},
 67 			
 68 			isManagingLayout: function() {
 69 				return page.__manageLayout;
 70 			},
 71 			
 72 			_manageLayout: function() {
 73 				if (page.__manageLayout) { 
 74 					this.__lm = new rio.LayoutManager(this.html());
 75 				}
 76 			},
 77 			
 78 			layoutManager: function() {
 79 				return this.__lm;
 80 			},
 81 			
 82 			_afterRender: function() {
 83 				if (this.isManagingLayout()) {
 84 					(function() { this.__lm.resize(); }.bind(this)).defer();
 85 					(function() { this.__lm.resize(); }.bind(this)).delay(2);
 86 					(function() { this.__lm.resize(); }.bind(this)).delay(5);
 87 				}
 88 			},
 89 			
 90 			render: function() {
 91 				this._manageLayout();
 92 				this._afterRender();
 93 			},
 94 			
 95 			show: function() {
 96 				this.html().show();
 97 				this.resizePopup();
 98 			},
 99 			
100 			hide: function() {
101 				this.html().hide();
102 			},
103 			
104 			addHistoryEntry: function(location, isTransient) {
105 				rio.app.addHistoryEntry(location, isTransient);
106 			},
107 			
108 			applyHistoryEntry: function() {
109 				// Meant to be overriden
110 			},
111 			
112 			keyPress: function(e) {
113 				// Meant to be overriden
114 			},
115 			
116 			keyDown: function(e) {
117 				// Meant to be overriden
118 			},
119 			
120 			resize: function() {
121 				// Meant to be overriden
122 			},
123 			
124 			getKeyMap: function() {
125 				if (!this._keyMap) {
126 					var maps = this.keyMap ? this.keyMap() : [];
127 
128 					if (page.__manageUndo) {
129 						maps.push({
130 							mac: { meta: true, shift: false, alt: false, key: "z" },
131 							win: { ctrl: true, alt: false, key: "z" },
132 							handler: function(e) {
133 								if (e.target.value == undefined && this.undoShortcutsEnabled()) {
134 									rio.Undo.undo();
135 								}
136 							}.bind(this)
137 						});
138 						maps.push({
139 							mac: { meta: true, shift: true, alt: false, key: "z" },
140 							win: { ctrl: true, alt: false, key: "y" },
141 							handler: function(e) {
142 								if (e.target.value == undefined && this.undoShortcutsEnabled()) {
143 									rio.Undo.redo();
144 									e.stop();
145 								}	
146 							}.bind(this)
147 						});
148 					}
149 					this._keyMap = rio.KeyMap.build(maps);
150 				}
151 				return this._keyMap;
152 			},
153 			
154 			enableUndoKeyboardShortcuts: function() {
155 				this._undoShortuctsEnabled = true;
156 			},
157 
158 			disableUndoKeyboardShortcuts: function() {
159 				this._undoShortuctsEnabled = false;
160 			},
161 			
162 			undoShortcutsEnabled: function() {
163 				if (this._undoShortuctsEnabled == undefined) { this._undoShortuctsEnabled = true; }
164 				return this._undoShortuctsEnabled;
165 			},
166 			
167 			_keyDown: function(e) {
168 				this.getKeyMap().handle(e);
169 				this.keyDown(e);
170 			},
171 			
172 			navigateTo: function(path) {
173 				rio.app.navigateTo(path);
174 			},
175 			
176 			refresh: function() {
177 				rio.app.refresh();
178 			},
179 			
180 			rootUrl: function() {
181 				return rio.app.rootUrl();
182 			},
183 			
184 			getCurrentLocation: function() {
185 				return rio.app.getCurrentLocation();
186 			},
187 			
188 			toString: function() {
189 				return "[rio.pages.*]";
190 			}
191 		});
192 		
193 		// if (last arg is a concise syntax initializer)
194 		if (args.length > 0 && args.last() != undefined && !args.last().ATTR) {
195 			var initializers = args.last();
196 			if (Object.isString(initializers.requireCss)) {
197 				page.requireCss(initializers.requireCss);
198 			}
199 
200 			if (Object.isString(initializers.template)) {
201 				page.template(initializers.template);
202 			}
203 			if (Object.isString(initializers.jstemplate)) {
204 				page.template(initializers.jstemplate);
205 			}
206 			
207 			if (initializers.manageLayout) {
208 				page.manageLayout();
209 			}
210 
211 			if (initializers.manageUndo) {
212 				page.manageUndo();
213 			}
214 
215 			rio.Page.extend(page, initializers.methods || {});
216 		}
217 		
218 		return page;
219 	},
220 	
221 	extend: function(page, extension) {
222 		extension._render = extension.render || Prototype.emptyFunction;
223 		extension.render = function() {
224 			this._manageLayout();
225 			this._render();
226 			this._afterRender();
227 		};
228 
229 		extension.__initialize = extension.initialize || Prototype.emptyFunction;
230 		extension.initialize = function(options) {
231 			page.applyTemplate();
232 		
233 			(this.__initialize.bind(this))(options);
234 		};
235 
236 		rio.Attr.extend(page, extension);
237 	}
238 };
239