https://www.stempeloase-frank.de/assets/lsjs/core/appBinder/binder…&debug=1&no-cache=1&no-minifier=1
1(function() { 2"use strict"; 3// ## CORE ## > 4/* */ 5(function() { 6 7/* 8 * ==> 9 * Make sure that we can safely use all the console methods even if some of 10 * them might not be available in a certain client 11 */ 12(function() { 13 var method; 14 var noop = function () {}; 15 var methods = [ 16 'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 17 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 18 'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd', 19 'timeStamp', 'trace', 'warn', 'lsjsConsoleTest' 20 ]; 21 var length = methods.length; 22 console = (window.console = window.console || {}); 23 24 while (length--) { 25 method = methods[length]; 26 27 // Only stub undefined methods. 28 if (!console[method]) { 29 console[method] = noop; 30 } 31 } 32})(); 33/* 34 * <== 35 */ 36 37/* 38 * Request.cajax makes an ajax call and expects an html response with cajax elements 39 * whose contents replace the contents of elements currently in the dom with 40 * matching ids. 41 */ 42Request.cajax = new Class({ 43 44 Extends: Request, 45 46 options: { 47 cajaxMode: 'update', // can be 'update', 'updateCompletely' or 'append', 48 el_formToUseForFormData: false, 49 obj_additionalFormData: {}, 50 headers: { 51 /* 52 * We only expect html but we keep the Accept value as it is in 53 * Request.HTML. Who knows what it might be good for... 54 */ 55 Accept: 'text/html, application/xml, text/xml, */*' 56 } 57 }, 58 59 initialize: function(options){ 60 this.parent(options); 61 }, 62 63 success: function(str_text){ 64 /* 65 * Strip javascript to make sure that we only have html 66 */ 67 this.response.html = str_text.stripScripts(function(str_script){ 68 this.response.javascript = str_script; 69 }.bind(this)); 70 71 /* 72 * Create a temporary element with the html response because this parses 73 * the html response so that we can actually work with the elements 74 * returned by ls_cajax 75 */ 76 var el_temp = new Element('div').set('html', this.response.html); 77 78 /* 79 * The children of el_temp are the elements that were requested from 80 * ls_cajax and that need to be updated in the dom. 81 */ 82 var els_updated = el_temp.getChildren(); 83 84 var els_toPassToOnSuccess = new Elements(); 85 86 Array.each(els_updated, function(el_updated) { 87 if ($(el_updated.getProperty('id')) === null) { 88 console.info('cajax response contains elements without IDs, which therefore can\'t be considered for content updates:') 89 console.log(el_updated); 90 return; 91 } 92 93 els_toPassToOnSuccess.push(this.options.cajaxMode === 'updateCompletely' ? el_updated : $(el_updated.getProperty('id'))); 94 95 /* 96 * We take the element whose id matches the id of the response element 97 * from the dom and update its contents with those of the response element. 98 */ 99 switch(this.options.cajaxMode) { 100 case 'update': 101 $(el_updated.getProperty('id')).empty().adopt(el_updated.childNodes); 102 break; 103 104 case 'updateCompletely': 105 var float_scrollTop = $(el_updated.getProperty('id')).scrollTop; 106 107 el_updated.replaces($(el_updated.getProperty('id'))); 108 109 el_updated.scrollTop = float_scrollTop; 110 break; 111 112 case 'append': 113 $(el_updated.getProperty('id')).adopt(el_updated.childNodes); 114 break; 115 } 116 }.bind(this)); 117 118 this.onSuccess(els_toPassToOnSuccess, this.response.html, this.response.javascript); 119 120 Array.each(els_toPassToOnSuccess, function(el_domReference) { 121 window.fireEvent('cajax_domUpdate', el_domReference); 122 }); 123 }, 124 125 send: function(options) { 126 /* 127 * If we don't use FormData, we call the parent function... 128 */ 129 if (typeOf(this.options.el_formToUseForFormData) !== 'element') { 130 this.parent(options); 131 return; 132 } 133 134 /* 135 * but if we use FormData, we have to use our own implementation of the send() function 136 */ 137 138 /* 139 * first, we build the FormData object from the given form, adding the key/value pairs of the 140 * additionalFormData object 141 */ 142 var obj_formData = new FormData(this.options.el_formToUseForFormData); 143 Object.each(this.options.obj_additionalFormData, function(value, key) { 144 obj_formData.append(key, value); 145 }); 146 147 /* 148 console.log('sending FormData: '); 149 console.log(this.options.el_formToUseForFormData); 150 console.log(this.options.obj_additionalFormData); 151 for (var pair of obj_formData.entries()) { 152 console.log(pair[0]+ ', ' + pair[1]); 153 } 154 */ 155 156 this.sendFormData(options, obj_formData); 157 }, 158 159 sendFormData: function(options, obj_formData){ 160 if (!this.check(options)) return this; 161 162 this.options.isSuccess = this.options.isSuccess || this.isSuccess; 163 this.running = true; 164 165 var xhr = this.xhr; 166 var progressSupport = ('onprogress' in new Browser.Request); 167 if (progressSupport){ 168 xhr.onloadstart = this.loadstart.bind(this); 169 xhr.onprogress = this.progress.bind(this); 170 xhr.upload.onprogress = this.progress.bind(this); 171 } 172 173 xhr.open('POST', this.options.url, true); 174 xhr.onreadystatechange = this.onStateChange.bind(this); 175 176 Object.each(this.headers, function(value, key){ 177 try { 178 xhr.setRequestHeader(key, value); 179 } catch (e){ 180 this.fireEvent('exception', [key, value]); 181 } 182 }, this); 183 184 this.fireEvent('request'); 185 xhr.send(obj_formData); 186 187 if (!this.options.async) this.onStateChange(); 188 if (this.options.timeout) this.timer = this.timeout.delay(this.options.timeout, this); 189 return this; 190 } 191 192}); 193 194var classdef_lsjs_apiInterface = { 195 str_apiUrl: '', 196 197 /* 198 * This function executes an api request and returns the request object. 199 * 200 * obj_options.str_resource needs to be provided in order to define the api 201 * resource addressed in the request. If obj_options.obj_additionalRequestOptions 202 * is given, this is not mandatory. 203 * 204 * With obj_options.obj_params the data/parameters to be sent with the 205 * request, can be defined. 206 * 207 * With "obj_options.obj_additionalRequestOptions" it is possible to completely 208 * override the predefined request settings. 209 */ 210 request: function(obj_options) { 211 var obj_params = {}, 212 obj_requestOptions = {}, 213 method = 'get', 214 self = this; 215
216 obj_params = obj_options.obj_params !== undefined && obj_options.obj_params !== null ? obj_options.obj_params : obj_params; 217 218 if ( 219 (obj_options.str_resource === undefined || obj_options.str_resource === null) 220 && (obj_options.obj_additionalRequestOptions === undefined || obj_options.obj_additionalRequestOptions === null) 221 ) { 222 console.error('no api resource specified for this request'); 223 return; 224 } 225 226 if ( 227 obj_options.obj_additionalRequestOptions !== undefined 228 && obj_options.obj_additionalRequestOptions.method !== undefined 229 && obj_options.obj_additionalRequestOptions.method === 'post' 230 ) { 231 method = 'post'; 232 } 233 234 if (method !== 'post') { 235 obj_params.resource = obj_options.str_resource; 236 } 237 238 obj_requestOptions = { 239 url: this.str_apiUrl + (method === 'post' ? (this.str_apiUrl.search(/\?/) === -1 ? '?' : '&') + 'resource=' + obj_options.str_resource : ''), 240 noCache: true, 241 method: method, 242 243 data: Object.toQueryString(obj_params), 244 245 onComplete: function(obj_response) { 246 if (obj_response === undefined || obj_response.status !== 'success') { 247 console.info('ajax/api request not successful'); 248 return; 249 } 250 251 if (typeOf(obj_options.func_onSuccess) === 'function') { 252 obj_options.func_onSuccess(obj_response.data, this); 253 } 254 } 255 }; 256 257 Object.merge(obj_requestOptions, obj_options.obj_additionalRequestOptions); 258 259 if (obj_requestOptions.url === undefined || !obj_requestOptions.url) { 260 console.error('no url specified for this request'); 261 return; 262 } 263 264 return new Request.JSON(obj_requestOptions).send(); 265 } 266}; 267var class_lsjs_apiInterface = new Class(classdef_lsjs_apiInterface); 268 269var classdef_lsjs = { 270 obj_moduleClasses: {}, 271 272 /* 273 * In the __moduleHelpers object, modules (not instances of a module but the 274 * module itself) can store module related helper functions. 275 * 276 * For example, the core module "messageBox" does this to provide a simplified 277 * creator function (lsjs.__moduleHelpers.messageBox.open()) 278 */ 279 __moduleHelpers: {}, 280 281 /* 282 * Apps can store a reference to themselves in __appHelpers if they need 283 * to be accessible in the global scope (lsjs.__appHelpers.appXyz) 284 */ 285 __appHelpers: {}, 286 287 createModule: function(obj_args) { 288 if (obj_args.__name === undefined || obj_args.__name === null) { 289 console.error('module name missing'); 290 return; 291 } 292 293 return new class_lsjs_module(obj_args); 294 }, 295 296 addViewClass: function(str_moduleName, obj_classDefinition) { 297 if (str_moduleName === undefined || str_moduleName === null || !str_moduleName) { 298 console.error('module name missing'); 299 } 300 301 if (obj_classDefinition === undefined || obj_classDefinition === null || !obj_classDefinition) { 302 console.error('class definition missing'); 303 } 304 305 if (this.obj_moduleClasses[str_moduleName] === undefined) { 306 this.obj_moduleClasses[str_moduleName] = {}; 307 } 308 309 if (this.obj_moduleClasses[str_moduleName].view !== undefined && this.obj_moduleClasses[str_moduleName].view !== null) { 310 console.error('view class already created for module "' + str_moduleName + '"'); 311 } 312 313 this.obj_moduleClasses[str_moduleName].view = new Class(obj_classDefinition); 314 }, 315 316 addModelClass: function(str_moduleName, obj_classDefinition) { 317 if (str_moduleName === undefined || str_moduleName === null || !str_moduleName) { 318 console.error('module name missing'); 319 } 320 321 if (obj_classDefinition === undefined || obj_classDefinition === null || !obj_classDefinition) { 322 console.error('class definition missing'); 323 } 324 325 if (obj_classDefinition.name === undefined || obj_classDefinition.name === null || !obj_classDefinition.name) { 326 console.error('model name missing'); 327 } 328 329 if (this.obj_moduleClasses[str_moduleName] === undefined) { 330 this.obj_moduleClasses[str_moduleName] = {}; 331 } 332 333 if (this.obj_moduleClasses[str_moduleName].model === undefined) { 334 this.obj_moduleClasses[str_moduleName].model = {}; 335 } 336 337 if (this.obj_moduleClasses[str_moduleName].model[obj_classDefinition.name] !== undefined && this.obj_moduleClasses[str_moduleName].model[obj_classDefinition.name] !== null) { 338 console.error('model class "' + obj_classDefinition.name + '" already created in module "' + str_moduleName + '"'); 339 } 340 341 /* 342 * We need to pass the data object along and then, after instantiating the 343 * model class, we write a clone of this data object to the model instance's 344 * data object. This way, we get an iterable data object and sidestep the
345 * problem that occurs when converting the data object into a hash for this 346 * purpose. When using the hash, we realized that the data objects of multiple 347 * modules' models pointed to the same resource. 348 */ 349 this.obj_moduleClasses[str_moduleName].model[obj_classDefinition.name] = { 350 class_module: new Class(obj_classDefinition), 351 obj_data: obj_classDefinition.data 352 }; 353 }, 354 355 addControllerClass: function(str_moduleName, obj_classDefinition) { 356 if (str_moduleName === undefined || str_moduleName === null || !str_moduleName) { 357 console.error('module name missing'); 358 } 359 360 if (obj_classDefinition === undefined || obj_classDefinition === null || !obj_classDefinition) { 361 console.error('class definition missing'); 362 } 363 364 if (this.obj_moduleClasses[str_moduleName] === undefined) { 365 this.obj_moduleClasses[str_moduleName] = {}; 366 } 367 368 if (this.obj_moduleClasses[str_moduleName].controller !== undefined && this.obj_moduleClasses[str_moduleName].controller !== null) { 369 console.error('controller class already created for module "' + str_moduleName + '"'); 370 } 371 372 this.obj_moduleClasses[str_moduleName].controller = new Class(obj_classDefinition); 373 }, 374 375 apiInterface: new class_lsjs_apiInterface() 376}; 377var class_lsjs = new Class(classdef_lsjs); 378 379var classdef_lsjs_module = { 380 __name: '', 381 __parentModule: null, 382 383 __el_container: null, 384 385 __view: null, // will later hold the view 386 __models: null, // will later hold the object that provides access to all models 387 __controller: null, // will later hold the controller 388 389 int_numModelsToLoad: 0, 390 391 obj_args: {}, 392 obj_registeredDataBindings: {}, 393 394 initialize: function(obj_args) { 395 this.obj_args = obj_args; 396 this.__name = this.obj_args.__name; 397 398 if (this.obj_args.__parentModule !== undefined && this.obj_args.__parentModule !== null) { 399 this.__parentModule = this.obj_args.__parentModule; 400 } 401 402 if (this.obj_args.__useLoadingIndicator === true && this.__name !== 'loadingIndicator' && lsjs.loadingIndicator !== undefined && lsjs.loadingIndicator !== null) { 403 lsjs.loadingIndicator.__controller.show(); 404 } 405 406 if (this.obj_args.__el_container !== undefined && typeOf(this.obj_args.__el_container) === 'element') { 407 this.__el_container = this.obj_args.__el_container; 408 } else { 409 if (this.obj_args.str_containerSelector === undefined || this.obj_args.str_containerSelector === null) { 410 this.__el_container = new Element('div.moduleContainer.autoModuleContainer.' + this.__name); 411 } else { 412 this.__el_container = $$(this.obj_args.str_containerSelector)[0]; 413 if (this.__el_container === undefined || this.__el_container === null) { 414 console.error('no element found in DOM for selector "'+ this.obj_args.str_containerSelector + '" in module "' + this.__name + '"'); 415 return; 416 } 417 } 418 } 419 420 if (lsjs.obj_moduleClasses[this.__name] === undefined || lsjs.obj_moduleClasses[this.__name] === null) { 421 console.error('module classes not registered for module "' + this.__name + '". Did you include all of the script files?'); 422 return; 423 } 424 425 /* 426 * Instantiate the controller 427 */ 428 this.__controller = new lsjs.obj_moduleClasses[this.__name].controller(); 429 this.__controller.__module = this; 430 431 /* 432 * Instantiate the view and make some module functions available 433 */ 434 if (lsjs.obj_moduleClasses[this.__name].view !== undefined && lsjs.obj_moduleClasses[this.__name].view !== null) { 435 this.__view = new lsjs.obj_moduleClasses[this.__name].view(); 436 this.__view.__module = this; 437 this.__view.__el_container = this.__el_container; 438 this.__view.tplPure = this.tplPure.bind(this); 439 this.__view.tplAdd = this.tplAdd.bind(this); 440 this.__view.tplReplace = this.tplReplace.bind(this); 441 this.__view.registerElements = this.registerElements.bind(this); 442 this.__view.removeDataBindings = this.removeDataBindings.bind(this); 443 this.__view.debugBindings = this.debugBindings.bind(this); 444 } 445 446 /* 447 * Make controller and view available for each other 448 */ 449 this.__controller.__view = this.__view; 450 if (this.__view !== undefined && this.__view !== null) { 451 this.__view.__controller = this.__controller; 452 } 453 454 455 /* 456 * Instantiate each model as a property of an object holding all models. 457 * Make controller and view available for each model and call each model's 458 * "start()" method. 459 */ 460 this.__models = {}; 461 462 /* 463 * Make the object holding all models available for controller and view. 464 * Important: This assignment needs to be created before actually instantiating 465 * any models because otherwise a race condition exists which can cause 466 * the controller's and the view's "start" method to be called before 467 * __models is assigned to them. 468 */ 469 this.__controller.__models = this.__models; 470 if (this.__view !== undefined && this.__view !== null) { 471 this.__view.__models = this.__models; 472 } 473 474 /* 475 * First, we walk through all model classes and instantiate them but we 476 * don't actually call the model's "start()" method because before the 477 * first model can be started, we need to know how many models we have 478 * to load in this module. This is important because only then we can 479 * determine later if all modules have been loaded successfully. 480 */ 481 /* 482 * [1.0.0_h_#3] make sure that we don't try to iterate over undefined or 483 * null because as of mootools 1.5.2. this would cause an error. 484 */ 485 if (lsjs.obj_moduleClasses[this.__name].model !== undefined && lsjs.obj_moduleClasses[this.__name].model !== null) { 486 Object.each(lsjs.obj_moduleClasses[this.__name].model, function(obj_modelClassAndData, str_moduleName) { 487 this.int_numModelsToLoad++; 488 this.__models[str_moduleName] = new obj_modelClassAndData.class_module(); 489 this.__models[str_moduleName].data = Object.clone(obj_modelClassAndData.obj_data); 490 this.__models[str_moduleName].__module = this; 491 this.__models[str_moduleName].__view = this.__view; 492 this.__models[str_moduleName].__controller = this.__controller; 493 this.__models[str_moduleName].__models = this.__models; 494 this.__models[str_moduleName].writeData = this.writeData; 495 this.__models[str_moduleName].readData = this.readData; 496 this.__models[str_moduleName].triggerDataBinding = this.triggerDataBinding; 497 this.__models[str_moduleName].handleDataBinding = this.handleDataBinding; 498 this.__models[str_moduleName].handleDataFunctionBinding = this.handleDataFunctionBinding; 499 this.__models[str_moduleName].lookForMatchingDataBindings = this.lookForMatchingDataBindings; 500 }.bind(this)); 501 } 502 503 /* 504 * Walk through all models again and start them or, if there are no models 505 * registered, call "onModelLoaded()" directly so that the module can work 506 * without a model. 507 */ 508 if (Object.getLength(this.__models) > 0) { 509 Object.each(lsjs.obj_moduleClasses[this.__name].model, function(class_module, str_moduleName) { 510 this.__models[str_moduleName].start(); 511 }.bind(this)); 512 } else { 513 this.onModelLoaded(); 514 } 515 }, 516 517 onModelLoaded: function() { 518 this.int_numModelsToLoad--; 519
520 /* 521 * If there are models that have yet to be loaded, we return 522 * right now and do nothing 523 */ 524 if (this.int_numModelsToLoad > 0) { 525 return; 526 } 527 528 /* 529 * If all models are loaded we start view and controller 530 */ 531 /* 532 * Start the view 533 */ 534 if (this.__view !== undefined && this.__view !== null) { 535 this.__view.start(); 536 } 537 538 /* 539 * Start the controller 540 */ 541 this.__controller.start(); 542 543 if (this.obj_args.__useLoadingIndicator === true && this.__name !== 'loadingIndicator' && lsjs.loadingIndicator !== undefined && lsjs.loadingIndicator !== null) { 544 lsjs.loadingIndicator.__controller.hide(); 545 } 546 }, 547 548 remove: function() { 549 this.__el_container.destroy(); 550 delete this.__el_container; 551 delete this.__view; 552 delete this.__models; 553 delete this.__controller; 554 }, 555 556 tplPure: function(obj_usageOptions, str_moduleName, bln_registerSingleElementsInElementList) { 557 return this.tplUse('pure', obj_usageOptions, str_moduleName, bln_registerSingleElementsInElementList); 558 }, 559 560 tplAdd: function(obj_usageOptions, str_moduleName, bln_registerSingleElementsInElementList) { 561 return this.tplUse('add', obj_usageOptions, str_moduleName, bln_registerSingleElementsInElementList); 562 }, 563 564 tplReplace: function(obj_usageOptions, str_moduleName, bln_registerSingleElementsInElementList) { 565 return this.tplUse('replace', obj_usageOptions, str_moduleName, bln_registerSingleElementsInElementList); 566 }, 567 568 tplUse: function(str_mode, obj_usageOptions, str_moduleName, bln_registerSingleElementsInElementList) { 569 var el_renderedTemplate; 570 571 if (str_moduleName === undefined || str_moduleName === null || !str_moduleName) { 572 str_moduleName = this.__name; 573 } 574 if (obj_usageOptions.arg === undefined || obj_usageOptions.arg === null) { 575 obj_usageOptions.arg = {}; 576 } 577 578 obj_usageOptions.arg.__models = this.__models; 579 obj_usageOptions.arg.__view = this.__view; 580 obj_usageOptions.arg.__controller = this.__controller; 581 obj_usageOptions.bind = obj_usageOptions.bind !== undefined && obj_usageOptions.bind !== null ? obj_usageOptions.bind : this; 582 obj_usageOptions.class = obj_usageOptions.class !== undefined && obj_usageOptions.class !== null ? obj_usageOptions.class : this.__name; 583 obj_usageOptions.parent = obj_usageOptions.parent !== undefined && obj_usageOptions.parent !== null ? obj_usageOptions.parent : this.__el_container; 584 obj_usageOptions.bln_discardContainerElement = obj_usageOptions.bln_discardContainerElement !== undefined && obj_usageOptions.bln_discardContainerElement ? true : false; 585 586 el_renderedTemplate = lsjs.tpl[str_mode](obj_usageOptions, str_moduleName); 587 588 if (el_renderedTemplate === null) { 589 return el_renderedTemplate; 590 } 591 592 this.registerElements(el_renderedTemplate, obj_usageOptions.name, bln_registerSingleElementsInElementList); 593 this.registerDataBindings(el_renderedTemplate); 594 this.registerMultipleDataBindings(el_renderedTemplate); 595 596 this.registerEvents(el_renderedTemplate); 597 598 this.replaceElementPlaceholders(el_renderedTemplate); 599 this.replaceTemplatePlaceholders(el_renderedTemplate); 600 601 if (obj_usageOptions.bln_discardContainerElement) { 602 var el_renderedTemplateContent = el_renderedTemplate.getChildren(); 603 el_renderedTemplate.getParent().adopt(el_renderedTemplateContent); 604 el_renderedTemplate.destroy(); 605 return el_renderedTemplateContent; 606 } else { 607 return el_renderedTemplate; 608 } 609 }, 610 611 replaceTemplatePlaceholders: function(el_toSearchForElementsToReplace) { 612 Array.each(el_toSearchForElementsToReplace.getElements('[data-lsjs-replaceWithTemplate]'), function(el_toReplace) { 613 var el_toReplaceWith, 614 arg; 615 616 /* 617 * In the scope that this function is executed in, the "arg" parameter 618 * that would be available in the template's scope does not exist. 619 * Since the value for the 'data-lsjs-replaceWith' property is written 620 * in the template we want the parameter "arg" to work, so we create 621 * it here so that it works when the 'data-lsjs-replaceWith' value 622 * is evaled here. 623 */ 624 arg = { 625 __view: this.__view, 626 __models: this.__models 627 }; 628 629 el_toReplaceWith = this.tplPure({name: el_toReplace.getProperty('data-lsjs-replaceWithTemplate')}); 630 631 if (typeOf(el_toReplaceWith) !== 'element') { 632 return; 633 } 634 635 el_toReplaceWith.wraps(el_toReplace); 636 el_toReplace.destroy(); 637 }.bind(this)); 638 }, 639 640 replaceElementPlaceholders: function(el_toSearchForElementsToReplace) { 641 Array.each(el_toSearchForElementsToReplace.getElements('[data-lsjs-replaceWithElement]'), function(el_toReplace) { 642 var el_toReplaceWith, 643 arg; 644 645 /* 646 * In the scope that this funciton is executed in, the "arg" parameter 647 * that would be available in the template's scope does not exist. 648 * Since the value for the 'data-lsjs-replaceWith' property is written 649 * in the template we want the parameter "arg" to work, so we create 650 * it here so that it works when the 'data-lsjs-replaceWith' value 651 * is evaled here. 652 */ 653 arg = { 654 __view: this.__view, 655 __models: this.__models 656 }; 657 658 eval('el_toReplaceWith = ' + el_toReplace.getProperty('data-lsjs-replaceWithElement')); 659 660 if (typeOf(el_toReplaceWith) !== 'element') { 661 return; 662 } 663 664 el_toReplaceWith.wraps(el_toReplace); 665 el_toReplace.destroy(); 666 }.bind(this)); 667 }, 668 669 registerEvents: function(el_toSearchForEvents) { 670 Array.each( 671 el_toSearchForEvents.getElements('[data-lsjs-events]'), 672 function(el_toRegisterEventTo) { 673 var str_events = el_toRegisterEventTo.getProperty('data-lsjs-events'); 674 var arr_events = null; 675 676 eval('arr_events = ' + str_events); 677 if (typeOf(arr_events) !== 'array') { 678 return; 679 } 680 681 Array.each( 682 arr_events, 683 function(obj_event) { 684 var func_toRegisterWithEvent = null; 685 eval('func_toRegisterWithEvent = this.' + obj_event.scope + '.' + obj_event.function + '.bind(this.' + obj_event.scope + ')'); 686 687 el_toRegisterEventTo.addEvent( 688 obj_event.event, 689 function(event) { 690 var test = 'hallo'; 691 func_toRegisterWithEvent(el_toRegisterEventTo, event); 692 }.bind(this) 693 ); 694 }.bind(this) 695 ); 696 }.bind(this) 697 ); 698 }, 699 700 removeDataBindings: function(el_toSearchForBindings) { 701 this.registerMultipleDataBindings(el_toSearchForBindings, true); 702 this.registerDataBindings(el_toSearchForBindings, true); 703 }, 704 705 registerMultipleDataBindings: function(el_toSearchForBindings, bln_performDeregistration) { 706 bln_performDeregistration = bln_performDeregistration !== undefined && bln_performDeregistration ? true : false;
707 708 Array.each( 709 el_toSearchForBindings.getElements('[data-lsjs-bindTo]'), 710 function(el_toBind) { 711 var str_bindTo = el_toBind.getProperty('data-lsjs-bindTo'); 712 var arr_bindings = null; 713 714 eval('arr_bindings = ' + str_bindTo); 715 if (typeOf(arr_bindings) !== 'array') { 716 return; 717 } 718 719 Array.each( 720 arr_bindings, 721 function(obj_binding) { 722 var str_bindToModel = obj_binding.model; 723 var str_bindToPath = obj_binding.path !== undefined && obj_binding.path !== null ? obj_binding.path : ''; 724 var str_bindToProperty = obj_binding.property !== undefined && obj_binding.property !== null ? obj_binding.property : ''; 725 726 /* 727 * use the new way to declare a binding translation but also accept the old way 728 * if the new is not being used for this binding 729 */ 730 var str_bindToTranslation = obj_binding.translationModelToView !== undefined && obj_binding.translationModelToView !== null ? obj_binding.translationModelToView : ''; 731 732 if (!str_bindToTranslation) { 733 str_bindToTranslation = obj_binding.translation !== undefined && obj_binding.translation !== null ? obj_binding.translation : ''; 734 } 735 736 var str_bindToTranslationViewToModel = obj_binding.translationViewToModel !== undefined && obj_binding.translationViewToModel !== null ? obj_binding.translationViewToModel : ''; 737 738 var str_bindToCallbackViewToModel = obj_binding.callbackViewToModel !== undefined && obj_binding.callbackViewToModel !== null ? obj_binding.callbackViewToModel : ''; 739 740 if (bln_performDeregistration) { 741 this.deregisterSingleDataBinding(el_toBind, str_bindToModel, str_bindToPath, str_bindToProperty, str_bindToTranslation, str_bindToTranslationViewToModel, str_bindToCallbackViewToModel); 742 } else { 743 this.registerSingleDataBinding(el_toBind, str_bindToModel, str_bindToPath, str_bindToProperty, str_bindToTranslation, str_bindToTranslationViewToModel, str_bindToCallbackViewToModel); 744 } 745 }.bind(this) 746 ); 747 }.bind(this) 748 ); 749 }, 750 751 registerDataBindings: function(el_toSearchForBindings, bln_performDeregistration) { 752 bln_performDeregistration = bln_performDeregistration !== undefined && bln_performDeregistration ? true : false; 753 754 Array.each(el_toSearchForBindings.getElements('[data-lsjs-bindToModel]'), function(el_toBind) { 755 var str_bindToModel, 756 str_bindToPath, 757 str_bindToProperty, 758 str_bindToTranslation, 759 str_bindToTranslationViewToModel, 760 str_bindToCallbackViewToModel; 761 762 str_bindToModel = el_toBind.getProperty('data-lsjs-bindToModel'); 763 764 str_bindToPath = el_toBind.getProperty('data-lsjs-bindToPath'); 765 str_bindToPath = str_bindToPath ? str_bindToPath : ''; 766 767 str_bindToProperty = el_toBind.getProperty('data-lsjs-bindToProperty'); 768 str_bindToProperty = str_bindToProperty ? str_bindToProperty : ''; 769 770 /* 771 * use the new way to declare a binding translation but also accept the old way 772 * if the new is not being used for this binding 773 */ 774 str_bindToTranslation = el_toBind.getProperty('data-lsjs-bindToTranslationModelToView'); 775 str_bindToTranslation = str_bindToTranslation ? str_bindToTranslation : ''; 776 777 if (!str_bindToTranslation) { 778 str_bindToTranslation = el_toBind.getProperty('data-lsjs-bindToTranslation'); 779 str_bindToTranslation = str_bindToTranslation ? str_bindToTranslation : ''; 780 } 781 782 str_bindToTranslationViewToModel = el_toBind.getProperty('data-lsjs-bindToTranslationViewToModel'); 783 str_bindToTranslationViewToModel = str_bindToTranslationViewToModel ? str_bindToTranslationViewToModel : ''; 784 785 str_bindToCallbackViewToModel = el_toBind.getProperty('data-lsjs-bindToCallbackViewToModel'); 786 str_bindToCallbackViewToModel = str_bindToCallbackViewToModel ? str_bindToCallbackViewToModel : ''; 787 788 if (bln_performDeregistration) { 789 this.deregisterSingleDataBinding(el_toBind, str_bindToModel, str_bindToPath, str_bindToProperty, str_bindToTranslation, str_bindToTranslationViewToModel, str_bindToCallbackViewToModel); 790 } else { 791 this.registerSingleDataBinding(el_toBind, str_bindToModel, str_bindToPath, str_bindToProperty, str_bindToTranslation, str_bindToTranslationViewToModel, str_bindToCallbackViewToModel); 792 } 793 794 }.bind(this)); 795 }, 796 797 debugBindings: function() { 798 console.log('DEBUG BINDINGS: this.obj_registeredDataBindings', Object.clone(this.obj_registeredDataBindings)
798); 799 }, 800 801 deregisterSingleDataBinding: function(el_toBind, str_bindToModel, str_bindToPath, str_bindToProperty, str_bindToTranslation, str_bindToTranslationViewToModel, str_bindToCallbackViewToModel) { 802 if (this.obj_registeredDataBindings[str_bindToModel] === undefined || this.obj_registeredDataBindings[str_bindToModel] === null) { 803 return; 804 } 805 806 if (this.obj_registeredDataBindings[str_bindToModel][str_bindToPath] === undefined || this.obj_registeredDataBindings[str_bindToModel][str_bindToPath] === null) { 807 return; 808 } 809 810 this.obj_registeredDataBindings[str_bindToModel][str_bindToPath] = this.obj_registeredDataBindings[str_bindToModel][str_bindToPath].filter( 811 function(obj_binding) { 812 var bln_keepBinding = 813 obj_binding.str_bindToProperty !== str_bindToProperty 814 || obj_binding.str_bindToTranslation !== str_bindToTranslation 815 || obj_binding.str_bindToTranslationViewToModel !== str_bindToTranslationViewToModel 816 || obj_binding.str_bindToCallbackViewToModel !== str_bindToCallbackViewToModel 817 || obj_binding.el_bound !== el_toBind; 818 819 if (!bln_keepBinding) { 820 /* 821 * If we remove the binding, we have to remove corresponding events as well 822 */ 823 if (obj_binding.func_bound !== null) { 824 el_toBind.removeEvent('change', obj_binding.func_bound); 825 } 826 } 827 828 return bln_keepBinding; 829 } 830 ); 831 832 if (this.obj_registeredDataBindings[str_bindToModel][str_bindToPath].length === 0) { 833 delete this.obj_registeredDataBindings[str_bindToModel][str_bindToPath]; 834 } 835 }, 836 837 registerSingleDataBinding: function(el_toBind, str_bindToModel, str_bindToPath, str_bindToProperty, str_bindToTranslation, str_bindToTranslationViewToModel, str_bindToCallbackViewToModel) { 838 if (this.obj_registeredDataBindings[str_bindToModel] === undefined || this.obj_registeredDataBindings[str_bindToModel] === null) { 839 this.obj_registeredDataBindings[str_bindToModel] = {}; 840 } 841 842 if (this.obj_registeredDataBindings[str_bindToModel][str_bindToPath] === undefined || this.obj_registeredDataBindings[str_bindToModel][str_bindToPath] === null) { 843 this.obj_registeredDataBindings[str_bindToModel][str_bindToPath] = []; 844 } 845 846 var func_bound = 847 str_bindToProperty === 'none' 848 ? null 849 : function (eventOrElement) { 850 var el_target, 851 var_newValue, 852 var_oldValue = this.__models[str_bindToModel].readData(str_bindToPath); 853 854 if (eventOrElement === undefined || eventOrElement === null) { 855 /* 856 * Before 2017-08-25 a missing eventOrElement caused an error 857 * but this was a problem when an input field was fired by a datepicker 858 * because in this case eventOrElement was missing and we couldn't do 859 * anything about it. So, as of 2017-08-25, if eventOrElement is missing 860 * we assume el_toBind is the element that fired the event. 861 * 862 * console.error('eventOrElement missing'); 863 * return; 864 * 865 */ 866 eventOrElement = el_toBind; 867 } 868 869 el_target = typeOf(eventOrElement) === 'element' ? eventOrElement : eventOrElement.target; 870 871 /* -> 872 * !!! Important: !!! 873 * Don't react on change events that did not originate on the bound element itself but another 874 * element inside from which the event bubbled up! 875 */ 876 if (el_target !== el_toBind) { 877 return; 878 } 879 /* 880 * <- 881 */ 882 883 if (str_bindToProperty) { 884 var_newValue = el_target.getProperty(str_bindToProperty); 885 } else { 886 if (el_target.match('input[type="checkbox"')) { 887 888 /* 889 * In case of a checkbox we might want to get a boolean value with either true set in case of 890 * a checked checkbox and false set in case of an unchecked checkbo or the other way around. 891 * 892 * But we might also want the checkbox to deliver an explicitly set value if its checked and 893 * an empty string its not. 894 * 895 * So the behavior implemented for handling checkbox values is as follows: 896 * 897 * - if a value attribute is set for the checkbox input element and this value is not the empty 898 * string, this value is delivered when checked and an empty string when unchecked 899 * 900 * - if no value attribute is set, the string 'on' is delivered when checked and an empty 901 * string when unchecked (this is to support the browser's default 'on' value) 902 * 903 * - if a value attribute is set and it is the empty string or the string 'true', a boolean 904 * true is delivered when checked and a boolean false when unchecked 905 * 906 * - if a value attribute is set and it is the string 'false', a boolean false is delivered 907 * when checked and a boolean true when unchecked 908 */ 909 910 var var_valueChecked, 911 var_valueUnchecked; 912 913 if ( 914 el_target.getProperty('value') === '' 915 || el_target.getProperty('value') === 'true' 916 ) { 917 var_valueChecked = true; 918 var_valueUnchecked = false;
919 } else if ( 920 el_target.getProperty('value') === 'false' 921 ) { 922 var_valueChecked = false; 923 var_valueUnchecked = true; 924 } else { 925 var_valueChecked = el_target.getProperty('value'); 926 var_valueUnchecked = ''; 927 } 928 929 var_newValue = el_target.checked ? var_valueChecked : var_valueUnchecked; 930 931 } else if (el_target.match('input') || el_target.match('select') || el_target.match('textarea')) { 932 var_newValue = el_target.getProperty('value'); 933 } else { 934 var_newValue = el_target.getProperty('html'); 935 } 936 } 937 938 if (str_bindToTranslationViewToModel) { 939 /* 940 * a translation function can also be used for a validation. Therefore, the data path 941 * as well as the old value and the bound element are passed as parameters. 942 * 943 * For example, the value could be validated with a validation pattern that depends on the 944 * given data path and if the value is valid, it is returned. But if it is not valid, 945 * the old value could be returned and the bound element could be the target for 946 * some kind of error message output. 947 */ 948 var_newValue = this.__models[str_bindToModel][str_bindToTranslationViewToModel]( 949 var_newValue, // new value 950 el_toBind, // the bound element 951 952 var_oldValue, // old value (useful if the old value needs to be reset for some reason) 953 954 /* 955 * the data path, also useful if the old value needs to be restored for some reason 956 * or if one callback function is registered for multiple bindings with different 957 * data paths and should behave differently depending on the used data path 958 */ 959 str_bindToPath 960 ); 961 } 962 963 this.__models[str_bindToModel].writeData(str_bindToPath, var_newValue); 964 965 if (str_bindToCallbackViewToModel) { 966 /* 967 * A translation function can be used to trigger external data storage usind some kind 968 * of api/ajax request. If external storage failed, it is possible to reset the old value 969 * using the old value parameter and the data path parameter which both are passed to 970 * the callback function. 971 */ 972 this.__models[str_bindToModel][str_bindToCallbackViewToModel]( 973 var_newValue, // new value 974 el_toBind, // the bound element 975 var_oldValue, // old value (useful if the old value needs to be reset for some reason 976 977 /* 978 * the data path, also useful if the old value needs to be restored for some reason 979 * or if one callback function is registered for multiple bindings with different 980 * data paths and should behave differently depending on the used data path 981 */ 982 str_bindToPath 983 ); 984 } 985 }.bind(this); 986 987 this.obj_registeredDataBindings[str_bindToModel][str_bindToPath].push({ 988 str_bindToProperty: str_bindToProperty, 989 str_bindToTranslation: str_bindToTranslation, 990 str_bindToTranslationViewToModel: str_bindToTranslationViewToModel, 991 str_bindToCallbackViewToModel: str_bindToCallbackViewToModel, 992 el_bound: el_toBind, 993 func_bound: func_bound 994 }); 995 996 if (func_bound !== null) { 997 el_toBind.addEvent('change', func_bound); 998 } 999 1000 /* 1001 * Initially set all bound elements to the current value 1002 */ 1003 this.__models[str_bindToModel].writeData(str_bindToPath, this.__models[str_bindToModel].readData(str_bindToPath), true, false); 1004 1005 // console.log('this.obj_registeredDataBindings', this.obj_registeredDataBindings); 1006 }, 1007 1008 registerElements: function(el_toSearchForElementsToRegister, str_registerGroup, bln_registerSingleElementsInElementList, str_elementNameOnly, str_subGroupNameOnly, bln_checkForParentSubGroups ) { 1009 str_registerGroup = str_registerGroup !== undefined && str_registerGroup ? str_registerGroup : 'defaultElementGroup'; 1010 bln_registerSingleElementsInElementList = bln_registerSingleElementsInElementList !== undefined && bln_registerSingleElementsInElementList ? true : false; 1011 str_elementNameOnly = str_elementNameOnly !== undefined && str_elementNameOnly ? str_elementNameOnly : null; 1012 str_subGroupNameOnly = str_subGroupNameOnly !== undefined && str_subGroupNameOnly ? str_subGroupNameOnly : null; 1013 bln_checkForParentSubGroups = bln_checkForParentSubGroups !== undefined && bln_checkForParentSubGroups ? true : false;
1014 1015 if (this.__view.__autoElements === undefined || this.__view.__autoElements === null) { 1016 this.__view.__autoElements = {}; 1017 } 1018 1019 if (this.__view.__autoElements[str_registerGroup] === undefined || this.__view.__autoElements[str_registerGroup] === null) { 1020 this.__view.__autoElements[str_registerGroup] = {}; 1021 } 1022 1023 Array.each(el_toSearchForElementsToRegister.getElements('[data-lsjs-element]'), function(el_toRegister) { 1024 var str_autoElementSubGroupKey = el_toRegister.getProperty('data-lsjs-autoElementSubGroup'); 1025 1026 if (bln_checkForParentSubGroups) { 1027 if (str_autoElementSubGroupKey === null) { 1028 var el_subGroupParent = el_toRegister.getParent('[data-lsjs-autoElementSubGroup]'); 1029 if (typeOf(el_subGroupParent) === 'element') { 1030 str_autoElementSubGroupKey = el_subGroupParent.getProperty('data-lsjs-autoElementSubGroup'); 1031 } 1032 } 1033 } 1034 1035 if (str_subGroupNameOnly !== null && str_autoElementSubGroupKey !== str_subGroupNameOnly) { 1036 return; 1037 } 1038 1039 var str_autoElementName = el_toRegister.getProperty('data-lsjs-element'); 1040 if (str_elementNameOnly !== null && str_autoElementName !== str_elementNameOnly) { 1041 return; 1042 } 1043 1044 1045 if ( 1046 str_autoElementSubGroupKey !== null 1047 && ( 1048 this.__view.__autoElements[str_registerGroup][str_autoElementSubGroupKey] === undefined 1049 || this.__view.__autoElements[str_registerGroup][str_autoElementSubGroupKey] === null 1050 ) 1051 ) { 1052 this.__view.__autoElements[str_registerGroup][str_autoElementSubGroupKey] = {}; 1053 } 1054 1055 var obj_autoElementsSubGroupPointer = 1056 str_autoElementSubGroupKey === null 1057 ? this.__view.__autoElements[str_registerGroup] 1058 : this.__view.__autoElements[str_registerGroup][str_autoElementSubGroupKey]; 1059 1060 var bln_isFirstElement = typeOf(obj_autoElementsSubGroupPointer[str_autoElementName]) !== 'element' 1061 && typeOf(obj_autoElementsSubGroupPointer[str_autoElementName]) !== 'elements'; 1062 if ( 1063 !bln_registerSingleElementsInElementList 1064 && bln_isFirstElement 1065 ) { 1066 /* 1067 * If single elements should not be stored in element lists and 1068 * there is no element registered under the name read out as the 1069 * "data-lsjs-element" value, we store the element itself 1070 */ 1071 obj_autoElementsSubGroupPointer[str_autoElementName] = el_toRegister; 1072 } else { 1073 /* 1074 * If single elements should be stored in element lists or 1075 * there's already an element registered, we create an element list, 1076 * store the already registered element (if there is one) as the 1077 * first array element and add the new element. 1078 * 1079 * If we already have an element list, we simply add the new element. 1080 */ 1081 if (typeOf(obj_autoElementsSubGroupPointer[str_autoElementName]) !== 'elements') { 1082 obj_autoElementsSubGroupPointer[str_autoElementName] = bln_registerSingleElementsInElementList && bln_isFirstElement ? new Elements() : new Elements([obj_autoElementsSubGroupPointer[str_autoElementName]]); 1083 } 1084 obj_autoElementsSubGroupPointer[str_autoElementName].push(el_toRegister); 1085 } 1086 }.bind(this)); 1087 }, 1088 1089 triggerDataBinding: function(str_dataPath, bln_triggerDataViewBindings, bln_triggerDataFunctionBindings) { 1090 this.writeData(str_dataPath, '___TRIGGER_ONLY___', bln_triggerDataViewBindings, bln_triggerDataFunctionBindings); 1091 }, 1092 1093 writeData: function(str_dataPath, var_value, bln_triggerDataViewBindings, bln_triggerDataFunctionBindings) { 1094 var obj_originalData; 1095 1096 /* 1097 * This weird assignment is only used to make sure that an IDE would 1098 * not think 'var_value' was unused because that might lead a developer 1099 * to the conclusion that the 'var_value' parameter could be removed, 1100 * which it can't. 1101 */ 1102 var_value = var_value !== undefined ? var_value : undefined; 1103 1104 bln_triggerDataViewBindings = bln_triggerDataViewBindings === undefined || bln_triggerDataViewBindings ? true : false; 1105 bln_triggerDataFunctionBindings = bln_triggerDataFunctionBindings === undefined || bln_triggerDataFunctionBindings ? true : false; 1106 1107 /* 1108 * Even the empty string is allowed as data path, pointing directly 1109 * to 'this.data' 1110 */ 1111 if (str_dataPath === undefined || !str_dataPath) { 1112 str_dataPath = ''; 1113 } 1114 1115 /* 1116 * If this function is called by a simple call to 'triggerDataBinding', 1117 * the only purpose is to fire all data bindings registered for the 1118 * data path and no data should actually be changed. Therefore we set the 1119 * data to write to the data that is currently stored. 1120 */ 1121 if (var_value === '___TRIGGER_ONLY___') { 1122 var_value = this.readData(str_dataPath); 1123 } 1124 1125 /* 1126 * -> Create a copy of the original data before writing the new value. 1127 * We need to pass the original data to the data function bindings so 1128 * that resetting the original data is possible if the bound function 1129 * needs to do so, e.g. if storing with an external storage engine or 1130 * something similar fails. 1131 * 1132 * We only create the copy if function bindings will be triggered later 1133 * and if there are actually function bindings registered. 1134 */ 1135 if ( 1136 bln_triggerDataFunctionBindings 1137 && this.obj_dataFunctionBindings !== undefined 1138 && this.obj_dataFunctionBindings !== null 1139 ) { 1140 obj_originalData = Object.clone(this.data); 1141 } 1142 /* 1143 * <- 1144 */ 1145 1146 if (var_value === '___DELETE___') { 1147 eval('delete this.data' + (str_dataPath ? '.' + str_dataPath : '')); 1148 } else { 1149 eval('this.data' + (str_dataPath ? '.' + str_dataPath : '') + ' = var_value'); 1150 } 1151 1152 if (bln_triggerDataViewBindings) { 1153 this.handleDataBinding(str_dataPath); 1154 } 1155 1156 if (bln_triggerDataFunctionBindings) { 1157 this.handleDataFunctionBinding(str_dataPath, obj_originalData); 1158 } 1159 }, 1160 1161 handleDataFunctionBinding: function(str_dataPath, obj_originalData) { 1162 var arr_matchingDataPaths; 1163 1164 /* 1165 * Do nothing if no dataFunctionBindings are registered with the model 1166 */ 1167 if (this.obj_dataFunctionBindings === undefined || this.obj_dataFunctionBindings === null) { 1168 return; 1169 } 1170 1171 arr_matchingDataPaths = this.lookForMatchingDataBindings(str_dataPath, this.obj_dataFunctionBindings, true); 1172 1173 Array.each(arr_matchingDataPaths, function(str_registeredDataPath) { 1174 this[this.obj_dataFunctionBindings[str_registeredDataPath]]( 1175 this.readData(str_registeredDataPath), // the new data that is written to the actual data path 1176 str_registeredDataPath, // the registered data path, which might be some levels above the actual data path where the data is being written to 1177 this.readData(str_registeredDataPath, obj_originalData), // the original data that is stored in the actual data path before the new data is being written to it 1178 str_dataPath // the actual data path where the new data is being written to 1179 ); 1180 }.bind(this)); 1181 }, 1182 1183 handleDataBinding: function(str_dataPath) { 1184 var arr_matchingDataPaths; 1185 1186 arr_matchingDataPaths = this.lookForMatchingDataBindings(str_dataPath, this.__module.obj_registeredDataBindings[this.name]); 1187 1188 Array.each(arr_matchingDataPaths, function(str_registeredDataPath) { 1189 if ( 1190 this.__module.obj_registeredDataBindings[this.name][str_registeredDataPath] === undefined 1191 || typeOf(this.__module.obj_registeredDataBindings[this.name][str_registeredDataPath]) !== 'array' 1192 ) { 1193 /* 1194 * Return if the value registered for the dataPath is not actually 1195 * an element collection 1196 */ 1197 return; 1198 } 1199 1200 Array.each(this.__module.obj_registeredDataBindings[this.name][str_registeredDataPath], function(obj_binding) { 1201 var var_valueToSet = this.readData(str_registeredDataPath);
1202 if (obj_binding.str_bindToTranslation) { 1203 var_valueToSet = this[obj_binding.str_bindToTranslation](var_valueToSet, obj_binding.el_bound); 1204 } 1205 1206 /* 1207 * Different element types need different ways to set their value 1208 */ 1209 if (obj_binding.str_bindToProperty) { 1210 if (obj_binding.str_bindToProperty !== 'none') { 1211 obj_binding.el_bound.setProperty(obj_binding.str_bindToProperty, var_valueToSet); 1212 } 1213 } else if ( 1214 obj_binding.el_bound.match('select') 1215 || obj_binding.el_bound.match('textarea') 1216 || (obj_binding.el_bound.match('input') && obj_binding.el_bound.getProperty('type') === 'text') 1217 ) { 1218 obj_binding.el_bound.setProperty('value', var_valueToSet); 1219 } else if ( 1220 (obj_binding.el_bound.match('input') && obj_binding.el_bound.getProperty('type') === 'checkbox') 1221 ) { 1222 if (typeOf(var_valueToSet) === 'boolean') { 1223 obj_binding.el_bound.setProperty('checked', obj_binding.el_bound.getProperty('value') === 'false' ? !var_valueToSet : var_valueToSet); 1224 } else { 1225 if (obj_binding.el_bound.getProperty('value') == var_valueToSet) { 1226 obj_binding.el_bound.setProperty('checked', true); 1227 } else { 1228 obj_binding.el_bound.setProperty('checked', false); 1229 } 1230 } 1231 } else if ( 1232 (obj_binding.el_bound.match('input') && obj_binding.el_bound.getProperty('type') === 'radio') 1233 ) { 1234 if (obj_binding.el_bound.getProperty('value') == var_valueToSet) { 1235 obj_binding.el_bound.setProperty('checked', true); 1236 } else { 1237 obj_binding.el_bound.setProperty('checked', false); 1238 } 1239 } else { 1240 obj_binding.el_bound.setProperty('html', var_valueToSet); 1241 } 1242 }.bind(this)); 1243 }.bind(this)); 1244 }, 1245 1246 /* 1247 * Multiple registered data bindings can match a dataPath used with writeData(). 1248 * In fact, all registered dataPaths that begin with the dataPath used with 1249 * writeData() match, but there's an exception: 1250 * 1251 * obj_test1.obj_test2.str_testValue 1252 * must match obj_test1.obj_test2 1253 * but must NOT match obj_test1.obj_test2.str_test 1254 */ 1255 lookForMatchingDataBindings: function(str_dataPath, obj_registeredDataBindings, bln_checkForFunctionBinding) { 1256 var arr_matchingDataPaths = []; 1257 1258 bln_checkForFunctionBinding = bln_checkForFunctionBinding !== undefined && bln_checkForFunctionBinding !== null && bln_checkForFunctionBinding ? true : false; 1259 1260 if (str_dataPath === undefined || str_dataPath === null) { 1261 console.error('str_dataPath missing'); 1262 } 1263 1264 /* 1265 * Nothing matches if there are no bindings defined for this model 1266 */ 1267 if (obj_registeredDataBindings === undefined || obj_registeredDataBindings === null) { 1268 return arr_matchingDataPaths; 1269 } 1270 1271 /* 1272 * Convert the data bindings object into a hash to make sure that 1273 * iterating over its keys will work, because otherwise in some cases, 1274 * it won't. 1275 */ 1276 obj_registeredDataBindings = new Hash(obj_registeredDataBindings); 1277 1278 /* 1279 * If str_dataPath is empty, all registered data paths match 1280 */ 1281 if (!str_dataPath) { 1282 arr_matchingDataPaths = Object.keys(obj_registeredDataBindings); 1283 return arr_matchingDataPaths; 1284 } 1285 1286 /* 1287 * Check for registered data paths that begin with the written data path 1288 * including the dot/square bracket or that are completely equal 1289 */ 1290 Array.each(Object.keys(obj_registeredDataBindings), function(str_registeredDataPath) { 1291 if ( 1292 str_registeredDataPath === str_dataPath 1293 || str_registeredDataPath.lastIndexOf(str_dataPath + '.', 0) === 0 1294 || str_registeredDataPath.lastIndexOf(str_dataPath + '[', 0) === 0 1295 1296 /* 1297 * If we are looking for "data function bindings" instead of 1298 * "data view/template bindings", we have to match the data path 1299 * whether the written data path is the first part of the registered 1300 * data path or it is the other way round. In case of "data view/template 1301 * bindings" we only consider it a match if the written data path 1302 * represents the beginning of a registered data path. 1303 */ 1304 || (bln_checkForFunctionBinding && str_dataPath.lastIndexOf(str_registeredDataPath + '.', 0) === 0) 1305 || (bln_checkForFunctionBinding && str_dataPath.lastIndexOf(str_registeredDataPath + '[', 0) === 0) 1306 ) { 1307 arr_matchingDataPaths.push(str_registeredDataPath); 1308 } 1309 }); 1310 1311 return arr_matchingDataPaths; 1312 }, 1313 1314 /* 1315 * This function expects a data path and returns the corresonding value from 1316 * the model's data. Additionally, it is possible to pass an object that is 1317 * used instead of the model's data. This feature is used to access the data 1318 * path from a copy of the model's data which is being used if written data 1319 * needs to be reset. 1320 * 1321 * If used without the optional "obj_dataToUseInstead" parameter, calling this 1322 * function is basically equivalent to addressing a model's data-object 1323 * directly: 1324 * this.__models.abc.readData('obj_test.str_test') === this.__models.abc.data.obj_test.str_test 1325 * 1326 */ 1327 readData: function(str_dataPath, obj_dataToUseInstead) { 1328 var var_value; 1329 1330 obj_dataToUseInstead = obj_dataToUseInstead !== undefined && obj_dataToUseInstead !== null ? obj_dataToUseInstead : null; 1331 1332 /* 1333 * Even the empty string is allowed as data path, pointing directly 1334 * to 'this.data' 1335 */ 1336 if (str_dataPath === undefined || !str_dataPath) { 1337 str_dataPath = ''; 1338 } 1339 1340 eval('var_value = ' + (obj_dataToUseInstead !== null ? 'obj_dataToUseInstead' : 'this.data') + (str_dataPath ? '.' + str_dataPath : '')); 1341 return var_value; 1342 } 1343}; 1344var class_lsjs_module = new Class(classdef_lsjs_module); 1345 1346if (window.lsjs === undefined || window.lsjs === null) { 1347 window.lsjs = new class_lsjs(); 1348 1349 window.addEvent('domready', function() { 1350 $$('body')[0].addClass('lsjs-active'); 1351 }); 1352} else { 1353 console.error('lsjs core already loaded!'); 1354} 1355}
1355)(); 1356(function() { 1357var classdef_lsjs_templateHandler = { 1358 obj_templateFunctions: {}, 1359 1360 initialize: function() { 1361 }, 1362 1363 use: function(obj_usageOptions, str_moduleName) { 1364 var obj_usageOptionsDefaults, 1365 func_templateBoundWithArguments, 1366 el_renderedTemplate; 1367 1368 if (str_moduleName === undefined || str_moduleName === null || !str_moduleName) { 1369 str_moduleName = 'universal'; 1370 } 1371 1372 obj_usageOptionsDefaults = { 1373 'name': '', // the name of the template function 1374 'arg': null, // the argument to pass to the template function 1375 'parent': null, // an element in which the parsed template should be inserted 1376 'mode': 'add', // the template usage mode (pure, add, replace) 1377 'bind': null, // an object to bind to the template function as "this" for the template function 1378 'class': null, // a class string to use with the template wrapping div 1379 'id': null // an id to use with the template wrapping div 1380 }; 1381 1382 Object.each(obj_usageOptionsDefaults, function(value, key) { 1383 if (obj_usageOptions[key] === undefined || obj_usageOptions[key] === null) { 1384 obj_usageOptions[key] = value; 1385 } 1386 }); 1387 1388 obj_usageOptions.mode = obj_usageOptions.mode === 'replace' || obj_usageOptions.mode === 'pure' ? obj_usageOptions.mode : 'add'; 1389 1390 if ( 1391 this.obj_templateFunctions[str_moduleName] === undefined 1392 || this.obj_templateFunctions[str_moduleName] === null 1393 ) { 1394 console.error('module "' + str_moduleName + '" is not registered'); 1395 return null; 1396 } 1397 1398 if ( 1399 obj_usageOptions.name === undefined 1400 || !obj_usageOptions.name 1401 ) { 1402 console.error('no template name given.'); 1403 return null; 1404 } 1405 1406 if ( 1407 this.obj_templateFunctions[str_moduleName][obj_usageOptions.name] === undefined 1408 || this.obj_templateFunctions[str_moduleName][obj_usageOptions.name] === null 1409 ) { 1410 console.info('template "' + obj_usageOptions.name + '" is not registered'); 1411 return null; 1412 } 1413 1414 func_templateBoundWithArguments = this.obj_templateFunctions[str_moduleName][obj_usageOptions.name].pass(obj_usageOptions.arg, (obj_usageOptions.bind !== undefined && obj_usageOptions.bind !== null ? obj_usageOptions.bind : window)); 1415 1416 el_renderedTemplate = new Element('div').setProperty('html', func_templateBoundWithArguments()); 1417 if (obj_usageOptions.id !== undefined && obj_usageOptions.id !== null) { 1418 el_renderedTemplate.setProperty('id', obj_usageOptions.id); 1419 } 1420 el_renderedTemplate.addClass(obj_usageOptions.name); 1421 if (obj_usageOptions.class !== undefined && obj_usageOptions.class !== null) { 1422 el_renderedTemplate.addClass(obj_usageOptions.class); 1423 } 1424 1425 if (typeOf(obj_usageOptions.parent) !== 'element') { 1426 return el_renderedTemplate; 1427 } 1428 1429 if (obj_usageOptions.mode === 'pure') { 1430 return el_renderedTemplate; 1431 } 1432 1433 if (obj_usageOptions.mode === 'replace') { 1434 /* 1435 * First, we use destroy() on all the children, because we want to 1436 * prepare them for garbage collection. This, however, does not eliminate 1437 * text nodes, which is why, secondly, we use parent.empty() to get 1438 * rid of them, too. 1439 */ 1440 obj_usageOptions.parent.getChildren().destroy(); 1441 obj_usageOptions.parent.empty(); 1442 } 1443 1444 obj_usageOptions.parent.adopt(el_renderedTemplate); 1445 1446 return el_renderedTemplate; 1447 }, 1448 1449 /* 1450 * 'pure' simply returns the rendered template but does not add it to the DOM. 1451 */ 1452 pure: function(obj_usageOptions, str_moduleName) { 1453 obj_usageOptions.mode = 'pure'; 1454 return this.use(obj_usageOptions, str_moduleName); 1455 }, 1456 1457 /* 1458 * 'add' puts the rendered template in the DOM as a child of the given parent 1459 */ 1460 add: function(obj_usageOptions, str_moduleName) { 1461 obj_usageOptions.mode = 'add'; 1462 return this.use(obj_usageOptions, str_moduleName); 1463 }, 1464 1465 /* 1466 * 'replace' first removes all children from the given parent and then does 1467 * what 'add' does. 1468 */ 1469 replace: function(obj_usageOptions, str_moduleName) { 1470 obj_usageOptions.mode = 'replace'; 1471 return this.use(obj_usageOptions, str_moduleName); 1472 }, 1473 1474 register: function(obj_templateFunctions, str_moduleName) { 1475 if (str_moduleName === undefined || str_moduleName === null || !str_moduleName) { 1476 str_moduleName = 'universal'; 1477 } 1478 Object.each(obj_templateFunctions, function(func_template, str_templateFunctionName) { 1479 this.registerSingleTemplate(str_templateFunctionName, func_template, str_moduleName); 1480 }.bind(this)); 1481 }, 1482 1483 registerSingleTemplate: function(str_templateFunctionName, func_template, str_moduleName) { 1484 if (this.obj_templateFunctions[str_moduleName] === undefined || this.obj_templateFunctions[str_moduleName] === null) { 1485 this.obj_templateFunctions[str_moduleName] = {}; 1486 } 1487 if (this.obj_templateFunctions[str_moduleName][str_templateFunctionName] !== undefined && typeOf(this.obj_templateFunctions[str_moduleName][str_templateFunctionName]) === 'function') { 1488 console.error('double assignment to template name "' + str_templateFunctionName + '". Is it possible that you included the same template file more than once?'); 1489 return; 1490 } 1491 1492 if (typeOf(func_template) !== 'function') { 1493 console.error('no template function given for template name "' + str_templateFunctionName + '"'); 1494 return; 1495 } 1496 1497 this.obj_templateFunctions[str_moduleName][str_templateFunctionName] = func_template; 1498 } 1499}; 1500 1501var class_lsjs_templateHandler = new Class(classdef_lsjs_templateHandler); 1502 1503lsjs.tpl = new class_lsjs_templateHandler(); 1504 1505})(); 1506// < ## CORE ## 1507 1508// ## CORE MODULES ## > 1509 1510 1511 1512// #### MODULE ## > 1513// ###### TEMPLATES ## > 1514(function() { 1515 1516var obj_templates = { 1517 'main': function(arg) { 1518 var str_return = '<div class="selectAll">\ 1519 <input id="' + arg.str_selectAllCheckboxId + '" type="checkbox">\ 1520 <label for="' + arg.str_selectAllCheckboxId + '">' + arg.__models.lang.data.selectAllLabel + '</label>\ 1521</div>'; 1522 1523 // add template info in debug mode 1524 var str_return = '<!-- BEGIN LSJS TEMPLATE: modules/automaticSelectAllCheckbox/templates/main.html/main.html -->' + str_return + '<!-- END LSJS TEMPLATE: modules/automaticSelectAllCheckbox/templates/main.html/main.html -->'; 1525 1526 return str_return; 1527 } 1528}; 1529 1530lsjs.tpl.register(obj_templates, 'automaticSelectAllCheckbox'); 1531 1532})(); 1533// < ###### TEMPLATES ## 1534 1535// ###### MODELS ## > 1536(function() { 1537 1538var obj_classdef_model = { 1539 name: 'lang', 1540 1541 data: { 1542 selectAllLabel: 'Alle auswählen' 1543 }, 1544 1545 start: function() { 1546 this.__module.onModelLoaded(); 1547 } 1548}; 1549 1550lsjs.addModelClass('automaticSelectAllCheckbox', obj_classdef_model); 1551 1552})(); 1553 1554 1555 1556(function() { 1557 1558var obj_classdef_model = { 1559 name: 'options', 1560 1561 data: {}, 1562 1563 start: function() { 1564 /* 1565 * Initializing the options in the data object with default values which 1566 * can later be overwritten when the "set" method is called with other options 1567 */ 1568 this.data = { 1569 str_selector: '.useAutomaticSelectAllCheckbox', 1570 str_appliedClass: 'automaticSelectAllCheckboxApplied' 1571 }; 1572 }, 1573 1574 set: function(obj_options) { 1575 Object.merge(this.data, obj_options); 1576 this.__module.onModelLoaded(); 1577 } 1578}; 1579 1580lsjs.addModelClass('automaticSelectAllCheckbox', obj_classdef_model); 1581 1582})(); 1583 1584 1585 1586 1587// < ###### MODELS ## 1588 1589// ###### VIEW ## > 1590(function() { 1591 1592// ### ENTER MODULE NAME HERE ###### 1593var str_moduleName = 'automaticSelectAllCheckbox'; 1594// ################################# 1595 1596var obj_classdef = { 1597 start: function() { 1598 this.addSelectAllCheckboxes(); 1599 }, 1600 1601 addSelectAllCheckboxes: function() { 1602 var self = this, 1603 els_checkboxGroup, 1604 int_checkboxGroupCounter = 0; 1605 1606 els_checkboxGroup = $$(this.__models.options.data.str_selector); 1607 1608 Array.each(els_checkboxGroup, function(el_checkboxGroup) { 1609 if (el_checkboxGroup.hasClass(this.__models.options.data.str_appliedClass)) { 1610 return; 1611 } 1612 1613 var el_selectAll = this.tplPure({ 1614 name: 'main', 1615 parent: el_checkboxGroup, 1616 arg: { 1617 str_selectAllCheckboxId: 'selectAllCheckbox_' + int_checkboxGroupCounter 1618 } 1619 }); 1620 1621 el_checkboxGroup.addClass(this.__models.options.data.str_appliedClass); 1622 el_selectAll.inject(el_checkboxGroup, 'top'); 1623 1624 el_selectAll.getElement('input').addEvent('change', function() { 1625 Array.each(this.getParent(self.__models.options.data.str_selector).getElements('input[type="checkbox"]'), function(el_checkbox) { 1626 el_checkbox.setProperty('checked', this.getProperty('checked')); 1627 }.bind(this)); 1628 }); 1629 1630 int_checkboxGroupCounter++; 1631 }.bind(this)); 1632 } 1633}; 1634 1635lsjs.addViewClass(str_moduleName, obj_classdef); 1636 1637})(); 1638// < ###### VIEW ## 1639// 1640// ###### CONTROLLER ## > 1641/* 1642 * 1643 * How to use this module: 1644 * 1645 * If you have multiple checkboxes and want to automatically add an additional 1646 * checkbox with which all the others can be checked and unchecked at once, 1647 * simply wrap all your checkboxes in one container element (e.g. a div) 1648 * and give this container element the class "useAutomaticSelectAllCheckbox" 1649 * (or whatever is defined as the str_selector property in this module's options model). 1650 * 1651 * Now, all you have to do is to run this module to automatically apply the 1652 * functionality: 1653 * 1654 * lsjs.__moduleHelpers.automaticSelectAllCheckbox.start(); 1655 * 1656 * If other checkbox groups are added to the page dynamically at a later point, 1657 * you can use 1658 * 1659 * lsjs.__moduleHelpers.automaticSelectAllCheckbox.refresh(); 1660 * 1661 * in order to rescan the page and apply the functionality wherever necessary. 1662 * Checkbox groups which already have the functionality are automatically skipped 1663 * in the refresh call. 1664 */ 1665 1666(function() { 1667 1668// ### ENTER MODULE NAME HERE ###### 1669var str_moduleName = 'automaticSelectAllCheckbox'; 1670// ################################# 1671 1672var obj_classdef = { 1673 start: function() { 1674 } 1675}; 1676 1677lsjs.addControllerClass(str_moduleName, obj_classdef); 1678 1679lsjs.__moduleHelpers[str_moduleName] = { 1680 self: null, 1681 1682 start: function(obj_options) { 1683 /* 1684 * Only allow one single instance of this module to be started! 1685 */ 1686 if (this.self !== null) { 1687 console.error('module ' + str_moduleName + ' has already been started'); 1688 return; 1689 } 1690 1691 this.self = lsjs.createModule({ 1692 __name: str_moduleName 1693 }); 1694 1695 this.self.__models.options.set(obj_options); 1696 }, 1697 1698 refresh: function() { 1699 this.self.__view.addSelectAllCheckboxes(); 1700 } 1701}; 1702 1703}
1703)(); 1704// < ###### CONTROLLER ## 1705// < #### MODULE ## 1706 1707 1708 1709 1710// #### MODULE ## > 1711// ###### TEMPLATES ## > 1712 1713// < ###### TEMPLATES ## 1714 1715// ###### MODELS ## > 1716(function() { 1717 1718var obj_classdef_model = { 1719 name: 'options', 1720 1721 data: { 1722 str_cajaxMode: 'updateCompletely', 1723 1724 obj_cajaxRequestData: { 1725 /* 1726 * This object has to be filled using the data-lsjs-cajaxCallerOptions parameter in the DOM element to be 1727 * enhanced by this module. 1728 * IMPORTANT: Values must not be predefined here! 1729 * 1730 requestedElementClass: 'requestedClass', 1731 requestedElementID: 'requestedId', 1732 custom: { 1733 testkey1: 'value1', 1734 testkey2: 'value2' 1735 } 1736 */ 1737 } 1738 }, 1739 1740 start: function() { 1741 this.__module.onModelLoaded(); 1742 }, 1743 1744 set: function(obj_options) { 1745 Object.merge(this.data, obj_options); 1746 } 1747}; 1748 1749lsjs.addModelClass('cajaxCallerInstance', obj_classdef_model); 1750 1751})(); 1752 1753 1754 1755 1756// < ###### MODELS ## 1757 1758// ###### VIEW ## > 1759(function() { 1760 1761// ### ENTER MODULE NAME HERE ###### 1762var str_moduleName = 'cajaxCallerInstance'; 1763// ################################# 1764 1765var obj_classdef = { 1766 start: function() { 1767 var str_options, 1768 obj_options = {}, 1769 str_eventType; 1770 1771 /* 1772 * Only "form" elements and "a" elements are supported. 1773 * If the container element itself is not a "form" or "a" 1774 * element, we look for one of these inside the container. 1775 */ 1776 switch (this.__el_container.get('tag')) { 1777 case 'a': 1778 str_eventType = 'click'; 1779 break; 1780 1781 case 'form': 1782 str_eventType = 'submit'; 1783 break; 1784 1785 default: 1786 var el_alternativeInside = null; 1787 1788 el_alternativeInside = this.__el_container.getElement('form:not(.skipCajaxCaller)'); 1789 1790 if (el_alternativeInside === null) { 1791 el_alternativeInside = this.__el_container.getElement('a:not(.skipCajaxCaller)'); 1792 } 1793 1794 if (el_alternativeInside === null) { 1795 return; 1796 } 1797 1798 el_alternativeInside.setProperty('data-lsjs-cajaxCallerOptions', this.__el_container.getProperty('data-lsjs-cajaxCallerOptions')); 1799 1800 this.__el_container = el_alternativeInside; 1801 1802 this.start(); 1803 return; 1804 break; 1805 } 1806 1807 str_options = this.__el_container.getProperty('data-lsjs-cajaxCallerOptions'); 1808 1809 if (str_options) { 1810 obj_options = JSON.decode(str_options); 1811 } 1812 1813 this.__models.options.set(obj_options); 1814 1815 this.registerElements(this.__el_container, 'main', false); 1816 1817 /* --> 1818 * Prepare bound functions that are needed for events 1819 */ 1820 this.bound_makeCajaxRequest = this.makeCajaxRequest.bind(this); 1821 1822 this.__el_container.addEvent( 1823 str_eventType, 1824 this.bound_makeCajaxRequest 1825 ); 1826 }, 1827 1828 bound_makeCajaxRequest: null, 1829 makeCajaxRequest: function(event) { 1830 event.stop(); 1831 1832 switch (this.__el_container.get('tag')) { 1833 case 'a': 1834 lsjs.loadingIndicator.__controller.show(); 1835 1836 new Request.cajax({ 1837 url: this.__el_container.getProperty('href'), 1838 method: 'get', 1839 noCache: true, 1840 cajaxMode: this.__models.options.data.str_cajaxMode, 1841 data: 'cajaxRequestData=' + JSON.encode(this.__models.options.data.obj_cajaxRequestData), 1842 onComplete: function() { 1843 lsjs.loadingIndicator.__controller.hide(); 1844 }, 1845 onSuccess: function(els, str_html, str_script) { 1846 Browser.exec(str_script); 1847 } 1848 }).send(); 1849 break; 1850 1851 case 'form': 1852 lsjs.loadingIndicator.__controller.show(); 1853 1854 new Request.cajax({ 1855 url: this.__el_container.getProperty('action'), 1856 method: 'post', 1857 noCache: true, 1858 cajaxMode: this.__models.options.data.str_cajaxMode, 1859 el_formToUseForFormData: this.__el_container, 1860 // obj_additionalFormData: this.__models.options.data.obj_additionalFormData, 1861 obj_additionalFormData: { 1862 cajaxRequestData: JSON.encode(this.__models.options.data.obj_cajaxRequestData) 1863 }, 1864 onComplete: function() { 1865 lsjs.loadingIndicator.__controller.hide(); 1866 }, 1867 onSuccess: function(els, str_html, str_script) { 1868 Browser.exec(str_script); 1869 } 1870 }).send(); 1871 break; 1872 1873 default: 1874 return; 1875 break; 1876 } 1877 } 1878}; 1879 1880lsjs.addViewClass(str_moduleName, obj_classdef); 1881 1882})(); 1883// < ###### VIEW ## 1884// 1885// ###### CONTROLLER ## > 1886(function() { 1887 1888// ### ENTER MODULE NAME HERE ###### 1889var str_moduleName = 'cajaxCallerInstance'; 1890// ################################# 1891 1892var obj_classdef = { 1893 start: function() { 1894 } 1895}; 1896 1897lsjs.addControllerClass(str_moduleName, obj_classdef); 1898 1899})(); 1900// < ###### CONTROLLER ## 1901// < #### MODULE ## 1902 1903 1904 1905 1906// #### MODULE ## > 1907// ###### TEMPLATES ## > 1908 1909// < ###### TEMPLATES ## 1910 1911// ###### MODELS ## > 1912(function() { 1913 1914var obj_classdef_model = { 1915 name: 'options', 1916 1917 data: {}, 1918 1919 start: function() { 1920 /* 1921 * Initializing the options in the data object with default values which 1922 * can later be overwritten when the "set" method is called with other options 1923 */ 1924 this.data = { 1925 el_domReference: null, 1926 str_selector: '[data-lsjs-component~="cajaxCaller"]', // the selector for the dom element to enrich, 1927 str_classToSetWhenModuleApplied: 'cajaxCallerApplied' 1928 }; 1929 }, 1930 1931 set: function(obj_options) { 1932 Object.merge(this.data, obj_options); 1933 this.__module.onModelLoaded(); 1934 } 1935}; 1936 1937lsjs.addModelClass('cajaxCallerManager', obj_classdef_model); 1938 1939})(); 1940 1941 1942 1943 1944// < ###### MODELS ## 1945 1946// ###### VIEW ## > 1947 1948// < ###### VIEW ## 1949// 1950// ###### CONTROLLER ## > 1951/* 1952 * -- ACTIVATION: -- 1953 * 1954 * To activate this module, the following code has to be put in the app.js: 1955 * 1956 lsjs.__moduleHelpers.cajaxCallerManager.start({ 1957 el_domReference: el_domReference 1958 }); 1959 * 1960 * The el_domReference parameter is only required if this module initialization code is called
1960in a cajax_domUpdate event. 1961 * 1962 * 1963 * 1964 * -- FUNCTIONALITY AND USAGE: -- 1965 * 1966 * Add the following attribute to a DOM element to apply this module: 1967 * 1968 * data-lsjs-component="cajaxCaller" 1969 * 1970 * TODO: Write a detailed functionality and usage description. Until then, the options models of the manager and 1971 * the instance submodule can be consulted for further information. 1972 */ 1973 1974(function() { 1975 1976// ### ENTER MODULE NAME HERE ###### 1977var str_moduleName = 'cajaxCallerManager'; 1978// ################################# 1979 1980var obj_classdef = { 1981 start: function() { 1982 var els_toEnhance; 1983 /* 1984 * Look for elements to enrich with the lsjs-module and then 1985 * instantiate instances for each element found. 1986 */ 1987 if (this.__models.options.data.el_domReference !== undefined && typeOf(this.__models.options.data.el_domReference) === 'element') { 1988 els_toEnhance = this.__models.options.data.el_domReference.getElements(this.__models.options.data.str_selector); 1989 } else { 1990 els_toEnhance = $$(this.__models.options.data.str_selectors); 1991 } 1992 1993 Array.each(els_toEnhance, function(el_container) { 1994 /* -> 1995 * Make sure not to handle an element more than once 1996 */ 1997 if (!el_container.retrieve('alreadyHandledBy_' + str_moduleName)) { 1998 el_container.store('alreadyHandledBy_' + str_moduleName, true); 1999 } else { 2000 return; 2001 } 2002 /* 2003 * <- 2004 */ 2005 2006 el_container.addClass(this.__models.options.data.str_classToSetWhenModuleApplied); 2007 2008 lsjs.createModule({ 2009 __name: 'cajaxCallerInstance', 2010 __parentModule: this.__module, 2011 __useLoadingIndicator: false, 2012 __el_container: el_container 2013 }); 2014 }.bind(this)); 2015 } 2016}; 2017 2018lsjs.addControllerClass(str_moduleName, obj_classdef); 2019 2020lsjs.__moduleHelpers[str_moduleName] = { 2021 self: null, 2022 2023 start: function(obj_options) { 2024 this.self = lsjs.createModule({ 2025 __name: str_moduleName 2026 }); 2027 this.self.__models.options.set(obj_options); 2028 } 2029}; 2030 2031})(); 2032// < ###### CONTROLLER ## 2033// < #### MODULE ## 2034 2035 2036 2037 2038// #### MODULE ## > 2039// ###### TEMPLATES ## > 2040 2041// < ###### TEMPLATES ## 2042 2043// ###### MODELS ## > 2044(function() { 2045 2046var obj_classdef_model = { 2047 name: 'options', 2048 2049 data: { 2050 }, 2051 2052 start: function() { 2053 this.__module.onModelLoaded(); 2054 }, 2055 2056 set: function(obj_options) { 2057 Object.merge(this.data, obj_options); 2058 } 2059}; 2060 2061lsjs.addModelClass('elementFolderInstance', obj_classdef_model); 2062 2063})(); 2064 2065 2066 2067 2068// < ###### MODELS ## 2069 2070// ###### VIEW ## > 2071(function() { 2072 2073// ### ENTER MODULE NAME HERE ###### 2074var str_moduleName = 'elementFolderInstance'; 2075// ################################# 2076 2077var obj_classdef = { 2078 str_containerID: null, 2079 2080 start: function() { 2081 var str_options, 2082 obj_options, 2083 obj_unfoldOptions; 2084 2085 this.registerElements(this.__el_container, 'main', true); 2086 2087 str_options = this.__el_container.getProperty('data-lsjs-elementFolderOptions'); 2088 2089 if (str_options) { 2090 obj_options = JSON.decode(str_options); 2091 } 2092 2093 this.__models.options.set(obj_options); 2094 2095 this.str_containerID = this.__el_container.getProperty('id'); 2096 2097 obj_unfoldOptions = Object.merge( 2098 { 2099 str_initialToggleStatus: 'open', 2100 bln_toggleOnInitialization: false, 2101 bln_skipAnimationWhenTogglingOnInitialization: false, 2102 var_togglerSelector: this.__autoElements.main.elementFolderToggler, 2103 var_contentBoxSelector: this.__autoElements.main.elementFolderContent, 2104 var_wrapperSelector: this.__el_container, 2105 str_cookieIdentifierName: this.str_containerID ? this.str_containerID : '', 2106 str_initialCookieStatus: 'open', 2107 obj_morphOptions: { 2108 'duration': 600 2109 } 2110 }, 2111 this.__models.options.data 2112 ); 2113 2114 lsjs.__moduleHelpers.unfold.start(obj_unfoldOptions); 2115 2116 } 2117}; 2118 2119lsjs.addViewClass(str_moduleName, obj_classdef); 2120 2121})(); 2122// < ###### VIEW ## 2123// 2124// ###### CONTROLLER ## > 2125(function() { 2126 2127// ### ENTER MODULE NAME HERE ###### 2128var str_moduleName = 'elementFolderInstance'; 2129// ################################# 2130 2131var obj_classdef = { 2132 start: function() { 2133 } 2134}; 2135 2136lsjs.addControllerClass(str_moduleName, obj_classdef); 2137 2138})(); 2139// < ###### CONTROLLER ## 2140// < #### MODULE ## 2141 2142 2143 2144 2145// #### MODULE ## > 2146// ###### TEMPLATES ## > 2147 2148// < ###### TEMPLATES ## 2149 2150// ###### MODELS ## > 2151(function() { 2152 2153var obj_classdef_model = { 2154 name: 'options', 2155 2156 data: {}, 2157 2158 start: function() { 2159 /* 2160 * Initializing the options in the data object with default values which 2161 * can later be overwritten when the "set" method is called with other options 2162 */ 2163 this.data = { 2164 el_domReference: null, 2165 str_selector: '[data-lsjs-component~="elementFolder"]', // the selector for the dom element to enrich, 2166 str_classToSetWhenModuleApplied: 'elementFolderApplied' 2167 }; 2168 }, 2169 2170 set: function(obj_options) { 2171 Object.merge(this.data, obj_options); 2172 this.__module.onModelLoaded(); 2173 } 2174}; 2175 2176lsjs.addModelClass('elementFolderManager', obj_classdef_model); 2177 2178})(); 2179 2180 2181 2182 2183// < ###### MODELS ## 2184 2185// ###### VIEW ## > 2186 2187// < ###### VIEW ## 2188// 2189// ###### CONTROLLER ## > 2190/* 2191 * -- ACTIVATION: -- 2192 * 2193 * To activate this module, the following code has to be put in the app.js: 2194 * 2195 lsjs.__moduleHelpers.elementFolderManager.start({ 2196 el_domReference: el_domReference 2197 }); 2198 * 2199 * The el_domReference parameter is only required if this module initialization code is called
2199in a cajax_domUpdate event. 2200 * 2201 * 2202 * 2203 * -- FUNCTIONALITY AND USAGE: -- 2204 * 2205 * Add the following attribute to a DOM element to apply this module: 2206 * 2207 * data-lsjs-component="elementFolder" 2208 * 2209 * 2210 * Add the following attribute to an another element inside the above mentioned 2211 * outer DOM element in order to use it as the toggler: 2212 * 2213 * data-lsjs-element="elementFolderToggler" 2214 * 2215 * 2216 * Add the following attribute to yet another element inside the above mentioned 2217 * outer DOM element in order to use it as the toggler's content: 2218 * 2219 * data-lsjs-element="elementFolderContent" 2220 * 2221 * 2222 * 2223 * Using the attribute "data-lsjs-elementFolderOptions" the default options for an 2224 * elementFolder instance can be overridden. Since "elementFolder" leveragesf the "unfold" module 2225 * all options supported by the "unfold" module can be used here. 2226 * 2227 * The following example shows how to create an elementFolder instance that starts closed 2228 * although by default it would start open. 2229 * 2230 * <section 2231 * data-lsjs-component="elementFolder" 2232 * data-lsjs-elementFolderOptions=" 2233 * { 2234 * str_initialCookieStatus: 'closed' 2235 * } 2236 * " 2237 * id="someId" 2238 * > 2239 * <div data-lsjs-element="elementFolderToggler">TOGGLE</div> 2240 * <div data-lsjs-element="elementFolderContent">CONTENT</div> 2241 * </section> 2242 * 2243 * 2244 * TODO: Write a detailed functionality and usage description. Until then, the options models of the manager and 2245 * the instance submodule can be consulted for further information. 2246 * 2247 */ 2248 2249(function() { 2250 2251// ### ENTER MODULE NAME HERE ###### 2252var str_moduleName = 'elementFolderManager'; 2253// ################################# 2254 2255var obj_classdef = { 2256 start: function() { 2257 var els_toEnhance; 2258 /* 2259 * Look for elements to enrich with the lsjs-module and then 2260 * instantiate instances for each element found. 2261 */ 2262 if (this.__models.options.data.el_domReference !== undefined && typeOf(this.__models.options.data.el_domReference) === 'element') { 2263 els_toEnhance = this.__models.options.data.el_domReference.getElements(this.__models.options.data.str_selector); 2264 } else { 2265 els_toEnhance = $$(this.__models.options.data.str_selectors); 2266 } 2267 2268 Array.each(els_toEnhance, function(el_container) { 2269 /* -> 2270 * Make sure not to handle an element more than once 2271 */ 2272 if (!el_container.retrieve('alreadyHandledBy_' + str_moduleName)) { 2273 el_container.store('alreadyHandledBy_' + str_moduleName, true); 2274 } else { 2275 return; 2276 } 2277 /* 2278 * <- 2279 */ 2280 2281 el_container.addClass(this.__models.options.data.str_classToSetWhenModuleApplied); 2282 2283 lsjs.createModule({ 2284 __name: 'elementFolderInstance', 2285 __parentModule: this.__module, 2286 __useLoadingIndicator: false, 2287 __el_container: el_container 2288 }); 2289 }.bind(this)); 2290 } 2291}; 2292 2293lsjs.addControllerClass(str_moduleName, obj_classdef); 2294 2295lsjs.__moduleHelpers[str_moduleName] = { 2296 self: null, 2297 2298 start: function(obj_options) { 2299 this.self = lsjs.createModule({ 2300 __name: str_moduleName 2301 }); 2302 this.self.__models.options.set(obj_options); 2303 } 2304}; 2305 2306})(); 2307// < ###### CONTROLLER ## 2308// < #### MODULE ## 2309 2310 2311 2312 2313// #### MODULE ## > 2314// ###### TEMPLATES ## > 2315 2316// < ###### TEMPLATES ## 2317 2318// ###### MODELS ## > 2319(function() { 2320 2321var obj_classdef_model = { 2322 name: 'options', 2323 2324 data: {}, 2325 2326 start: function() { 2327 /* 2328 * Initializing the options in the data object with default values which 2329 * can later be overwritten when the "set" method is called with other options 2330 */ 2331 this.data = { 2332 var_naviSelector: null, 2333 bln_automaticallyToggleOnInitialization: true, 2334 bln_closeOnOutsideClick: false, 2335 bln_onlyFirstLevel: false 2336 }; 2337 }, 2338 2339 set: function(obj_options) { 2340 Object.merge(this.data, obj_options); 2341 this.__module.onModelLoaded(); 2342 } 2343}; 2344 2345lsjs.addModelClass('foldedNavi', obj_classdef_model); 2346 2347})(); 2348 2349 2350 2351 2352// < ###### MODELS ## 2353 2354// ###### VIEW ## > 2355(function() { 2356 2357// ### ENTER MODULE NAME HERE ###### 2358var str_moduleName = 'foldedNavi'; 2359// ################################# 2360 2361var obj_classdef = { 2362 start: function() { 2363 this.__el_container = 2364 ( 2365 typeOf(this.__models.options.data.var_naviSelector) === 'element' 2366 || typeOf(this.__models.options.data.var_naviSelector) === 'null' 2367 ) 2368 ? this.__models.options.data.var_naviSelector 2369 : $$(this.__models.options.data.var_naviSelector)[0]; 2370 2371 if (typeOf(this.__el_container) !== 'element') { 2372 return; 2373 } 2374 2375 this.__el_container.addClass('useFoldedNavi'); 2376 2377 Array.each(this.__el_container.getElements('li').filter(this.__models.options.data.bln_onlyFirstLevel ? '.level_1 > .submenu' : '.submenu'), function(el_li) { 2378 var el_toggler = new Element('span.foldedNaviToggler').inject(el_li, 'top'); 2379 2380 lsjs.__moduleHelpers.unfold.start({ 2381 bln_automaticallyCreateResizeBox: false, 2382 str_initialDisplayType: 'block', 2383 str_initialToggleStatus: 'closed', 2384 bln_toggleOnInitialization: this.__models.options.data.bln_automaticallyToggleOnInitialization ? (el_li.hasClass('trail') || el_li.hasClass('active')) : false, 2385 var_togglerSelector: el_toggler, 2386 var_contentBoxSelector: el_li.getElement('> ul'), 2387 var_wrapperSelector: el_li, 2388 str_animationMode: 'height', 2389 obj_morphOptions: { 2390 'duration': 400 2391 }, 2392 bln_closeOnOutsideClick: this.__models.options.data.bln_closeOnOutsideClick 2393 }); 2394 }.bind(this)); 2395 } 2396}; 2397 2398lsjs.addViewClass(str_moduleName, obj_classdef); 2399 2400})(); 2401// < ###### VIEW ## 2402// 2403// ###### CONTROLLER ## > 2404(function() { 2405 2406// ### ENTER MODULE NAME HERE ###### 2407var str_moduleName = 'foldedNavi'; 2408// ################################# 2409 2410var obj_classdef = { 2411 start: function() { 2412 } 2413}; 2414 2415lsjs.addControllerClass(str_moduleName, obj_classdef); 2416 2417lsjs.__moduleHelpers[str_moduleName] = { 2418 self: null, 2419 2420 start: function(obj_options) { 2421 this.self = lsjs.createModule({ 2422 __name: str_moduleName 2423 }); 2424 2425 this.self.__models.options.set(obj_options); 2426 } 2427}; 2428 2429}
2429)(); 2430// < ###### CONTROLLER ## 2431// < #### MODULE ## 2432 2433 2434 2435 2436// #### MODULE ## > 2437// ###### TEMPLATES ## > 2438 2439// < ###### TEMPLATES ## 2440 2441// ###### MODELS ## > 2442 2443// < ###### MODELS ## 2444 2445// ###### VIEW ## > 2446 2447// < ###### VIEW ## 2448// 2449// ###### CONTROLLER ## > 2450(function() { 2451 2452// ### ENTER MODULE NAME HERE ###### 2453var str_moduleName = 'formRedirect'; 2454// ################################# 2455 2456var obj_classdef = { 2457 start: function() { 2458 lsjs[str_moduleName] = this.__module; 2459 }, 2460 2461 /* 2462 * This function redirects to a url sending data (normally as post data) 2463 * with a dynamically created form. 2464 * 2465 * As a first parameter it takes an object holding the data to send. 2466 * The property names are used as field names in the form and the 2467 * property values as field values. 2468 * 2469 * As a second parameter it takes the url to redirect to or an empty string 2470 * which would result in reloading the current page sending post data 2471 * 2472 * As a third parameter it takes a string defining the method used when sending 2473 * the form. It defaults to 'post'. 2474 */ 2475 send: function(obj_valuesToSend, str_targetUrl, str_method) { 2476 var el_form, 2477 els_inputs; 2478 2479 obj_valuesToSend = obj_valuesToSend !== undefined ? obj_valuesToSend : {}; 2480 str_targetUrl = str_targetUrl !== undefined ? str_targetUrl : ''; 2481 str_method = str_method !== undefined ? str_method : 'post'; 2482 2483 console.log('post redirect sending...'); 2484
2485 el_form = new Element('form') 2486 .setProperties({ 2487 'method': str_method, 2488 'action': str_targetUrl 2489 }) 2490 .setStyles({ 2491 'position': 'absolute', 2492 'top': '-5000px', 2493 'left': '-5000px' 2494 }); 2495 2496 els_inputs = new Elements(); 2497 2498 Object.each(obj_valuesToSend, function(str_fieldValue, str_fieldName) { 2499 els_inputs.push( 2500 new Element('input').setProperties({ 2501 'type': 'hidden', 2502 'name': str_fieldName, 2503 'value': str_fieldValue 2504 }) 2505 ); 2506 }); 2507 2508 $$('body')[0].adopt( 2509 el_form.adopt(els_inputs) 2510 ); 2511 2512 el_form.submit(); 2513 } 2514}; 2515 2516lsjs.addControllerClass(str_moduleName, obj_classdef); 2517 2518lsjs.createModule({ 2519 __name: str_moduleName 2520}); 2521 2522})(); 2523// < ###### CONTROLLER ## 2524// < #### MODULE ## 2525 2526 2527 2528 2529// #### MODULE ## > 2530// ###### TEMPLATES ## > 2531(function() { 2532 2533var obj_templates = { 2534 'editToggler': function(arg) { 2535 var str_return = '<div class="pointer txs-xl-all aptr-0" data-lsjs-element="openFormToggler"><i class="fi fi-edit"></i></div>'; 2536 2537 // add template info in debug mode 2538 var str_return = '<!-- BEGIN LSJS TEMPLATE: files/merconisfiles/themes/theme8/lsjsModsCoreApps/modules/formReviewer/formReviewerInstance/templates/editToggler.html/editToggler.html -->' + str_return + '<!-- END LSJS TEMPLATE: files/merconisfiles/themes/theme8/lsjsModsCoreApps/modules/formReviewer/formReviewerInstance/templates/editToggler.html/editToggler.html -->'; 2539 2540 return str_return; 2541 } 2542}; 2543 2544lsjs.tpl.register(obj_templates, 'formReviewerInstance'); 2545 2546})(); 2547// < ###### TEMPLATES ## 2548 2549// ###### MODELS ## > 2550(function() { 2551 2552var obj_classdef_model = { 2553 name: 'options', 2554 2555 data: { 2556 }, 2557 2558 start: function() { 2559 this.__module.onModelLoaded(); 2560 }, 2561 2562 set: function(obj_options) { 2563 Object.merge(this.data, obj_options); 2564 } 2565}; 2566 2567lsjs.addModelClass('formReviewerInstance', obj_classdef_model); 2568 2569})(); 2570 2571 2572 2573 2574// < ###### MODELS ## 2575 2576// ###### VIEW ## > 2577(function() { 2578 2579// ### ENTER MODULE NAME HERE ###### 2580var str_moduleName = 'formReviewerInstance'; 2581// ################################# 2582 2583var obj_classdef = { 2584 els_allFormFields: null, 2585 2586 start: function() { 2587 this.getAllFormFields(); 2588 if (this.els_allFormFields.length > 0) { 2589 this.initializeOpenCloseFunctionality(); 2590 } 2591 }, 2592 2593 getAllFormFields: function() { 2594 this.els_allFormFields = new Elements(); 2595 Array.prototype.push.apply(this.els_allFormFields, this.__el_container.getElements('input')); 2596 Array.prototype.push.apply(this.els_allFormFields, this.__el_container.getElements('select')); 2597 Array.prototype.push.apply(this.els_allFormFields, this.__el_container.getElements('textarea')); 2598 }, 2599 2600 initializeOpenCloseFunctionality: function() { 2601 var el_renderedTogglerTemplate = this.tplPure({ 2602 name: 'editToggler' 2603 }); 2604 2605 el_renderedTogglerTemplate.inject(this.__el_container, 'top'); 2606 2607 2608 2609 /* 2610 * Close or open the form initially, based on whether or not data is valid 2611 */ 2612 if ( 2613 this.__el_container.getProperty('data-misc-close-form-initially') == '1' 2614 && this.__el_container.getElement('.error') === null 2615 ) { 2616 this.closeForm(); 2617 } 2618 2619 this.__autoElements.editToggler.openFormToggler.addEvent( 2620 'click', 2621 this.openForm.bind(this) 2622 ) 2623 }, 2624 2625 closeForm: function() { 2626 this.els_allFormFields.setProperty('disabled', true); 2627 this.__el_container.addClass('formClosed'); 2628 }, 2629 2630 openForm: function() { 2631 this.els_allFormFields.setProperty('disabled', false); 2632 this.__el_container.removeClass('formClosed'); 2633 } 2634}; 2635 2636lsjs.addViewClass(str_moduleName, obj_classdef); 2637 2638})(); 2639// < ###### VIEW ## 2640// 2641// ###### CONTROLLER ## > 2642(function() { 2643 2644// ### ENTER MODULE NAME HERE ###### 2645var str_moduleName = 'formReviewerInstance'; 2646// ################################# 2647 2648var obj_classdef = { 2649 start: function() { 2650 } 2651}; 2652 2653lsjs.addControllerClass(str_moduleName, obj_classdef); 2654 2655})(); 2656// < ###### CONTROLLER ## 2657// < #### MODULE ## 2658 2659 2660 2661 2662// #### MODULE ## > 2663// ###### TEMPLATES ## > 2664 2665// < ###### TEMPLATES ## 2666 2667// ###### MODELS ## > 2668(function() { 2669 2670var obj_classdef_model = { 2671 name: 'options', 2672 2673 data: {}, 2674 2675 start: function() { 2676 /* 2677 * Initializing the options in the data object with default values which 2678 * can later be overwritten when the "set" method is called with other options 2679 */ 2680 this.data = { 2681 el_domReference: null, 2682 str_selector: '[data-lsjs-component~="formReviewer"]', // the selector for the dom element to enrich, 2683 str_classToSetWhenModuleApplied: 'formReviewerApplied' 2684 }; 2685 }, 2686 2687 set: function(obj_options) { 2688 Object.merge(this.data, obj_options); 2689 this.__module.onModelLoaded(); 2690 } 2691}; 2692 2693lsjs.addModelClass('formReviewerManager', obj_classdef_model); 2694 2695})(); 2696 2697 2698 2699 2700// < ###### MODELS ## 2701 2702// ###### VIEW ## > 2703 2704// < ###### VIEW ## 2705// 2706// ###### CONTROLLER ## > 2707/* 2708 * -- ACTIVATION: -- 2709 * 2710 * To activate this module, the following code has to be put in the app.js: 2711 * 2712 lsjs.__moduleHelpers.formReviewerManager.start({ 2713 el_domReference: el_domReference 2714 }); 2715 * 2716 * The el_domReference parameter is only required if this module initialization code is called
2716in a cajax_domUpdate event. 2717 * 2718 * 2719 * 2720 * -- FUNCTIONALITY AND USAGE: -- 2721 * 2722 * Add the following attribute to a DOM element to apply this module: 2723 * 2724 * data-lsjs-component="formReviewer" 2725 * 2726 * Add the following attribute to the DOM element to indicate that the form reviewer should initially close the form 2727 * unless there's a child DOM element with an ".error" class. 2728 * 2729 * data-misc-close-form-initially="1" 2730 * 2731 * TODO: Write a detailed functionality and usage description. Until then, the options models of the manager and 2732 * the instance submodule can be consulted for further information. 2733 * 2734 */ 2735 2736(function() { 2737 2738// ### ENTER MODULE NAME HERE ###### 2739var str_moduleName = 'formReviewerManager'; 2740// ################################# 2741 2742var obj_classdef = { 2743 start: function() { 2744 var els_toEnhance; 2745 /* 2746 * Look for elements to enrich with the lsjs-module and then 2747 * instantiate instances for each element found. 2748 */ 2749 if (this.__models.options.data.el_domReference !== undefined && typeOf(this.__models.options.data.el_domReference) === 'element') { 2750 els_toEnhance = this.__models.options.data.el_domReference.getElements(this.__models.options.data.str_selector); 2751 } else { 2752 els_toEnhance = $$(this.__models.options.data.str_selectors); 2753 } 2754 2755 Array.each(els_toEnhance, function(el_container) { 2756 /* -> 2757 * Make sure not to handle an element more than once 2758 */ 2759 if (!el_container.retrieve('alreadyHandledBy_' + str_moduleName)) { 2760 el_container.store('alreadyHandledBy_' + str_moduleName, true); 2761 } else { 2762 return; 2763 } 2764 /* 2765 * <- 2766 */ 2767 2768 el_container.addClass(this.__models.options.data.str_classToSetWhenModuleApplied); 2769 2770 lsjs.createModule({ 2771 __name: 'formReviewerInstance', 2772 __parentModule: this.__module, 2773 __useLoadingIndicator: false, 2774 __el_container: el_container 2775 }); 2776 }.bind(this)); 2777 } 2778}; 2779 2780lsjs.addControllerClass(str_moduleName, obj_classdef); 2781 2782lsjs.__moduleHelpers[str_moduleName] = { 2783 self: null, 2784 2785 start: function(obj_options) { 2786 this.self = lsjs.createModule({ 2787 __name: str_moduleName 2788 }); 2789 this.self.__models.options.set(obj_options); 2790 } 2791}; 2792 2793})(); 2794// < ###### CONTROLLER ## 2795// < #### MODULE ## 2796 2797 2798 2799 2800// #### MODULE ## > 2801// ###### TEMPLATES ## > 2802(function() { 2803 2804var obj_templates = { 2805 'main': function(arg) { 2806 var str_return = '<div data-lsjs-element="loadingText">' + arg.__models.lang.data.standardText + '</div>'; 2807 2808 // add template info in debug mode 2809 var str_return = '<!-- BEGIN LSJS TEMPLATE: modules/loadingIndicator/templates/main.html/main.html -->' + str_return + '<!-- END LSJS TEMPLATE: modules/loadingIndicator/templates/main.html/main.html -->'; 2810 2811 return str_return; 2812 } 2813}; 2814 2815lsjs.tpl.register(obj_templates, 'loadingIndicator'); 2816 2817})(); 2818// < ###### TEMPLATES ## 2819 2820// ###### MODELS ## > 2821(function() { 2822 2823var obj_classdef_model = { 2824 name: 'lang', 2825 2826 data: { 2827 standardText: 'Loading ...' 2828 }, 2829 2830 start: function() { 2831 this.__module.onModelLoaded(); 2832 } 2833}; 2834 2835lsjs.addModelClass('loadingIndicator', obj_classdef_model); 2836 2837})(); 2838 2839 2840 2841(function() { 2842 2843var obj_classdef_model = { 2844 name: 'loadingCounter', 2845 2846 data: { 2847 numLoading: 0 2848 }, 2849 2850 start: function() { 2851 this.__module.onModelLoaded(); 2852 }, 2853 2854 increase: function() { 2855 this.data.numLoading++; 2856 }, 2857 2858 decrease: function() { 2859 if (this.data.numLoading > 0) { 2860 this.data.numLoading--; 2861 } 2862 }, 2863 2864 getNumLoading: function() { 2865 return this.data.numLoading; 2866 } 2867}; 2868 2869lsjs.addModelClass('loadingIndicator', obj_classdef_model); 2870 2871})(); 2872 2873 2874 2875 2876// < ###### MODELS ## 2877 2878// ###### VIEW ## > 2879(function() { 2880 2881// ### ENTER MODULE NAME HERE ###### 2882var str_moduleName = 'loadingIndicator'; 2883// ################################# 2884 2885var obj_classdef = { 2886 2887 start: function() { 2888 this.tplReplace({ 2889 name: 'main' 2890 }); 2891 }, 2892 2893 show: function() { 2894 this.__el_container.addClass('show'); 2895 new Fx.Morph(this.__el_container, { 2896 duration: 100 2897 }) 2898 .start({ 2899 'opacity': 1 2900 }); 2901 }, 2902 2903 hide: function(bln_resetText) { 2904 new Fx.Morph(this.__el_container, { 2905 duration: 300, 2906 onComplete: function() { 2907 this.__el_container.removeClass('show'); 2908 if (bln_resetText) { 2909 this.resetText(); 2910 } 2911 }.bind(this) 2912 }) 2913 .start({ 2914 'opacity': 0 2915 }); 2916 }, 2917 2918 overwriteText: function(str_deviantText) { 2919 this.__autoElements.main.loadingText.setProperty('html', str_deviantText); 2920 }, 2921 2922 resetText: function() { 2923 this.__autoElements.main.loadingText.setProperty('html', this.__models.lang.data.standardText); 2924 } 2925}; 2926 2927lsjs.addViewClass(str_moduleName, obj_classdef); 2928 2929})(); 2930// < ###### VIEW ## 2931// 2932// ###### CONTROLLER ## > 2933(function() { 2934 2935// ### ENTER MODULE NAME HERE ###### 2936var str_moduleName = 'loadingIndicator';
2937// ################################# 2938 2939var obj_classdef = { 2940 start: function() { 2941 if (this.__view === undefined || this.__view === null) { 2942 console.error('The loading indicator\'s view is not available in its controller.js! Please make sure that controller.js is loaded after all the other module scripts (templates.js, view.js, model.js)'); 2943 } 2944 2945 lsjs[str_moduleName] = this.__module; 2946 window.addEvent('domready', function() { 2947 $$('body')[0].adopt(this.__module.__el_container); 2948 }.bind(this)); 2949 }, 2950 2951 show: function(str_deviantText) { 2952 if (str_deviantText !== undefined && str_deviantText !== null) { 2953 this.overwriteText(str_deviantText); 2954 } 2955 2956 this.__models.loadingCounter.increase(); 2957 if (this.__models.loadingCounter.getNumLoading() === 1) { 2958 this.__view.show(); 2959 } 2960 }, 2961 2962 hide: function(bln_resetText) { 2963 bln_resetText = bln_resetText !== undefined && bln_resetText; 2964 2965 this.__models.loadingCounter.decrease(); 2966 if (this.__models.loadingCounter.getNumLoading() === 0) { 2967 this.__view.hide(bln_resetText); 2968 } else if (bln_resetText) { 2969 this.resetText(); 2970 } 2971 }, 2972 2973 overwriteText: function(str_deviantText) { 2974 if (str_deviantText === undefined || str_deviantText === null) { 2975 console.info('no deviant text given as argument'); 2976 return; 2977 } 2978 2979 this.__view.overwriteText(str_deviantText); 2980 }, 2981 2982 resetText: function() { 2983 this.__view.resetText(); 2984 } 2985}; 2986 2987lsjs.addControllerClass(str_moduleName, obj_classdef); 2988 2989lsjs.createModule({ 2990 __name: str_moduleName 2991}); 2992 2993})(); 2994// < ###### CONTROLLER ## 2995// < #### MODULE ## 2996 2997 2998 2999 3000// #### MODULE ## > 3001// ###### TEMPLATES ## > 3002 3003// < ###### TEMPLATES ## 3004 3005// ###### MODELS ## > 3006(function() { 3007 3008var obj_classdef_model = { 3009 name: 'options', 3010 3011 data: {}, 3012 3013 start: function() { 3014 /* 3015 * Initializing the options in the data object with default values which 3016 * can later be overwritten when the "set" method is called with other options 3017 */ 3018 this.data = { 3019 var_naviSelector: null, 3020 int_offsetLeft: 0, 3021 int_offsetRight: 0, 3022 bln_onlyFirstLevel: false 3023 }; 3024 }, 3025 3026 set: function(obj_options) { 3027 Object.merge(this.data, obj_options); 3028 this.__module.onModelLoaded(); 3029 } 3030}; 3031 3032lsjs.addModelClass('maxWidthNavi', obj_classdef_model); 3033 3034})(); 3035 3036 3037 3038 3039// < ###### MODELS ## 3040 3041// ###### VIEW ## > 3042(function() { 3043 3044// ### ENTER MODULE NAME HERE ###### 3045 var str_moduleName = 'maxWidthNavi'; 3046// ################################# 3047 3048 var obj_classdef = { 3049 start: function() { 3050 this.__el_container = 3051 ( 3052 typeOf(this.__models.options.data.var_naviSelector) === 'element' 3053 || typeOf(this.__models.options.data.var_naviSelector) === 'null' 3054 ) 3055 ? this.__models.options.data.var_naviSelector 3056 : $$(this.__models.options.data.var_naviSelector)[0]; 3057 3058 if (typeOf(this.__el_container) !== 'element') { 3059 return; 3060 } 3061 3062 this.__el_container.addClass('useMaxWidthNavi'); 3063 this.calculateAndSetSize(); 3064 window.addEvent('resize', this.calculateAndSetSize.bind(this)); 3065 }, 3066 3067 calculateAndSetSize: function() { 3068 Array.each(this.__el_container.getElements('li').filter(this.__models.options.data.bln_onlyFirstLevel ? '.level_1 > .submenu' : '.submenu'), function(el_li) { 3069 var obj_li = el_li.getCoordinates(); 3070 var obj_nav = el_li.getParent('nav').getCoordinates(); 3071 var float_offsetLeft = obj_li.left - obj_nav.left; 3072 var float_offsetRight = obj_nav.right - obj_li.right; 3073 3074 el_li.getElement('> ul').setStyles({ 3075 'left': (Math.round(float_offsetLeft) + this.__models.options.data.int_offsetLeft) * -1, 3076 'right': (Math.round(float_offsetRight) + this.__models.options.data.int_offsetRight) * -1 3077 }); 3078 }.bind(this)); 3079 } 3080 }; 3081 3082 lsjs.addViewClass(str_moduleName, obj_classdef); 3083 3084})(); 3085// < ###### VIEW ## 3086// 3087// ###### CONTROLLER ## > 3088(function() { 3089 3090// ### ENTER MODULE NAME HERE ###### 3091var str_moduleName = 'maxWidthNavi'; 3092// ################################# 3093 3094var obj_classdef = { 3095 start: function() { 3096 } 3097}; 3098 3099lsjs.addControllerClass(str_moduleName, obj_classdef); 3100 3101lsjs.__moduleHelpers[str_moduleName] = { 3102 self: null, 3103 3104 start: function(obj_options) { 3105 this.self = lsjs.createModule({ 3106 __name: str_moduleName 3107 }); 3108 3109 this.self.__models.options.set(obj_options); 3110 } 3111}; 3112 3113}
3113)(); 3114// < ###### CONTROLLER ## 3115// < #### MODULE ## 3116 3117 3118 3119 3120// #### MODULE ## > 3121// ###### TEMPLATES ## > 3122(function() { 3123 3124var obj_templates = { 3125 'main': function(arg) { 3126 var str_return = '<div class="msgBoxInside">\ 3127 <div class="msg">' + arg.__models.options.data.str_msg + '</div>\ 3128 '; 3129 Object.each(arg.__models.options.data.obj_buttons, function(obj_buttonSettings, str_buttonLabel) { 3130 str_return += '\ 3131 <button data-lsjs-element="btn_' + str_buttonLabel + '" class="btn_' + str_buttonLabel + '">' + obj_buttonSettings.str_label + '</button>\ 3132 '; 3133 }); 3134 str_return += '\ 3135</div>'; 3136 3137 // add template info in debug mode 3138 var str_return = '<!-- BEGIN LSJS TEMPLATE: modules/messageBox/templates/main.html/main.html -->' + str_return + '<!-- END LSJS TEMPLATE: modules/messageBox/templates/main.html/main.html -->'; 3139 3140 return str_return; 3141 } 3142}; 3143 3144lsjs.tpl.register(obj_templates, 'messageBox'); 3145 3146})(); 3147// < ###### TEMPLATES ## 3148 3149// ###### MODELS ## > 3150(function() { 3151 3152var obj_classdef_model = { 3153 name: 'lang', 3154 3155 data: { 3156 str_standardMessage: 'Hi, I am the message box. Please give me a message to display!', 3157 str_defaultButtonText: 'OK' 3158 }, 3159 3160 start: function() { 3161 /* 3162 * Every model needs to call the "this.__module.onModelLoaded()" method 3163 * when its data is completely loaded and available or, since in some 3164 * cases data is loaded later, when the model is ready for the view 3165 * to be rendered. 3166 */ 3167 this.__module.onModelLoaded(); 3168 } 3169}; 3170 3171lsjs.addModelClass('messageBox', obj_classdef_model); 3172 3173})(); 3174 3175 3176 3177(function() { 3178 3179var obj_classdef_model = { 3180 name: 'options', 3181 3182 data: {}, 3183 3184 start: function() { 3185 /* 3186 * Initializing the options in the data object with default values which 3187 * can later be overwritten when the "set" method is called with other options 3188 */ 3189 this.data = { 3190 str_msg: this.__models.lang.data.str_standardMessage, 3191 obj_buttons: { 3192 default: { 3193 str_label: this.__models.lang.data.str_defaultButtonText, 3194 bln_closeOnClick: true 3195 } 3196 } 3197 }; 3198 }, 3199 3200 set: function(obj_options) { 3201 Object.merge(this.data, obj_options); 3202 this.__module.onModelLoaded(); 3203 } 3204}; 3205 3206lsjs.addModelClass('messageBox', obj_classdef_model); 3207 3208})(); 3209 3210 3211 3212 3213// < ###### MODELS ## 3214 3215// ###### VIEW ## > 3216(function() { 3217 3218// ### ENTER MODULE NAME HERE ###### 3219var str_moduleName = 'messageBox'; 3220// ################################# 3221 3222var obj_classdef = { 3223 start: function() { 3224 this.tplAdd({ 3225 name: 'main' 3226 }); 3227 3228 Object.each(this.__models.options.data.obj_buttons, function (obj_buttonSettings, str_buttonLabel) { 3229 this.__autoElements.main['btn_' + str_buttonLabel].addEvent('click', this.callCallbackFunction.bind(this, obj_buttonSettings)); 3230 }.bind(this)); 3231 3232 $$('body')[0].adopt(this.__el_container); 3233 }, 3234 3235 callCallbackFunction: function(obj_buttonSettings) { 3236 if (obj_buttonSettings.bln_closeOnClick === undefined || obj_buttonSettings.bln_closeOnClick) { 3237 var myFx = new Fx.Tween( 3238 this.__el_container, 3239 { 3240 duration: 200, 3241 link: 'cancel', 3242 property: 'opacity', 3243 onComplete: this.__module.remove.bind(this.__module) 3244 } 3245 ); 3246 3247 myFx.start(1,0); 3248 } 3249 3250 if (typeOf(obj_buttonSettings.func_callback) === 'function') { 3251 obj_buttonSettings.func_callback(); 3252 } 3253 } 3254}; 3255 3256lsjs.addViewClass(str_moduleName, obj_classdef); 3257 3258})(); 3259// < ###### VIEW ## 3260// 3261// ###### CONTROLLER ## > 3262(function() { 3263 3264// ### ENTER MODULE NAME HERE ###### 3265var str_moduleName = 'messageBox'; 3266// ################################# 3267 3268var obj_classdef = { 3269 start: function() { 3270 }, 3271 3272 setOptions: function(obj_options) { 3273 this.__models.options.set(obj_options); 3274 } 3275}; 3276 3277lsjs.addControllerClass(str_moduleName, obj_classdef); 3278 3279/* 3280 * Registering a specific instantiation method with lsjs that can (or rather should) 3281 * be used instead of manually calling "lsjs.createModule()" in order to open 3282 * a messageBox. Not every module needs to use this option but in case of standard 3283 * core modules it often is a good idea. 3284 */ 3285lsjs.__moduleHelpers[str_moduleName] = { 3286 open: function(obj_options) { 3287 lsjs.createModule({ 3288 __name: str_moduleName 3289 }).__controller.setOptions(obj_options); 3290 } 3291}; 3292 3293/* 3294 * When the message box is opened, people might try to close it by pressing the 3295 * enter or escape key. We don't provide that functionality yet but at least we 3296 * want to make sure that pressing these keys while the message box is opened 3297 * will not do anything else, like, for example, sending a form. 3298 */ 3299window.addEvent('keydown', function(event) { 3300 /* 3301 * Don't stop anything with the event if no message box is opened 3302 */ 3303 if (typeOf($$('.autoModuleContainer.messageBox')[0]) !== 'element') { 3304 return; 3305 } 3306 3307 switch (event.key) { 3308 case 'enter': // Enter key pressed 3309 case 'esc': // Escape key pressed 3310 event.stop(); 3311 break; 3312 } 3313}.bind(this)); 3314 3315})(); 3316// < ###### CONTROLLER ## 3317// < #### MODULE ## 3318 3319 3320 3321 3322// #### MODULE ## > 3323// ###### TEMPLATES ## > 3324 3325// < ###### TEMPLATES ## 3326 3327// ###### MODELS ## > 3328(function() { 3329 3330var obj_classdef_model = { 3331 name: 'status', 3332 3333 data: { 3334 autoplayStatus: false, 3335 stoppedOnMouseenter: false, 3336 int_currentTimeout: null, 3337 currentTabKey: 0, 3338 bln_firstElementHasBeenActivated: false 3339 }, 3340 3341 start: function() { 3342 this.__module.onModelLoaded(); 3343 }, 3344 3345 set_autoplayStatus: function(bln_status) { 3346 bln_status = bln_status !== undefined ? bln_status : false;
3347 this.writeData('autoplayStatus', bln_status); 3348 }, 3349 3350 set_stoppedOnMouseenter: function(bln_status) { 3351 bln_status = bln_status !== undefined ? bln_status : false; 3352 this.writeData('stoppedOnMouseenter', bln_status); 3353 }, 3354 3355 set_bln_firstElementHasBeenActivated: function(bln_firstElementHasBeenActivated) { 3356 bln_firstElementHasBeenActivated = bln_firstElementHasBeenActivated !== undefined ? bln_firstElementHasBeenActivated : false; 3357 this.writeData('bln_firstElementHasBeenActivated', bln_firstElementHasBeenActivated); 3358 }, 3359 3360 set_currentTabKey: function(currentTabKey) { 3361 this.writeData('currentTabKey', currentTabKey); 3362 }, 3363 3364 set_currentTimeout: function(int_timeout) { 3365 int_timeout = int_timeout ? int_timeout : null; 3366 this.writeData('int_currentTimeout', int_timeout); 3367 } 3368}; 3369 3370lsjs.addModelClass('navtabInstance', obj_classdef_model); 3371 3372})(); 3373 3374 3375 3376 3377// < ###### MODELS ## 3378 3379// ###### VIEW ## > 3380/** 3381 * This class creates a tab navigation/slideshow from a simple html construction. 3382 * 3383 * Example: 3384 <div data-lsjs-component="navtab"> 3385 <div data-lsjs-element="navtabLabelContainer"> 3386 <div data-lsjs-element="navtabLabel" class="start">TAB 01</div> 3387 <div data-lsjs-element="navtabLabel" >TAB 02</div> 3388 <div data-lsjs-element="navtabLabel" >TAB 03</div> 3389 </div> 3390 <div data-lsjs-element="navtabContentContainer"> 3391 <div data-lsjs-element="navtabContent" class="noJsDefault">CONTENT TAB 01</div> 3392 <div data-lsjs-element="navtabContent" >CONTENT TAB 02</div> 3393 <div data-lsjs-element="navtabContent" >CONTENT TAB 03</div> 3394 </div> 3395 <div data-lsjs-element="navtabNavigation" class="navigation"> 3396 <div data-lsjs-element="btn_prev" class="prev">PREV</div> 3397 <div data-lsjs-element="btn_next" class="next">NEXT</div> 3398 <div data-lsjs-element="btn_play" class="play">PLAY</div> 3399 <div data-lsjs-element="btn_stop" class="stop">STOP</div> 3400 </div> 3401 </div> 3402 3403 * FIXME: Add more detailed description 3404 */ 3405 3406(function() { 3407 3408// ### ENTER MODULE NAME HERE ###### 3409var str_moduleName = 'navtabInstance'; 3410// ################################# 3411 3412var obj_classdef = { 3413 start: function() { 3414 this.registerElements(this.__el_container, 'main', true); 3415 3416// console.log(this.__module.__parentModule.__models.options.data); 3417// console.log(this.__autoElements); 3418 3419 if (this.__autoElements.main.navtabContent.length !== this.__autoElements.main.navtabLabel.length) { 3420 console.error('number of label elements does not match the number of content elements'); 3421 return; 3422 } 3423 3424 this.__models.status.set_autoplayStatus(this.__module.__parentModule.__models.options.data.defaultAutoplayStatus); 3425 3426 this.showHiddenElements(); 3427 this.addNavigationEvents(); 3428 this.prepareContentElements(); 3429 this.prepareLabelElements(); 3430 this.__controller.activateStartLabel(); 3431 }, 3432 3433 prepareLabelElements: function() { 3434 /* 3435 * Get the corresponding content div for each label div 3436 * and add the click event to each label div 3437 */ 3438 Array.each(this.__autoElements.main.navtabLabel, function(el_label, key) { 3439 el_label.store('tabKey', key); 3440 3441 var el_correspondingContent = this.__autoElements.main.navtabContent[key]; 3442 3443 el_label.activate = function() { 3444 window.clearTimeout(this.__models.status.data.int_currentTimeout); 3445 3446 if (this.__models.status.data.autoplayStatus) { 3447 this.__models.status.set_currentTimeout( 3448 window.setTimeout( 3449 function() { 3450 this.__controller.gotoNext(); 3451 }.bind(this), 3452 this.__module.__parentModule.__models.options.data.autoplayDelay 3453 ) 3454 ); 3455 } 3456 3457 // do nothing more if the label is already active 3458 if (el_label.hasClass('active')) { 3459 return; 3460 } 3461 3462 /* 3463 * Remove 'active' classes from all labels and set it for the 3464 * currently clicked label 3465 */ 3466 this.__autoElements.main.navtabLabel.removeClass('active'); 3467 el_label.addClass('active'); 3468 3469 // store the tabKey of the currently clicked label 3470 this.__models.status.set_currentTabKey(el_label.retrieve('tabKey')); 3471 3472 /* 3473 * Walk through all contents and remove the 'active' class wherever it is present and 3474 * call the content's tweenOut function 3475 */ 3476 Array.each(this.__autoElements.main.navtabContent, function(el_content) { 3477 if (el_content.hasClass('active')) { 3478 el_content.removeClass('active'); 3479 el_content.tweenOut(); 3480 } 3481 }); 3482 3483 /* 3484 * add the 'active' class for the content corresponding with the currently clicked label 3485 * and call the content's tweenIn function 3486 */ 3487 el_correspondingContent.addClass('active'); 3488
3489 if (!this.__models.status.data.bln_firstElementHasBeenActivated) { 3490 this.__models.status.set_bln_firstElementHasBeenActivated(true); 3491 el_correspondingContent.removeClass(this.__module.__parentModule.__models.options.data.cssClassForHide); 3492 el_correspondingContent.addClass(this.__module.__parentModule.__models.options.data.cssClassForShow); 3493 } else { 3494 el_correspondingContent.tweenIn(); 3495 } 3496 }.bind(this); 3497 3498 el_label.addEvent('click', function() { 3499 this.__controller.stopAutoplay(); 3500 el_label.activate(); 3501 }.bind(this)); 3502 }.bind(this)); 3503 }, 3504 3505 prepareContentElements: function() { 3506 this.__autoElements.main.navtabContent.addClass(this.__module.__parentModule.__models.options.data.cssClassForHide); 3507 3508 /* 3509 * Store the tween effect for each content div and create the tweenIn and tweenOut functions 3510 */ 3511 Array.each(this.__autoElements.main.navtabContent, function(el_content) { 3512 var obj_tweenInOptions, 3513 obj_tweenOutOptions; 3514 3515 el_content.store('obj_originalPositionValues', { 3516 'position': el_content.getStyle('position'), 3517 'top': el_content.getStyle('top'), 3518 'z-index': el_content.getStyle('z-index') 3519 }); 3520 3521 el_content.store('cssClassForShow', this.__module.__parentModule.__models.options.data.cssClassForShow); 3522 el_content.store('cssClassForHide', this.__module.__parentModule.__models.options.data.cssClassForHide); 3523 3524 /* 3525 * add the onComplete function for the tweenIn because if the tweenIn transition 3526 * is finished, the content div's position properties need to be set to the 3527 * original values 3528 */ 3529 obj_tweenInOptions = this.__module.__parentModule.__models.options.data.tweenInOptions; 3530 3531 obj_tweenInOptions.onComplete = function() { 3532 el_content.setStyles(el_content.retrieve('obj_originalPositionValues')); 3533 }; 3534 3535 el_content.store('fxTweenIn', new Fx.Morph( 3536 el_content, 3537 obj_tweenInOptions 3538 )); 3539 3540 3541 /* 3542 * add the onComplete function for the tweenOut because if the tweenOut transition 3543 * is finished, the content div's display property needs to be set to 'none' 3544 */ 3545 obj_tweenOutOptions = this.__module.__parentModule.__models.options.data.tweenOutOptions; 3546 3547 obj_tweenOutOptions.onComplete = function() { 3548 el_content.setStyle('display', 'none'); 3549 }; 3550 3551 el_content.store('fxTweenOut', new Fx.Morph( 3552 el_content, 3553 obj_tweenOutOptions 3554 )); 3555 3556 el_content.tweenIn = this.tweenIn; 3557 el_content.tweenOut = this.tweenOut; 3558 3559 el_content.addEvent('mouseenter', function() { 3560 if (this.__module.__parentModule.__models.options.data.stopAutoplayOnMouseenter) { 3561 if (this.__models.status.data.autoplayStatus) { 3562 this.__models.status.set_stoppedOnMouseenter(true); 3563 this.__controller.stopAutoplay(); 3564 } 3565 } 3566 }.bind(this)); 3567 3568 el_content.addEvent('mouseleave', function() { 3569 if (this.__module.__parentModule.__models.options.data.startAutoplayOnMouseleave) { 3570 if (this.__models.status.data.stoppedOnMouseenter) { 3571 this.__models.status.set_stoppedOnMouseenter(false); 3572 this.__controller.startAutoplay(); 3573 } 3574 } 3575 }.bind(this)); 3576 }.bind(this)); 3577 }, 3578 3579 addNavigationEvents: function() { 3580 if (this.__autoElements.main.btn_prev) { 3581 this.__autoElements.main.btn_prev.addEvent('click', this.__controller.gotoPrev.bind(this.__controller)); 3582 } 3583 if (this.__autoElements.main.btn_next) { 3584 this.__autoElements.main.btn_next.addEvent('click', this.__controller.gotoNext.bind(this.__controller, true)); 3585 } 3586 if (this.__autoElements.main.btn_play) { 3587 this.__autoElements.main.btn_play.addEvent('click', this.__controller.startAutoplay.bind(this.__controller)); 3588 if (this.__module.__parentModule.__models.options.data.defaultAutoplayStatus) { 3589 this.__autoElements.main.btn_play.addClass('active'); 3590 } 3591 } 3592 if (this.__autoElements.main.btn_stop) { 3593 this.__autoElements.main.btn_stop.addEvent('click', this.__controller.stopAutoplay.bind(this.__controller)); 3594 if (!this.__module.__parentModule.__models.options.data.defaultAutoplayStatus) { 3595 this.__autoElements.main.btn_stop.addClass('active'); 3596 } 3597 } 3598 }, 3599 3600 /* 3601 * Show elements that are hidden by default because they must not be 3602 * displayed without js. 3603 */ 3604 showHiddenElements: function() { 3605 if (this.__autoElements.main.navtabNavigation) { 3606 this.__autoElements.main.navtabNavigation.addClass('visible'); 3607 } 3608 3609 if (this.__autoElements.main.navtabLabelContainer) { 3610 this.__autoElements.main.navtabLabelContainer.addClass('visible'); 3611 } 3612 }, 3613 3614 tweenIn: function() { 3615 // first, make the content visible 3616 this.setStyles({ 3617 'display': 'block' 3618 }); 3619 3620 /* 3621 * Set the absolute position if the element originally wasn't positioned 3622 * absolutely. This is necessary because the elements morphing in and out 3623 * should overlay each other. 3624 */ 3625 if (this.retrieve('obj_originalPositionValues').position !== 'absolute') { 3626 this.setStyles({ 3627 'position': 'absolute', 3628 'top': '0' 3629 }); 3630 } 3631 3632 this.setStyles({ 3633 'z-index': '1000' 3634 }); 3635 3636 /* 3637 * If a tweenOut transition is already/still running, cancel it because 3638 * otherwise its onComplete function will be called in the end and hide 3639 * the content that should be visible 3640 */ 3641 if (this.retrieve('fxTweenOut').isRunning()) { 3642 this.retrieve('fxTweenOut').cancel(); 3643 } 3644 3645 /* 3646 * Start the transition that has been stored in the content element 3647 */
3648 this.retrieve('fxTweenIn').start('.' + this.retrieve('cssClassForShow')); 3649 }, 3650 3651 tweenOut: function() { 3652 /* 3653 * Start the transition that has been stored in the content element 3654 */ 3655 this.retrieve('fxTweenOut').start('.' + this.retrieve('cssClassForHide')); 3656 } 3657}; 3658 3659lsjs.addViewClass(str_moduleName, obj_classdef); 3660 3661})(); 3662// < ###### VIEW ## 3663// 3664// ###### CONTROLLER ## > 3665(function() { 3666 3667// ### ENTER MODULE NAME HERE ###### 3668var str_moduleName = 'navtabInstance'; 3669// ################################# 3670 3671var obj_classdef = { 3672 start: function() { 3673 }, 3674 3675 activateStartLabel: function() { 3676 Array.each(this.__view.__autoElements.main.navtabLabel.filter('.start'), function(el_startLabel) { 3677 el_startLabel.activate(); 3678 }); 3679 }, 3680 3681 gotoPrev: function() { 3682 var numTabs = this.__view.__autoElements.main.navtabLabel.length; 3683 var currentTabKey = this.__models.status.data.currentTabKey; 3684 var newTabKey = currentTabKey - 1; 3685 3686 if (newTabKey < 0) { 3687 newTabKey = numTabs - 1; 3688 } 3689 3690 this.__view.__autoElements.main.navtabLabel[newTabKey].fireEvent('click'); 3691 }, 3692 3693 gotoNext: function(bln_useClick) { 3694 bln_useClick = bln_useClick != undefined ? bln_useClick : false; 3695 3696 var numTabs, 3697 currentTabKey, 3698 newTabKey; 3699 3700 numTabs = this.__view.__autoElements.main.navtabLabel.length; 3701 3702 if (!this.__module.__parentModule.__models.options.data.autoplayRandomOrder || bln_useClick) { 3703 currentTabKey = this.__models.status.data.currentTabKey; 3704 newTabKey = currentTabKey + 1; 3705 if (newTabKey > numTabs - 1) { 3706 newTabKey = 0; 3707 } 3708 } else { 3709 newTabKey = Number.random(0, numTabs - 1); 3710 } 3711 3712 /* 3713 * If the gotoNext function is called by clicking on the next button, the autoplay 3714 * has to stop, i.e. a real click on the next label has to be performed because 3715 * this stops the autoplay, whereas just activating the next tab doesn't. 3716 */ 3717 if (bln_useClick) { 3718 this.__view.__autoElements.main.navtabLabel[newTabKey].fireEvent('click'); 3719 } else { 3720 this.__view.__autoElements.main.navtabLabel[newTabKey].activate(); 3721 } 3722 }, 3723 3724 startAutoplay: function() { 3725 this.__models.status.set_autoplayStatus(true); 3726 3727 this.__view.__autoElements.main.navtabLabel[this.__models.status.data.currentTabKey].activate(); 3728 3729 if (this.__view.__autoElements.main.btn_play) { 3730 this.__view.__autoElements.main.btn_play.addClass('active'); 3731 } 3732 if (this.__view.__autoElements.main.btn_stop) { 3733 this.__view.__autoElements.main.btn_stop.removeClass('active'); 3734 } 3735 }, 3736 3737 stopAutoplay: function() { 3738 window.clearTimeout(this.__models.status.data.int_currentTimeout); 3739 3740 this.__models.status.set_autoplayStatus(false); 3741 3742 if (this.__view.__autoElements.main.btn_play) { 3743 this.__view.__autoElements.main.btn_play.removeClass('active'); 3744 } 3745 if (this.__view.__autoElements.main.btn_stop) { 3746 this.__view.__autoElements.main.btn_stop.addClass('active'); 3747 } 3748 } 3749}; 3750 3751lsjs.addControllerClass(str_moduleName, obj_classdef); 3752 3753})(); 3754// < ###### CONTROLLER ## 3755// < #### MODULE ## 3756 3757 3758 3759 3760// #### MODULE ## > 3761// ###### TEMPLATES ## > 3762 3763// < ###### TEMPLATES ## 3764 3765// ###### MODELS ## > 3766(function() { 3767 3768var obj_classdef_model = { 3769 name: 'options', 3770 3771 data: {}, 3772 3773 start: function() { 3774 /* 3775 * Initializing the options in the data object with default values which 3776 * can later be overwritten when the "set" method is called with other options 3777 */ 3778 this.data = { 3779 el_domReference: null, 3780 navtabContainerSelector: '[data-lsjs-component~="navtab"]', // the selector for the dom element containing the navtab 3781 defaultAutoplayStatus: false, // true if the autoplay should start automatically 3782 autoplayDelay: 2000, // the time between to tabs in autoplay mode (milliseconds) 3783 autoplayRandomOrder: false, // use a random tab order in autoplay mode 3784 3785 stopAutoplayOnMouseenter: false, // does just what the option name suggests 3786 startAutoplayOnMouseleave: false, // does just what the option name suggests 3787 3788 tweenOutOptions: { // the mootools Fx options (see http://mootools.net/docs/core/Fx/Fx) 3789 transition: Fx.Transitions.Quad.easeIn, 3790 duration: 1000 3791 }, 3792 3793 tweenInOptions: { // the mootools Fx options (see http://mootools.net/docs/core/Fx/Fx) 3794 transition: Fx.Transitions.Quad.easeIn, 3795 duration: 1000 3796 }, 3797 3798 cssClassForShow: 'navtabContentShow', 3799 cssClassForHide: 'navtabContentHide' 3800 }; 3801 }, 3802 3803 set: function(obj_options) { 3804 Object.merge(this.data, obj_options); 3805 this.__module.onModelLoaded(); 3806 } 3807}; 3808 3809lsjs.addModelClass('navtabManager', obj_classdef_model); 3810 3811})(); 3812 3813 3814 3815 3816// < ###### MODELS ## 3817 3818// ###### VIEW ## > 3819 3820// < ###### VIEW ## 3821// 3822// ###### CONTROLLER ## > 3823(function() { 3824 3825// ### ENTER MODULE NAME HERE ###### 3826var str_moduleName = 'navtabManager'; 3827// ################################# 3828 3829var obj_classdef = { 3830 start: function() { 3831 var els_toEnhance; 3832 /* 3833 * Look for navtabs to enrich with the lsjs-module and then 3834 * instantiate variantSelectorInstance for each variant-selector found. 3835 */ 3836 if (this.__models.options.data.el_domReference !== undefined && typeOf(this.__models.options.data.el_domReference) === 'element') { 3837 els_toEnhance = this.__models.options.data.el_domReference.getElements(this.__models.options.data.navtabContainerSelector); 3838 } else { 3839 els_toEnhance = $$(this.__models.options.data.navtabContainerSelector); 3840 } 3841 3842 Array.each(els_toEnhance, function(el_container) { 3843 /* -> 3844 * Make sure not to handle an element more than once 3845 */ 3846 if (!el_container.retrieve('alreadyHandledBy_' + str_moduleName)) { 3847 el_container.store('alreadyHandledBy_' + str_moduleName, true); 3848 } else { 3849 return; 3850 } 3851 /* 3852 * <- 3853 */ 3854 3855 lsjs.createModule({ 3856 __name: 'navtabInstance', 3857 __parentModule: this.__module, 3858 __useLoadingIndicator: false, 3859 __el_container: el_container 3860 }); 3861 }.bind(this)); 3862 } 3863}; 3864 3865lsjs.addControllerClass(str_moduleName, obj_classdef); 3866 3867lsjs.__moduleHelpers[str_moduleName] = { 3868 self: null, 3869 3870 start: function(obj_options) { 3871 this.self = lsjs.createModule({ 3872 __name: str_moduleName 3873 }); 3874 this.self.__models.options.set(obj_options); 3875 } 3876}; 3877 3878}
3878)(); 3879// < ###### CONTROLLER ## 3880// < #### MODULE ## 3881 3882 3883 3884 3885// #### MODULE ## > 3886// ###### TEMPLATES ## > 3887 3888// < ###### TEMPLATES ## 3889 3890// ###### MODELS ## > 3891(function() { 3892 3893var obj_classdef_model = { 3894 name: 'options', 3895 3896 data: {}, 3897 3898 start: function() { 3899 /* 3900 * Initializing the options in the data object with default values which 3901 * can later be overwritten when the "set" method is called with other options 3902 */ 3903 this.data = { 3904 str_togglerSelector: '#lsOcNaviToggler', 3905 str_naviSelector: '#lsOcNavi', 3906 str_contentSelector: '' 3907 }; 3908 }, 3909 3910 set: function(obj_options) { 3911 Object.merge(this.data, obj_options); 3912 this.__module.onModelLoaded(); 3913 } 3914}; 3915 3916lsjs.addModelClass('ocNavi', obj_classdef_model); 3917 3918})(); 3919 3920 3921 3922 3923// < ###### MODELS ## 3924 3925// ###### VIEW ## > 3926(function() { 3927 3928// ### ENTER MODULE NAME HERE ###### 3929var str_moduleName = 'ocNavi'; 3930// ################################# 3931 3932var obj_classdef = { 3933 el_toggler: null, 3934 el_navi: null, 3935 el_body: null, 3936 el_content: null, 3937 3938 start: function() { 3939 this.el_body = $$('body')[0]; 3940 3941 this.el_navi = $$(this.__models.options.data.str_naviSelector)[0]; 3942 if (typeOf(this.el_navi) !== 'element') { 3943 this.el_navi = new Element('div' + this.__models.options.data.str_naviSelector); 3944 this.el_navi.inject(this.el_body, 'top'); 3945 } 3946 3947 this.el_toggler = $$(this.__models.options.data.str_togglerSelector)[0]; 3948 if (typeOf(this.el_toggler) !== 'element') { 3949 this.el_toggler = new Element('div' + this.__models.options.data.str_togglerSelector); 3950 this.el_toggler.inject(this.el_body, 'top'); 3951 } 3952 3953 if (this.__models.options.data.str_contentSelector) { 3954 this.el_content = $$(this.__models.options.data.str_contentSelector)[0]; 3955 if (typeOf(this.el_content) === 'element') { 3956 this.el_navi.adopt(this.el_content); 3957 this.el_content.setStyle('display', 'block'); 3958 } 3959 } 3960 3961 if ( 3962 typeOf(this.el_toggler) !== 'element' 3963 || typeOf(this.el_navi) !== 'element' 3964 || typeOf(this.el_body) !== 'element' 3965 ) { 3966 return; 3967 } 3968 3969 this.el_body.addClass('useOcNavi'); 3970 3971 lsjs.__moduleHelpers.unfold.start({ 3972 bln_automaticallyCreateResizeBox: false, 3973 str_initialDisplayType: 'block', 3974 str_initialToggleStatus: 'closed', 3975 var_togglerSelector: this.el_toggler, 3976 var_contentBoxSelector: this.el_navi, 3977 var_wrapperSelector: this.el_body, 3978 str_animationMode: 'margin-top', 3979 str_classOpen: 'lsOcNaviOpen', 3980 str_classClosed: 'lsOcNaviClosed', 3981 str_classRunning: 'lsOcNaviRunning', 3982 str_classUseLsUnfold: '', 3983 3984 obj_morphOptions: { 3985 'duration': 600 3986 } 3987 }); 3988 3989 lsjs.__moduleHelpers.foldedNavi.start({ 3990 var_naviSelector: this.el_navi 3991 }); 3992 } 3993}; 3994 3995lsjs.addViewClass(str_moduleName, obj_classdef); 3996 3997})(); 3998// < ###### VIEW ## 3999// 4000// ###### CONTROLLER ## > 4001(function() { 4002 4003// ### ENTER MODULE NAME HERE ###### 4004var str_moduleName = 'ocNavi'; 4005// ################################# 4006 4007var obj_classdef = { 4008 start: function() { 4009 } 4010}; 4011 4012lsjs.addControllerClass(str_moduleName, obj_classdef); 4013 4014lsjs.__moduleHelpers[str_moduleName] = { 4015 self: null, 4016 4017 start: function(obj_options) { 4018 this.self = lsjs.createModule({ 4019 __name: str_moduleName 4020 }); 4021 4022 this.self.__models.options.set(obj_options); 4023 } 4024}; 4025 4026})(); 4027// < ###### CONTROLLER ## 4028// < #### MODULE ## 4029 4030 4031 4032 4033// #### MODULE ## > 4034// ###### TEMPLATES ## > 4035 4036// < ###### TEMPLATES ## 4037 4038// ###### MODELS ## > 4039 4040// < ###### MODELS ## 4041 4042// ###### VIEW ## > 4043 4044// < ###### VIEW ## 4045// 4046// ###### CONTROLLER ## > 4047(function() { 4048 4049// ### ENTER MODULE NAME HERE ###### 4050var str_moduleName = 'scrollAssistant'; 4051// ################################# 4052 4053var obj_classdef = { 4054 start: function() { 4055 lsjs[str_moduleName] = this.__module; 4056 4057 window.addEvent('domready', function() { 4058 Array.each($$('.onClickToScrollPosition'), function(el) { 4059 el.addEvent('click', function() { 4060 this.getLSFEScrollOffset(); 4061 }.bind(this)); 4062 }.bind(this)); 4063 }.bind(this)); 4064 4065 window.addEvent('load', function() { 4066 this.scrollToLSFEOffset(); 4067 }.bind(this)); 4068 }, 4069 4070 /** 4071 * Saves the current vertical page offset in a cookie which causes 4072 * the page to scroll to that vertical position on the next page load. 4073 */ 4074 getLSFEScrollOffset: function() { 4075 Cookie.write('LSFE_PAGE_OFFSET', window.getScroll().y); 4076 }, 4077 4078 /** 4079 * This function scrolls to a vertical page offset if it has been 4080 * saved previously in a cookie 4081 */ 4082 scrollToLSFEOffset: function() { 4083 var LSFE_PAGE_OFFSET = Cookie.read('LSFE_PAGE_OFFSET'); 4084 if (LSFE_PAGE_OFFSET === undefined || LSFE_PAGE_OFFSET === null) { 4085 return; 4086 } 4087 window.scrollTo(null, parseInt(LSFE_PAGE_OFFSET)); 4088 Cookie.dispose('LSFE_PAGE_OFFSET'); 4089 } 4090}; 4091 4092lsjs.addControllerClass(str_moduleName, obj_classdef); 4093
4094lsjs.createModule({ 4095 __name: str_moduleName 4096}); 4097 4098})(); 4099// < ###### CONTROLLER ## 4100// < #### MODULE ## 4101 4102 4103 4104 4105// #### MODULE ## > 4106// ###### TEMPLATES ## > 4107 4108// < ###### TEMPLATES ## 4109 4110// ###### MODELS ## > 4111(function() { 4112 4113var obj_classdef_model = { 4114 name: 'options', 4115 4116 data: { 4117 arr_reactions: [] 4118 }, 4119 4120 start: function() { 4121 }, 4122 4123 set: function(obj_options) { 4124 Object.merge(this.data, obj_options); 4125 this.__module.onModelLoaded(); 4126 } 4127}; 4128 4129lsjs.addModelClass('scrollReactor', obj_classdef_model); 4130 4131})(); 4132 4133 4134 4135 4136// < ###### MODELS ## 4137 4138// ###### VIEW ## > 4139(function() { 4140 4141// ### ENTER MODULE NAME HERE ###### 4142var str_moduleName = 'scrollReactor'; 4143// ################################# 4144 4145var obj_classdef = { 4146 arr_preparedReactions: [], 4147 4148 str_currentDirection: 'down', 4149 4150 int_currentScrollY: 0, 4151 int_lastScrollY: 0, 4152 4153 obj_currentReaction: null, 4154 4155 obj_defaultReaction: { 4156 func_reactionCrossingTop: function() { 4157 console.log('default reaction crossing top ' + this.str_currentDirection); 4158 }, 4159 func_reactionCrossingBottom: function() { 4160 console.log('default reaction crossing bottom ' + this.str_currentDirection); 4161 }, 4162 func_reactionBetween: function() { 4163 console.log('default reaction ' + this.str_currentDirection + ' between ' + this.obj_currentReaction.int_scrollPositionTop + ' and ' + this.obj_currentReaction.int_scrollPositionBottom); 4164 }, 4165 int_scrollPositionTop: 100, 4166 int_scrollPositionBottom: 300, 4167 obj_reactOn: { 4168 str_crossingTop: 'both', // up, down, both, none 4169 str_crossingBottom: 'both', // up, down, both, none 4170 str_between: 'both' // up, down, both, none 4171 }, 4172 4173 /* 4174 * Defines how many times the reactio for a specific scroll event will be performed. 4175 * 0 means no limit at all; any integer value will be counted down to zero and then the 4176 * corresponding obj_reactOn value will be set to 'none' 4177 */ 4178 obj_reactTimes: { 4179 int_crossingTop: 0, 4180 int_crossingBottom: 0, 4181 int_between: 0 4182 }, 4183 4184 /* 4185 * If the elements involved in the reaction are stated here, 4186 * the reaction will be dismissed if none of the elements are 4187 * present during initialization 4188 */ 4189 arr_elementSelectorsInvolved: [], 4190 4191 bln_aboveScrollPositionTop: true, 4192 bln_aboveScrollPositionBottom: true, 4193 bln_crossedScrollPositionTop: false, 4194 bln_crossedScrollPositionBottom: false 4195 }, 4196 4197 start: function() { 4198 this.initializeReactor(); 4199 window.addEvent('resize', this.initializeReactor.bind(this)); 4200 window.addEvent('scroll', this.react.bind(this)); 4201 }, 4202 4203 initializeReactor: function() { 4204 this.prepareReactions(); 4205 4206 // console.log('this.arr_preparedReactions', this.arr_preparedReactions); 4207 // console.log('this.__models.options.data.arr_reactions', this.__models.options.data.arr_reactions); 4208 4209 /* 4210 * Make sure to call the react function once in the beginning even if no scroll event has been 4211 * triggered because if a page is being opened by an external link using an anchor, the page is being 4212 * loaded in an already scrolled position but no scroll event is being triggered. 4213 */ 4214 this.react(); 4215 }, 4216 4217 prepareReactions: function() { 4218 this.arr_preparedReactions = []; 4219 4220 /* 4221 * -> 4222 * Only clone and further prepare reactions whose involved elements exist 4223 */ 4224 Array.each(this.__models.options.data.arr_reactions, function(obj_reaction) { 4225 var obj_defaultReaction = Object.clone(this.obj_defaultReaction); 4226 obj_reaction = Object.merge(obj_defaultReaction, obj_reaction); 4227 4228 if (!this.check_involvedElementsExist(obj_reaction)) { 4229 return; 4230 } 4231 4232 this.arr_preparedReactions.push(obj_reaction); 4233 }.bind(this)); 4234 /* 4235 * <- 4236 */ 4237 4238 Array.each(this.arr_preparedReactions, function(obj_reaction) { 4239 obj_reaction.func_reactionCrossingTop = obj_reaction.func_reactionCrossingTop.bind(this); 4240 obj_reaction.func_reactionCrossingBottom = obj_reaction.func_reactionCrossingBottom.bind(this); 4241 obj_reaction.func_reactionBetween = obj_reaction.func_reactionBetween.bind(this); 4242 }.bind(this)); 4243 }, 4244 4245 check_involvedElementsExist: function(obj_reaction) { 4246 /* 4247 * If no selectors for involved elements are given, we assume that everything that's required for the 4248 * reaction exists and therefore return true. 4249 */ 4250 if (obj_reaction.arr_elementSelectorsInvolved.length == 0) { 4251 return true; 4252 } 4253 4254 var bln_elementsExist = false;
4255 4256 Array.some(obj_reaction.arr_elementSelectorsInvolved, function(str_selector) { 4257 var els_elementsInvolved = $$(str_selector); 4258 4259 if (typeOf(els_elementsInvolved) === 'elements' && els_elementsInvolved.length > 0) { 4260 bln_elementsExist = true; 4261 return true; 4262 } 4263 }); 4264 4265 return bln_elementsExist; 4266 }, 4267 4268 react: function() { 4269 this.determineScrollPositionAndDirection(); 4270 4271 // console.log('this.str_currentDirection', this.str_currentDirection); 4272 4273 Array.each(this.arr_preparedReactions, function (obj_reaction) { 4274 this.determineReactionRelatedScrollPosition(obj_reaction); 4275 this.processReaction(obj_reaction); 4276 }.bind(this)); 4277 }, 4278 4279 determineReactionRelatedScrollPosition: function(obj_reaction) { 4280 if (obj_reaction.obj_reactOn.str_crossingTop !== 'none' || obj_reaction.obj_reactOn.str_between !== 'none') { 4281 var bln_aboveScrollPositionTop = this.int_currentScrollY < obj_reaction.int_scrollPositionTop; 4282 if (obj_reaction.bln_aboveScrollPositionTop !== bln_aboveScrollPositionTop) { 4283 obj_reaction.bln_crossedScrollPositionTop = true; 4284 obj_reaction.bln_aboveScrollPositionTop = bln_aboveScrollPositionTop; 4285 } 4286 } 4287 4288 if (obj_reaction.obj_reactOn.str_crossingBottom !== 'none' || obj_reaction.obj_reactOn.str_between !== 'none') { 4289 var bln_aboveScrollPositionBottom = this.int_currentScrollY < obj_reaction.int_scrollPositionBottom; 4290 if (obj_reaction.bln_aboveScrollPositionBottom !== bln_aboveScrollPositionBottom) { 4291 obj_reaction.bln_crossedScrollPositionBottom = true; 4292 obj_reaction.bln_aboveScrollPositionBottom = bln_aboveScrollPositionBottom; 4293 } 4294 } 4295 }, 4296 4297 determineScrollPositionAndDirection: function() { 4298 this.int_currentScrollY = window.getScroll().y; 4299 if (this.int_currentScrollY > this.int_lastScrollY) { 4300 this.str_currentDirection = 'down'; 4301 } else if (this.int_currentScrollY < this.int_lastScrollY) { 4302 this.str_currentDirection = 'up'; 4303 } 4304 this.int_lastScrollY = this.int_currentScrollY; 4305 }, 4306 4307 processReaction: function(obj_reaction) { 4308 this.obj_currentReaction = obj_reaction; 4309 4310 // console.log(obj_reaction); 4311 4312 if (obj_reaction.bln_crossedScrollPositionTop) { 4313 if (obj_reaction.obj_reactOn.str_crossingTop === 'both' || obj_reaction.obj_reactOn.str_crossingTop === this.str_currentDirection) { 4314 if (obj_reaction.obj_reactTimes.int_crossingTop > 0) { 4315 obj_reaction.obj_reactTimes.int_crossingTop--; 4316 if (obj_reaction.obj_reactTimes.int_crossingTop === 0) { 4317 obj_reaction.obj_reactOn.str_crossingTop = 'none'; 4318 } 4319 } 4320 obj_reaction.func_reactionCrossingTop(); 4321 } 4322 } 4323 4324 if (obj_reaction.bln_crossedScrollPositionBottom) { 4325 if (obj_reaction.obj_reactOn.str_crossingBottom === 'both' || obj_reaction.obj_reactOn.str_crossingBottom === this.str_currentDirection) { 4326 if (obj_reaction.obj_reactTimes.int_crossingBottom > 0) { 4327 obj_reaction.obj_reactTimes.int_crossingBottom--; 4328 if (obj_reaction.obj_reactTimes.int_crossingBottom === 0) { 4329 obj_reaction.obj_reactOn.str_crossingBottom = 'none'; 4330 } 4331 } 4332 obj_reaction.func_reactionCrossingBottom(); 4333 } 4334 } 4335 4336 if (!obj_reaction.bln_aboveScrollPositionTop && obj_reaction.bln_aboveScrollPositionBottom) { 4337 if (obj_reaction.obj_reactOn.str_between === 'both' || obj_reaction.obj_reactOn.str_between === this.str_currentDirection) { 4338 if (obj_reaction.obj_reactTimes.int_between> 0) { 4339 obj_reaction.obj_reactTimes.int_between--; 4340 if (obj_reaction.obj_reactTimes.int_between === 0) { 4341 obj_reaction.obj_reactOn.str_between = 'none'; 4342 } 4343 } 4344 obj_reaction.func_reactionBetween(); 4345 } 4346 } 4347 4348 obj_reaction.bln_crossedScrollPositionTop = false; 4349 obj_reaction.bln_crossedScrollPositionBottom = false; 4350 4351 this.obj_currentReaction = null; 4352 } 4353 4354}; 4355 4356lsjs.addViewClass(str_moduleName, obj_classdef); 4357 4358})(); 4359// < ###### VIEW ## 4360// 4361// ###### CONTROLLER ## > 4362(function() { 4363 4364// ### ENTER MODULE NAME HERE ###### 4365var str_moduleName = 'scrollReactor'; 4366// ################################# 4367 4368var obj_classdef = { 4369 start: function() { 4370 } 4371}; 4372 4373lsjs.addControllerClass(str_moduleName, obj_classdef); 4374 4375lsjs.__moduleHelpers[str_moduleName] = { 4376 self: null, 4377 4378 start: function(obj_options) { 4379 this.self = lsjs.createModule({ 4380 __name: str_moduleName 4381 }); 4382 4383 this.self.__models.options.set(obj_options); 4384 } 4385}; 4386 4387}
4387)(); 4388// < ###### CONTROLLER ## 4389// < #### MODULE ## 4390 4391 4392 4393 4394// #### MODULE ## > 4395// ###### TEMPLATES ## > 4396 4397// < ###### TEMPLATES ## 4398 4399// ###### MODELS ## > 4400 4401// < ###### MODELS ## 4402 4403// ###### VIEW ## > 4404(function() { 4405 4406// ### ENTER MODULE NAME HERE ###### 4407var str_moduleName = 'slidesInstance'; 4408// ################################# 4409 4410var obj_classdef = { 4411 el_slideboxContent: null, 4412 obj_contentSize: null, 4413 4414 el_slideboxContentSizeProtector: null, 4415 4416 el_slidebox: null, 4417 4418 el_buttonUp: null, 4419 el_buttonRight: null, 4420 el_buttonDown: null, 4421 el_buttonLeft: null, 4422 4423 obj_currentPosition: { 4424 x: 0, 4425 y: 0 4426 }, 4427 4428 start: function() { 4429 this.el_slideboxContent = this.__el_container; 4430 4431 this.performAutoSize(); 4432 4433 this.obj_contentSize = this.el_slideboxContent.measure(function() { 4434 return this.getSize(); 4435 }); 4436 4437 4438 // Create the slidebox element and set the given slideboxSize 4439 this.el_slidebox = new Element('div.lsSlideboxContainer' + (this.__module.__parentModule.__models.options.data.containerClass != '' ? '.' + this.__module.__parentModule.__models.options.data.containerClass : '')); 4440 this.el_slidebox.setStyles({ 4441 width: this.__module.__parentModule.__models.options.data.slideboxSize.x, 4442 height: this.__module.__parentModule.__models.options.data.slideboxSize.y, 4443 overflow: 'hidden', 4444 position: 'relative' 4445 }); 4446 4447 // Inject the slidebox so that it gets it's real dimensions (e.g. if the size is given percentaged) 4448 this.el_slidebox.inject(this.el_slideboxContent, 'before'); 4449 4450 // overwrite the given slideboxSize with the actual size 4451 this.updateSlideboxSize(); 4452 4453 /* 4454 * el_slideboxContentSizeProtector is needed because a content with a variable width would adjust to the 4455 * smaller slidebox if injected into it and that must not happen. Therefore el_slideboxContentSizeProtector 4456 * gets exactly the size that the content had before injecting it into the slidebox and thus preserves the 4457 * contents size. 4458 * 4459 * el_slideboxContentSizeProtector is also the container that actually gets slided. 4460 */ 4461 this.el_slideboxContentSizeProtector = new Element('div').setStyles({ 4462 width: this.obj_contentSize.x, 4463 height: this.obj_contentSize.y 4464 }); 4465 4466 this.el_slideboxContentSizeProtector.setStyles({ 4467 position: 'absolute', 4468 top: this.obj_currentPosition.y, 4469 left: this.obj_currentPosition.x 4470 }); 4471 4472 this.el_slideboxContentSizeProtector.set('morph', {duration: this.__module.__parentModule.__models.options.data.duration, transition: this.__module.__parentModule.__models.options.data.transition}); 4473 4474 this.el_buttonUp = new Element('div.button.buttonUp').addEvents({click: this.moveUp.bind(this), dblclick: this.preventSelection}); 4475 this.el_buttonRight = new Element('div.button.buttonRight').addEvents({click: this.moveRight.bind(this), dblclick: this.preventSelection}); 4476 this.el_buttonDown = new Element('div.button.buttonDown').addEvents({click: this.moveDown.bind(this), dblclick: this.preventSelection}); 4477 this.el_buttonLeft = new Element('div.button.buttonLeft').addEvents({click: this.moveLeft.bind(this), dblclick: this.preventSelection}); 4478 4479 this.el_slidebox.adopt( 4480 this.el_slideboxContentSizeProtector, 4481 this.el_buttonUp, 4482 this.el_buttonRight, 4483 this.el_buttonDown, 4484 this.el_buttonLeft 4485 ); 4486 4487 this.el_buttonUp.setStyle('left', this.__module.__parentModule.__models.options.data.slideboxSize.x / 2 - this.el_buttonUp.measure(function() { return this.getSize();}).x / 2); 4488 this.el_buttonRight.setStyle('top', this.__module.__parentModule.__models.options.data.slideboxSize.y / 2 - this.el_buttonRight.measure(function() { return this.getSize();}).y / 2); 4489 this.el_buttonDown.setStyle('left', this.__module.__parentModule.__models.options.data.slideboxSize.x / 2 - this.el_buttonDown.measure(function() { return this.getSize();}).x / 2); 4490 this.el_buttonLeft.setStyle('top', this.__module.__parentModule.__models.options.data.slideboxSize.y / 2 - this.el_buttonLeft.measure(function() { return this.getSize();}).y / 2); 4491 4492 this.el_slideboxContent.inject(this.el_slideboxContentSizeProtector); 4493 4494 this.setMovingPossibilitiyClasses(); 4495 4496 if (this.__module.__parentModule.__models.options.data.blnUpdateMovingPossibilityClassesOnWindowResize) { 4497 window.addEvent('resize', this.setMovingPossibilitiyClasses.bind(this)); 4498 } 4499 }, 4500 4501 updateSlideboxSize: function() { 4502 this.__module.__parentModule.__models.options.data.slideboxSize = this.el_slidebox.measure(function() {
4503 return this.getSize(); 4504 }); 4505 }, 4506 4507 performAutoSize: function() { 4508 var el_target; 4509 if ( 4510 this.__module.__parentModule.__models.options.data.autoSize == undefined 4511 || this.__module.__parentModule.__models.options.data.autoSize == null 4512 || this.__module.__parentModule.__models.options.data.autoSize.targetSelector == undefined 4513 || this.__module.__parentModule.__models.options.data.autoSize.targetSelector == null 4514 || this.__module.__parentModule.__models.options.data.autoSize.countingElementSelector == undefined 4515 || this.__module.__parentModule.__models.options.data.autoSize.countingElementSelector == null 4516 || this.__module.__parentModule.__models.options.data.autoSize.sizePerElement == undefined 4517 || this.__module.__parentModule.__models.options.data.autoSize.sizePerElement == null 4518 || ( 4519 (this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.x == undefined || this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.x == null || this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.x == 0 || this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.x == '') 4520 && (this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.y == undefined || this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.y == null || this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.y == 0 || this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.y == '') 4521 ) 4522 ) { 4523 return; 4524 } 4525 4526 el_target = this.__module.__parentModule.__models.options.data.autoSize.targetSelector == '' ? this.el_slideboxContent : this.el_slideboxContent.getElement(this.__module.__parentModule.__models.options.data.autoSize.targetSelector); 4527 if (el_target == undefined || el_target == null) { 4528 return; 4529 } 4530 4531 var offsetX = 0; 4532 var offsetY = 0; 4533 if (this.__module.__parentModule.__models.options.data.autoSize.sizeOffset != undefined && this.__module.__parentModule.__models.options.data.autoSize.sizeOffset != null) { 4534 if (this.__module.__parentModule.__models.options.data.autoSize.sizeOffset.x != undefined && this.__module.__parentModule.__models.options.data.autoSize.sizeOffset.x != null) { 4535 offsetX = this.__module.__parentModule.__models.options.data.autoSize.sizeOffset.x; 4536 } 4537 if (this.__module.__parentModule.__models.options.data.autoSize.sizeOffset.y != undefined && this.__module.__parentModule.__models.options.data.autoSize.sizeOffset.y != null) { 4538 offsetY = this.__module.__parentModule.__models.options.data.autoSize.sizeOffset.y; 4539 } 4540 } 4541 4542 var numCountingElements = this.el_slideboxContent.getElements(this.__module.__parentModule.__models.options.data.autoSize.countingElementSelector).length; 4543 if (this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.x != undefined && this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.x != null && this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.x != 0 && this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.x != '') { 4544 el_target.setStyle('width', this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.x * numCountingElements + offsetX); 4545 } 4546 if (this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.y != undefined && this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.y != null && this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.y != 0 && this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.y != '') { 4547 el_target.setStyle('height', this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.y * numCountingElements + offsetY); 4548 } 4549 }, 4550 4551 moveUp: function() { 4552 this.move('up'); 4553 }, 4554 4555 moveRight: function() { 4556 this.move('right'); 4557 }, 4558 4559 moveDown: function() { 4560 this.move('down'); 4561 }, 4562 4563 moveLeft: function() { 4564 this.move('left'); 4565 }, 4566 4567 preventSelection: function(event) { 4568 event.stop(); 4569 if (window.getSelection) { 4570 if (window.getSelection().empty) { 4571 window.getSelection().empty(); 4572 } else if (window.getSelection().removeAllRanges) { 4573 window.getSelection().removeAllRanges(); 4574 } 4575 } else if (document.selection) { 4576 document.selection.empty(); 4577 } 4578 }, 4579 4580 move: function(direction) { 4581 var objNewPosition = this.getNewPosition(direction); 4582 this.el_slideboxContentSizeProtector.morph({ 4583 top: objNewPosition.y, 4584 left: objNewPosition.x 4585 }); 4586 this.obj_currentPosition = objNewPosition; 4587 4588 this.setMovingPossibilitiyClasses(); 4589 }, 4590 4591 getMoveDistance: function(direction) { 4592 if (this.__module.__parentModule.__models.options.data.blnUseVariableSlideDistance) { 4593 this.updateSlideboxSize(); 4594 } 4595 4596 var distance = 0; 4597 4598 switch(direction) { 4599 case 'up': 4600 case 'down': 4601 if (!this.__module.__parentModule.__models.options.data.blnUseVariableSlideDistance || this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.y == undefined || this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.y == null) { 4602 distance = this.__module.__parentModule.__models.options.data.slideboxSize.y; 4603 } else { 4604 distance = Math.floor(this.__module.__parentModule.__models.options.data.slideboxSize.y / this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.y) * this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.y; 4605 } 4606 distance = distance + this.__module.__parentModule.__models.options.data.slideDistanceOffset.y; 4607 break; 4608 4609 case 'right': 4610 case 'left': 4611 if (!this.__module.__parentModule.__models.options.data.blnUseVariableSlideDistance || this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.x == undefined || this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.x == null) { 4612 distance = this.__module.__parentModule.__models.options.data.slideboxSize.x; 4613 } else { 4614 distance = Math.floor(this.__module.__parentModule.__models.options.data.slideboxSize.x / this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.x) * this.__module.__parentModule.__models.options.data.autoSize.sizePerElement.x; 4615 } 4616 distance = distance + this.__module.__parentModule.__models.options.data.slideDistanceOffset.x; 4617 break; 4618 } 4619 4620 return distance; 4621 }, 4622 4623 getNewPosition: function(direction) { 4624 var objNewPosition = {x: this.obj_currentPosition.x, y: this.obj_currentPosition.y}; 4625 switch(direction) { 4626 case 'up': 4627 objNewPosition.y = this.obj_currentPosition.y + this.getMoveDistance(direction); 4628 break; 4629 4630 case 'right': 4631 objNewPosition.x = this.obj_currentPosition.x - this.getMoveDistance(direction); 4632 break; 4633 4634 case 'down': 4635 objNewPosition.y = this.obj_currentPosition.y - this.getMoveDistance(direction); 4636 break; 4637 4638 case 'left': 4639 objNewPosition.x = this.obj_currentPosition.x + this.getMoveDistance(direction); 4640 break; 4641 } 4642 4643 /* 4644 * check if the new position is within the limits. If a value 4645 * is outside the limits, set it to the old position value (do 4646 * not move) 4647 */ 4648 if (objNewPosition.x > 0) { 4649 // objNewPosition.x = this.obj_currentPosition.x; 4650 objNewPosition.x = 0; 4651 } 4652 if (objNewPosition.y > 0) { 4653 // objNewPosition.y = this.obj_currentPosition.y; 4654 objNewPosition.y = 0; 4655 } 4656 if (objNewPosition.x <= this.obj_contentSize.x * -1) { 4657 objNewPosition.x = this.obj_currentPosition.x; 4658 } 4659 if (objNewPosition.y <= this.obj_contentSize.y * -1) { 4660 objNewPosition.y = this.obj_currentPosition.y; 4661 } 4662 return objNewPosition; 4663 }, 4664 4665 setMovingPossibilitiyClasses: function() { 4666 this.setMovingPossibilitiyClass('up'); 4667 this.setMovingPossibilitiyClass('right'); 4668 this.setMovingPossibilitiyClass('down'); 4669 this.setMovingPossibilitiyClass('left'); 4670 }, 4671 4672 setMovingPossibilitiyClass: function(direction) { 4673 var objNewPosition = this.getNewPosition(direction); 4674 if (objNewPosition.y != this.obj_currentPosition.y || objNewPosition.x != this.obj_currentPosition.x) { 4675 this.el_slidebox.addClass(direction + 'Possible'); 4676 this.el_slidebox.removeClass(direction + 'Impossible'); 4677 } else { 4678 this.el_slidebox.removeClass(direction + 'Possible'); 4679 this.el_slidebox.addClass(direction + 'Impossible'); 4680 } 4681 } 4682}; 4683 4684lsjs.addViewClass(str_moduleName, obj_classdef); 4685 4686})(); 4687// < ###### VIEW ## 4688// 4689// ###### CONTROLLER ## > 4690(function() { 4691 4692// ### ENTER MODULE NAME HERE ###### 4693var str_moduleName = 'slidesInstance'; 4694// ################################# 4695 4696var obj_classdef = { 4697 start: function() { 4698 } 4699}; 4700 4701lsjs.addControllerClass(str_moduleName, obj_classdef); 4702 4703})(); 4704// < ###### CONTROLLER ## 4705// < #### MODULE ## 4706 4707 4708 4709 4710// #### MODULE ## > 4711// ###### TEMPLATES ## > 4712 4713// < ###### TEMPLATES ## 4714 4715// ###### MODELS ## > 4716(function() { 4717 4718var obj_classdef_model = { 4719 name: 'options', 4720 4721 data: {}, 4722 4723 start: function() { 4724 /* 4725 * Initializing the options in the data object with default values which 4726 * can later be overwritten when the "set" method is called with other options 4727 */ 4728 this.data = { 4729 el_domReference: null, 4730 contentSelector: '.lsSlides', 4731 containerClass: '', 4732 slideboxSize: { 4733 x: 200, // if you want to use a percentage value, please declare it as a string (e.g. '100%') 4734 y: 200 4735 },
4736 duration: 800, // duration of the sliding effect in ms 4737 transition: 'sine:in:out', // any string that defines a transition type in the mootools way (see http://mootools.net/docs/core/Fx/Fx.Transitions#Fx-Transitions:sine) 4738 4739 /* 4740 * If multiple elements should be positioned horizontally next to each other (horizontal slides) but the number of elements is variable, 4741 * the surrounding element can be set to a width calculated based on the number of elements counted on the basis of a given selector. 4742 * Without this autoSize functionality the only way to perform a horizontal slide with an unknown number of contained elements would be 4743 * to set the surrounding container to a much too big width but this would cause the lsSlides to slide into a whole lot of empty space. 4744 * 4745 * In the vertical direction it is easy to work with an unknown number of elements because they will automatically be positioned right 4746 * below each other using a simple css floating technique or even without any special css because of the standard text flow. However, 4747 * in order to create a flexible and consistent autoSize functionality it is even possible to autoSize the height of the surrounding element. 4748 */ 4749 autoSize: { 4750 targetSelector: '', // The selector for the element which should be autosized. The selector will only be searched inside the element found using the contentSelector. If set to an empty string, the element found using the contentSelector will be resized itself. 4751 countingElementSelector: '', // The selector for the elements which should be counted (only counted inside the element found using the contentSelector) 4752 sizePerElement: { 4753 x: 0, // The width that should be added for each counted element. If set to 0, the width will not be calculated and not set. 4754 y: 0 // The height that should be added for each counted element. If set to 0, the height will not be calculated and not set. 4755 }, 4756 sizeOffset: { 4757 x: 0, // A horizontal offset. This can be useful for example if all counted elements have a right margin except for the last one. In this case the width including the right margin will be added for each element but the missing right margin of the last element needs to be subtracted. A negative offset can provide this. 4758 y: 0 // A vertical offset. See description of the horizontal offset 4759 } 4760 }, 4761 blnUseVariableSlideDistance: false, // Set this parameter to true if your slideboxSize is set as a percentage and you want the slide distance to be adjusted dynamically if the actual slidebox size changes 4762 slideDistanceOffset: { 4763 x: 0, // A horizontal offset. By default lsSlides slides the width of the slidebox but this distance can be adjusted with this offset. 4764 y: 0 // A vertical offset. See description of the horizontal offset 4765 }, 4766 blnUpdateMovingPossibilityClassesOnWindowResize: false // Set this parameter to true if you want the moving possibility classes of the buttons to be updated if the window resizes. This way, if the slidebox size changes because the user resized the window, the sliding buttons will appear as needed. 4767 }; 4768 }, 4769 4770 set: function(obj_options) { 4771 Object.merge(this.data, obj_options); 4772 this.__module.onModelLoaded(); 4773 } 4774}; 4775 4776lsjs.addModelClass('slidesManager', obj_classdef_model); 4777 4778})(); 4779 4780 4781 4782 4783// < ###### MODELS ## 4784 4785// ###### VIEW ## > 4786 4787// < ###### VIEW ## 4788// 4789// ###### CONTROLLER ## > 4790(function() { 4791 4792// ### ENTER MODULE NAME HERE ###### 4793var str_moduleName = 'slidesManager'; 4794// ################################# 4795 4796var obj_classdef = { 4797 start: function() { 4798 var els_toEnhance; 4799 /* 4800 * Look for elements to enrich with the lsjs-module and then 4801 * create an instance for each element found. 4802 */ 4803 if (this.__models.options.data.el_domReference !== undefined && typeOf(this.__models.options.data.el_domReference) === 'element') { 4804 els_toEnhance = this.__models.options.data.el_domReference.getElements(this.__models.options.data.contentSelector); 4805 } else { 4806 els_toEnhance = $$(this.__models.options.data.contentSelector); 4807 } 4808 4809 Array.each(els_toEnhance, function(el_container) { 4810 /* -> 4811 * Make sure not to handle an element more than once 4812 */ 4813 if (!el_container.retrieve('alreadyHandledBy_' + str_moduleName)) { 4814 el_container.store('alreadyHandledBy_' + str_moduleName, true); 4815 } else { 4816 return; 4817 } 4818 /* 4819 * <- 4820 */ 4821 4822 lsjs.createModule({ 4823 __name: 'slidesInstance', 4824 __parentModule: this.__module, 4825 __useLoadingIndicator: false, 4826 __el_container: el_container 4827 }); 4828 }.bind(this)); 4829 } 4830}; 4831 4832lsjs.addControllerClass(str_moduleName, obj_classdef); 4833 4834lsjs.__moduleHelpers[str_moduleName] = { 4835 self: null, 4836 4837 start: function(obj_options) { 4838 this.self = lsjs.createModule({ 4839 __name: str_moduleName 4840 }); 4841 this.self.__models.options.set(obj_options); 4842 } 4843}; 4844 4845}
4845)(); 4846// < ###### CONTROLLER ## 4847// < #### MODULE ## 4848 4849 4850 4851 4852// #### MODULE ## > 4853// ###### TEMPLATES ## > 4854 4855// < ###### TEMPLATES ## 4856 4857// ###### MODELS ## > 4858(function() { 4859 4860var obj_classdef_model = { 4861 name: 'options', 4862 4863 data: { 4864 str_eventType: 'click', 4865 str_propertyToToggle: 'data-lsjs-statusTogglerStatus', 4866 4867 /* 4868 * Important: Only string values are allowed as status values. If a status should be 8, it has to be defined as '8'. 4869 * The first value in this array is the value that will be set initially. 4870 * It is possible to define more than two status values in which case they will be looped over. 4871 */ 4872 arr_statusValue: ['off', 'on'], 4873 4874 str_sessionStorageKey: null 4875 }, 4876 4877 start: function() { 4878 this.__module.onModelLoaded(); 4879 }, 4880 4881 set: function(obj_options) { 4882 Object.merge(this.data, obj_options); 4883 } 4884}; 4885 4886lsjs.addModelClass('statusTogglerInstance', obj_classdef_model); 4887 4888})(); 4889 4890 4891 4892 4893// < ###### MODELS ## 4894 4895// ###### VIEW ## > 4896(function() { 4897 4898// ### ENTER MODULE NAME HERE ###### 4899var str_moduleName = 'statusTogglerInstance'; 4900// ################################# 4901 4902var obj_classdef = { 4903 start: function() { 4904 var str_options, 4905 obj_options = {}, 4906 str_startStatusValueFromSessionStorage, 4907 str_startStatusValue; 4908 4909 str_options = this.__el_container.getProperty('data-lsjs-statusTogglerOptions'); 4910 4911 if (str_options) { 4912 obj_options = JSON.decode(str_options); 4913 } 4914 4915 this.__models.options.set(obj_options); 4916 4917 this.registerElements(this.__el_container, 'main', false); 4918 4919 /* --> 4920 * Prepare bound functions that are needed for events 4921 */ 4922 this.bound_changeStatus = this.changeStatus.bind(this); 4923 4924 str_startStatusValue = this.__models.options.data.arr_statusValue[0]; 4925 4926 if (this.__models.options.data.str_sessionStorageKey) { 4927 try { 4928 str_startStatusValueFromSessionStorage = sessionStorage.getItem('lsjs_statusToggler_' + this.__models.options.data.str_sessionStorageKey); 4929 if (str_startStatusValueFromSessionStorage !== null) { 4930 str_startStatusValue = str_startStatusValueFromSessionStorage; 4931 } 4932 } catch(e) {} 4933 } 4934 4935 this.__el_container.setProperty(this.__models.options.data.str_propertyToToggle, str_startStatusValue); 4936 4937 if (typeOf(this.__autoElements.main.toggler) === 'element' || typeOf(this.__autoElements.main.toggler) === 'elements') { 4938 this.__autoElements.main.toggler.addEvent(this.__models.options.data.str_eventType, this.bound_changeStatus); 4939 } else { 4940 this.__el_container.addEvent(this.__models.options.data.str_eventType, this.bound_changeStatus); 4941 } 4942 }, 4943 4944 bound_changeStatus: null, 4945 changeStatus: function() { 4946 var str_currentStatusValue = this.__el_container.getProperty(this.__models.options.data.str_propertyToToggle); 4947 var int_currentStatusIndex = this.__models.options.data.arr_statusValue.indexOf(str_currentStatusValue); 4948 if (int_currentStatusIndex < 0) { 4949 int_currentStatusIndex = 0; 4950 } 4951 4952 var int_nextStatusIndex = int_currentStatusIndex === this.__models.options.data.arr_statusValue.length - 1 ? 0 : int_currentStatusIndex + 1; 4953 4954 this.__el_container.setProperty(this.__models.options.data.str_propertyToToggle, this.__models.options.data.arr_statusValue[int_nextStatusIndex]); 4955 4956 if (this.__models.options.data.str_sessionStorageKey) { 4957 try { 4958 sessionStorage.setItem('lsjs_statusToggler_' + this.__models.options.data.str_sessionStorageKey, this.__models.options.data.arr_statusValue[int_nextStatusIndex]); 4959 } catch(e) {} 4960 } 4961 } 4962}; 4963 4964lsjs.addViewClass(str_moduleName, obj_classdef); 4965 4966})(); 4967// < ###### VIEW ## 4968// 4969// ###### CONTROLLER ## > 4970(function() { 4971 4972// ### ENTER MODULE NAME HERE ###### 4973var str_moduleName = 'statusTogglerInstance'; 4974// ################################# 4975 4976var obj_classdef = { 4977 start: function() { 4978 } 4979}; 4980 4981lsjs.addControllerClass(str_moduleName, obj_classdef); 4982 4983})(); 4984// < ###### CONTROLLER ## 4985// < #### MODULE ## 4986 4987 4988 4989 4990// #### MODULE ## > 4991// ###### TEMPLATES ## > 4992 4993// < ###### TEMPLATES ## 4994 4995// ###### MODELS ## > 4996(function() { 4997 4998var obj_classdef_model = { 4999 name: 'options', 5000 5001 data: {}, 5002 5003 start: function() { 5004 /* 5005 * Initializing the options in the data object with default values which 5006 * can later be overwritten when the "set" method is called with other options 5007 */ 5008 this.data = { 5009 el_domReference: null, 5010 str_selector: '[data-lsjs-component~="statusToggler"]', // the selector for the dom element to enrich, 5011 str_classToSetWhenModuleApplied: 'statusTogglerApplied' 5012 }; 5013 }, 5014 5015 set: function(obj_options) { 5016 Object.merge(this.data, obj_options); 5017 this.__module.onModelLoaded(); 5018 } 5019}; 5020 5021lsjs.addModelClass('statusTogglerManager', obj_classdef_model); 5022 5023})(); 5024 5025 5026 5027 5028// < ###### MODELS ## 5029 5030// ###### VIEW ## > 5031 5032// < ###### VIEW ## 5033// 5034// ###### CONTROLLER ## > 5035/* 5036 * -- ACTIVATION: -- 5037 * 5038 * To activate this module, the following code has to be put in the app.js: 5039 * 5040 lsjs.__moduleHelpers.statusTogglerManager.start({ 5041 el_domReference: el_domReference 5042 }); 5043 * 5044 * The el_domReference parameter is only required if this module initialization code is called
5044in a cajax_domUpdate event. 5045 * 5046 * 5047 * 5048 * -- FUNCTIONALITY AND USAGE: -- 5049 * 5050 * This module enriches DOM elements by giving them a status that can be toggled and then be used for CSS styling 5051 * based on an element attribute that holds the status value. 5052 * 5053 * Example: 5054 * 5055 * We have an element with the following DOM structure: 5056 * 5057 <div> 5058 <h1>Click me!</h1> 5059 <div class="hidden"> 5060 Show me! 5061 </div> 5062 </div> 5063 * 5064 * The div with the class hidden originally has a "display: none" style set. If the user clicks on "Click me!" we 5065 * want the hidden div to be displayed if it is currently hidden or to hide again if it is currently displayed. 5066 * 5067 * We can achieve this by making the following changes to the markup 5068 * 5069 <div data-lsjs-component="statusToggler"> 5070 <h1 data-lsjs-element="toggler">Click me!</h1> 5071 <div class="hidden"> 5072 Show me! 5073 </div> 5074 </div> 5075 * 5076 * By default, the element that has the statusToggler applied (data-lsjs-component="statusToggler") gets an attribute 5077 * "data-lsjs-statusTogglerStatus" initially set to the value "off" and by clicking on the element marked as the 5078 * toggler (data-lsjs-element="toggler") the value toggles between "off" and "on". 5079 * 5080 * This attribute can then be used to create a CSS selector that applies any kind of styling to the element itself 5081 * or any of its children. 5082 * 5083 * Therefore, this styling would achieve the desired show/hide effect: 5084 * 5085 #div[data-lsjs-statusTogglerStatus="off"] .hidden { 5086 display: none; 5087 } 5088 5089 #div[data-lsjs-statusTogglerStatus="on"] .hidden { 5090 display: block; 5091 } 5092 * 5093 * Defining an explicit toggler element is optional. Without it, the whole element that has the statusToggler module 5094 * applied, reacts on the click event. 5095 * 5096 * The element marked with data-lsjs-component="statusToggler" can also send custom configurator options to the module: 5097 * 5098 <div 5099 data-lsjs-component="statusToggler" 5100 data-lsjs-statusTogglerOptions=" 5101 { 5102 str_eventType: 'mouseover', 5103 str_propertyToToggle: 'class', 5104 arr_statusValue: ['status1', 'status2', 'status3'], 5105 str_sessionStorageKey: 'test123' 5106 } 5107 " 5108 > 5109 <h1 data-lsjs-element="toggler">Click me!</h1> 5110 <div class="hidden"> 5111 Show me! 5112 </div> 5113 </div> 5114 * 5115 * In this example, every time a mouseover event on the toggler element occurs, the class value will be changed. 5116 * Since we have more than two status values defined, they will be looped over. 5117 * 5118 * The status values could be numeric values but they have to be defined as strings ("1" instead of 1) because applying 5119 * these values to an html attribute would cast them as strings anyway and detection of the currently set status value 5120 * would fail if they had been defined as integers in the module configuration. 5121 * 5122 * If the configuration parameter "str_sessionStorageKey" is used, the status value will be stored in the sessionStorage 5123 * so that the status can be set to its last value if the page gets reloaded. 5124 */ 5125 5126(function() { 5127 5128// ### ENTER MODULE NAME HERE ###### 5129var str_moduleName = 'statusTogglerManager'; 5130// ################################# 5131 5132var obj_classdef = { 5133 start: function() { 5134 var els_toEnhance; 5135 /* 5136 * Look for elements to enrich with the lsjs-module and then 5137 * instantiate instances for each element found. 5138 */ 5139 if (this.__models.options.data.el_domReference !== undefined && typeOf(this.__models.options.data.el_domReference) === 'element') { 5140 els_toEnhance = this.__models.options.data.el_domReference.getElements(this.__models.options.data.str_selector); 5141 } else { 5142 els_toEnhance = $$(this.__models.options.data.str_selectors); 5143 } 5144 5145 Array.each(els_toEnhance, function(el_container) { 5146 /* -> 5147 * Make sure not to handle an element more than once 5148 */ 5149 if (!el_container.retrieve('alreadyHandledBy_' + str_moduleName)) { 5150 el_container.store('alreadyHandledBy_' + str_moduleName, true); 5151 } else { 5152 return; 5153 } 5154 /* 5155 * <- 5156 */ 5157 5158 el_container.addClass(this.__models.options.data.str_classToSetWhenModuleApplied); 5159 5160 lsjs.createModule({ 5161 __name: 'statusTogglerInstance', 5162 __parentModule: this.__module, 5163 __useLoadingIndicator: false, 5164 __el_container: el_container 5165 }); 5166 }.bind(this)); 5167 } 5168}; 5169 5170lsjs.addControllerClass(str_moduleName, obj_classdef); 5171 5172lsjs.__moduleHelpers[str_moduleName] = { 5173 self: null, 5174 5175 start: function(obj_options) { 5176 this.self = lsjs.createModule({ 5177 __name: str_moduleName 5178 }); 5179 this.self.__models.options.set(obj_options); 5180 } 5181}; 5182 5183}
5183)(); 5184// < ###### CONTROLLER ## 5185// < #### MODULE ## 5186 5187 5188 5189 5190// #### MODULE ## > 5191// ###### TEMPLATES ## > 5192 5193// < ###### TEMPLATES ## 5194 5195// ###### MODELS ## > 5196(function() { 5197 5198var obj_classdef_model = { 5199 name: 'options', 5200 5201 data: { 5202 str_headerSelector: '#header', 5203 str_stickyClass: 'sticky', 5204 int_offset: 0 5205 }, 5206 5207 start: function() { 5208 }, 5209 5210 set: function(obj_options) { 5211 Object.merge(this.data, obj_options); 5212 this.__module.onModelLoaded(); 5213 } 5214}; 5215 5216lsjs.addModelClass('stickyHeader', obj_classdef_model); 5217 5218})(); 5219 5220 5221 5222 5223// < ###### MODELS ## 5224 5225// ###### VIEW ## > 5226(function() { 5227 5228// ### ENTER MODULE NAME HERE ###### 5229var str_moduleName = 'stickyHeader'; 5230// ################################# 5231 5232var obj_classdef = { 5233 start: function() { 5234 var els_header, 5235 el_header, 5236 self = this; 5237 5238 els_header = $$(this.__models.options.data.str_headerSelector); 5239 5240 if (typeOf(els_header) !== 'elements' || els_header.length < 1) { 5241 console.log('no elements found for selector "' + this.__models.options.data.str_headerSelector + '"'); 5242 return; 5243 } 5244 5245 if (els_header.length > 1) { 5246 console.log('more than one element found for selector "' + this.__models.options.data.str_headerSelector + '"'); 5247 return; 5248 } 5249 5250 el_header = els_header[0]; 5251 5252 lsjs.__moduleHelpers.scrollReactor.start({ 5253 arr_reactions: [ 5254 { 5255 func_reactionCrossingTop: function () { 5256 if (this.str_currentDirection === 'down') { 5257 el_header.addClass(self.__models.options.data.str_stickyClass); 5258 } else { 5259 el_header.removeClass(self.__models.options.data.str_stickyClass); 5260 } 5261 }, 5262 int_scrollPositionTop: el_header.getPosition().y + self.__models.options.data.int_offset, 5263 obj_reactOn: { 5264 str_crossingTop: 'both', 5265 str_crossingBottom: 'none', 5266 str_between: 'none' 5267 } 5268 } 5269 ] 5270 }); 5271 } 5272}; 5273 5274lsjs.addViewClass(str_moduleName, obj_classdef); 5275 5276})(); 5277// < ###### VIEW ## 5278// 5279// ###### CONTROLLER ## > 5280(function() { 5281 5282// ### ENTER MODULE NAME HERE ###### 5283var str_moduleName = 'stickyHeader'; 5284// ################################# 5285 5286/* 5287 * Use like this: 5288 * 5289 * lsjs.__moduleHelpers.stickyHeader.start({str_headerSelector: '#header', str_stickyClass: 'sticky'}); 5290 * 5291 * The object given as a parameter in this example can be omitted since the values in the example are used as default. 5292 */ 5293var obj_classdef = { 5294 start: function() { 5295 } 5296}; 5297 5298lsjs.addControllerClass(str_moduleName, obj_classdef); 5299 5300lsjs.__moduleHelpers[str_moduleName] = { 5301 self: null, 5302 5303 start: function(obj_options) { 5304 this.self = lsjs.createModule({ 5305 __name: str_moduleName 5306 }); 5307 5308 this.self.__models.options.set(obj_options); 5309 } 5310}; 5311 5312})(); 5313// < ###### CONTROLLER ## 5314// < #### MODULE ## 5315 5316 5317 5318 5319// #### MODULE ## > 5320// ###### TEMPLATES ## > 5321 5322// < ###### TEMPLATES ## 5323 5324// ###### MODELS ## > 5325(function() { 5326 5327var obj_classdef_model = { 5328 name: 'options', 5329 5330 data: {}, 5331 5332 start: function() { 5333 /* 5334 * Initializing the options in the data object with default values which 5335 * can later be overwritten when the "set" method is called with other options 5336 */ 5337 this.data = { 5338 /* 5339 * Mandatory. 5340 * 5341 * The selector for the toggler. If the selector matches multiple elements, 5342 * they all will be used. Can also be a DOM element or a collection of DOM elements. 5343 */ 5344 var_togglerSelector: '#whateverToggler', 5345 5346 /* 5347 * Mandatory. 5348 * 5349 * The selector for the resize box. Only the first matched element will 5350 * be used. Can also be a DOM element. 5351 */ 5352 var_contentBoxSelector: '#whateverContentBox', 5353 5354 /* 5355 * Optional. 5356 * 5357 * The selector for the wrapper. Only the first matched element will 5358 * be used. Can also be a DOM element. 5359 */ 5360 var_wrapperSelector: null, 5361 5362 /* 5363 * Optional. 5364 * 5365 * The selector for a specific close button. If the selector 5366 * matches multiple elements, they all will be used. Can also be a 5367 * DOM element or a collection of DOM elements. 5368 */ 5369 var_closeButtonSelector: null, 5370 5371 /* 5372 * Optional. 5373 * 5374 * True, if the content box should be automatically wrapped in another 5375 * element, that's being resized instead of the given content box itself. 5376 * 5377 * False, if no new element should be created and if the content box itself
5378 * should be resized when folding/unfolding 5379 */ 5380 bln_automaticallyCreateResizeBox: true, 5381 5382 /* 5383 * Optional. 5384 * 5385 * Defines, which events are used to toggle the unfold effect. 5386 * All event type values for the Mootools addEvent function are possible. 5387 * Also, an array holding two event types can be given. In this case, 5388 * the two event types will be switched, which makes mouseenter/mouseleave 5389 * or mouseover/mouseout possible. 5390 * 5391 * If the only way to close the element should be the click on a special 5392 * close button or an outside click, the second event in the array can be 5393 * 'closeButton'. Actually, every non-existing can be used, because the 5394 * only way that this event can be triggered is by explicitly calling it, 5395 * which happens in case of a close button click or outside click. 5396 * 5397 * Examples: ['mouseenter', 'mouseleave'] or ['mouseenter', 'closeButton'] etc. 5398 */ 5399 str_togglerEventType: 'click', 5400 5401 /* 5402 * Optional. 5403 * Possible values: 'open' or 'closed'. 5404 * 5405 * Defines, whether the element is already opened when instantiating the 5406 * effect. Please note that this value refers to the element's status 5407 * before the lsUnfold effect is initialized. This means that an element 5408 * that, without active javascript, would be displayed as open because 5409 * of its css styles needs this value set to 'open' even if (with active 5410 * javascript and lsUnfold applied) it will be closed instantly using 5411 * the options 'bln_toggleOnInitialization' and 'bln_skipAnimationWhenTogglingOnInitialization' 5412 * or the option 'str_initialCookieStatus'. 5413 */ 5414 str_initialToggleStatus: 'open', 5415 5416 /* 5417 * Optional. 5418 * Boolean. 5419 * 5420 * True if the element should be morphed right after initialization 5421 * (simulating an automatic toggle). 5422 */ 5423 bln_toggleOnInitialization: false, 5424 5425 /* 5426 * Optional. 5427 * Boolean. 5428 * 5429 * True if the animation should be skipped when the element is being 5430 * toggled on initialization 5431 */ 5432 bln_skipAnimationWhenTogglingOnInitialization: false, 5433 5434 /* 5435 * Optional. 5436 * 5437 * If set, this name will be used to store the toggle status in a cookie 5438 * and then later restore it on page reload 5439 */ 5440 str_cookieIdentifierName: '', 5441 5442 /* 5443 * Optional. 5444 * 5445 * If set to either closed or open, this value is used as the initial 5446 * cookie status if the cookie doesn't exist yet. 5447 */ 5448 str_initialCookieStatus: '', 5449 5450 /* 5451 * Optional. 5452 * Boolean. 5453 * 5454 * True if a click outside the unfolded element should close it 5455 */ 5456 bln_closeOnOutsideClick: false, 5457 5458 /* 5459 * Optional. 5460 * Possible values: 'height', 'margin-top' 5461 * 5462 * The animation mode defines which css property is used to unfold 5463 * the resizeBox. 5464 */ 5465 str_animationMode: 'height', 5466 5467 /* 5468 * Optional. 5469 * Boolean. 5470 * 5471 * True, if the paddings of the resizeBox should also be morphed to 5472 * and from 0. Please keep in mind, that it is much better not to have 5473 * a padding on the resizeBox at all but instead set the padding to an 5474 * element inside the resizeBox. 5475 */ 5476 bln_morphPaddings: false, 5477 5478 /* 5479 * Optional. 5480 * Boolean. 5481 * 5482 * When using the animation mode "margin-top" the resizeBox is probably 5483 * hidden outside the window using a negative margin. Unfolding the element 5484 * means morphing from the negative margin to zero margin. 5485 * 5486 * Using this flag, the current window scroll position can be considered, 5487 * so that the element will not morph from its negative margin to zero margin 5488 * but to the scroll position as a positive margin, which means that the 5489 * element still moves inside the screen from the top, no matter what 5490 * the current scroll position is. 5491 */ 5492 bln_considerWindowScrollInMarginAnimationMode: false, 5493 5494 /* 5495 * Optional. 5496 * Boolean. 5497 * 5498 * If the animation mode "margin-top" has been usde to slide the element 5499 * in from the top, this flag can be used to make sure, that the element 5500 * stays visible even if the user scrolls the window. 5501 */ 5502 bln_moveWithWindowScrollInMarginAnimationMode: false, 5503 5504 /* 5505 * Optional. 5506 * Object 5507 * 5508 * If bln_moveWithWindowScrollInMarginAnimationMode is true, then the 5509 * element stays in its position when the window is being scrolled. However, 5510 * if the element is higher then the visible screen/window, it will be scrolled 5511 * up and down until its top or bottom edge is inside the visible area. 5512 * The offsets that can be defined in this object will be considered when 5513 * determining whether the top or bottom edge is in the visible area. 5514 * Example: A bottom value of 10 means that when scrolling down, the element 5515 * will move up until its bottom edge is 10 px inside the visible area. 5516 */ 5517 obj_moveWithWindowScrollInMarginAnimationModeOffsets: { 5518 /* 5519 * Integer 5520 * 5521 * The offset in px without unit. 5522 */ 5523 top: 0, 5524 5525 /* 5526 * Integer 5527 * 5528 * The offset in px without unit. 5529 */ 5530 bottom: 0 5531 }, 5532 5533 /* 5534 * Optional, if the resizeBox starts opened. 5535 * Mandatory, if the resizeBox starts closed. 5536 * 5537 * Can be any valid value for the css property "display" (e.g. "block", 5538 * "inline-block" etc.). This option needs to be set, if the element 5539 * to resize starts with "display: none;" in which case lsUnfold would 5540 * not be able to determine the correct display type to use for the 5541 * visible/unfolded state of the element. 5542 */ 5543 str_initialDisplayType: null, 5544 5545 /* 5546 * Optional. 5547 * Possible values: 'measure', 'getStyle', 'auto' or the height in 5548 * pixels without unit, e.g. 150 5549 * 5550 * The initial height value can either be given directly (var_initalHeight: 'auto', 5551 * var_initalHeight: 150) or it can be determined using the getStyle method 5552 * (var_initalHeight: 'getStyle') or it can be determined using the 5553 * getDimensions method (var_initalHeight: 'measure'). 5554 * 5555 * In most cases, it's best not to use 'measure' or 'getStyle' because 5556 * it's not always easy to understand what the expected result should be. 5557 * 5558 * If the resizeBox should be able to adapt its size dynamically (e.g. 5559 * because its content can grow), the value 'auto' should be set and it 5560 * is also important do define 'height: auto;' in the stylesheet for this 5561 * element as well, if the resizeBox starts opened. 5562 */ 5563 var_initialHeight: 'auto', 5564 5565 /* 5566 * Optional. 5567 * Integer. 5568 * 5569 * When lsUnfold determines the size to which the element should be unfolded, 5570 * it is sometimes necessary to manipulate that value by adding a positive 5571 * or negative offset. 5572 */ 5573 int_heightOffset: 0, 5574 5575 /* 5576 * Optional. 5577 * 5578 * The class to add to the wrapper, the toggler and the resizeBox when 5579 * the status is open. 5580 */ 5581 str_classOpen: 'lsUnfoldOpen', 5582 5583 /* 5584 * Optional. 5585 * 5586 * The class to add to the wrapper, the toggler and the resizeBox when 5587 * the status is closed. 5588 */ 5589 str_classClosed: 'lsUnfoldClosed', 5590 5591 /* 5592 * Optional. 5593 * 5594 * The class to add to the wrapper, the toggler and the resizeBox when 5595 * the status is running. 5596 */ 5597 str_classRunning: 'lsUnfoldRunning', 5598 5599 /* 5600 * For development purposes you can activate a logging in the console 5601 */ 5602 bln_useLogging: false, 5603 5604 /* 5605 * Optional. 5606 * 5607 * The class to add to the wrapper and close button to indicate that lsUnfold is 5608 * used with this element. This can be used in a css selector in order to only 5609 * display a toggler button if the element can actually be toggled. 5610 */ 5611 str_classUseLsUnfold: 'useLsUnfold', 5612 5613 obj_morphOptions: { 5614 'duration': 250, 5615 'link': 'ignore' 5616 } 5617 }; 5618 }, 5619 5620 set: function(obj_options) { 5621 /* 5622 * The user calls this function in order to override our default values. 5623 * 5624 * After that we add our onComplete function (not before, because it must 5625 * not be overridden) and in the onComplete function we make a call to 5626 * the onComplete function possibly given as a user argument. 5627 */ 5628 Object.merge(this.data, obj_options); 5629 5630 this.data.obj_morphOptions.onComplete = function() { 5631 this.__view.setEndStyles(); 5632 this.__view.toggleStatus(); 5633 this.__view.onMorphComplete(); 5634 if ( 5635 obj_options.obj_morphOptions !== undefined 5636 && obj_options.obj_morphOptions.onComplete !== undefined 5637 && typeOf(obj_options.obj_morphOptions.onComplete) === 'function' 5638 ) { 5639 obj_options.obj_morphOptions.onComplete(); 5640 } 5641 }.bind(this); 5642 5643 this.__module.onModelLoaded(); 5644 } 5645}; 5646 5647lsjs.addModelClass('unfold', obj_classdef_model); 5648 5649})(); 5650 5651 5652 5653 5654// < ###### MODELS ## 5655 5656// ###### VIEW ## > 5657(function() { 5658 5659// ### ENTER MODULE NAME HERE ###### 5660var str_moduleName = 'unfold'; 5661// ################################# 5662 5663var obj_classdef = { 5664 bound_onToggleEvent: null, 5665 bound_closeOnOutsideClick: null, 5666 bound_closeOnCloseButtonClick: null, 5667 5668 interval_scrollPositionChecker: null, 5669 5670 els_toggler: null, 5671 el_resizeBox: null, 5672 el_contentBox: null, 5673 el_wrapper: null, 5674 els_closeButton: null, 5675 5676 var_initialHeight: null, 5677 obj_initialPaddings: { 5678 'top': 0, 5679 'right': 0, 5680 'bottom': 0, 5681 'left': 0 5682 }, 5683 str_initialDisplayType: null, 5684 int_windowScrollY: 0, 5685 5686 obj_morph: null, 5687 obj_morphSkipAnimation: null, 5688 obj_morph_slideToScrollPosition: null, 5689 5690 obj_startStyles: {}, 5691 obj_endStyles: {}, 5692 obj_morphParams: {}, 5693 5694 str_toggleStatus: 'open', 5695 str_togglerEventTypeToUse: 'click', 5696 5697 bln_skipAnimation: false, 5698 5699 start: function() { 5700 /* --> 5701 * Prepare bound functions that are needed for events 5702 */ 5703 this.bound_onToggleEvent = this.onToggleEvent.bind(this); 5704 this.bound_closeOnOutsideClick = this.closeOnOutsideClick.bind(this); 5705 this.bound_closeOnCloseButtonClick = this.closeOnCloseButtonClick.bind(this); 5706 /* 5707 * <-- 5708 */ 5709 5710 /* 5711 * Determine the event type to start with. If the given option value is 5712 * a string, this event will be used. If it's an array, which means that 5713 * two values should be switched, we start using the first array value. 5714 */ 5715 this.str_togglerEventTypeToUse = typeOf(this.__models.options.data.str_togglerEventType) === 'array' 5716 ? this.__models.options.data.str_togglerEventType[0] 5717 : this.__models.options.data.str_togglerEventType; 5718 5719 /* 5720 * Preparing the DOM elements to use. toggler and closeButton can also be 5721 * a collection of elements. 5722 */ 5723 this.els_toggler = typeOf(this.__models.options.data.var_togglerSelector) === 'element' || typeOf(this.__models.options.data.var_togglerSelector) === 'null' ? this.__models.options.data.var_togglerSelector : $$(this.__models.options.data.var_togglerSelector); 5724 this.el_wrapper = typeOf(this.__models.options.data.var_wrapperSelector) === 'element' || typeOf(this.__models.options.data.var_wrapperSelector) === 'null' ? this.__models.options.data.var_wrapperSelector : $$(this.__models.options.data.var_wrapperSelector)[0]; 5725 this.els_closeButton = typeOf(this.__models.options.data.var_closeButtonSelector) === 'element' || typeOf(this.__models.options.data.var_closeButtonSelector) === 'null' ? this.__models.options.data.var_closeButtonSelector : $$(this.__models.options.data.var_closeButtonSelector); 5726 this.el_contentBox = typeOf(this.__models.options.data.var_contentBoxSelector) === 'element' || typeOf(this.__models.options.data.var_contentBoxSelector) === 'null' ? this.__models.options.data.var_contentBoxSelector : $$(this.__models.options.data.var_contentBoxSelector)[0]; 5727 5728 /* 5729 * Don't do anything without a toggler element and a resizeBox element. 5730 */ 5731 if ( 5732 this.els_toggler === undefined || this.els_toggler === null 5733 || this.el_contentBox === undefined || this.el_contentBox === null 5734 ) { 5735 if (this.__models.options.data.bln_useLogging) { 5736 console.error('required elements missing:'); 5737 console.log('this.els_toggler', this.els_toggler); 5738 console.log('this.el_contentBox', this.el_contentBox); 5739 console.log('this.__models.options.data', this.__models.options.data); 5740 } 5741 return; 5742 } 5743 5744 this.str_toggleStatus = this.__models.options.data.str_initialToggleStatus; 5745 5746 if (this.__models.options.data.bln_automaticallyCreateResizeBox) { 5747 /* 5748 * Create a resizeBox and wrap the given content box in it. The 5749 * newly created resizeBox is then used for the morph. 5750 */ 5751 this.el_resizeBox = new Element('div.lsUnfoldResizeBox').wraps(this.el_contentBox); 5752 this.el_resizeBox.setStyles({ 5753 height: 'auto', 5754 overflow: 'hidden', 5755 display: this.__models.options.data.str_initialToggleStatus === 'open' ? 'block' : 'none' 5756 }); 5757 } else { 5758 /* 5759 * Don't create a new element but insead use the given contentBox 5760 * directly for the morph. 5761 */ 5762 this.el_resizeBox = this.el_contentBox; 5763 } 5764 5765 /* 5766 * Determining the intial height 5767 */ 5768 if (this.__models.options.data.var_initialHeight === 'getStyle') { 5769 this.var_initialHeight = this.el_resizeBox.getStyle('height'); 5770 } else if (this.__models.options.data.var_initialHeight === 'measure') { 5771 this.var_initialHeight = this.getResizeBoxHeight(); 5772 } else { 5773 this.var_initialHeight = this.__models.options.data.var_initialHeight; 5774 } 5775 5776 /* 5777 * Determining the initial paddings 5778 */ 5779 this.obj_initialPaddings = this.el_resizeBox.measure(function() { 5780 var obj_paddings, 5781 obj_dimensions; 5782 5783 obj_dimensions = this.getDimensions({computeSize: true}); 5784 obj_paddings = { 5785 'top': obj_dimensions['padding-top'], 5786 'right': obj_dimensions['padding-right'], 5787 'bottom': obj_dimensions['padding-bottom'], 5788 'left': obj_dimensions['padding-left'] 5789 }; 5790 5791 return obj_paddings; 5792 }); 5793 5794 /* 5795 * Determining the initial display type ('block', 'inline-block' etc.) 5796 */ 5797 this.str_initialDisplayType = 5798 ( 5799 this.__models.options.data.str_initialDisplayType !== undefined 5800 && this.__models.options.data.str_initialDisplayType !== null 5801 ) 5802 ? this.__models.options.data.str_initialDisplayType 5803 : this.el_resizeBox.getStyle('display'); 5804 5805 /* 5806 * Preparing the morph effect for folding/unfolding the resizeBox 5807 */ 5808 this.obj_morph = new Fx.Morph(this.el_resizeBox, this.__models.options.data.obj_morphOptions); 5809 var obj_morphOptionsSkipAnimation = this.__models.options.data.obj_morphOptions; 5810 obj_morphOptionsSkipAnimation.duration = 0; 5811 this.obj_morphSkipAnimation = new Fx.Morph(this.el_resizeBox, obj_morphOptionsSkipAnimation); 5812 5813 /* 5814 * Preparing the morph effect for sliding to the scroll position 5815 */ 5816 this.obj_morph_slideToScrollPosition = new Fx.Morph(this.el_resizeBox, { 5817 'duration': 1000, 5818 'link': 'cancel' 5819 }); 5820 5821 /* --> 5822 * Adding a class to the wrapper and the closeButton (if existing), 5823 * indicating that lsUnfold is being used. 5824 */ 5825 if (this.el_wrapper !== undefined && this.el_wrapper !== null) { 5826 this.el_wrapper.addClass(this.__models.options.data.str_classUseLsUnfold); 5827 } 5828 5829 if (this.els_closeButton !== undefined && this.els_closeButton !== null) { 5830 this.els_closeButton.addClass(this.__models.options.data.str_classUseLsUnfold); 5831 } 5832 /* 5833 * <-- 5834 */ 5835 5836 /* 5837 * Add the event to the toggler 5838 */ 5839 this.els_toggler.addEvent(this.str_togglerEventTypeToUse, this.bound_onToggleEvent); 5840 5841 /* 5842 * Make sure classes are set correctly 5843 */ 5844 this.adjustClasses(); 5845 5846 /* 5847 * Toggle directly on initialization, if the corresponding flag is set 5848 */ 5849 if (this.__models.options.data.bln_toggleOnInitialization) {
5850 if (this.__models.options.data.bln_skipAnimationWhenTogglingOnInitialization) { 5851 this.bln_skipAnimation = true; 5852 } 5853 this.els_toggler.fireEvent(this.str_togglerEventTypeToUse); 5854 if (this.__models.options.data.bln_skipAnimationWhenTogglingOnInitialization) { 5855 this.bln_skipAnimation = false; 5856 } 5857 } 5858 5859 this.considerStatusFromCookie(); 5860 }, 5861 5862 considerStatusFromCookie: function() { 5863 if (!this.__models.options.data.str_cookieIdentifierName) { 5864 return; 5865 } 5866 5867 if ( 5868 Cookie.read(this.__models.options.data.str_cookieIdentifierName) !== 'open' 5869 && Cookie.read(this.__models.options.data.str_cookieIdentifierName) !== 'closed' 5870 ) { 5871 Cookie.write(this.__models.options.data.str_cookieIdentifierName, this.__models.options.data.str_initialCookieStatus ? this.__models.options.data.str_initialCookieStatus : this.str_toggleStatus); 5872 } 5873 if (this.str_toggleStatus != Cookie.read(this.__models.options.data.str_cookieIdentifierName)) { 5874 this.bln_skipAnimation = true; 5875 this.els_toggler.fireEvent(this.str_togglerEventTypeToUse); 5876 this.bln_skipAnimation = false; 5877 } 5878 }, 5879 5880 onToggleEvent: function(event) { 5881 /* 5882 * If an event is given, we want to prevent the default action, 5883 * i.e. to prevent a hyperlink to be followed because if a hyperlinked 5884 * anchor is used as the toggler, its link is considered the "fallback 5885 * action" if javascript is not active. 5886 */ 5887 if (event !== undefined) { 5888 event.preventDefault(); 5889 } 5890 5891 /* 5892 * If the element is currently being unfolded/morphed, we don't 5893 * accept another toggle. 5894 */ 5895 if (this.obj_morph.isRunning()) { 5896 return; 5897 } 5898 5899 /* 5900 * The function called here makes sure that the correct toggle event 5901 * is active. This is important because if two different event types 5902 * are given, they need to be switched (mouseenter -> mouseout -> mouseenter ...) 5903 */ 5904 this.updateTogglerEventTypeToUse(); 5905 5906 /* 5907 * Adding the element classes indicating that the morph is currently running 5908 */ 5909 if (this.el_wrapper !== undefined && this.el_wrapper !== null) { 5910 this.el_wrapper.addClass(this.__models.options.data.str_classRunning); 5911 } 5912 this.el_resizeBox.addClass(this.__models.options.data.str_classRunning); 5913 this.els_toggler.addClass(this.__models.options.data.str_classRunning); 5914 5915 /* 5916 * Call the function to either open or close the resizeBox, 5917 * depending on the current toggle status. 5918 */ 5919 if (this.str_toggleStatus === 'closed') { 5920 this.openResizeBox(); 5921 } else { 5922 this.closeResizeBox(); 5923 } 5924 }, 5925 5926 /* 5927 * This function makes sure that the correct toggle event is active. 5928 * This is important because if two different event types are given, 5929 * they need to be switched (mouseenter -> mouseout -> mouseenter ...) 5930 */ 5931 updateTogglerEventTypeToUse: function() { 5932 /* 5933 * If the given togglerEventType is not an array, we don't have to 5934 * do anything, because only one single event type is given and 5935 * there's nothing to switch. 5936 */ 5937 if (typeOf(this.__models.options.data.str_togglerEventType) !== 'array') { 5938 return; 5939 } 5940 5941 /* 5942 * If the event type needs to be switched, we first have to remove the 5943 * currently existing event listener, because it listens for the previous 5944 * event type. 5945 */ 5946 this.els_toggler.removeEvent(this.str_togglerEventTypeToUse, this.bound_onToggleEvent); 5947 5948 /* 5949 * Determine which event type to use now. 5950 */ 5951 this.str_togglerEventTypeToUse = this.__models.options.data.str_togglerEventType[0] === this.str_togglerEventTypeToUse ? this.__models.options.data.str_togglerEventType[1] : this.__models.options.data.str_togglerEventType[0]; 5952 5953 /* 5954 * Adding the new event listener for the determined event type. 5955 */ 5956 this.els_toggler.addEvent(this.str_togglerEventTypeToUse, this.bound_onToggleEvent); 5957 }, 5958 5959 /* 5960 * Opening the resizeBox 5961 */ 5962 openResizeBox: function() { 5963 /* 5964 * Determine the height that the opened resizeBox needs to have 5965 */ 5966 var int_endHeight = this.getResizeBoxHeight(); 5967 5968 /* 5969 * Adding the event to listen for the outside click if necessary 5970 */ 5971 if (this.__models.options.data.bln_closeOnOutsideClick) { 5972 window.addEvent('click', this.bound_closeOnOutsideClick); 5973 } 5974 5975 /* 5976 * Adding the event to listen for the close button click if necessary 5977 */ 5978 if (this.els_closeButton) { 5979 this.els_closeButton.addEvent('click', this.bound_closeOnCloseButtonClick); 5980 } 5981 5982 /* 5983 * If the window scroll position needs to be considered, it is stored 5984 * here for future reference. 5985 */ 5986 if (this.__models.options.data.bln_considerWindowScrollInMarginAnimationMode) { 5987 this.int_windowScrollY = window.getScroll().y; 5988 } 5989 5990 /* 5991 * Adding the offset to the element's target height 5992 */ 5993 int_endHeight += this.__models.options.data.int_heightOffset; 5994 5995 /* 5996 * Resetting the styles which have to be applied before starting the morph 5997 * and the parameters for the morph effect. 5998 */ 5999 this.obj_startStyles = {}; 6000 this.obj_morphParams = {}; 6001 6002 /* 6003 * Different styles and morph parameters have to be used depending on 6004 * the animation mode 6005 */ 6006 switch (this.__models.options.data.str_animationMode) { 6007 case 'height': 6008 /* 6009 * When opening, we start with 0 height and we set the initial 6010 * display type to make the element visible because before, it 6011 * was set to display: none. 6012 */ 6013 this.obj_startStyles = { 6014 'height': 0, 6015 'display': this.str_initialDisplayType 6016 }; 6017 6018 /* 6019 * We morph the element's height from 0 to the end height 6020 */ 6021 this.obj_morphParams = { 6022 'height': [0, int_endHeight] 6023 }; 6024 6025 /* 6026 * After the morph effect, we set the initial height value, which 6027 * is necessary to make sure that the resizeBox can be set to 6028 * auto to allow it to grow flexibly with its content. Otherwise 6029 * even if the resizeBox started with height: auto, it would have 6030 * a fixed height value after the morph. 6031 */ 6032 this.obj_endStyles = { 6033 'height': this.var_initialHeight 6034 }; 6035 break; 6036 6037 case 'margin-top': 6038 /* 6039 * When opening, we start with a negative margin that is as big 6040 * as the determined element's height. We add the window scroll 6041 * position, which might be 0 if the flag indicating that the 6042 * scroll position should be considered is not set. If the scroll 6043 * position should be considered, the window scroll position added 6044 * here makes sure, that the margin where the morph begins positions 6045 * the element right outside the window. 6046 * 6047 * We also set the initial display type to make the element 6048 * visible because before, it was set to display: none. 6049 */ 6050 this.obj_startStyles = { 6051 'margin-top': this.int_windowScrollY + (int_endHeight * -1), 6052 'display': this.str_initialDisplayType 6053 }; 6054 6055 /* 6056 * We morph the element's margin-top from the negative margin 6057 * (as big as the element's height) to margin-top 0. To both values 6058 * we add the window scroll position which is either a positive 6059 * value or 0 if the window scroll position should not be considered. 6060 */ 6061 this.obj_morphParams = { 6062 'margin-top': [this.int_windowScrollY + (int_endHeight * -1), this.int_windowScrollY + 0] 6063 }; 6064 break; 6065 } 6066 6067 /* 6068 * If the paddings should also be morphed, we have to adjust the morphParams 6069 * as well as the startStyles and endStyles accordingly. 6070 */ 6071 if (this.__models.options.data.bln_morphPaddings) { 6072 this.obj_morphParams['padding-top'] = [0, this.obj_initialPaddings.top]; 6073 this.obj_morphParams['padding-bottom'] = [0, this.obj_initialPaddings.bottom]; 6074 6075 this.obj_startStyles['padding-top'] = 0; 6076 this.obj_startStyles['padding-bottom'] = 0; 6077 6078 this.obj_endStyles['padding-top'] = this.obj_initialPaddings.top; 6079 this.obj_endStyles['padding-bottom'] = this.obj_initialPaddings.bottom; 6080 } 6081 6082 /* 6083 * We apply the startStyles ... 6084 */ 6085 this.el_resizeBox.setStyles(this.obj_startStyles); 6086 6087 /* 6088 * ... and then we start the morph effect. 6089 */
6090 if (this.bln_skipAnimation) { 6091 this.obj_morphSkipAnimation.start(this.obj_morphParams); 6092 } else { 6093 this.obj_morph.start(this.obj_morphParams); 6094 } 6095 }, 6096 6097 /* 6098 * Closing the resize box 6099 */ 6100 closeResizeBox: function() { 6101 /* 6102 * We have to determine the element's current height before starting 6103 * the closing morph because with an auto height the element's height 6104 * might have changed. 6105 */ 6106 var int_beginHeight = this.getResizeBoxHeight(); 6107 6108 /* 6109 * We don't want any unnecessary event listeners, so we remove the listener 6110 * for the click event on the outside click 6111 */ 6112 window.removeEvent('click', this.bound_closeOnOutsideClick); 6113 6114 /* 6115 * Adding the offset to the element's determined height 6116 */ 6117 int_beginHeight += this.__models.options.data.int_heightOffset; 6118 6119 /* 6120 * Resetting the startSTyles and morphParams 6121 */ 6122 this.obj_startStyles = {}; 6123 this.obj_morphParams = {}; 6124 6125 /* 6126 * Different styles and morph parameters have to be used depending on 6127 * the animation mode 6128 */ 6129 switch (this.__models.options.data.str_animationMode) { 6130 case 'height': 6131 /* 6132 * When closing, we morph from the element's current height to 0 6133 */ 6134 this.obj_morphParams = { 6135 'height': [int_beginHeight, 0] 6136 }; 6137 6138 /* 6139 * After the morph, we set the inital height value to make sure 6140 * that a subsequent call to openResizeBox() can determine the 6141 * element's correct height. We also set display: none to make 6142 * sure that the element is hidden. 6143 */ 6144 this.obj_endStyles = { 6145 'height': this.var_initialHeight, 6146 'display': 'none' 6147 }; 6148 break; 6149 6150 case 'margin-top': 6151 /* 6152 * When opening, we morph from a zero margin to a negative margin 6153 * as big as the element's height. To both values 6154 * we add the window scroll position which is either a positive 6155 * value or 0 if the window scroll position should not be considered. 6156 */ 6157 this.obj_morphParams = { 6158 'margin-top': [this.el_resizeBox.getStyle('margin-top').toInt(), this.el_resizeBox.getStyle('margin-top').toInt() - int_beginHeight] 6159 }; 6160 6161 /* 6162 * After the morph we set display: none to make 6163 * sure that the element is hidden. 6164 */ 6165 this.obj_endStyles = { 6166 'display': 'none' 6167 }; 6168 break; 6169 } 6170 6171 /* 6172 * If the paddings should also be morphed, we have to adjust the morphParams 6173 * as well as the startStyles and endStyles accordingly. 6174 */ 6175 if (this.__models.options.data.bln_morphPaddings) { 6176 this.obj_morphParams['padding-top'] = [this.obj_initialPaddings.top, 0]; 6177 this.obj_morphParams['padding-bottom'] = [this.obj_initialPaddings.bottom, 0]; 6178 6179 this.obj_startStyles['padding-top'] = this.obj_initialPaddings.top; 6180 this.obj_startStyles['padding-bottom'] = this.obj_initialPaddings.bottom; 6181 6182 this.obj_endStyles['padding-top'] = 0; 6183 this.obj_endStyles['padding-bottom'] = 0; 6184 } 6185 6186 /* 6187 * We apply the start styles ... 6188 */ 6189 this.el_resizeBox.setStyles(this.obj_startStyles); 6190 6191 /* 6192 * ... and then we start the morph effect. 6193 */ 6194 if (this.bln_skipAnimation) { 6195 this.obj_morphSkipAnimation.start(this.obj_morphParams); 6196 } else { 6197 this.obj_morph.start(this.obj_morphParams); 6198 } 6199 }, 6200 6201 /* 6202 * We keep the current toggle status (open or closed) in a variable and 6203 * with this function, we toggle the status. This function gets called 6204 * after the morph has finisehd. 6205 */ 6206 toggleStatus: function() { 6207 if (this.str_toggleStatus === 'open') { 6208 this.str_toggleStatus = 'closed'; 6209 } else { 6210 this.str_toggleStatus = 'open'; 6211 } 6212 6213 if (this.__models.options.data.str_cookieIdentifierName) { 6214 Cookie.write(this.__models.options.data.str_cookieIdentifierName, this.str_toggleStatus); 6215 } 6216 }, 6217 6218 /* 6219 * The styles to apply after finishing the morph are stored in a variable 6220 * and these styles are being applied with this function. 6221 */ 6222 setEndStyles: function() { 6223 this.el_resizeBox.setStyles(this.obj_endStyles); 6224 6225 /* 6226 * Reset the end styles so that the end styles of the opening morph 6227 * will not be used with the closing morph if the closing morph doesn't 6228 * explicitly define end styles. 6229 */ 6230 this.obj_endStyles = {}; 6231 }, 6232 6233 adjustClasses: function() { 6234 if (!this.obj_morph.isRunning()) { 6235 /* 6236 * Remove the running class from the elements 6237 */ 6238 if (this.el_wrapper !== undefined && this.el_wrapper !== null) { 6239 this.el_wrapper.removeClass(this.__models.options.data.str_classRunning); 6240 } 6241 this.el_resizeBox.removeClass(this.__models.options.data.str_classRunning); 6242 this.els_toggler.removeClass(this.__models.options.data.str_classRunning); 6243 } 6244 6245 switch (this.str_toggleStatus) { 6246 case 'open': 6247 /* 6248 * Setting the classes indicating the current status 6249 */ 6250 if (this.el_wrapper !== undefined && this.el_wrapper !== null) { 6251 this.el_wrapper.removeClass(this.__models.options.data.str_classClosed); 6252 this.el_wrapper.addClass(this.__models.options.data.str_classOpen); 6253 } 6254 this.el_resizeBox.removeClass(this.__models.options.data.str_classClosed); 6255 this.el_resizeBox.addClass(this.__models.options.data.str_classOpen); 6256 this.els_toggler.removeClass(this.__models.options.data.str_classClosed); 6257 this.els_toggler.addClass(this.__models.options.data.str_classOpen); 6258 break; 6259 6260 case 'closed': 6261 /* 6262 * Setting the classes indicating the current status 6263 */ 6264 if (this.el_wrapper !== undefined && this.el_wrapper !== null) { 6265 this.el_wrapper.removeClass(this.__models.options.data.str_classOpen); 6266 this.el_wrapper.addClass(this.__models.options.data.str_classClosed); 6267 } 6268 this.el_resizeBox.removeClass(this.__models.options.data.str_classOpen); 6269 this.el_resizeBox.addClass(this.__models.options.data.str_classClosed); 6270 this.els_toggler.removeClass(this.__models.options.data.str_classOpen); 6271 this.els_toggler.addClass(this.__models.options.data.str_classClosed); 6272 break; 6273 } 6274 }, 6275 6276 /* 6277 * Things to do when the morph is completed 6278 */ 6279 onMorphComplete: function() { 6280 this.adjustClasses(); 6281 switch (this.str_toggleStatus) { 6282 case 'open': 6283 /* 6284 * Set the interval for sliding to the scroll position if necessary 6285 */ 6286 if (this.__models.options.data.bln_moveWithWindowScrollInMarginAnimationMode) { 6287 this.interval_scrollPositionChecker = setInterval(this.slideToScrollPosition.bind(this), 100); 6288 } 6289 break; 6290 6291 case 'closed': 6292 /* 6293 * Clear the interval for sliding to the scroll position if necessary 6294 */ 6295 if (this.__models.options.data.bln_moveWithWindowScrollInMarginAnimationMode) { 6296 if (this.interval_scrollPositionChecker !== null) { 6297 clearInterval(this.interval_scrollPositionChecker); 6298 } 6299 } 6300 break; 6301 } 6302 }, 6303 6304 /* 6305 * This function slides the resizeBox to the current scroll position, using 6306 * a quite complex logic to determine what the correct position is, if the 6307 * resizeBox is bigger than the window. 6308 * 6309 * It is very important, that scrolling the window allows the user to also 6310 * scroll to the top and bottom of the resizeBox so that he is able to see 6311 * its full content. 6312 */ 6313 slideToScrollPosition: function() { 6314 var morph_options = {}, 6315 int_slideTarget,
6316 str_edgeToSlideTo, 6317 obj_windowCoordinates, 6318 obj_elementCoordinates, 6319 obj_elementCoordinatesWithOffset; 6320 6321 /* 6322 * Don't start a new animation if the scroll position has not changed 6323 * since the last time that this function has been called. 6324 */ 6325 if (window.getScroll().y === this.int_windowScrollY) { 6326 return; 6327 } 6328 6329 /* 6330 * Store the current position 6331 */ 6332 this.int_windowScrollY = window.getScroll().y; 6333 6334 obj_windowCoordinates = { 6335 'top': window.getScroll().y, 6336 'bottom': window.getScroll().y + window.getSize().y, 6337 'width': window.getSize().x, 6338 'height': window.getSize().y 6339 }; 6340 6341 obj_elementCoordinates = this.el_resizeBox.getCoordinates(); 6342 obj_elementCoordinatesWithOffset = obj_elementCoordinates; 6343 obj_elementCoordinatesWithOffset.height = obj_elementCoordinatesWithOffset.height + this.__models.options.data.obj_moveWithWindowScrollInMarginAnimationModeOffsets.top + this.__models.options.data.obj_moveWithWindowScrollInMarginAnimationModeOffsets.bottom; 6344 obj_elementCoordinatesWithOffset.top -= this.__models.options.data.obj_moveWithWindowScrollInMarginAnimationModeOffsets.top; 6345 obj_elementCoordinatesWithOffset.bottom -= this.__models.options.data.obj_moveWithWindowScrollInMarginAnimationModeOffsets.bottom; 6346 6347 6348 if (obj_elementCoordinatesWithOffset.height <= obj_windowCoordinates.height) { 6349 /* 6350 * The element including its offsets is smaller than or equal the window. 6351 * This means that no scrolling is required for the user to see its 6352 * full content. In this situation, we slide the element to its 6353 * top position. 6354 */ 6355 str_edgeToSlideTo = 'top'; 6356 } else { 6357 /* 6358 * The element including its offsets is bigger than the window. 6359 * Scrolling is required for the user to see its full content. 6360 */ 6361 if (obj_elementCoordinatesWithOffset.top > obj_windowCoordinates.top && obj_elementCoordinatesWithOffset.top < obj_windowCoordinates.bottom) { 6362 /* 6363 * The element's top edge is visible 6364 */ 6365 str_edgeToSlideTo = 'top'; 6366 } else if (obj_elementCoordinatesWithOffset.bottom > obj_windowCoordinates.top && obj_elementCoordinatesWithOffset.bottom < obj_windowCoordinates.bottom) { 6367 /* 6368 * The element's bottom edge is visible 6369 */ 6370 str_edgeToSlideTo = 'bottom'; 6371 } else { 6372 /* 6373 * None of the element's edges is visible 6374 */ 6375 if (obj_elementCoordinatesWithOffset.bottom < obj_windowCoordinates.top) { 6376 /* 6377 * The complete element is above the window's top edge, so we 6378 * slide to the element's bottom edge 6379 */ 6380 str_edgeToSlideTo = 'bottom'; 6381 } else if (obj_elementCoordinatesWithOffset.top > obj_windowCoordinates.bottom) { 6382 /* 6383 * The complete element is below the window's top edge, so we 6384 * slide to the element's top edge 6385 */ 6386 str_edgeToSlideTo = 'top'; 6387 } else { 6388 /* 6389 * The element must be higher than the screen and the visible 6390 * screen is right between the element's top and bottom edge. 6391 * In this situation, we don't slide anywhere which allows the 6392 * user to actually scroll to the top and bottom of the element 6393 * and to see its full content. 6394 */ 6395 str_edgeToSlideTo = 'none'; 6396 } 6397 } 6398 } 6399 6400 if (str_edgeToSlideTo === 'top') { 6401 int_slideTarget = this.int_windowScrollY + this.__models.options.data.obj_moveWithWindowScrollInMarginAnimationModeOffsets.top; 6402 } else if (str_edgeToSlideTo === 'bottom') { 6403 int_slideTarget = (this.int_windowScrollY - (obj_elementCoordinatesWithOffset.height - obj_windowCoordinates.height)); 6404 } else { 6405 // don't slide anywhere 6406 return; 6407 } 6408 6409 morph_options[this.__models.options.data.str_animationMode] = int_slideTarget; 6410 this.obj_morph_slideToScrollPosition.start(morph_options); 6411 }, 6412 6413 /* 6414 * Closing the element with a click outside the wrapper element 6415 */ 6416 closeOnOutsideClick: function(event) { 6417 var bln_clickedInside = false;
6418 6419 /* 6420 * Ignore the click if the morph is running because we don't want 6421 * the resizeBox to be closed before it is even completely open. 6422 */ 6423 if (this.obj_morph.isRunning()) { 6424 return; 6425 } 6426 6427 /* --> 6428 * Don't close if the click was inside, which is the case if the 6429 * event.target itself or one of its parents is either el_resizeBox 6430 * or el_wrapper 6431 */ 6432 if (event.target === this.el_resizeBox || event.target === this.el_wrapper) { 6433 bln_clickedInside = true; 6434 } 6435 6436 Array.each(event.target.getParents(), function(el_parent) { 6437 if (el_parent === this.el_resizeBox || el_parent === this.el_wrapper) { 6438 bln_clickedInside = true; 6439 } 6440 }.bind(this)); 6441 6442 if (bln_clickedInside) { 6443 return; 6444 } 6445 /* 6446 * <-- 6447 */ 6448 6449 /* 6450 * Call the onToggleEvent function which actually closes the resizeBox 6451 */ 6452 this.onToggleEvent(); 6453 }, 6454 6455 /* 6456 * Close the resizeBox if the close button is clicked. 6457 */ 6458 closeOnCloseButtonClick: function() { 6459 /* 6460 * Ignore the click if the morph is running because we don't want 6461 * the resizeBox to be closed before it is even completely open. 6462 */ 6463 if (this.obj_morph.isRunning()) { 6464 return; 6465 } 6466 6467 /* 6468 * Call the onToggleEvent function which actually closes the resizeBox 6469 */ 6470 this.onToggleEvent(); 6471 }, 6472 6473 /* 6474 * This function determines the resizeBox's height and also considers the 6475 * element's box-sizing, which requires the element's padding and border to 6476 * be subtracted (content-box) or not (border-box). 6477 */ 6478 getResizeBoxHeight: function() { 6479 var obj_dimensions, 6480 int_height; 6481 6482 obj_dimensions = this.el_resizeBox.getDimensions({computeSize: true}); 6483 int_height = obj_dimensions.height; 6484 6485 if (this.el_resizeBox.getStyle('box-sizing') !== 'border-box') { 6486 int_height = int_height - obj_dimensions['padding-top'] - obj_dimensions['padding-bottom'] - obj_dimensions['border-top-width'] - obj_dimensions['border-bottom-width']; 6487 } 6488 6489 return int_height; 6490 } 6491}; 6492 6493lsjs.addViewClass(str_moduleName, obj_classdef); 6494 6495})(); 6496// < ###### VIEW ## 6497// 6498// ###### CONTROLLER ## > 6499(function() { 6500 6501// ### ENTER MODULE NAME HERE ###### 6502var str_moduleName = 'unfold'; 6503// ################################# 6504 6505var obj_classdef = { 6506 start: function() { 6507 } 6508}; 6509 6510lsjs.addControllerClass(str_moduleName, obj_classdef); 6511 6512lsjs.__moduleHelpers[str_moduleName] = { 6513 self: null, 6514 6515 start: function(obj_options) { 6516 this.self = lsjs.createModule({ 6517 __name: str_moduleName 6518 }); 6519 6520 this.self.__models.options.set(obj_options); 6521 } 6522}; 6523 6524})(); 6525// < ###### CONTROLLER ## 6526// < #### MODULE ## 6527 6528 6529 6530 6531// #### MODULE ## > 6532// ###### TEMPLATES ## > 6533 6534// < ###### TEMPLATES ## 6535 6536// ###### MODELS ## > 6537 6538// < ###### MODELS ## 6539 6540// ###### VIEW ## > 6541(function() { 6542 6543// ### ENTER MODULE NAME HERE ###### 6544var str_moduleName = 'zoomInstance'; 6545// ################################# 6546 6547var obj_classdef = { 6548 el_zoomImage: null, 6549 el_zoomImageWrapper: null, 6550 el_zoomBigBox: null, 6551 el_zoomedImage: null, 6552 el_zoomAreaMarker: null, 6553 el_zoomedImagePreloader: null, 6554 6555 obj_zoomImageSize: null, 6556 obj_zoomImagePosition: null, 6557 6558 obj_zoomedImageSize: null, 6559 6560 int_zoomAreaMarkerWidth: 0, 6561 int_zoomAreaMarkerHeight: 0, 6562 obj_zoomAreaMarkerBorderSize: null, 6563 6564 int_zoomAreaMarkerTop: 0, 6565 int_zoomAreaMarkerLeft: 0, 6566 6567 start: function() { 6568 if (typeOf(this.__module.__parentModule.__models.options.data.func_beforeInit) === 'function') { 6569 this.__module.__parentModule.__models.options.data.func_beforeInit(this); 6570 } 6571 6572 this.el_zoomImage = this.__module.obj_args.el_zoomImage; 6573 this.el_zoomImage.addClass('zoomImage'); 6574 6575 window.addEvent('resize', this.startZoomer.bind(this)); 6576 6577 this.el_zoomImageWrapper = new Element('span.lsZoomImageWrapper'); 6578 this.el_zoomImageWrapper.inject(this.el_zoomImage, 'before'); 6579 this.el_zoomImage.inject(this.el_zoomImageWrapper); 6580 6581 this.el_zoomAreaMarker = new Element('span.lsZoomAreaMarker'); 6582 this.el_zoomAreaMarker.inject(this.el_zoomImageWrapper, 'bottom'); 6583 6584 var elementTypeZoomBigBox = this.__module.__parentModule.__models.options.data.bigBoxFitsThumb == true ? 'span' : 'div'; 6585 this.el_zoomBigBox = new Element(elementTypeZoomBigBox + '.lsZoomBigBox'); 6586 6587 if (this.__module.__parentModule.__models.options.data.bigBoxFitsThumb == true) { 6588 this.el_zoomBigBox.inject(this.el_zoomImageWrapper, 'top'); 6589 } else if (this.__module.__parentModule.__models.options.data.bigBoxContainerSelector != '' && $$(this.__module.__parentModule.__models.options.data.bigBoxContainerSelector)[0] != undefined) { 6590 this.el_zoomBigBox.inject($$(this.__module.__parentModule.__models.options.data.bigBoxContainerSelector)[0], 'bottom'); 6591 } else { 6592 this.el_zoomBigBox.inject(this.el_zoomImageWrapper, 'top'); 6593 } 6594 6595 this.el_zoomedImage = new Element('img').setProperties({'src': this.__module.obj_args.str_href, 'alt': ''}).setStyle('display', 'none'); 6596 this.el_zoomedImagePreloader = new Element('div.lsZoomPreloader'); 6597 this.el_zoomBigBox.adopt( 6598 this.el_zoomedImagePreloader, 6599 this.el_zoomedImage 6600 ); 6601 6602 this.el_zoomedImage.addEvent('load', this.removePreloader.bind(this)); 6603 6604 this.el_zoomImageWrapper.addEvent('mouseenter', this.showBigBox.bind(this)); 6605 this.el_zoomImageWrapper.addEvent('mouseleave', this.hideBigBox.bind(this)); 6606 6607 if (typeOf(this.__module.__parentModule.__models.options.data.func_afterInit) === 'function') { 6608 this.__module.__parentModule.__models.options.data.func_afterInit(this); 6609 } 6610 }, 6611 6612 startZoomer: function() { 6613 this.el_zoomImageWrapper.setStyles({ 6614 'position': 'relative', 6615 'width': 'auto', 6616 'height': 'auto' 6617 }); 6618 6619 this.obj_zoomImageSize = this.el_zoomImage.measure(function() {
6620 return this.getSize(); 6621 }); 6622 6623 if (this.__module.__parentModule.__models.options.data.bigBoxFitsThumb == true) { 6624 this.__module.__parentModule.__models.options.data.bigBoxSize = this.obj_zoomImageSize; 6625 } 6626 6627 this.getZoomImagePosition(); 6628 6629 this.el_zoomImageWrapper.setStyles({ 6630 'width': this.obj_zoomImageSize.x, 6631 'height': this.obj_zoomImageSize.y 6632 }); 6633 6634 this.el_zoomBigBox.setStyles({ 6635 'width': this.__module.__parentModule.__models.options.data.bigBoxSize.x, 6636 'height': this.__module.__parentModule.__models.options.data.bigBoxSize.y, 6637 'left': 0, 6638 'top': 0, 6639 'position': 'absolute' 6640 }); 6641 6642 if ( 6643 this.__module.__parentModule.__models.options.data.bigBoxFitsThumb == false 6644 && (this.__module.__parentModule.__models.options.data.bigBoxContainerSelector == '' || $$(this.__module.__parentModule.__models.options.data.bigBoxContainerSelector)[0] == undefined) 6645 ) { 6646 var topDistance = 0; 6647 var leftDistance = 0; 6648 switch(this.__module.__parentModule.__models.options.data.side) { 6649 case 'top': 6650 topDistance = (topDistance - this.__module.__parentModule.__models.options.data.bigBoxSize.y) + this.__module.__parentModule.__models.options.data.offset.y; 6651 leftDistance = this.__module.__parentModule.__models.options.data.offset.x; 6652 break; 6653 6654 case 'bottom': 6655 topDistance = (topDistance + this.obj_zoomImageSize.y) + this.__module.__parentModule.__models.options.data.offset.y; 6656 leftDistance = this.__module.__parentModule.__models.options.data.offset.x; 6657 break; 6658 6659 case 'left': 6660 topDistance = this.__module.__parentModule.__models.options.data.offset.y; 6661 leftDistance = (leftDistance - this.__module.__parentModule.__models.options.data.bigBoxSize.x) + this.__module.__parentModule.__models.options.data.offset.x; 6662 break; 6663 6664 case 'right': 6665 topDistance = this.__module.__parentModule.__models.options.data.offset.y; 6666 leftDistance = (leftDistance + this.obj_zoomImageSize.x) + this.__module.__parentModule.__models.options.data.offset.x; 6667 break; 6668 } 6669 6670 this.el_zoomBigBox.setStyles({ 6671 'left': leftDistance + 'px', 6672 'top': topDistance + 'px' 6673 }); 6674 } 6675 6676 this.setZoomAreaMarkerSize(); 6677 this.initializeZoomAreaMarkerMovement(); 6678 }, 6679 6680 showBigBox: function() { 6681 this.el_zoomBigBox.setStyle('display', 'block'); 6682 this.showZoomAreaMarker(); 6683 }, 6684 6685 hideBigBox: function() { 6686 this.el_zoomBigBox.setStyle('display', 'none'); 6687 this.hideZoomAreaMarker(); 6688 }, 6689 6690 removePreloader: function() { 6691 this.el_zoomedImage.setStyle('display', 'inline'); 6692 this.el_zoomedImagePreloader.destroy(); 6693 this.startZoomer(); 6694 }, 6695 6696 setZoomAreaMarkerSize: function() { 6697 var objZoomAreaMarkerStyles; 6698 6699 this.obj_zoomedImageSize = this.el_zoomedImage.measure(function() { 6700 return this.getSize(); 6701 }); 6702 6703 this.obj_zoomAreaMarkerBorderSize = { 6704 'left': this.el_zoomAreaMarker.getStyle('border-left-width'), 6705 'right': this.el_zoomAreaMarker.getStyle('border-right-width'), 6706 'top': this.el_zoomAreaMarker.getStyle('border-top-width'), 6707 'bottom': this.el_zoomAreaMarker.getStyle('border-bottom-width') 6708 }; 6709 6710 this.obj_zoomAreaMarkerBorderSize = { 6711 'left': parseInt(this.obj_zoomAreaMarkerBorderSize.left.substring(0,this.obj_zoomAreaMarkerBorderSize.left.length - 2)), 6712 'right': parseInt(this.obj_zoomAreaMarkerBorderSize.right.substring(0,this.obj_zoomAreaMarkerBorderSize.right.length - 2)), 6713 'top': parseInt(this.obj_zoomAreaMarkerBorderSize.top.substring(0,this.obj_zoomAreaMarkerBorderSize.top.length - 2)), 6714 'bottom': parseInt(this.obj_zoomAreaMarkerBorderSize.bottom.substring(0,this.obj_zoomAreaMarkerBorderSize.bottom.length - 2)) 6715 }; 6716 6717 this.int_zoomAreaMarkerWidth = this.obj_zoomImageSize.x / (this.obj_zoomedImageSize.x / this.__module.__parentModule.__models.options.data.bigBoxSize.x); 6718 this.int_zoomAreaMarkerWidth = this.int_zoomAreaMarkerWidth > this.obj_zoomImageSize.x ? this.obj_zoomImageSize.x : this.int_zoomAreaMarkerWidth; 6719 6720 this.int_zoomAreaMarkerHeight = this.obj_zoomImageSize.y / (this.obj_zoomedImageSize.y / this.__module.__parentModule.__models.options.data.bigBoxSize.y); 6721 this.int_zoomAreaMarkerHeight = this.int_zoomAreaMarkerHeight > this.obj_zoomImageSize.y ? this.obj_zoomImageSize.y : this.int_zoomAreaMarkerHeight; 6722 6723 objZoomAreaMarkerStyles = { 6724 'width': (this.int_zoomAreaMarkerWidth - this.obj_zoomAreaMarkerBorderSize.left - this.obj_zoomAreaMarkerBorderSize.right).round(), 6725 'height': (this.int_zoomAreaMarkerHeight - this.obj_zoomAreaMarkerBorderSize.top - this.obj_zoomAreaMarkerBorderSize.bottom).round() 6726 }; 6727 6728 this.el_zoomAreaMarker.setStyles(objZoomAreaMarkerStyles); 6729 }, 6730 6731 initializeZoomAreaMarkerMovement: function() { 6732 this.el_zoomImageWrapper.addEvent('mousemove', this.moveZoomAreaMarker.bind(this)); 6733 }, 6734 6735 moveZoomAreaMarker: function(event) { 6736 /* 6737 * The mouse position (relative to the hovered image) represents the center of the zoomAreaMarker 6738 */ 6739 var mouseX = event.page.x - this.obj_zoomImagePosition.x; 6740 mouseX = mouseX > this.obj_zoomImageSize.x ? this.obj_zoomImageSize.x : mouseX; 6741 6742 var mouseY = event.page.y - this.obj_zoomImagePosition.y;
6743 mouseY = mouseY > this.obj_zoomImageSize.y ? this.obj_zoomImageSize.y : mouseY; 6744 6745 this.int_zoomAreaMarkerLeft = mouseX - (this.int_zoomAreaMarkerWidth / 2); 6746 if (this.int_zoomAreaMarkerLeft + this.int_zoomAreaMarkerWidth > this.obj_zoomImageSize.x) { 6747 this.int_zoomAreaMarkerLeft = this.obj_zoomImageSize.x - this.int_zoomAreaMarkerWidth; 6748 } 6749 if (this.int_zoomAreaMarkerLeft < 0) { 6750 this.int_zoomAreaMarkerLeft = 0; 6751 } 6752 6753 this.int_zoomAreaMarkerTop = mouseY - (this.int_zoomAreaMarkerHeight / 2); 6754 if (this.int_zoomAreaMarkerTop + this.int_zoomAreaMarkerHeight > this.obj_zoomImageSize.y) { 6755 this.int_zoomAreaMarkerTop = this.obj_zoomImageSize.y - this.int_zoomAreaMarkerHeight; 6756 } 6757 if (this.int_zoomAreaMarkerTop < 0) { 6758 this.int_zoomAreaMarkerTop = 0; 6759 } 6760 6761 this.el_zoomAreaMarker.setStyles({ 6762 'left': this.int_zoomAreaMarkerLeft, 6763 'top': this.int_zoomAreaMarkerTop 6764 }); 6765 6766 this.adjustBigBoxOffset(); 6767 }, 6768 6769 adjustBigBoxOffset: function() { 6770 var objOffsets = { 6771 x: this.obj_zoomedImageSize.x / (this.obj_zoomImageSize.x / this.int_zoomAreaMarkerLeft), 6772 y: this.obj_zoomedImageSize.y / (this.obj_zoomImageSize.y / this.int_zoomAreaMarkerTop) 6773 } 6774 this.el_zoomedImage.setStyles({ 6775 'left': objOffsets.x * -1, 6776 'top': objOffsets.y * -1 6777 }); 6778 }, 6779 6780 showZoomAreaMarker: function() { 6781 this.el_zoomAreaMarker.setStyle('display', 'block'); 6782 }, 6783 6784 hideZoomAreaMarker: function() { 6785 this.el_zoomAreaMarker.setStyle('display', 'none'); 6786 }, 6787 6788 getZoomImagePosition: function() { 6789 if (typeOf(this.__module.__parentModule.__models.options.data.func_beforePositionDetection) === 'function') { 6790 this.__module.__parentModule.__models.options.data.func_beforePositionDetection(this); 6791 } 6792 6793 this.obj_zoomImagePosition = this.el_zoomImage.measure(function() { 6794 return this.getPosition(); 6795 }); 6796 6797 if (typeOf(this.__module.__parentModule.__models.options.data.func_afterPositionDetection) === 'function') { 6798 this.__module.__parentModule.__models.options.data.func_afterPositionDetection(this); 6799 } 6800 } 6801}; 6802 6803lsjs.addViewClass(str_moduleName, obj_classdef); 6804 6805})(); 6806// < ###### VIEW ## 6807// 6808// ###### CONTROLLER ## > 6809(function() { 6810 6811// ### ENTER MODULE NAME HERE ###### 6812var str_moduleName = 'zoomInstance'; 6813// ################################# 6814 6815var obj_classdef = { 6816 start: function() { 6817 } 6818}; 6819 6820lsjs.addControllerClass(str_moduleName, obj_classdef); 6821 6822})(); 6823// < ###### CONTROLLER ## 6824// < #### MODULE ## 6825 6826 6827 6828 6829// #### MODULE ## > 6830// ###### TEMPLATES ## > 6831 6832// < ###### TEMPLATES ## 6833 6834// ###### MODELS ## > 6835(function() { 6836 6837var obj_classdef_model = { 6838 name: 'options', 6839 6840 data: {}, 6841 6842 start: function() { 6843 /* 6844 * Initializing the options in the data object with default values which 6845 * can later be overwritten when the "set" method is called with other options 6846 */ 6847 this.data = { 6848 el_domReference: null, 6849 selector: '.lsZoom a img', 6850 func_beforeInit: null, 6851 func_afterInit: null, 6852 func_beforePositionDetection: null, 6853 func_afterPositionDetection: null, 6854 bigBoxFitsThumb: false, 6855 bigBoxContainerSelector: '', 6856 bigBoxSize: { 6857 x: 400, 6858 y: 200 6859 }, 6860 bigImageUrlAttribute: 'href', // defines which attribute holds the url of the big image, defaults to href 6861 useAnchorElement: true, // defines whether the bigImageUrlAttribute will be checked for the image itself or it's parent anchor element 6862 side: 'right', // left, right, top or bottom 6863 offset: { 6864 x: 10, 6865 y: 10 6866 } // offset in px 6867 }; 6868 }, 6869 6870 set: function(obj_options) { 6871 Object.merge(this.data, obj_options); 6872 this.__module.onModelLoaded(); 6873 } 6874}; 6875 6876lsjs.addModelClass('zoomManager', obj_classdef_model); 6877 6878})(); 6879 6880 6881 6882 6883// < ###### MODELS ## 6884 6885// ###### VIEW ## > 6886 6887// < ###### VIEW ## 6888// 6889// ###### CONTROLLER ## > 6890(function() { 6891 6892// ### ENTER MODULE NAME HERE ###### 6893var str_moduleName = 'zoomManager'; 6894// ################################# 6895 6896var obj_classdef = { 6897 start: function() { 6898 var els_toEnhance; 6899 6900 if (this.__models.options.data.el_domReference !== undefined && typeOf(this.__models.options.data.el_domReference) === 'element') { 6901 els_toEnhance = this.__models.options.data.el_domReference.getElements(this.__models.options.data.selector); 6902 } else { 6903 els_toEnhance = $$(this.__models.options.data.selector); 6904 } 6905 6906 Array.each(els_toEnhance, function(el_zoomImage) { 6907 /* -> 6908 * Make sure not to handle an element more than once 6909 */ 6910 if (!el_zoomImage.retrieve('alreadyHandledBy_' + str_moduleName)) { 6911 el_zoomImage.store('alreadyHandledBy_' + str_moduleName, true); 6912 } else { 6913 return; 6914 } 6915 /* 6916 * <- 6917 */ 6918 6919 /* 6920 * Try to verify that the element identified with the selector is an 6921 * image and return if it isn't. We check for an existing src attribute 6922 * because an image without a src attribute wouldn't be okay as well. 6923 */ 6924 var str_checkSrcProperty = el_zoomImage.getProperty('src'); 6925 if (str_checkSrcProperty === undefined || str_checkSrcProperty === null || str_checkSrcProperty == '') { 6926 return; 6927 } 6928 6929 var el_toDetermineHref = 6930 ( 6931 this.__models.options.data.useAnchorElement != undefined 6932 && this.__models.options.data.useAnchorElement == true 6933 ) 6934 ? el_zoomImage.getParent('a') 6935 : el_zoomImage; 6936 if (el_toDetermineHref === undefined || el_toDetermineHref === null) { 6937 return; 6938 } 6939 6940 var str_href = el_toDetermineHref.getProperty(this.__models.options.data.bigImageUrlAttribute); 6941 if (str_href === undefined || str_href === null || str_href === '' || !str_href) { 6942 return; 6943 } 6944 6945 var str_tmpImageSrc = el_zoomImage.getProperty('src'); 6946 el_zoomImage.removeProperty('src'); 6947 el_zoomImage.addEvent('load', function() { 6948 lsjs.createModule({ 6949 __name: 'zoomInstance', 6950 __parentModule: this.__module, 6951 __useLoadingIndicator: false, 6952 el_zoomImage: el_zoomImage, 6953 str_href: str_href 6954 }); 6955 }.bind(this)); 6956 el_zoomImage.setProperty('src', str_tmpImageSrc); 6957 6958 }.bind(this)); 6959 } 6960}; 6961 6962lsjs.addControllerClass(str_moduleName, obj_classdef); 6963 6964lsjs.__moduleHelpers[str_moduleName] = { 6965 self: null, 6966 6967 start: function(obj_options) { 6968 this.self = lsjs.createModule({ 6969 __name: str_moduleName 6970 }); 6971 this.self.__models.options.set(obj_options); 6972 } 6973}; 6974 6975}
6975)(); 6976// < ###### CONTROLLER ## 6977// < #### MODULE ## 6978 6979 6980// < ## CORE MODULES ## 6981 6982// ## APP MODULES ## > 6983 6984// < ## APP MODULES ## 6985 6986// ## APP ## > 6987 6988// < ## APP ## 6989})();
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.