EGroupware /
egroupware
| 1 | /** |
||
| 2 | * EGroupware eTemplate2 widget browser |
||
| 3 | * View & play with et2 widgets - javascript |
||
| 4 | * |
||
| 5 | * @link http://www.egroupware.org |
||
| 6 | * @author Nathan Gray |
||
| 7 | * @author Hadi Nategh |
||
| 8 | * @copyright 2013 Nathan Gray |
||
| 9 | * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License |
||
| 10 | * @package etemplate |
||
| 11 | * @subpackage tools |
||
| 12 | * @version $Id$ |
||
| 13 | */ |
||
| 14 | |||
| 15 | /*egw:uses |
||
| 16 | etemplate2; |
||
| 17 | */ |
||
| 18 | |||
| 19 | /** |
||
| 20 | * widget_browser shows a list of widgets, and allows you to view them one at a time. |
||
| 21 | * You can view and edit defined properties to see the effect. |
||
| 22 | */ |
||
| 23 | function widget_browser(list_div, widget_div) |
||
| 24 | { |
||
| 25 | |||
| 26 | // Initialize etemplate2 |
||
| 27 | this.et2 = new etemplate2(widget_div, "etemplate::ajax_process_content"); |
||
| 28 | |||
| 29 | // Normally this would be passed from app |
||
| 30 | var _data = {}; |
||
| 31 | |||
| 32 | // Create the basic widget container and attach it to the DOM |
||
| 33 | // Not really needed, but let's be consitent with et2 |
||
| 34 | this.et2.widgetContainer = new et2_container(null); |
||
| 35 | this.et2.widgetContainer.setApiInstance(egw('etemplate', egw.elemWindow(this.et2.DOMContainer))); |
||
| 36 | this.et2.widgetContainer.setInstanceManager(this.et2); |
||
| 37 | this.et2.widgetContainer.setParentDOMNode(this.et2.DOMContainer); |
||
| 38 | this.et2.widgetContainer.setArrayMgrs(this.et2._createArrayManagers(_data)); |
||
| 39 | |||
| 40 | // Set up UI |
||
| 41 | this.list_div = jQuery(list_div); |
||
| 42 | this.widget_div = jQuery(widget_div); |
||
| 43 | this.attribute_list = null; |
||
| 44 | |||
| 45 | // Create and popuplate the widget list |
||
| 46 | this._init_list(); |
||
| 47 | |||
| 48 | // Build DTD |
||
| 49 | this._init_dtd(); |
||
| 50 | } |
||
| 51 | |||
| 52 | /** |
||
| 53 | * Read the widget registry and create a list. |
||
| 54 | * The user can pick a widget, and we'll instanciate it. |
||
| 55 | */ |
||
| 56 | widget_browser.prototype._init_list = function() |
||
| 57 | { |
||
| 58 | var self = this; |
||
| 59 | |||
| 60 | // Create list |
||
| 61 | var list = jQuery(document.createElement('ul')) |
||
| 62 | .attr('id', 'widgets') |
||
| 63 | .click(function(e) {self.select_widget(e);}) |
||
| 64 | .appendTo(this.list_div); |
||
| 65 | |||
| 66 | // Sort the registry |
||
| 67 | var types = []; |
||
| 68 | for(var type in et2_registry) |
||
| 69 | { |
||
| 70 | types.push(type); |
||
| 71 | } |
||
| 72 | types.sort(); |
||
| 73 | for(var i = 0; i < types.length; i++) |
||
| 74 | { |
||
| 75 | list.append('<li>'+types[i]+'</li>'); |
||
| 76 | this.dump_attributes(types[i]); |
||
| 77 | } |
||
| 78 | |||
| 79 | // Build attribute table |
||
| 80 | attribute_table = jQuery(document.createElement('table')); |
||
| 81 | attribute_table.append('<thead class = "ui-widget-header"><td>'+egw().lang('Name')+"</td><td>"+egw().lang("Data type") + |
||
| 82 | "</td><td>"+egw().lang("Value") + "</td></thead>"); |
||
| 83 | this.attribute_list = jQuery(document.createElement('tbody')) |
||
| 84 | .appendTo(attribute_table); |
||
| 85 | |||
| 86 | this.list_div.append( |
||
| 87 | jQuery(document.createElement('div')) |
||
| 88 | .attr('id', 'widget_attributes') |
||
| 89 | .append(attribute_table) |
||
| 90 | ); |
||
| 91 | }; |
||
| 92 | |||
| 93 | widget_browser.prototype.dump_attributes = function(_type) |
||
| 94 | { |
||
| 95 | console.log(_type); |
||
|
0 ignored issues
–
show
Debugging Code
introduced
by
Loading history...
|
|||
| 96 | |||
| 97 | try { |
||
| 98 | var attrs = {}; |
||
| 99 | window.wb_widget = this.widget = et2_createWidget(_type, attrs, this.et2.widgetContainer); |
||
| 100 | this.widget.loadingFinished(); |
||
| 101 | |||
| 102 | if(this.widget !== null && this.widget.attributes) |
||
| 103 | { |
||
| 104 | for(var attr in this.widget.attributes) |
||
| 105 | { |
||
| 106 | console.log(attr, this.widget.attributes[attr]); |
||
| 107 | } |
||
| 108 | } |
||
| 109 | } |
||
| 110 | catch(e) { |
||
| 111 | console.log('*** '+_type+' error '+(typeof e.message != 'undefined' ? e.message : e)); |
||
| 112 | } |
||
| 113 | try { |
||
| 114 | if (this.widget) |
||
| 115 | { |
||
| 116 | this.widget.destroy(); |
||
| 117 | delete this.widget; |
||
| 118 | } |
||
| 119 | } |
||
| 120 | catch(e) { |
||
|
0 ignored issues
–
show
Coding Style
Comprehensibility
Best Practice
introduced
by
|
|||
| 121 | |||
| 122 | } |
||
| 123 | }; |
||
| 124 | |||
| 125 | /** |
||
| 126 | * User selected a widget from the list |
||
| 127 | * |
||
| 128 | * Create an instance of the widget, get its attributes, and display it. |
||
| 129 | */ |
||
| 130 | widget_browser.prototype.select_widget = function(e,f) |
||
| 131 | { |
||
| 132 | // UI prettyness - clear selected |
||
| 133 | jQuery(e.target).parent().children().removeClass("ui-state-active"); |
||
| 134 | |||
| 135 | // Clear previous widget |
||
| 136 | if(this.widget) |
||
| 137 | { |
||
| 138 | this.et2.widgetContainer.removeChild(this.widget); |
||
| 139 | this.widget.free(); |
||
| 140 | this.widget = null; |
||
| 141 | } |
||
| 142 | |||
| 143 | // Get the type of widget |
||
| 144 | var type = jQuery(e.target).text(); |
||
| 145 | if(!type || e.target.nodeName != 'LI') |
||
| 146 | { |
||
| 147 | return; |
||
| 148 | } |
||
| 149 | |||
| 150 | // UI prettyness - show item as selected |
||
| 151 | jQuery(e.target).addClass('ui-state-active'); |
||
| 152 | |||
| 153 | // Widget attributes |
||
| 154 | var attrs = {}; |
||
| 155 | |||
| 156 | |||
| 157 | window.wb_widget = this.widget = et2_createWidget(type, attrs, this.et2.widgetContainer); |
||
| 158 | this.widget.loadingFinished(); |
||
| 159 | |||
| 160 | // Attribute list |
||
| 161 | this.attribute_list.empty(); |
||
| 162 | if(this.widget !== null && this.widget.attributes) |
||
| 163 | { |
||
| 164 | for(var attr in this.widget.attributes) |
||
| 165 | { |
||
| 166 | if(this.widget.attributes[attr].ignore) continue; |
||
| 167 | this.create_attribute(attr, this.widget.attributes[attr]) |
||
| 168 | .appendTo(this.attribute_list); |
||
| 169 | } |
||
| 170 | } |
||
| 171 | }; |
||
| 172 | |||
| 173 | |||
| 174 | /** |
||
| 175 | * Create the UI (DOM) elements for a single widget attribute |
||
| 176 | * |
||
| 177 | * @param name Name of the attribute |
||
| 178 | * @param settings attribute attributes (Human name, description, etc) |
||
| 179 | */ |
||
| 180 | widget_browser.prototype.create_attribute = function(name, settings) |
||
| 181 | { |
||
| 182 | var set_function_name = "set_"+name; |
||
| 183 | var row = jQuery(document.createElement("tr")) |
||
| 184 | .addClass(typeof this.widget[set_function_name] == 'function' ? 'ui-state-default':'ui-state-disabled') |
||
| 185 | // Human Name |
||
| 186 | .append(jQuery(document.createElement('td')) |
||
| 187 | .text(settings.name) |
||
| 188 | ) |
||
| 189 | // Data type |
||
| 190 | .append(jQuery(document.createElement('td')) |
||
| 191 | .text(settings.type) |
||
| 192 | ); |
||
| 193 | // Add attribute name & description as a tooltip |
||
| 194 | if(settings.description) |
||
| 195 | { |
||
| 196 | egw().tooltipBind(row,settings.description); |
||
| 197 | } |
||
| 198 | |||
| 199 | // Value |
||
| 200 | var value = jQuery(document.createElement('td')).appendTo(row); |
||
| 201 | if(row.hasClass('ui-state-disabled')) |
||
| 202 | { |
||
| 203 | // No setter - just use text |
||
| 204 | value.text(this.widget.options[name]); |
||
| 205 | return row; |
||
| 206 | } |
||
| 207 | |||
| 208 | // Setter function - maybe editable? |
||
| 209 | var self = this; |
||
| 210 | var input = null; |
||
| 211 | switch(settings.type) |
||
| 212 | { |
||
| 213 | case 'string': |
||
| 214 | input = jQuery('<input/>') |
||
| 215 | .change(function(e) { |
||
| 216 | self.widget[set_function_name].apply(self.widget, [jQuery(e.target).val()]); |
||
| 217 | }); |
||
| 218 | input.val(this.widget.options[name]); |
||
| 219 | break; |
||
| 220 | case 'boolean': |
||
| 221 | input = jQuery('<input type="checkbox"/>') |
||
| 222 | .attr("checked", this.widget.options[name]) |
||
| 223 | .change(function(e) { |
||
| 224 | self.widget[set_function_name].apply(self.widget, [e.target.checked]); |
||
| 225 | }); |
||
| 226 | break; |
||
| 227 | default: |
||
| 228 | value.text(this.widget.options[name]); |
||
| 229 | return row; |
||
| 230 | } |
||
| 231 | input.appendTo(value); |
||
| 232 | |||
| 233 | return row; |
||
| 234 | }; |
||
| 235 | |||
| 236 | /** |
||
| 237 | * Initialise the DTD generator |
||
| 238 | */ |
||
| 239 | widget_browser.prototype._init_dtd = function () |
||
| 240 | { |
||
| 241 | //Contains all widgets |
||
| 242 | this.widgets = []; |
||
| 243 | |||
| 244 | //Contains not readonly widgets |
||
| 245 | this.dtd_widgets = []; |
||
| 246 | |||
| 247 | //Contians readonly widgets |
||
| 248 | this.dtd_widgets_ro = []; |
||
| 249 | |||
| 250 | // Contains the whole DTD string |
||
| 251 | this.dtd = ""; |
||
| 252 | |||
| 253 | var self = this; |
||
| 254 | |||
| 255 | // Create DTD Generator button and bind click handler on it |
||
| 256 | var dtd_btn = jQuery(document.createElement('button')) |
||
| 257 | .attr({id:'dtd_btn', title:'Generates Document Type Definition (DTD) for all widgets'}) |
||
| 258 | .click(function(){ |
||
| 259 | self._dtd_builder(); |
||
| 260 | }) |
||
| 261 | .addClass('dtd_btn') |
||
| 262 | .appendTo('body'); |
||
| 263 | dtd_btn.text('DTD Generator'); |
||
| 264 | } |
||
| 265 | |||
| 266 | /** |
||
| 267 | * Iterates over all et2_widget to build DTD tags |
||
| 268 | * and display them as string |
||
| 269 | * |
||
| 270 | */ |
||
| 271 | widget_browser.prototype._dtd_builder = function() |
||
| 272 | { |
||
| 273 | var dtdContentW = ""; |
||
| 274 | var i = 0; |
||
| 275 | for (var widget_type in et2_registry) |
||
| 276 | { |
||
| 277 | var attrs = {}; |
||
| 278 | |||
| 279 | // creating a dialog popups an empty dialog, |
||
| 280 | // which we don't want therefore |
||
| 281 | // we eliminate dialog tag from dtd ATM. |
||
| 282 | if (widget_type.match(/dialog/,'i')) continue; |
||
| 283 | |||
| 284 | if (!widget_type.match(/nextmatch/,'i')) |
||
| 285 | { |
||
| 286 | |||
| 287 | this.widgets[i] = et2_createWidget(widget_type ,attrs, this.et2.widgetContainer) |
||
| 288 | if (widget_type.match(/_ro/,'i')) |
||
| 289 | { |
||
| 290 | this.dtd_widgets_ro.push( widget_type.replace('_ro','')); |
||
| 291 | } |
||
| 292 | else |
||
| 293 | { |
||
| 294 | this.dtd_widgets.push(widget_type); |
||
| 295 | dtdContentW += this._dtd_widgets(widget_type, this.widgets[i]) |
||
| 296 | } |
||
| 297 | i++; |
||
| 298 | } |
||
| 299 | } |
||
| 300 | // DTD Final Content |
||
| 301 | this.dtd = this._dtd_header() + dtdContentW; |
||
| 302 | |||
| 303 | //Display DTD resault and UI to copy/download them |
||
| 304 | et2_createWidget("dialog", { |
||
| 305 | callback: function() {}, |
||
| 306 | title: egw.lang('DTD Result'), |
||
| 307 | buttons:et2_dialog.BUTTONS_OK, |
||
| 308 | value: { |
||
| 309 | content: { |
||
| 310 | value: this.dtd, |
||
| 311 | message: egw.lang('DTD Content') |
||
| 312 | } |
||
| 313 | }, |
||
| 314 | template: egw.webserverUrl+'/api/templates/default/dtd.xet', |
||
| 315 | modal:true, |
||
| 316 | resizable:false |
||
| 317 | }); |
||
| 318 | } |
||
| 319 | |||
| 320 | /** |
||
| 321 | * Builds some specific header DTD tags (e.g. ENTITY) |
||
| 322 | * |
||
| 323 | * @returns {String} returns dtd header tags as string |
||
| 324 | */ |
||
| 325 | widget_browser.prototype._dtd_header = function () |
||
| 326 | { |
||
| 327 | var dtd = ''; |
||
| 328 | dtd = '<!ENTITY % Widgets "' + this.dtd_widgets.join('|') + '">\r\n'; |
||
| 329 | dtd += '<!ELEMENT overlay (%Widgets;)*>\r\n'; |
||
| 330 | return dtd; |
||
| 331 | } |
||
| 332 | |||
| 333 | /** |
||
| 334 | * Builds DTD ELEMENTS and teir ATTRLIST for given widget |
||
| 335 | * |
||
| 336 | * @param {string} _type widget type |
||
| 337 | * @param {object} _widget widget object |
||
| 338 | * @returns {String} returns generated dtd tags in string |
||
| 339 | */ |
||
| 340 | widget_browser.prototype._dtd_widgets = function (_type, _widget) |
||
| 341 | { |
||
| 342 | var dtd = ''; |
||
| 343 | switch (_type) |
||
| 344 | { |
||
| 345 | // Special handling for menulist widget as it has a complicated structure |
||
| 346 | case 'menulist': |
||
| 347 | dtd = '<!ELEMENT menulist (menupopup)>\r\n'; |
||
| 348 | break; |
||
| 349 | |||
| 350 | // Special handling for grid widget as it has a complicated structure |
||
| 351 | case 'grid': |
||
| 352 | dtd += '<!ELEMENT grid (columns,rows)>\r\n'; |
||
| 353 | dtd += '<!ELEMENT columns (column)*>\r\n\ |
||
| 354 | <!ELEMENT column EMPTY >\n\ |
||
| 355 | <!ATTLIST column\n\ |
||
| 356 | disabled CDATA #IMPLIED\n\ |
||
| 357 | class CDATA #IMPLIED\n\ |
||
| 358 | width CDATA #IMPLIED>\n\ |
||
| 359 | <!ELEMENT rows (row)*>\n\ |
||
| 360 | <!ELEMENT row (%Widgets;)>\n\ |
||
| 361 | <!ATTLIST row\n\ |
||
| 362 | class CDATA #IMPLIED\n\ |
||
| 363 | height CDATA #IMPLIED\n\ |
||
| 364 | valign CDATA #IMPLIED\n\ |
||
| 365 | disabled CDATA #IMPLIED\n\ |
||
| 366 | part CDATA #IMPLIED\n\ |
||
| 367 | >\r\n'; |
||
| 368 | break; |
||
| 369 | |||
| 370 | // Special handling for tabbox widget as it has a complicated structure |
||
| 371 | case 'tabbox': |
||
| 372 | dtd += '<!ELEMENT tabbox (tabs,tabpanels)>\r\n'; |
||
| 373 | dtd += '<!ELEMENT tabs (tab)>\r\n'; |
||
| 374 | dtd += '<!ELEMENT tabpanels (template)>\r\n'; |
||
| 375 | break; |
||
| 376 | |||
| 377 | // Widget which can be a parent |
||
| 378 | case 'vbox': |
||
| 379 | case 'hbox': |
||
| 380 | case 'box': |
||
| 381 | case 'groupbox': |
||
| 382 | case 'template': |
||
| 383 | dtd = '<!ELEMENT '+ _type + ' (%Widgets;)*>\r\n'; |
||
| 384 | break; |
||
| 385 | |||
| 386 | // Other widgets which only can be used as child |
||
| 387 | default: |
||
| 388 | dtd = '<!ELEMENT '+ _type + ' EMPTY>\r\n'; |
||
| 389 | } |
||
| 390 | |||
| 391 | dtd +='<!ATTLIST ' + _type + '\r\n'; |
||
| 392 | for(var attr in _widget.attributes) |
||
| 393 | { |
||
| 394 | //DTD attribute helper object |
||
| 395 | var dtdAttrObj = {attrType:'CDATA',attrVal:'', attrReq:' #IMPLIED'}; |
||
| 396 | |||
| 397 | //if(_widget.attributes[attr].ignore) continue; |
||
| 398 | switch (_widget.attributes[attr].type) |
||
| 399 | { |
||
| 400 | case 'boolean': |
||
| 401 | dtdAttrObj.attrType = ' (true|false)'; |
||
| 402 | dtdAttrObj.attrVal = ' "' + _widget.attributes[attr].default + '"'; |
||
| 403 | dtdAttrObj.attrReq = ''; |
||
| 404 | break; |
||
| 405 | default: |
||
| 406 | dtdAttrObj.attrType = ' CDATA'; |
||
| 407 | |||
| 408 | if (_widget.attributes[attr].default !="" && |
||
| 409 | typeof _widget.attributes[attr].default != 'undefined' && |
||
| 410 | !jQuery.isEmptyObject(_widget.attributes[attr].default)) |
||
| 411 | { |
||
| 412 | dtdAttrObj.attrReq = ''; |
||
| 413 | dtdAttrObj.attrVal = ' "' + _widget.attributes[attr].default + '"'; |
||
| 414 | } |
||
| 415 | else |
||
| 416 | { |
||
| 417 | dtdAttrObj.attrVal = ""; |
||
| 418 | } |
||
| 419 | |||
| 420 | } |
||
| 421 | dtd += attr + dtdAttrObj.attrType + |
||
| 422 | (dtdAttrObj.attrVal !=""?dtdAttrObj.attrVal:'') + |
||
| 423 | dtdAttrObj.attrReq +'\r\n'; |
||
| 424 | } |
||
| 425 | dtd += '>\r\n'; |
||
| 426 | return dtd; |
||
| 427 | } |
||
| 428 | |||
| 429 | egw_LAB.wait(function() { |
||
| 430 | var wb = new widget_browser( |
||
| 431 | document.getElementById("widget_list"), |
||
| 432 | document.getElementById("widget_container") |
||
| 433 | ); |
||
| 434 | }); |
||
| 435 |