1/* This file is part of cbui. 2 * Copyright © 2010-2012 stiftung kulturserver.de ggmbh <[email protected]> 3 * 4 * cbui is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * cbui is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with cbui. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18/** 19 * A widget toolkit for culturebase. A widget is a simle GUI element with some 20 * functionality and an internal state. Widgets are 21 * - extendable: If you need a widget similar to one we already have you can 22 * subclass the existing one. Always keep this in mind when you do, though. 23 * - reusable: Widgets are to be collected in a central place and are thus 24 * available to all CB applications and web sites. 25 * - translatable: Widgets integrate with cb-ml. 26 * - managable: Widgets have a life cycle. You can create and destroy them and 27 * they will behave nicely when you do. Widgets can be composed into windows 28 * which will take away much of the management burden. 29 * - validatable: Certain widgets can accomodate subclassses of 30 * CbValidate.validator and provide validate() methods. 31 * 32 * In order for this to work you need to include: 33 * - jquery.js 34 * - base2.js 35 * - cb_ui/validate.js (if there are any widgets to be validated) 36 * - some styles for '__CbUi*' 37 * 38 */ 39 40/** 41 * The widget registry collects information about all available widget classes 42 * and manages translation, instantiation and destruction of widgets. 43 * 44 */ 45jQuery.CbWidgetRegistry = jQuery.CbWidgetRegistry || { 46 /** 47 * get a widget or validator name from a class name 48 */ 49 translateToClass : function(name, prefix) { 50 return '.__Cb' + prefix + name.substring(0, 1).toUpperCase() + name.substring(1); 51 }, 52 53 /* some properties you can change to modify the behaviour of the widget registry */ 54 55 /** 56 * The projects to be queried for ML bricks. 57 */ 58 projects : ['cb-framework'], 59 60 /** old API for querying bricks; deprecated */ 61 project : '', 62 baseProject : '', 63 64 /** 65 * language to be used 66 */ 67 language : 'en_EN', 68 69 brickSource : "/module/lib/framework/getMlBricks.php", 70 71 /** 72 * The actual translated bricks. Be careful when modifying this. 73 */ 74 bricks : {}, 75 76 /** 77 * Create a proper projects array from this.project and this.baseProject if 78 * they're given. 79 */ 80 retrofitProjects : function() { 81 var projects = []; 82 if (this.project) projects.push(this.project); 83 if (this.baseProject) projects.push(this.baseProject); 84 if (projects.length > 0) { 85 this.projects = projects 86 this.project = ''; 87 this.baseProject = ''; 88 } 89 }, 90 91 /** 92 * change the language of all widgets in the given context 93 * @param language the new language (locale) 94 * @param context the context to be manipulated - for example a window 95 * @param callback Function to be called when done 96 */ 97 changeLanguage : function(language, context, callback) { 98 var labels = []; 99 this.language = language; 100 if (!this.language) { 101 if (callback !== undefined) callback(); 102 return; // translation disabled 103 } 104 var self = this; 105 try { 106 /* collect labels */ 107 jQuery.each(jQuery.CbWidget, function(name) { 108 var clazz = self.translateToClass(name, 'Ui'); 109 jQuery(clazz, context).each(function() { 110 jQuery.merge(labels, jQuery(this).CbWidget().getLabels()); 111 }); 112 if (context && context.is(clazz)) { 113 jQuery.merge(labels, jQuery(context).CbWidget().getLabels()); 114 } 115 labels = jQuery.unique(labels); 116 }); 117 }catch(e){ 118 if(console) console.log(e); 119 } 120 121 122 if (labels.length === 0) { 123 if (callback !== undefined) callback(); 124 return; // no translation needed 125 } 126 127 /* for backward compatibility */ 128 this.retrofitProjects(); 129 130 /* fetch bricks */ 131 jQuery.ajax({ 132 type : 'GET', 133 url : this.brickSource, 134 dataType : 'json', 135 data : { 136 "projects" : this.projects, 137 "language" : this.language, 138 "labels[]" : labels 139 }, 140 success : function(bricks) { 141 /* loop over widgets again and apply bricks */ 142 self.bricks = bricks; 143 jQuery.each(jQuery.CbWidget, function(name) { 144 var clazz = self.translateToClass(name, 'Ui'); 145 jQuery(clazz, context).each(function() { 146 //if (jQuery(this).CbWidget()) 147 jQuery(this).CbWidget().changeLanguage(bricks); 148 });
149 if (context && context.is(clazz)) { 150 jQuery(context).CbWidget().changeLanguage(bricks); 151 } 152 }); 153 if (callback !== undefined) callback(); 154 } 155 }); 156 }, 157 158 /** 159 * Instantiate widgets and validators in the given context and translate them. 160 * @param context A DOM element to restrict the operation to (e.g. a window) 161 * @param callback Function to be called when done 162 */ 163 apply : function(context, callback) { 164 var self = this; 165 166 /* create widgets */ 167 jQuery.each(jQuery.CbWidget, function(name, widget) { 168 jQuery(self.translateToClass(name, 'Ui'), context).each(function() { 169 var el = jQuery(this); 170 if (el.CbWidget(undefined, false) === undefined) { // but don't overwrite existing ones 171 new widget(el); 172 } 173 }); 174 }); 175 176 jQuery.each(jQuery.CbValidate, function(name, validator) { 177 jQuery(self.translateToClass(name, 'Validate'), context).each(function() { 178 new validator(jQuery(this).CbWidget()); 179 }); 180 }); 181 182 this.changeLanguage(this.language, context, callback); 183 } 184}; 185 186/** 187 * Event Handling 188 * -------------- 189 * 190 * Events are declared by implementing an "init" method in the class interface 191 * of your widget and stating "jQuery.CbEvent(this, <name>)" there. There are 192 * two ways of triggering the event: 193 * 194 * 1. Call widget.<name>() without parameters. This is the shortcut for events 195 * you don't want to specify any special parameters for. 196 * 197 * 2. Call widget.trigger(<name>, <parameters>) where <parameters> are a JS 198 * object. The parameters will be passed on to the event handlers then. 199 * 200 * Event handlers can be attached to events in three ways. 201 * 202 * 1. Override handle<Name>(). This method is the first to be called every time 203 * the event is triggered. You can use base2's inheritance mechanism here and 204 * call base() to invoke the handle<Name>() method of super classes. 205 * 206 * 2. Call widget.<name>(<handler>, <params>). <handler> should be a function to be 207 * executed when the event is triggered. Like this you can attach multiple handlers 208 * to the same event in jquery-style. They will be invoked after handle<Name>(). 209 * Any parameters will be passed on to all event handlers when they are invoked. 210 * You can extend and override them with trigger(). 211 * 212 * 3. Call widget.bind(<name>, <handler>, <params>). This is the same as 213 * widget.<name>(<handler>, <params>). 214 * 215 * You can unbind handlers from an event by calling widget.unbind(<event>, <handler>). 216 * If you omit the second parameter all handlers will be unbound from the specified 217 * event. 218 * 219 * (Class interfaces aren't inherited in base2 so we have to create an external 220 * CbEvent function to add events to classes.) 221 * 222 */ 223jQuery.CbEvent = jQuery.CbEvent || function(target, name) { 224 jQuery.CbEvent.getHandleName = function(name) { 225 return "handle" + name.substring(0,1).toUpperCase() + name.substring(1); 226 }; 227 228 var event = {}; 229 event[name] = function(callback, staticParams) { 230 if (callback) { 231 if (typeof(this.handlers[name]) == 'undefined') this.handlers[name] = []; 232 this.handlers[name].push({ 233 'callback' : callback, 234 'params': staticParams || {} 235 }); 236 } else { 237 this.trigger(name); 238 } 239 }; 240 241 var handleName = jQuery.CbEvent.getHandleName(name); 242 if (typeof(target.prototype[handleName]) == 'undefined') { 243 event[handleName] = function() {return this;}; 244 } 245 246 target.implement(event); 247 return target[name]; 248}; 249 250jQuery.CbWidget = jQuery.CbWidget || function() {}; // maybe do something useful here; e.g. find elements belonging to certain widget types 251jQuery.CbValidate = jQuery.CbValidate || function() {}; 252 253/** 254 * jquery plugin to enable $(element).CbWidget() functionality. Saves information 255 * about widgets via $.data(...). 256 * @param widget if set attach the widget to the element otherwise find the 257 * widget belonging to the element. If set to null remove widget information 258 * from the element. 259 * When querying for the widget CbWidget will recursively search the elements 260 * parent nodes. 261 * @param recursive If the widget should be looked for recursively up the DOM 262 * tree. If undefined, true is assumed. 263 */ 264jQuery.fn.CbWidget = function(widget, recursive) { 265 if (recursive === undefined) recursive = true; 266 if (this.length == 0) return undefined; 267 if (widget !== undefined) { 268 if (widget !== null) { 269 this.data("cb_widget", widget); 270 } else { 271 this.removeData("cb_widget"); 272 } 273 } 274 275 if (!this.data("cb_widget")) { 276 var parent = this.parent(); 277 if (recursive && parent && parent != this) { 278 return parent.CbWidget(); 279 } else { 280 return undefined; 281 } 282 } else { 283 return this.data("cb_widget"); 284 } 285}; 286 287/** 288 * the base widget. All widgets are to be subclassed from this one. 289 */ 290jQuery.CbWidget.widget = base2.Base.extend({ 291 292 /** 293 * create a widget on the given element. 294 * @param element the element the widget should attach to 295 */ 296 constructor : function(element) { 297 this.base(); 298 base2.assignID(this); 299 this.parentElement = element;
300 this.element().CbWidget(this); 301 /* element() and parentElement may differ */ 302 this.parentElement.CbWidget(this); 303 304 /** 305 * association of positions -> labels 306 */ 307 this.texts = {}; 308 309 /** 310 * event handlers 311 */ 312 this.handlers = {}; 313 314 this.validationSuspended = false; 315 }, 316 317 /** 318 * show the widget 319 */ 320 handleShow : function() { 321 this.element().show(); 322 return this; 323 }, 324 325 /** 326 * hide the widget 327 */ 328 handleHide : function() { 329 this.element().hide(); 330 return this; 331 }, 332 333 /** 334 * get the element the widget belongs to. Some widgets might override this 335 * method and/or change their element. Don't assume the element to be the 336 * same every time you use it. 337 */ 338 element : function() { 339 return this.parentElement; 340 }, 341 342 /** 343 * Refresh the element associated with this widget. This is necessary if it 344 * has been inserted or moved in the DOM. You will want to rebind all events 345 * here. 346 */ 347 refreshElement : function() { 348 this.parentElement = jQuery(this.parentElement); 349 this.element().CbWidget(this); 350 }, 351 352 /** 353 * get labels for all ML bricks needed by this widget. 354 * @return an array of labels for all ML bricks needed. 355 */ 356 getLabels : function() { 357 var labels = []; 358 jQuery.each(this.texts, function(i, text) { 359 labels.push(text); 360 }); 361 return labels; 362 }, 363 364 isValidationSuspended : function() { 365 return this.validationSuspended; 366 }, 367 368 suspendValidation : function() { 369 this.validationSuspended = true; 370 }, 371 372 resumeValidation : function() { 373 this.validationSuspended = false; 374 }, 375 376 /** 377 * hook for changing or initializing the language. 378 * @param bricks map of labels to ML bricks for the new language. 379 */ 380 changeLanguage : function(bricks) {}, 381 382 /** 383 * bind a handler to an event 384 * @param name name of the event 385 * @param callback the handler to be called when the event is triggered 386 * @param staticParams default parameters for the handler (optional) 387 * @return this 388 */ 389 bind : function(name, callback, staticParams) { 390 this[name](callback, staticParams); 391 return this; 392 }, 393 394 /** 395 * unbind a handler from an event 396 * @param name the name of the event 397 * @param callback the handler to be removed (if omitted all handlers are removed) 398 * @return this 399 */ 400 unbind : function(name, callback) { 401 if (callback && typeof(this.handlers[name]) != 'undefined') { 402 var self = this; 403 jQuery.each(self.handlers[name], function (i, handler) { 404 if (handler.callback == callback) { 405 self.handlers[name] = self.handlers[name].splice(i, 1); 406 return false; 407 } else { 408 return true; 409 } 410 }); 411 } else { 412 this.handlers[name] = []; 413 } 414 return this; 415 }, 416 417 /** 418 * trigger an event 419 * @param name the event to be triggered 420 * @param extraParams additional parameters to be passed to the event 421 * @return this 422 */ 423 trigger : function(name, extraParams) { 424 this[jQuery.CbEvent.getHandleName(name)](extraParams || {}); 425 426 if (typeof(this.handlers[name]) != 'undefined') { 427 jQuery.each(this.handlers[name], function(i, handler) { 428 var params = jQuery.extend({}, handler.params, extraParams || {}); 429 handler.callback(params); 430 return true; 431 }); 432 } 433 return this; 434 }, 435 436 /** 437 * get the width of this widget 438 * @return the width 439 */ 440 width : function() { 441 return this.element().width(); 442 }, 443 444 /** 445 * get the height of this widget 446 * @return the height 447 */ 448 height : function() { 449 return this.element().height(); 450 }, 451 452 /** 453 * resize the widget in horizontal direction 454 * @param x the new width in pixels 455 * @return this 456 */ 457 resizeX : function(x) { 458 this.element().width(x); 459 return this; 460 }, 461 462 /** 463 * resize the widget in vertical direction 464 * @param y the new height in pixels 465 * @return this 466 */ 467 resizeY : function(y) {
468 this.element().height(y); 469 return this; 470 }, 471 472 /** 473 * resize the widget in both directions 474 * @param x the new width 475 * @param y the new height 476 * @return this 477 */ 478 resize : function(x, y) { 479 this.resizeX(x); 480 this.resizeY(y); 481 return this; 482 }, 483 484 /** 485 * move the widget in horizontal direction 486 * @param x the new position in pixels 487 * @return this 488 */ 489 moveToX : function(x) { 490 this.element().css('left', x + 'px'); 491 return this; 492 }, 493 494 /** 495 * move the widget in vertical direction 496 * @param y the new position in pixels 497 * @return this 498 */ 499 moveToY : function(y) { 500 this.element().css('top', y + 'px'); 501 return this; 502 }, 503 504 /** 505 * move the widget in both directions 506 * @param x the new X position 507 * @param y the new Y position 508 * @return this 509 */ 510 moveTo : function(x, y) { 511 this.moveToX(x); 512 this.moveToY(y); 513 return this; 514 }, 515 516 /** 517 * destroy the widget. Leave the element alone, though. 518 * As the widget doesn't create the element on construction it won't remove 519 * it on destruction. Widgets are expected to remove any extra elements they 520 * might have created and revert the original element to its original state, 521 * though. 522 */ 523 handleDestroy : function() { 524 this.element().CbWidget(null); 525 } 526}, { 527 528 529 init : function() { 530 /* 531 * "ready" is triggered by the containing frame when everything has been 532 * translated and set up. The widget is not necessarily shown at that 533 * point, but it is in the DOM. 534 */ 535 jQuery.CbEvent(this, 'ready'); 536 537 jQuery.CbEvent(this, 'show'); 538 jQuery.CbEvent(this, 'hide'); 539 jQuery.CbEvent(this, 'destroy'); 540 this.base(); 541 } 542}); 543 544 545
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.