1 /** 2 * @fileOverview xq.controls provides common UI elements such as dialog. 3 */ 4 xq.controls = {}; 5 6 7 8 xq.controls.FormDialog = Class.create({ 9 /** 10 * @constructor 11 * 12 * @param {String} html HTML string which contains FORM 13 * @param {Function} [onLoadHandler] callback function to be called when the form is loaded 14 */ 15 initialize: function(xed, html, onLoadHandler, onCloseHandler) { 16 this.xed = xed; 17 this.html = html; 18 this.onLoadHandler = onLoadHandler || function() {}; 19 this.onCloseHandler = onCloseHandler || function() {}; 20 this.form = null; 21 }, 22 /** 23 * Show dialog 24 * 25 * @param {Object} [options] collection of options 26 */ 27 show: function(options) { 28 options = options || {}; 29 options.position = options.position || 'centerOfWindow'; 30 options.mode = options.mode || 'modal'; 31 options.cancelOnEsc = options.cancelOnEsc || true; 32 33 var self = this; 34 35 // create and append container 36 var container = $(document.createElement('DIV')); 37 container.style.display = 'none'; 38 document.body.appendChild(container); 39 40 // initialize form 41 container.innerHTML = this.html; 42 this.form = $(container.getElementsByTagName('FORM')[0]); 43 44 this.form.onsubmit = function() { 45 self.onCloseHandler($(this).serialize(true)); 46 self.close(); 47 return false; 48 }; 49 50 var cancelButton = this.form.getElementsByClassName('cancel')[0]; 51 cancelButton.onclick = function() { 52 self.onCloseHandler(); 53 self.close(); 54 }; 55 56 // append dialog 57 document.body.appendChild(this.form); 58 container.parentNode.removeChild(container); 59 60 // place dialog to center of window 61 this.setPosition(options.position); 62 63 // give focus 64 var elementToFocus = this.form.getElementsByClassName('initialFocus'); 65 if(elementToFocus.length > 0) elementToFocus[0].focus(); 66 67 // handle cancelOnEsc option 68 if(options.cancelOnEsc) { 69 Event.observe(this.form, 'keydown', function(e) { 70 if(e.keyCode == 27) { 71 this.onCloseHandler(); 72 this.close(); 73 } 74 return false; 75 }.bind(this)); 76 } 77 78 this.onLoadHandler(this); 79 }, 80 close: function() { 81 this.form.parentNode.removeChild(this.form); 82 }, 83 setPosition: function(target) { 84 var targetElement; 85 86 if(target == 'centerOfWindow') { 87 targetElement = document.documentElement; 88 } else if(target == 'centerOfEditor') { 89 targetElement = this.xed.getDoc()[xq.Browser.isTrident ? "body" : "documentElement"]; 90 } else if(target == 'nearbyCaret') { 91 throw "Not implemented yet"; 92 } else { 93 throw "Invalid argument: " + target; 94 } 95 96 var targetWidth = targetElement.clientWidth; 97 var targetHeight = targetElement.clientHeight; 98 var dialogWidth = this.form.clientWidth; 99 var dialogHeight = this.form.clientHeight; 100 101 var x = parseInt((targetWidth - dialogWidth) / 2); 102 var y = parseInt((targetHeight - dialogHeight) / 2); 103 104 this.form.style.left = x + "px"; 105 this.form.style.top = y + "px"; 106 } 107 }) 108 109 110 111 xq.controls.QuickSearchDialog = Class.create({ 112 /** 113 * @constructor 114 */ 115 initialize: function(xed, param) { 116 this.xed = xed; 117 118 this.rdom = xq.RichDom.createInstance(); 119 this.rdom.setRoot(document.body); 120 121 this.param = param; 122 if(!this.param.renderItem) this.param.renderItem = function(item) { 123 return this.rdom.getInnerText(item); 124 }.bind(this); 125 126 this.container = null; 127 }, 128 129 getQuery: function() { 130 if(!this.container) return ""; 131 return this._getInputField().value; 132 }, 133 134 onSubmit: function(e) { 135 if(this.matchCount() > 0) { 136 this.param.onSelect(this.xed, this.list[this._getSelectedIndex()]); 137 } 138 139 this.close(); 140 Event.stop(e); 141 return false; 142 }, 143 144 onCancel: function(e) { 145 if(this.param.onCancel) this.param.onCancel(this.xed); 146 this.close(); 147 }, 148 149 onBlur: function(e) { 150 // TODO: Ugly 151 setTimeout(function() {this.onCancel(e)}.bind(this), 400); 152 }, 153 154 onKey: function(e) { 155 var esc = new xq.Shortcut("ESC"); 156 var enter = new xq.Shortcut("ENTER"); 157 var up = new xq.Shortcut("UP"); 158 var down = new xq.Shortcut("DOWN"); 159 160 if(esc.matches(e)) { 161 this.onCancel(e); 162 } else if(enter.matches(e)) { 163 this.onSubmit(e); 164 } else if(up.matches(e)) { 165 this._moveSelectionUp(); 166 } else if(down.matches(e)) { 167 this._moveSelectionDown(); 168 } else { 169 this.updateList(); 170 } 171 }, 172 173 onClick: function(e) { 174 var target = e.srcElement || e.target; 175 if(target.nodeName == "LI") { 176 177 var index = this._getIndexOfLI(target); 178 this.param.onSelect(this.xed, this.list[index]); 179 } 180 }, 181 182 onList: function(list) { 183 this.list = list; 184 this.renderList(list); 185 }, 186 187 updateList: function() { 188 window.setTimeout(function() { 189 this.param.listProvider(this.getQuery(), this.xed, this.onList.bind(this)); 190 }.bind(this), 0); 191 }, 192 193 renderList: function(list) 194 { 195 var ol = this._getListContainer(); 196 ol.innerHTML = ""; 197 198 for(var i = 0; i < list.length; i++) { 199 var li = this.rdom.createElement('LI'); 200 li.innerHTML = this.param.renderItem(list[i]); 201 ol.appendChild(li); 202 } 203 204 if(ol.hasChildNodes()) { 205 ol.firstChild.className = "selected"; 206 } 207 }, 208 209 show: function() { 210 if(!this.container) this.container = this._create(); 211 212 var dialog = this.rdom.insertNodeAt(this.container, this.rdom.getRoot(), "end"); 213 this.setPosition('centerOfEditor'); 214 this.updateList(); 215 this.focus(); 216 }, 217 218 close: function() { 219 this.rdom.deleteNode(this.container); 220 }, 221 222 focus: function() { 223 this._getInputField().focus(); 224 }, 225 226 setPosition: function(target) { 227 var targetElement; 228 229 if(target == 'centerOfWindow') { 230 targetElement = document.documentElement; 231 } else if(target == 'centerOfEditor') { 232 targetElement = this.xed.getDoc().documentElement; 233 } else if(target == 'nearbyCaret') { 234 throw "Not implemented yet"; 235 } else { 236 throw "Invalid argument: " + target; 237 } 238 239 var targetWidth = targetElement.clientWidth; 240 var targetHeight = targetElement.clientHeight; 241 var dialogWidth = this.container.clientWidth; 242 var dialogHeight = this.container.clientHeight; 243 244 var x = parseInt((targetWidth - dialogWidth) / 2); 245 var y = parseInt((targetHeight - dialogHeight) / 2); 246 this.container.style.left = x + "px"; 247 this.container.style.top = y + "px"; 248 }, 249 250 matchCount: function() { 251 return this.list ? this.list.length : 0; 252 }, 253 254 _create: function() { 255 // make container 256 var container = this.rdom.createElement("DIV"); 257 container.className = "xqQuickSearch"; 258 259 // make title 260 if(this.param.title) { 261 var title = this.rdom.createElement("H1"); 262 title.innerHTML = this.param.title; 263 container.appendChild(title); 264 } 265 266 // make input field 267 var inputWrapper = this.rdom.createElement("DIV"); 268 inputWrapper.className = "input"; 269 var form = this.rdom.createElement("FORM"); 270 var input = this.rdom.createElement("INPUT"); 271 input.type = "text"; 272 input.value = ""; 273 form.appendChild(input); 274 inputWrapper.appendChild(form); 275 container.appendChild(inputWrapper); 276 277 // make list 278 var list = this.rdom.createElement("OL"); 279 280 Event.observe(input, 'blur', this.onBlur.bindAsEventListener(this)); 281 Event.observe(input, 'keypress', this.onKey.bindAsEventListener(this)); 282 Event.observe(list, 'click', this.onClick.bindAsEventListener(this), true); 283 Event.observe(form, 'submit', this.onSubmit.bindAsEventListener(this)); 284 Event.observe(form, 'reset', this.onCancel.bindAsEventListener(this)); 285 286 container.appendChild(list); 287 return container; 288 }, 289 290 _getInputField: function() { 291 return this.container.getElementsByTagName('INPUT')[0]; 292 }, 293 294 _getListContainer: function() { 295 return this.container.getElementsByTagName('OL')[0]; 296 }, 297 298 _getSelectedIndex: function() { 299 var ol = this._getListContainer(); 300 for(var i = 0; i < ol.childNodes.length; i++) { 301 if(ol.childNodes[i].className == 'selected') return i; 302 } 303 }, 304 305 _getIndexOfLI: function(li) { 306 var ol = this._getListContainer(); 307 for(var i = 0; i < ol.childNodes.length; i++) { 308 if(ol.childNodes[i] == li) return i; 309 } 310 }, 311 312 _moveSelectionUp: function() { 313 var count = this.matchCount(); 314 if(count == 0) return; 315 var index = this._getSelectedIndex(); 316 var ol = this._getListContainer(); 317 ol.childNodes[index].className = ""; 318 319 index--; 320 if(index < 0) index = count - 1; 321 322 ol.childNodes[index].className = "selected"; 323 }, 324 325 _moveSelectionDown: function() { 326 var count = this.matchCount(); 327 if(count == 0) return; 328 var index = this._getSelectedIndex(); 329 var ol = this._getListContainer(); 330 ol.childNodes[index].className = ""; 331 332 index++; 333 if(index >= count) index = 0; 334 335 ol.childNodes[index].className = "selected"; 336 } 337 });