1 /** 2 * yaf4ajn.js - Frontendcontroller 1.90.0 3 * Copyright(c) 2009, Deutsche Telekom AG, Products & Innovation 4 * GNU LESSER GENERAL PUBLIC LICENSE 5 * http://www.gnu.org/licenses/lgpl-3.0.txt 6 * 7 * What is it? 8 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 9 * This is the Javascript part of the AJAX Hello World 1.90.0 Demo from Deutsche 10 * Telekom AG, Products & Innovation for the iX article. 11 * If this script is included in a webpage, it will start an AJAX call to a server 12 * and manipulate the html page depending on the answer from the server. 13 * Data exchange between the server and client happens with JSON data-inter 14 * -change format. 15 * Dependencies are prototype 1.6.0.3 and effects.js from script.aculo.us 1.8.2. 16 * This code is tempered with jslint (see tools/jslint). It has a sound set of 17 * UnitTests (see tools/jsunit) and it has documentation (see tools/jsdoc and 18 * docs-directory). 19 * This file is also available as a compressed version (see tools/compressed and 20 * yaf4ajn-compressed.js in js-directory.) 21 * 22 * Where to go? 23 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 24 * First examine the fecPagecontrol variable in the HTML page (where this 25 * script is included). It defines what the Frontendcontroller (fec) should do. 26 * (e.g. scopes - load data given by the scopenames) 27 * 28 * After the DOM of the webpage is loaded, these functions will be called 29 * automatically: 30 * >>> fec.base.initialize 31 * Initialize prototype-JS classes, read fecPagecontrol and start 32 * dataLoad 33 * >>> fec.base.dataLoad 34 * loads the data depending on given scopes in fecPagecontrol by firing AJAX 35 * requests to the server. If the server gives a valid response (HTTP2XX) 36 * processResponse is called 37 * >>> fec.main.processResponse 38 * This function receives the response data from the server and starts to 39 * process it. Depending on the response, requests will be fired again, the 40 * DOM of the will be manipulated or errormessages will be shown. 41 * 42 * Please use Firefox with Firebug-debugger to examine further functionality 43 * of this script. 44 * 45 * Change Log: 46 * v1.90.0 47 * - renamed this file to yaf4ajn.js 48 * - complete namespace refactoring (fec) 49 * - extracted configuration variables (fec.config) 50 * - replaced references to production environment (vespa, vsp -> fec) 51 * - fixed jslint findings 52 * - added documentation 53 */ 54 /*! yaf4ajn.js - Deutsche Telekom AG, Products & Innovation */ 55 /*jslint laxbreak: true */ 56 57 58 /* ___________ INIT GLOBAL VARIABLES/OBJECTS ________________________________ */ 59 60 var 61 /** 62 * Main object holder (namespace fec -> FrontEndController). In this object 63 * all important variables and functions are placed in a class-type hierarchy. 64 * 65 * @namespace Main object holder. 66 */ 67 fec = {}, 68 /** 69 * Holds the data that can be used by special DOM-setter (hook 70 * jsSetterPrefix) 71 * 72 * @type object 73 * @default undefined 74 * @see fec.config.jsSetterPrefix 75 */ 76 fecSetterValue = undefined, 77 /** 78 * Variable for an exception 79 * 80 * @type object 81 * @default undefined 82 */ 83 e = undefined; 84 85 /** instance for class FecMain */ 86 fec.main = undefined; 87 /** instance for class FecPageControl */ 88 fec.pageControl = undefined; 89 90 91 /* ___________ CONFIGIGURATION _____________________________________________ */ 92 /** 93 * Base configuration class 94 * 95 * @namespace fec.config 96 */ 97 fec.config = { 98 /** 99 * Element-Id of your HTML-form. 100 * 101 * @type string 102 * @default "fecForm" 103 * @constant 104 */ 105 htmlIdForm : 'fecForm', 106 /** 107 * Prefix of element-Ids, which should be replaced. 108 * 109 * @type string 110 * @default "fecData" 111 * @constant 112 */ 113 htmlIdDataPrefix : 'fecData', 114 /** 115 * Prefix of element-Id, which will be shown after completion (initially hidden) 116 * 117 * @type string 118 * @default "fecScope" 119 * @constant 120 */ 121 htmlIdScopePrefix : 'fecScope', 122 /** 123 * Prefix of element-Id, which will be hidden after completion (initially visible) 124 * 125 * @type string 126 * @default "fecScopeLoad" 127 * @constant 128 */ 129 htmlIdLoaderPrefix : 'fecScopeLoad', 130 /** 131 * Prefix of JS-Functions, which should be called to set data. 132 * 133 * @type string 134 * @default "fecHookSetData" 135 * @constant 136 * @see fecHookSetDataValuekey 137 */ 138 jsSetterPrefix : 'fecHookSetData', 139 /** 140 * Prefix of JS-Functions, which validate the form 141 * 142 * @type string 143 * @default "fecHookCheckForm" 144 * @constant 145 * @see fecHookCheckFormScopename 146 */ 147 jsCheckformPrefix : 'fecHookCheckForm', 148 /** 149 * URL to the server for ajax request 150 * 151 * @type string 152 * @default "/app/json" 153 * @constant 154 * @see http://www.prototypejs.org/api/ajax/request 155 */ 156 urlAjaxRequest : '/app/json', 157 /** 158 * URL to an errorpage 159 * 160 * @type string 161 * @default "/error.html" 162 * @constant 163 */ 164 urlErrorpage : '/error.html' 165 166 }; 167 // look for miscellaneous config-objects at the end of the file 168 169 170 /* ___________ START INIT AFTER PAGE IS LOADED ______________________________ */ 171 document.observe("dom:loaded", function() { fec.base.initialize(); }); 172 173 174 /** 175 * Base class for the main public fec methods 176 * 177 * @namespace fec.base 178 */ 179 fec.base = { 180 /** 181 * This function is called, after the DOM is loaded. It prepares the page, 182 * initializes classes and starts an AJAX request to load data from the 183 * server (dataLoad). 184 */ 185 initialize: function(){ 186 try { fecHookInitialize(); } catch (e) {} 187 if (fec.util.escapeFrame()) { return; } 188 fec.util.keypressObserver(document); 189 if (fec.main === undefined) { 190 fec.main = new FecMain(); 191 } 192 if ('undefined' != typeof fecPagecontrol) { 193 fec.pageControl = new FecPageControl(fecPagecontrol); 194 if (fec.pageControl.getScopes().length > 0) { 195 fec.base.dataLoad(); 196 $$('form[id^="'+fec.config.htmlIdForm+'"]').each ( function (fe) { 197 fec.util.submitObserver(fe.id); 198 }); 199 } 200 } 201 }, 202 /** 203 * Prepares the page and starts loading data from the server. 204 */ 205 dataLoad: function (){ 206 fec.main.showSpinner(fec.pageControl.getScopes(),true); 207 try { fec.form.clearErrorsInDom(); } catch(e){} 208 fec.main.getServerData(); 209 }, 210 /** 211 * Prepares the page and starts sending data to the server. This function 212 * will be called if a form-submit event (through event observer) fires but 213 * it can also be called directly. 214 */ 215 dataSend: function () { 216 fec.main.showScope('vspScopeFehler',false); 217 if (fec.main.checkForm()) { 218 fec.main.disableSubmitButtons(true); 219 fec.main.sendServerData(); 220 } else { fec.main.scrollToTop(); } 221 } 222 }; 223 224 /** 225 * Values Factory handles updating an html-element with a new value 226 * 227 * @namespace fec.valuesFactory 228 */ 229 fec.valuesFactory = { 230 /** 231 * Updates a TD-Element with newValue. If the Value is boolean, default 232 * Strings are set. 233 * 234 * @param {object} element Element that will be updated 235 * @param {object} newValue newValue 236 */ 237 td: function(element, newValue){ 238 if (typeof newValue == 'boolean') { 239 newValue = newValue === true ? fec.msg.YES : fec.msg.NO; 240 } 241 $(element).update(newValue.toString()); 242 }, 243 /** 244 * Updates a P-element by calling fec.valuesFactory.td . 245 * 246 * @see fec.valuesFactory.td 247 */ 248 p: function(element, newValue){ 249 fec.valuesFactory.td(element, newValue); 250 }, 251 /** 252 * Updates a SPAN-element by calling fec.valuesFactory.td . 253 * 254 * @see fec.valuesFactory.td 255 */ 256 span: function(element, newValue){ 257 fec.valuesFactory.td(element, newValue); 258 }, 259 /** 260 * Updates a INPUT-element, accordingly to the input type 261 * 262 * @param {object} element Element that will be updated 263 * @param {object} newValue newValue will be HTML-unescaped 264 */ 265 input: function(element, newValue){ 266 switch (element.type.toLowerCase()) { 267 case 'submit': return true; 268 case 'hidden': 269 if ($(element.name + 'Hidden')) { 270 $(element.name + 'Hidden').update(newValue.toString().unescapeHTML()); 271 } 272 fec.valuesFactory.textarea(element, newValue); 273 break; 274 case 'password': 275 case 'text': 276 fec.valuesFactory.textarea(element, newValue); 277 break; 278 case 'checkbox': 279 case 'radio': 280 fec.valuesFactory.inputSelector(element, newValue); 281 break; 282 } 283 return false; 284 }, 285 /** 286 * Handles lists of radio- and checbox-tags 287 * 288 * @param {object} element Element that will be updated 289 * @param {object} newValue newValue 290 * @private 291 */ 292 inputSelector: function(element, newValue){ 293 fields = document.getElementsByName(element.name); 294 for (var i = 0; i < fields.length; i++) { 295 if (fields[i].type == 'radio') { 296 if (fields[i].value == newValue.toString()) { 297 fields[i].checked = true; 298 } 299 } else { 300 if (fields[i].type == 'checkbox') { 301 fields[i].checked = (newValue.toString() == "true") ? true : false; 302 } 303 } 304 } 305 }, 306 /** 307 * Updates a TEXTAREA-element 308 * 309 * @param {object} element Element that will be updated 310 * @param {object} newValue newValue will be HTML-unescaped 311 */ 312 textarea: function(element, newValue){ 313 element.value = newValue.toString().unescapeHTML(); 314 }, 315 /** 316 * Selects the option of a SELECT-element 317 * 318 * @param {object} element Element that will be updated 319 * @param {object} newValue newValue 320 */ 321 select: function(element, newValue){ 322 var value = '', opt; 323 for (var i = 0; i < element.options.length; i++) { 324 if (element.options[i].value == newValue) { 325 element.selectedIndex = i; 326 return true; 327 } 328 } 329 } 330 }; 331 332 /** 333 * General form checking class - before data is submitted to the server, the 334 * formdata can be checked through javascript hooks (fecHookCheckFormXYZ). This 335 * class contains functions for validation. 336 * 337 * @namespace fec.util 338 * @see FecMain.checkForm 339 */ 340 fec.form = { 341 /** 342 * Keeps the state of form-validation, only if this property is true data 343 * will be submitted to the server. 344 * 345 * @type boolean 346 */ 347 isValid: true, 348 /** 349 * Counts the form-errors 350 * 351 * @type number 352 */ 353 errorCount: 0, 354 /** 355 * Prepares the page for JS-Form Validation. This method is called from the 356 * JS-validation-hook-functions. 357 * 358 * @see fecHookCheckFormScopename 359 * @todo extract html to config-class 360 */ 361 initFormValidation: function() { 362 this.errorCount = 0; 363 this.isValid = true; 364 var s = ""; 365 s += "\n<div id='vspFormError' class='large box naked image error' style='display:none;'>"; 366 s += "\n<img src='"+fec.util.getImageRoot()+"fileadmin/VOS/images/16x16/warnung.png' alt='"+ fec.msg.FE004 +"'/>"; 367 s += "\n<h3>"+ fec.msg.FE003 +":</h3>"; 368 s += "\n<ul class='errorlist' id='vspFormErrorList'>\n</ul>"; 369 s += "\n</div>\n"; 370 s += "\n<div style='clear:both;height:1px'></div>"; 371 if($('vspFormError') === null) { 372 if ($('vspScopeFehler')) { 373 $('vspScopeFehler').insert({'after':s}); 374 } 375 } else { 376 if ($('vspFormError').childElements().size() < 1) { 377 $('vspFormError').replace(s); 378 } else { 379 this.clearErrorsInDom(); 380 381 } 382 } 383 }, 384 /** 385 * Adds an error to the DOM and tries to change the class of the surrounding 386 * HTML code of the input field 387 * 388 * @param {object} element - HTML-Element that should me marked 389 * @param {string} errtxt - Errortext 390 */ 391 setErrorInDom: function(element, errtxt) { 392 this.errorCount++; 393 try{ 394 try { 395 var trElement = element.up('tr'); 396 trElement.addClassName('error'); 397 var tdElements = trElement.select('td'); 398 if (tdElements.length > 1) { 399 tdElements[0].addClassName('left'); 400 tdElements[1].addClassName('right'); 401 } 402 if (this.errorCount==1) { 403 element.focus(); 404 } 405 } catch (e) {} 406 if (errtxt) { 407 $('vspFormErrorList').insert({'bottom':'<li>'+errtxt+'</li>\n'}); 408 } 409 } catch(e) {} 410 this.isValid = false; 411 }, 412 /** 413 * Clear the errors on the page and try to remove error-CSS-classes. 414 */ 415 clearErrorsInDom: function() { 416 // Entferne rote Umrandungen 417 var trElements = $$("tr[class*='error']"); 418 try { 419 for(var i=0; i<trElements.length; i++) { 420 trElements[i].removeClassName('error'); 421 trElements[i].select('td').each(function(element) { 422 if (element.hasClassName('left')) { 423 element.removeClassName('left'); 424 } 425 if (element.hasClassName('right')) { 426 element.removeClassName('right'); 427 } 428 }); 429 } 430 } catch (e) {} 431 // Entferne Fehlermeldung in Box und blende Box aus 432 if ($('vspFormErrorList')) { 433 $('vspFormErrorList').update(""); 434 } 435 if ($('vspFormError')) { 436 $('vspFormError').hide(); 437 } 438 this.isValid = true; 439 }, 440 /** 441 * Checks if the form ist valid and returns true or shows the ErrorBox. 442 * 443 * @returns {boolean} valid or not 444 */ 445 checkIfFormValid: function() { 446 var warn = $('vspFormError'); 447 if (this.isValid) { 448 warn.hide(); 449 return true; 450 } else { 451 if ('undefined' != typeof Effect) { 452 var x = new Effect.Appear( warn, { duration:0.3, keepBackgroundImage:true } ); 453 } else { 454 warn.show(); 455 } 456 return false; 457 } 458 }, 459 /** 460 * Checks if the value is a number. 461 * 462 * @param {object} val - input value 463 * @returns {boolean} number or not 464 */ 465 checkTypeNumber: function(val) { 466 var regex = /^\d+$/; 467 return regex.test(val); 468 }, 469 /** 470 * Converts a date Sring DD.MM.YYYY to date object 471 * 472 * @param {String} val - Datestring in format DD.MM.YYYY 473 * @returns {Date|null} Date-object or null 474 */ 475 stringToDate: function (val) { 476 var regex = /^\d{2}(\.)\d{2}\1\d{4}$/; 477 if (regex.test(val)) { 478 var parts = val.split("."); 479 if (3 == parts.length) { 480 var testdate = new Date(parts[2],parts[1]-1,parts[0]); 481 var testyear = testdate.getFullYear(); 482 var testmonth = testdate.getMonth()+1; 483 var testday = testdate.getDate(); 484 if (testyear == parts[2] && testmonth == parts[1]*1 && testday == parts[0]*1) { 485 return testdate; 486 } 487 } 488 } 489 return null; 490 }, 491 /** 492 * checks if String is a date 493 * 494 * @param {object} val - input value 495 * @returns {boolean} is a Date-object or not 496 */ 497 checkTypeDate: function(val) { 498 var testdate = fec.form.stringToDate(val); 499 return null !== testdate; 500 }, 501 /** 502 * checks if the input value is in the past 503 * 504 * @param {object} val - input value 505 * @returns {boolean} 506 */ 507 checkTypeDateInPast: function(val) { 508 return fec.form.checkTypeDate(val) && fec.form.stringToDate(val) < new Date(); 509 }, 510 /** 511 * checks if the input value is a german zipcode 512 * 513 * @param {object} val - input value 514 * @returns {boolean} 515 */ 516 checkTypePostleitzahl: function(val) { 517 var regex = /^[0-9]{5}$/; 518 return regex.test(val); 519 }, 520 /** 521 * checks if the input value is a HausnummerMitZusatz ;) 522 * 523 * @param {object} val - input value 524 * @returns {boolean} 525 */ 526 checkTypeHausnummerMitZusatz: function(val) { 527 var regex = /^[1-9]{1}[0-9a-zA-Z \-]*$/; 528 return regex.test(val); 529 }, 530 /** 531 * Checks if the input value is a email adresse - intentionally weak check. 532 * Serverside validation should go deeper. 533 * 534 * @param {object} val - input value 535 * @returns {boolean} 536 */ 537 checkTypeEmail: function(val) { 538 var regex = /.*@.*\..*/; 539 return regex.test(val); 540 }, 541 /** 542 * regex check helper function 543 * 544 * @param {Regex} regex - the regex test string 545 * @param {string} value - String on which the regex-condition is tested 546 * @returns {boolean} 547 */ 548 checkRegex: function(regex, val) { 549 return regex.test(val); 550 }, 551 /** 552 * Set an HTML input field to readonly. 553 * 554 * @param {object} element - HTML input value element 555 * @param {boolean} setIt - set it to readonly or not 556 */ 557 setElementReadonly: function(element, setIt) { 558 if(setIt) { 559 element.readOnly = true; 560 element.value = ''; 561 element.style.backgroundColor = '#EEEEEE'; 562 } else { 563 element.readOnly = false; 564 element.style.backgroundColor = ''; 565 } 566 }, 567 /** 568 * Checks if a string validates to certain conditions and adds errors 569 * if its not. 570 * 571 * @param {object} input - HTML input value element 572 * @param {boolean} manda - is it mandatory? 573 * @param {number} min - minimal string length 574 * @param {number} max - maximal string length 575 * @param {string} text - The beginning of the error message, e.G. "The name" or "The birthday". 576 * @returns {boolean} valid or not 577 */ 578 checkTypeString: function(input, manda, min, max, text) { 579 var retval = true; 580 if (input) { 581 var s = input.value; 582 var err = text.strip(); 583 if (manda && s.length === 0) { 584 fec.form.setErrorInDom(input, err+ " " + fec.msg.FE005); 585 retval = false; 586 } else if(s.length > 0){ 587 if (min === null || max === null) { 588 if (min === null && max !== null && s.length > max) { 589 err += " " + fec.util.formatMessage(fec.msg.FE006, {'MAX' : max}); 590 } 591 if (max === null && min !== null && s.length < min) { 592 err += " " + fec.util.formatMessage(fec.msg.FE007, {'MIN' : min}); 593 } 594 } else if (s.length < min || s.length > max) { 595 err += " " + fec.util.formatMessage(fec.msg.FE008, {'MIN' : min, 'MAX' : max}); 596 } 597 if (err.length > text.strip().length) { 598 fec.form.setErrorInDom(input, err); 599 retval = false; 600 } 601 } 602 } 603 return retval; 604 } 605 }; 606 607 /** 608 * General utils class 609 * 610 * @namespace fec.util 611 */ 612 fec.util = { 613 /** 614 * StartTime for alertTime function (for basic profiling) 615 */ 616 timeStart : new Date().getTime(), 617 618 /** 619 * Formats a message with object-values 620 * 621 * @param {string} msg - message-String with placeholders 622 * @param {object} hobj - object with values 623 */ 624 formatMessage:function(msg, hobj) { 625 var tokens = msg.match(/##\w+##/g); 626 for (var i=0; i<tokens.length; i++) { 627 var varStr = tokens[i].replace(/#/g, ''); 628 eval("msg = msg.replace(/"+tokens[i]+"/g, '"+hobj[varStr]+"');"); 629 } 630 return msg; 631 }, 632 /** 633 * Adds functions to observation list, which are executed when DOM ist loaded 634 * 635 * @param {object} func - a function 636 */ 637 addLoadEvent:function(func) { 638 document.observe("dom:loaded",func); 639 }, 640 /** 641 * Adds forms to observation list, which will processed when the submit event 642 * is fired 643 * 644 * @param {object} elem - Form-Element 645 */ 646 submitObserver: function(elem) { 647 Event.observe(elem, 'submit', 648 function(event) { 649 fec.base.dataSend(); 650 Event.stop(event); 651 } 652 ); 653 }, 654 /** 655 * Adds keypress-interception for the RETURN KEY - if return key is send, 656 * then the defaultbutton behaviour will be executed or the form will be 657 * processed (dataSend). 658 * 659 * @param {object} elem - document 660 */ 661 keypressObserver: function (elem) { 662 Event.observe(elem, 'keypress',function(event) { 663 if(event.keyCode == Event.KEY_RETURN) { 664 try { 665 var defaultbutton = $('defaultbutton'); 666 var onclick = defaultbutton.readAttribute("onclick"); 667 if (null !== onclick) { 668 eval(onclick); 669 } else { 670 fec.base.dataSend(); 671 } 672 Event.stop(event); 673 } catch (e) {} 674 } 675 }); 676 }, 677 /** 678 * Sets a gotoPage and processes the form (dataSend) without JS-validation 679 * 680 * @param {string} url - URL where the server should redirect 681 */ 682 submitWithGotoPage: function (url) { 683 fec.pageControl.setGotoPage(url); 684 fec.pageControl.setParameter('validate',false); 685 fec.base.dataSend(); 686 }, 687 /** 688 * Sets a action and processes the form (dataSend) without JS-validation 689 * 690 * @param {string} url - URL where the server should redirect 691 */ 692 submitWithAction: function (action) { 693 fec.pageControl.setControl('paramsSend', action); 694 fec.pageControl.setParameter('validate', false); 695 fec.base.dataSend(); 696 }, 697 /** 698 * Turn an undefined object into an empty string and add a suffix-string 699 * 700 * @param {object} value - tested object 701 * @param {string} suffix - suffix String 702 * @returns {string} 703 */ 704 chkUndef: function(value, suffix) { 705 return ((value) ? value:'') + ((suffix) ? suffix:''); 706 }, 707 /** 708 * Basic profiling - add this function in your code, and the time 709 * between the initial load of the page and the code will be put at the end 710 * of the page 711 * 712 * @param {string} uc - UseCase, addition information 713 */ 714 alertTime: function(uc){ 715 var now = new Date().getTime(); 716 var s = '<br/>' + uc +': '+ (now - fec.util.timeStart) + 'ms'; 717 $$('body')[0].insert({'bottom':s}); 718 }, 719 /** 720 * Reads the URL query parameters, put the keys to lower case and return the 721 * parameters as a hash 722 * 723 * @return {hash} query as hash object 724 */ 725 getQueryParams: function() { 726 var query = new Hash(); 727 // try wegen %% in URI 728 try { 729 query = $H(window.location.search.toQueryParams()); 730 // alle keys toLower 731 query = query.inject(new Hash(), 732 function(hash, obj) { 733 hash.set(obj[0].toLowerCase(),obj[1]); 734 return hash; 735 } 736 ); 737 } catch (e) {} 738 return query; 739 }, 740 /** 741 * Shortcut to openWindow, with less amount of parameters. 742 * @see fec.util.openWindow 743 */ 744 popup: function(w, h, site, opts) { 745 fec.util.openWindow(site, 'popUpWin', w, h, null, null, null, opts, false); 746 }, 747 /** 748 * Pop-Up function 749 * 750 * @param {string} url - popup-url 751 * @param {string} winname - window name 752 * @param {number} width 753 * @param {number} height 754 * @param {null|boolean} toolbr - with a toolbar? 755 * @param {null|boolean} scrollbrs - with scrollbars? 756 * @param {null|boolean} resizable - resizable? 757 * @param {string} winoptions - more options 758 * @param {null|boolean} trackit - refresh parent after popup closes 759 * @returns {object} pop-window object 760 */ 761 openWindow: function(url, winname, width, height, toolbr, scrollbrs, resizable, winoptions, trackit) { 762 var posx = screen.availWidth/2 - width/2; 763 var posy = screen.availHeight/2 - height/2; 764 var xyPos = 'top=' + posy + ',left=' + posx + ',screenX=' + posx + ',screenY=' + posy; 765 var opt = ''; 766 opt += 'width=' + width + ',height=' + height; 767 if (toolbr !== null) { 768 opt += ',toolbar=' + (toolbr ? 'yes':'no'); 769 } 770 if (scrollbrs !== null) { 771 opt += ',scrollbars=' + (scrollbrs ? 'yes':'no'); 772 } 773 if (resizable !== null) { 774 opt += ',resizable=' + (resizable ? 'yes':'no'); 775 } 776 opt += ', ' + xyPos; 777 opt += (winoptions) ? ', '+winoptions : ''; 778 var newWindow = window.open(url, winname, opt); 779 newWindow.focus(); 780 if (trackit !== null && trackit) { 781 var x = new PeriodicalExecuter(function(pe) { 782 if (newWindow.closed) { 783 pe.stop(); 784 fec.util.redirectReplace(self.location.href); 785 } 786 }, 0.5); 787 } 788 return newWindow; 789 }, 790 /** 791 * Get the document dimensions. 792 * 793 * @returns {object} { width: Number, height: Number } 794 */ 795 getDimensions: function() { 796 var dimensions = document.viewport.getDimensions(); 797 if (dimensions.width === 0 || dimensions.height === 0) { 798 if (document.body) { 799 dimensions.width = document.body.offsetWidth; 800 dimensions.height = document.body.offsetHeight; 801 } 802 } 803 return dimensions; 804 }, 805 resizeAndCenter: function(width, height) { 806 self.resizeTo(width, height); 807 self.moveTo(Math.floor((screen.availWidth-width)/2), Math.floor((screen.availHeight-height)/2)); 808 }, 809 maximizeWindow: function() { 810 window.moveTo(0,0); 811 window.resizeTo(screen.availWidth,screen.availHeight); 812 }, 813 escapeFrame: function () { 814 if (parent && (self != parent)) { 815 try { 816 parent.document.body.style.color = '#000000'; 817 parent.location.href = self.location.href; 818 return true; 819 } catch (e) {} 820 } 821 return false; 822 }, 823 setLocation: function(url, toTop) { 824 if (toTop && toTop.toString() == 'true') { 825 top.location.replace(url); 826 top.focus(); 827 } else { 828 self.location.replace(url); 829 self.focus(); 830 } 831 }, 832 getImageRoot: function(url) { 833 var p = 'http://localhost:8080'; 834 if ($('defaultImage')) { 835 var src = $('defaultImage').src; 836 p = src.substr(0, src.search('fileadmin')); 837 } 838 p = p.endsWith('/') ? p : p+'/'; 839 if (url) { 840 url = (url.startsWith('/') ? url.sub('/', '') : url); 841 } 842 return (url) ? p+url : p; 843 }, 844 getPageAddressRoot: function(url){ 845 var u = '#{protocol}//#{domain}#{port}'.interpolate({ 846 protocol: location.protocol, 847 848 domain: document.domain, 849 port: location.port ? ':' + location.port : '' 850 }) + '/'; 851 if (url) { 852 url = (url.startsWith('/') ? url.sub('/', '') : url); 853 } 854 return (url) ? u+url : u; 855 }, 856 getErrorPage: function(errid, code) { 857 return fec.config.urlErrorpage + ((errid) ? ('?errid='+errid) : ''); 858 }, 859 // func muss als "function() { #code# }" übergeben werden 860 delay: function(func,time) { 861 window.setTimeout(func,time); 862 863 }, 864 setDatumsfelder: function(eleTag, eleMonat, eleJahr, datum) { 865 // fuelle Datumsfelder auf 866 var y = ""; 867 if (eleTag.options.length == 1) { 868 for (var i=1; i<=31; i++) { 869 y = ( i<=9 ? '0'+i : ''+i ); 870 $(eleTag).insert({bottom: '\n<option value="'+y+'">'+y+'</option>'}); 871 } 872 $(eleTag).selectedIndex = 0; 873 } 874 var m = ['Januar','Februar','März','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember']; 875 if (eleMonat.options.length == 1) { 876 for (i=0; i<m.length; i++) { 877 y = i<9 ? '0'+ (i+1) : ''+(i+1); 878 $(eleMonat).insert({bottom: '\n<option value="'+ y +'">'+m[i]+'</option>'}); 879 } 880 $(eleMonat).selectedIndex = 0; 881 } 882 var d = new Date(); 883 if (eleJahr.options.length == 1) { 884 for (i=d.getFullYear(); i>=1900; i--) { 885 $(eleJahr).insert({bottom: '<option value="'+i+'">'+i+'</option>'}); 886 } 887 $(eleJahr).selectedIndex = 0; 888 } 889 if (datum) { 890 try { 891 d = datum.split('.'); 892 fec.valuesFactory.select(eleTag, d[0]); 893 fec.valuesFactory.select(eleMonat, d[1]); 894 fec.valuesFactory.select(eleJahr, d[2]); 895 } catch(e){} 896 } 897 }, 898 redirectReplace: function(url) { 899 fec.util.redirect(url, 10, true); 900 }, 901 redirect: function(url, delay, replace) { 902 try { fecHookRedirect(url, delay, replace); return true;} catch(e) {} 903 setTimeout( 904 function() { 905 // Parameter aus aktuellem Pfad mit hinzufügen, aber nur wenn keine ID dabei ist 906 // sonst wird per ID immer wieder das Entity auf der Folgeseite geladen (siehe TBE-Beruf) 907 var params = window.location.search; 908 if (params.length > 0) { 909 if (params.toUpperCase().indexOf('ID=') > -1) { 910 // ID in den Params -> nichts anhängen 911 // todo nur id aus params entfernen 912 } else { 913 if(url.indexOf('?') != -1) { 914 params = '&' + params.substring(1, params.length); 915 } else { 916 url += params; 917 } 918 } 919 } 920 url = url.gsub(' ',''); 921 if (replace) { 922 window.location.replace(url); 923 } else { 924 window.location=url; 925 } 926 }.bind(this), (delay === undefined) ? 10 : delay ); 927 // wegen Back-Button 928 if (fec.main) { 929 fec.main.disableForm(false); 930 } 931 return true; 932 }, 933 capitalizeByChar: function(str, chr) { 934 var parts = str.split('/'), len = parts.length; 935 if (len == 1) { 936 return parts[0]; 937 } 938 var capitalized = str.charAt(0) == '-' ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) : parts[0]; 939 for (var i = 1; i < len; i++) { 940 capitalized += '/'+ parts[i].charAt(0).toUpperCase() + parts[i].substring(1); 941 } 942 return capitalized; 943 }, 944 setKeepAlive: function() { 945 var pb = '{ "referer": "'+fec.main.getReferer()+'", "times": ['+new Date().getTime()+'], "request": ["registernext"] }'; 946 var x = new Ajax.Request(fec.config.urlAjaxRequest, 947 { 948 contentType: 'application/json', 949 asynchronous: true, 950 postBody: pb 951 }); 952 return x; 953 }, 954 runKeepAlive: function(delay) { 955 var x = new PeriodicalExecuter(fec.util.setKeepAlive, delay ? delay : 60); 956 }, 957 isCompleted: function() { 958 return (fec.main && fec.main.completed.length > 0) ? true : false; 959 }, 960 addParamToUrl: function(url, name, value) { 961 url += (url.include('?') ? '&' : '?') + name + '=' + encodeURIComponent(value); 962 return url; 963 }, 964 replaceAllCss: function(oldCss, newCss) { 965 $$('div[class~="'+oldCss+'"]').each ( function(ele) { 966 ele.removeClassName(oldCss); 967 ele.addClassName(newCss); 968 }); 969 }, 970 getPageName: function() { 971 var path = window.location.pathname; 972 return path.substring(path.lastIndexOf('/') + 1); 973 }, 974 eleId: function(eleName) { 975 return fec.config.htmlIdDataPrefix + eleName; 976 } 977 }; 978 979 /** 980 * Cookie handling class 981 * 982 * @namespace fec.cookies 983 */ 984 fec.cookies = { 985 /** 986 * Creates a cookie 987 * 988 * @param {string} cname The name of the cookie. 989 * @param {string} value The value of the cookie. 990 * @param {null|number} hours Number of hours after the cookie will expire. 991 * If hours is null, the cookie will be a session cookie. 992 */ 993 set: function(cname, value, hours) { 994 var expires = ""; 995 if (hours) { 996 var date = new Date(); 997 date.setTime(date.getTime()+(hours*60*60*1000)); 998 expires = "; expires="+date.toGMTString(); 999 } 1000 var cstr = cname + "=" + escape(value) + expires + "; path=/"; 1001 document.cookie = cstr; 1002 }, 1003 /** 1004 * Returns the cookie value for the given name. 1005 * 1006 * @param {string} cname The name of the cookie to retrieve. 1007 * @param {string} defaultValue The defaultValue for the cookie, if none 1008 * is found. 1009 */ 1010 1011 get: function(cname, defaultValue) { 1012 var regex = new RegExp(cname + "\\s*=\\s*(.*?)(;|$)"); 1013 var cookies = document.cookie.toString(); 1014 var match = cookies.match(regex); 1015 if (match) { 1016 return unescape(match[1]); 1017 } 1018 return defaultValue; 1019 }, 1020 /** 1021 * Removes a cookie 1022 * 1023 * @param {string} cname The name of the cookie to be erased. 1024 */ 1025 erase: function(cname) { 1026 fec.cookies.set(cname, '', -1); 1027 } 1028 }; 1029 1030 /* _____________________ CLASSES (PROTOTYPE) ________________________________ */ 1031 1032 /** 1033 * Main processing class - This class holds the main functions for retrieving 1034 * and submitting data && processing the server response && DOM manipulation 1035 * 1036 * @class FecMain 1037 */ 1038 var FecMain = Class.create({ 1039 /** URL to the server for ajax request */ 1040 ajaxRequestUrl: fec.config.urlAjaxRequest, 1041 /** experimental: if true you can develop html without a server */ 1042 staticOn: false, 1043 /** experimental for staticOn */ 1044 staticResponseUrl: '/staticResponse', 1045 /** holds the initial state of all form-buttons */ 1046 submitButtons: [], 1047 /** holds the data object from the response */ 1048 data: '', 1049 /** list of completed scopes */ 1050 completed: $A([]), 1051 /** flag for JS-form-validation */ 1052 isFormValid: false, 1053 /** will run automatically by instantiation */ 1054 initialize: function() { 1055 // errorinit 1056 this.initErrorList(); 1057 }, 1058 /** 1059 * Creates an Ajax-request to the server, depending on fecPagecontrol values. 1060 * The server sends data back, that can be processes. 1061 * 1062 * @returns {boolean} true 1063 */ 1064 getServerData: function(scope){ 1065 var request = "", reqObj; 1066 var scopeArr = (scope) ? [scope]:fec.pageControl.getScopes(); 1067 if(!this.isValueInArray(fec.pageControl.getControl('paramsLoad'),'void')){ 1068 for(var i = 0; i < scopeArr.size(); i++) { 1069 if (request !== "") { 1070 reqObj = request.evalJSON(true); 1071 reqObj.request = [scopeArr[i]]; 1072 request = Object.toJSON(reqObj); 1073 } else { 1074 request = this.getRequestBody('load', scopeArr[i]); 1075 } 1076 var x = new Ajax.Request(this.getAjaxRequestUrl('load'), 1077 { 1078 contentType:'application/json', postBody: request, 1079 onSuccess:function(r){ 1080 this.processResponse('load',r.responseText); 1081 }.bind(this), 1082 on403:function(r){ 1083 fec.util.redirect(fec.util.getErrorPage('getServerData_http'+r.status,'403')); 1084 }.bind(this), 1085 onFailure:function(r){ 1086 fec.util.redirect(fec.util.getErrorPage('getServerData_http'+r.status)); 1087 }.bind(this) 1088 }); 1089 } 1090 } else { 1091 this.showSpinner(scopeArr,false); 1092 try{ executeVoid(); } catch(e){} 1093 } 1094 return true; 1095 }, 1096 /** 1097 * Creates an Ajax-request to the server, depending on fecPagecontrol values. 1098 * The server receives data through request data (e.G. form-data). 1099 * 1100 * @returns {boolean} true 1101 */ 1102 sendServerData: function() { 1103 var request = this.getRequestBody("send"); 1104 var x = new Ajax.Request(this.getAjaxRequestUrl('send'), 1105 { 1106 contentType:'application/json', postBody: request, 1107 onSuccess:function(r){ 1108 this.processResponse('send',r.responseText); 1109 }.bind(this), 1110 on403:function(r){ 1111 fec.util.redirect(fec.util.getErrorPage('sendServerData_http'+r.status,'403')); 1112 }.bind(this), 1113 onFailure:function(r){ 1114 fec.util.redirect(fec.util.getErrorPage('sendServerData_http'+r.status)); 1115 }.bind(this) 1116 }); 1117 return true; 1118 }, 1119 /** 1120 * Creates an Ajax-request to a given url and returns the received data. 1121 * 1122 * @param {string} url - Serverurl or a file-url (e.G. ../data.txt) 1123 * @returns {object} received data object 1124 */ 1125 getAjaxData: function(url) { 1126 var data = ''; 1127 var x = new Ajax.Request(url, 1128 { 1129 contentType:'application/json', asynchronous:false, 1130 onSuccess:function(r){ data = r.responseText; }.bind(this), 1131 on403:function(r){ fec.util.redirect(fec.util.getErrorPage('getAjaxData_http'+r.status,'403')); }.bind(this), 1132 onFailure:function(r){ fec.util.redirect(fec.util.getErrorPage('getAjaxData_http'+r.status));}.bind(this) 1133 }); 1134 return data; 1135 }, 1136 1137 /** 1138 * Processes the response from the server 1139 * 1140 * @param {string} type - load or send 1141 * @param {object} response - server response object 1142 */ 1143 processResponse: function(type, response) { 1144 if (response !== "") { 1145 //response = response.substring(0, response.length - 1) + ', "error":{"PAGE":{"ERKUND":"","E37P2F":"Juhee","E37XXX":"FEHLER FEHLER"}} }'; 1146 var vspData = response.evalJSON(true); 1147 // vspData fuer spezielle Setter in HTML verfuegbar machen 1148 this.data = $H(vspData.data); 1149 this.completed = $A(vspData.completed); 1150 try { fecHookInitData(this.data); } catch (e) {} 1151 if (vspData.redirect !== undefined) { 1152 fec.util.redirect(vspData.redirect); 1153 return; 1154 } 1155 this.showContainer(); 1156 this.processResponseTimes(vspData); 1157 if (this.processResponseError(vspData)) { 1158 if (vspData.gotoPage !== undefined) { 1159 // pc zuruecksetzen, falls er veraendert wurde (submitWithAction) 1160 fec.pageControl = new FecPageControl(fecPagecontrol); 1161 this.disableSubmitButtons(false); 1162 fec.util.redirect(fec.pageControl.getGotoUrl(vspData.gotoPage)); 1163 return; 1164 } 1165 this.replaceWerteInDOM(vspData.data); 1166 if(vspData.retry && vspData.retry.length > 0) { 1167 var retryScope = vspData.retry[0]; 1168 if (vspData.wait === undefined) { vspData.wait = 1000; } 1169 if (type == 'load') { 1170 fec.util.delay( function() { fec.main.getServerData(retryScope); }, vspData.wait ); 1171 } 1172 if (type == 'send') { 1173 fec.util.delay( function() { fec.base.dataSend(); }, vspData.wait ); 1174 } 1175 } else { 1176 this.showSpinner(vspData.completed,false); 1177 } 1178 } else { 1179 // auch bei Fehler, Werte ersetzen 1180 this.replaceWerteInDOM(vspData.data); 1181 // Ladefehler anstatt Ladespinner 1182 if (type == 'load') { 1183 fec.util.replaceAllCss('ladebalken','ladebalken-error'); 1184 } else { 1185 this.showSpinner(fec.pageControl.getScopes(),false); 1186 } 1187 // Hook fuer spezielle Fehlerbehandlung (zb siehe service-anmelden\index.html) 1188 try { fecHookHandleError(vspData); } catch (e) {} 1189 1190 } 1191 1192 } else { 1193 // ein leerer Response, kann vorkommen, wenn schnell geklicked wird -> keine Fehlerseite anzeigen 1194 } 1195 }, 1196 /** 1197 * @param {object} data 1198 */ 1199 replaceWerteInDOM: function(data) { 1200 var domId = false, dataKeys = Object.keys(data), foundId = true; 1201 dataKeys = dataKeys.sortBy(function(s) { 1202 return (/.*en$/).test(s) ? 0 : s.length; 1203 }); 1204 try { fecHookInitially(); } catch (e) {} 1205 for (var index = 0, len = dataKeys.length; index < len; ++index) { 1206 foundId = true; 1207 var dataKey = dataKeys[index]; 1208 domId = fec.config.htmlIdDataPrefix + dataKey.capitalize(); 1209 // Die Variable fecSetterValue kann von dem zusaetzlichen Setter verwendet werden. 1210 fecSetterValue = ('string' == typeof data[dataKey]) ? data[dataKey].escapeHTML():data[dataKey]; 1211 try { 1212 eval('fecHookSetData'+dataKey.capitalize()+'();'); 1213 } catch (e) { 1214 if (this.setElementValue(domId, fecSetterValue) === false) { foundId = false; } 1215 // keine IDs per autoincrement updaten 1216 if (dataKey.toLowerCase() != 'id') { 1217 InnerFor: for (var i = 1; i < 10; ++i) { 1218 try { 1219 var el = $(domId+i); 1220 this.setElementValue(el.id, fecSetterValue); 1221 } catch(e) { 1222 break InnerFor; 1223 } 1224 } 1225 } 1226 } 1227 } 1228 try { fecHookFinally(); } catch (e) { } 1229 }, 1230 /** 1231 * @param {object} vspData 1232 */ 1233 processResponseError: function(vspData) { 1234 if (vspData.error !== undefined) { 1235 fec.main.isFormValid = false; // Formvalidierung beim nächsten submit wieder anstossen 1236 this.disableForm(false); 1237 this.initErrorList(); 1238 var errors = vspData.error, errorKey = "", errorValues = ""; 1239 var errorKeys = Object.keys(errors); 1240 for (var index = 0, len = errorKeys.length; index < len; ++index) { 1241 errorKey = errorKeys[index]; 1242 var errorsInner = $A(errors[errorKey]); 1243 errorsInner.each(function(err) { 1244 this.errorAdd(err,errorKey); 1245 }.bind(this)); 1246 fec.form.setErrorInDom($("vspData"+errorKey)); 1247 } 1248 return false; 1249 } else { 1250 fec.main.showScope('vspScopeFehler',false); 1251 } 1252 return true; 1253 }, 1254 /** 1255 * @param {object} vspData 1256 */ 1257 processResponseTimes: function(vspData) { 1258 if (vspData.times !== undefined) { 1259 vspData.times.push(new Date().getTime()); 1260 } 1261 }, 1262 1263 /** 1264 * @param {object} scope 1265 * @param {boolean} show 1266 */ 1267 showScope: function(scope, show) { 1268 var el = $(scope); 1269 if (el) { 1270 Element[(show) ? 'show' : 'hide'](el); 1271 return true; 1272 } else { 1273 return false; 1274 } 1275 }, 1276 /** 1277 * @param {object} scopes 1278 * @param {boolean} show 1279 */ 1280 showSpinner: function(scopes, show) { 1281 if(Object.isString(scopes)) { 1282 scopes = [scopes]; 1283 } 1284 if(Object.isArray(scopes)) { 1285 for (var index = 0, len = scopes.length; index < len; ++index) { 1286 var scope = scopes[index]; 1287 try { 1288 if (show) { 1289 this.showScope(fec.config.htmlIdScopePrefix + scope.capitalize(),false); 1290 this.showScope(fec.config.htmlIdLoaderPrefix + scope.capitalize(),true); 1291 // Ladebalken Style wieder zuruecksetzen 1292 fec.util.replaceAllCss('ladebalken-error', 'ladebalken'); 1293 } else { 1294 this.showScope(fec.config.htmlIdLoaderPrefix + scope.capitalize(),false); 1295 if ('undefined' != typeof Effect) { 1296 var x = Effect.Appear($(fec.config.htmlIdScopePrefix + scope.capitalize()), { duration: 0.25 }); 1297 } else { 1298 $(fec.config.htmlIdScopePrefix + scope.capitalize()).show(); 1299 } 1300 // Weitere Elemente einblenden per Hook 1301 try{ fecHookShowScope(); }catch(e){} 1302 } 1303 } catch(e) {} 1304 } 1305 } 1306 }, 1307 /** 1308 * If a container element is in the page, toggle visibility. This is 1309 * usefull, when the dom shouldn't be visibility, before the response has 1310 * arrived. 1311 */ 1312 showContainer: function() { 1313 if ($('container')) { 1314 $('container').show(); 1315 } 1316 if ($('containerWarten')) { 1317 $('containerWarten').hide(); 1318 } 1319 }, 1320 /** 1321 * @param {object} element 1322 * @param {boolean} newValue 1323 */ 1324 setElementValue: function(element, newValue) { 1325 var element_id = element; 1326 element = $(element); 1327 if (!element){ 1328 element = document.getElementsByName(element_id)[0]; 1329 } 1330 if (!element){ return false; } 1331 var method = element.tagName.toLowerCase(); 1332 var parameter = fec.valuesFactory[method](element, newValue); 1333 }, 1334 /** 1335 * 1336 */ 1337 getVspDataFormElements: function() { 1338 var form = $(fec.config.htmlIdForm), elements = new Hash(); 1339 if (null !== form) { 1340 elements = $H(this.serializeElements(form.getElements(),true)); 1341 elements.each(function(ele){ 1342 if(!ele[0].startsWith(fec.config.htmlIdDataPrefix)) { 1343 elements.unset(ele[0]); 1344 } 1345 }); 1346 } 1347 return elements; 1348 }, 1349 /** 1350 * @param {string} type 1351 */ 1352 getRequestData: function(type) { 1353 var elements = fec.main.getVspDataFormElements(); 1354 if (type == 'send') { 1355 this.disableForm(true); 1356 try { 1357 var data = eval('fecHookGetRequestData' + fec.pageControl.getScopes()[0].capitalize()+'()'); 1358 elements.update(data); 1359 } catch (e) { } 1360 } 1361 elements.update(this.getUriControlParams()); 1362 if (elements.size() !== 0) { 1363 var formData = elements.toJSON(); 1364 if (formData == '{}') { return ""; } 1365 return formData.gsub(fec.config.htmlIdDataPrefix,''); 1366 } 1367 return ''; 1368 }, 1369 /** 1370 * 1371 */ 1372 getUriControlParams: function() { 1373 var h = fec.util.getQueryParams(), rv = new Hash(); 1374 // URI Parameter die dem JSON Request angefuegt werden sollen, nur Kleinbuchstaben 1375 // confirmid, regid kommen von Bestaetigungsemail 1376 var controlParams = ['id','confirmid','regid','wdywtg','wdycf']; 1377 if(h.size() !== 0) { 1378 controlParams.each(function(key) { 1379 if (h.get(key) !== undefined) { 1380 rv.set(key.capitalize(),h.get(key)); 1381 } 1382 }.bind(this)); 1383 } 1384 return rv; 1385 }, 1386 /** 1387 * @param {object} elements 1388 * @param {object} options 1389 */ 1390 serializeElements: function(elements, options) { 1391 if (typeof options != 'object') { 1392 options = { hash: !!options }; 1393 } 1394 else if (options.hash === undefined) { 1395 options.hash = true; 1396 } 1397 var key, value, submitted = false, submit = options.submit; 1398 var data = elements.inject({ }, 1399 function(result, element) { 1400 if (!element.disabled && element.name) { 1401 key = element.name; value = $(element).getValue(); 1402 if ((element.type != 'submit' || (!submitted && 1403 submit !== false && (!submit || key == submit) && (submitted = true)))) { 1404 // msch hier besondere behandlung fuer checkboxes 1405 if(element.type === 'checkbox' && !(key in result)) { 1406 result[key] = (element.checked === true) ? "true":"false"; 1407 } else { 1408 if (value != null) { 1409 if (key in result) { 1410 // a key is already present; construct an array of values 1411 if (!Object.isArray(result[key])) { 1412 result[key] = [result[key]]; 1413 } 1414 result[key].push(value); 1415 } else { 1416 result[key] = value; 1417 } 1418 } 1419 } 1420 } 1421 } 1422 return result; 1423 }); 1424 return options.hash ? data : Object.toQueryString(data); 1425 }, 1426 1427 1428 /* ___________ GENERELLE FUNKTIONEN __________ */ 1429 /** 1430 * Validate if an object is an array and if it contains a given value 1431 * 1432 * @param {object} arr 1433 * @param {object} value 1434 */ 1435 1436 isValueInArray: function(arr, value) { 1437 if (!Object.isArray(arr)) { 1438 return false; 1439 } else if (arr.indexOf(value) > -1) { 1440 return true; 1441 } 1442 }, 1443 /** 1444 * Get the referer, and use experimental staticOn flag 1445 */ 1446 getReferer: function() { 1447 var url = window.location.pathname; 1448 if (window.location.search !== "") { 1449 url = url + window.location.search; 1450 } 1451 url = url.replace('yaf4ajn.war','yaf4ajn'); 1452 if(this.staticOn) { 1453 var pos = url.search(/yaf4ajn/); 1454 if(pos > 0) { 1455 url = url.substring(0,pos) + this.staticResponseUrl +'//yaf4ajn//'+ url.substring(pos + 'yaf4ajn'.length + 1,url.length); 1456 } else if (url.search(/index.php/) > 0) { 1457 url = '..' + this.staticResponseUrl +'/index.php'; 1458 } else { 1459 url = window.location.pathname.replace(/yaf4ajn/g, this.staticResponseUrl+"/yaf4ajn"); 1460 } 1461 pos = (url.lastIndexOf('\\') > pos) ? url.lastIndexOf('\\') : url.lastIndexOf('/'); 1462 var file = url.substring(pos+1,url.length); 1463 if (file.indexOf('-') > 0) { 1464 file = file.replace(file.match(/-.*\./g),'.'); 1465 url = url.substring(0, pos+1) + file; 1466 } 1467 } 1468 return url; 1469 }, 1470 /** 1471 * Get the data for the server request. 1472 * 1473 * @param {string} type - load or send 1474 * @param {string} scope 1475 */ 1476 getRequestBody: function(type, scope) { 1477 var req = '{ "referer": "'+this.getReferer()+'", "times": ['+new Date().getTime()+']'; 1478 if (type == 'load') { 1479 req += ', "request": '+ ((scope) ? ([scope]).toJSON():fec.pageControl.getScopes().toJSON()); 1480 } 1481 if (type == 'send') { 1482 req += ', "request": ["' + fec.pageControl.getScopes()[0] + '"]'; 1483 } 1484 req += this.getRequestBodyParams(type); 1485 var data = this.getRequestData(type); 1486 if (data !== "") { 1487 req += ', "data": '+data; 1488 } 1489 var gotoPage = fec.pageControl.getGotoPage(); 1490 if (gotoPage !== "") { 1491 req += ', "gotoPage": "'+gotoPage+'"'; 1492 } 1493 if (fec.pageControl.getPageId() !== "") { 1494 req += ', "pageid": "'+fec.pageControl.getPageId()+'"'; 1495 } 1496 req += ' }'; 1497 return req; 1498 }, 1499 /** 1500 * Get additional parameters for the server request to control the server. 1501 * 1502 * @param {string} type - load or send 1503 */ 1504 getRequestBodyParams: function(type) { 1505 var params = $H({}); 1506 if (type == 'load') { 1507 var pl = fec.pageControl.getControl('paramsLoad'); 1508 if ( this.isValueInArray(pl,'relogin') ){ 1509 params.set('action','relogin'); 1510 if ($('fecParamWdywtg')) { 1511 params.set('wdywtg',$('fecParamWdywtg').value); 1512 } 1513 pl = pl.without('relogin'); 1514 } 1515 if ( this.isValueInArray(pl,'merge') ){ 1516 params.set('merge',true); 1517 pl = pl.without('merge'); 1518 } 1519 if (pl && pl.size() > 0) { 1520 params.set('action',pl[0]); 1521 } 1522 } 1523 if (type == 'send') { 1524 var ps = fec.pageControl.getControl('paramsSend'); 1525 if( ps && ps.size() > 0 ) { 1526 params.set('action',ps[0]); 1527 } 1528 } 1529 params = params.merge(fec.pageControl.getParameter()); 1530 return (params.size() > 0) ? ', "parameter": '+params.toJSON() : ""; 1531 }, 1532 /** 1533 * Disable the submit buttons. 1534 * 1535 * @param {boolean} bol - disable or not 1536 */ 1537 disableSubmitButtons: function(bol) { 1538 var butValue; 1539 var buttons = $$('input[type="submit"]','input[type="button"]'); 1540 if (this.submitButtons.size() === 0) { 1541 this.submitButtons = buttons.pluck('value'); 1542 } 1543 //this.submitButtons.pop(); 1544 for (var i = 0, len = buttons.size(); i < len; ++i) { 1545 butValue = fec.msg.FE010; 1546 for(var y = 0; y < buttons[i].title.length; ++y) { 1547 if (y%2 === 0) { 1548 butValue += " "; 1549 } else { 1550 butValue = " " + butValue; 1551 } 1552 } 1553 buttons[i].value=butValue; 1554 if (bol) { 1555 buttons[i].disable(); 1556 } else { 1557 buttons[i].value = (this.submitButtons[i] === '') ? 'Weiter' : this.submitButtons[i]; 1558 buttons[i].enable(); 1559 } 1560 } 1561 }, 1562 /** 1563 * Disable the form. 1564 * 1565 * @param {boolean} bol - disable or not 1566 */ 1567 disableForm: function(bol) { 1568 if ($(fec.config.htmlIdForm) !== null) { 1569 if (bol) { 1570 $(fec.config.htmlIdForm).disable(); 1571 } else { 1572 $(fec.config.htmlIdForm).enable(); 1573 } 1574 } 1575 }, 1576 /** 1577 * Returns the configured server url 1578 * 1579 * @param {string} type - load or send 1580 */ 1581 getAjaxRequestUrl: function(type) { 1582 retVal = this.ajaxRequestUrl; 1583 if (this.staticOn) { 1584 retVal = ('send' == type) ? this.getReferer()+'.txt.send' : this.getReferer()+'.txt'; 1585 } 1586 return retVal; 1587 }, 1588 /** 1589 * Scroll to top if possible. 1590 */ 1591 scrollToTop: function() { 1592 var yPos = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0; 1593 if (yPos > 150) { 1594 window.scrollTo(0,100); 1595 } 1596 }, 1597 /** 1598 * Scroll to bottom if possible. 1599 */ 1600 scrollToBottom: function() { 1601 window.scrollTo(0,document.viewport.getHeight()); 1602 }, 1603 /** 1604 * Checks the form and sets Variable isFormValid 1605 * 1606 * @returns {boolean} Form valid or not 1607 */ 1608 checkForm: function() { 1609 var retval = false; 1610 // Das Form nur pruefen wenn kein validate und wenn noch nicht geprueft wurde 1611 if (fec.pageControl.getParameter().get('validate') === undefined && !fec.main.isFormValid) { 1612 try { 1613 retval = eval(fec.config.jsCheckformPrefix + fec.pageControl.getScopes()[0].capitalize()+'();'); 1614 } catch(e) { retval = true; } 1615 } else { 1616 retval = true; 1617 } 1618 fec.main.isFormValid = retval; 1619 return retval; 1620 }, 1621 /** 1622 * Inits the error output. 1623 */ 1624 initErrorList: function() { 1625 if($('vspScopeFehler') === null) { 1626 var s = "\n<div id='vspScopeFehler' style='display:none'>\n</div>\n"; 1627 Try.these( 1628 function() {$$('h1')[0].insert({'after':s});}, 1629 function() {$('content').insert({'top':s});}, 1630 function() {$$('body')[0].insert({'top':s});} 1631 ); 1632 $('vspScopeFehler').hide(); 1633 } else { 1634 $('vspScopeFehler').update(''); 1635 } 1636 }, 1637 /** 1638 * Inits the error layout. 1639 * 1640 * @param {string} errCss - Css class like error, warning, info 1641 * @param {boolean} clear - init and clear existing errors 1642 */ 1643 initErrorBox: function(errCss, clear) { 1644 errCss = errCss.capitalize(); 1645 if($('vspScopeFehler'+errCss) === null) { 1646 var s = ""; 1647 if (errCss.toLowerCase()=='error') { 1648 s += "\n<div id='vspScopeFehler"+errCss+"' class='box large naked image "+errCss.toLowerCase()+"'>"; 1649 s += "\n<img src='"+fec.util.getImageRoot()+"fileadmin/VOS/images/16x16/warnung.png' alt='"+errCss.toLowerCase()+"'/>"; 1650 s += "\n<ul id='vspErrorList"+errCss+"'></ul>"; 1651 s += "\n</div>\n"; 1652 s += "\n<div style='clear:both;height:1px'></div>"; 1653 } else { 1654 s += "\n<div id='vspScopeFehler"+errCss+"' class='box large image "+errCss.toLowerCase()+"'>"; 1655 s += "\n<h2 id='vspErrorHeader"+errCss+"'>" + fec.msg.FE001 +"</h2>"; 1656 s += "\n<img src='"+fec.util.getImageRoot()+"fileadmin/VOS/images/48x48/"+errCss.toLowerCase()+".gif' alt='"+errCss.toLowerCase()+"'/>"; 1657 s += "\n<ul id='vspErrorList"+errCss+"'></ul>"; 1658 s += "\n</div>\n"; 1659 s += "\n<div style='clear:both;height:1px'></div>"; 1660 } 1661 $('vspScopeFehler').insert({'bottom':s}); 1662 $('vspScopeFehler').hide(); 1663 } else { 1664 if (clear) { 1665 $('vspErrorList'+errCss).update(''); 1666 } 1667 } 1668 }, 1669 /** 1670 * Adds an error. 1671 * 1672 * @param {object} err - error object 1673 * @param {string} errtxt - a special errortext, that overrides the default 1674 */ 1675 errorAdd: function(err, errtxt) { 1676 var errObj = fec.main.getErrorObject(err, errtxt); 1677 // redirect? 1678 if (errObj.get('redir')) { 1679 var errUrl = fec.pageControl.getGotoUrl(errObj.get('redir')); 1680 // Todo jsunit 1681 // Verhindern, dass auf sich selbst geleitet wird 1682 if (errUrl === 'index.html' && errUrl === fec.util.getPageName()) { 1683 errUrl = '../' + errUrl; 1684 } 1685 fec.util.redirect(errUrl); 1686 return false; 1687 } 1688 // errObj verarbeiten 1689 var errCss = errObj.get('css') ? errObj.get('css').capitalize():"Error"; 1690 this.initErrorBox(errCss,false); 1691 if (errObj.get('head')) { 1692 if ($('vspErrorHeader'+errCss)) { 1693 $('vspErrorHeader'+errCss).update(errObj.get('head')); 1694 } 1695 1696 } 1697 //Fehler hinzufuegen 1698 $('vspErrorList'+errCss).insert({'bottom':'<li>'+errObj.get('text')+'</li>\n'}); 1699 //Fehler anzeigen 1700 this.showScope('vspScopeFehler',true); 1701 this.scrollToTop(); 1702 this.disableSubmitButtons(false); 1703 this.showContainer(); 1704 return true; 1705 }, 1706 /** 1707 * Resolves the error configuration. 1708 * 1709 * @param {object} err - error object 1710 * @param {string} errtxt - a special errortext, that overrides the default 1711 */ 1712 getErrorObject: function(err, errtxt) { 1713 //erstelle ErrorObjekt 1714 if (!errtxt || errtxt === "" || errtxt === "PAGE") { 1715 errtxt = fec.errorCode.EUNEXP; 1716 } 1717 var errid = Object.isString(err) ? err : err.Code; 1718 var errObj = new Hash(); 1719 // Setze den Errortext als Template 1720 errObj.set('template', new Template(fec.errorCode[errid] ? fec.errorCode[errid] : errtxt)); 1721 // setze die Templateparameter 1722 var params = new Hash(); 1723 if (Object.isString(err)) { 1724 params.set('0', errtxt); 1725 } else { 1726 if (Object.isString(err.Args)) { 1727 params.set('0', err.Args); 1728 } else { 1729 if (err.Args) { 1730 for (var i = 0; i < err.Args.length; i++) { 1731 params.set(''+i, err.Args[i]); 1732 } 1733 } 1734 } 1735 } 1736 errObj.set('parameter', params); 1737 // merge mit vorhandender fec.errorConfig 1738 if (fec.errorConfig[errid]) { 1739 errObj = errObj.merge(fec.errorConfig[errid]); 1740 } 1741 var t = errObj.get('template').evaluate(errObj.get('parameter')); 1742 errObj.set('text', t); 1743 1744 return errObj; 1745 } 1746 }); 1747 1748 1749 /** 1750 * PageControl class - This class holds the fecPagecontrol variable and 1751 * offers getters and setters to the values 1752 * 1753 * @class FecPageControl 1754 */ 1755 var FecPageControl = Class.create({ 1756 /** 1757 * Represents the pageControl object 1758 * 1759 * @type object 1760 * @default false 1761 */ 1762 pc: false, 1763 /** 1764 * Parses the pageControl variable. 1765 */ 1766 initialize: function(pcString) { 1767 if (this.pc === false) { 1768 try { 1769 // Wenn Fehler, dann saubere Fehlermeldung ausgeben 1770 // anstatt SyntaxException 1771 this.pc = pcString.evalJSON(true); 1772 }catch(e){ 1773 fec.main.errorAdd('',fec.msg.FE002); 1774 } 1775 this.processErrorConfig(); 1776 } 1777 }, 1778 /** 1779 * @returns {array} array of scopes 1780 */ 1781 getScopes: function(){ 1782 return Object.isString(this.pc.scopes) ? [this.pc.scopes] : this.pc.scopes; 1783 }, 1784 /** 1785 * @returns {object} Value for the given control-key. 1786 */ 1787 getControl: function(control){ return this.pc[control]; }, 1788 /** 1789 * @params {string} control - Key for a server-control 1790 * @params {string} value - Value for a server-control variable 1791 */ 1792 setControl: function(control, value){ 1793 this.pc[control] = Object.isArray(value) ? value : [value]; 1794 }, 1795 /** 1796 * @returns {object} Server parameters for the request. 1797 */ 1798 getParameter: function(){ 1799 return (typeof this.pc.parameter == 'undefined') ? $H({}) : $H(this.pc.parameter); 1800 }, 1801 /** 1802 * Additional parameters to control the server (through request). 1803 * 1804 * @params {string} key - key for an additional parameter 1805 * @params {string} val - value for an additional parameter 1806 */ 1807 setParameter: function(key, value){ 1808 var params = this.getParameter(); 1809 params.set(key, value); 1810 this.pc.parameter = params; 1811 }, 1812 /** 1813 * Set special parameter for a load request 1814 * 1815 * @params {string} val - a loading parameter like void, merge 1816 */ 1817 setParamsLoad: function(val){ 1818 this.pc.paramsLoad = val; 1819 }, 1820 /** 1821 * Server can return urls or ids. Ids are the better option, so the server 1822 * doesn't have to know about urls. Urls can be configured completely in the 1823 * frontend. 1824 * 1825 * @params {string} gotoid - An id to a page or an url. Url will be returned 1826 * immediately, and id will be resolved. 1827 */ 1828 getGotoUrl: function(gotoid){ 1829 if (gotoid.include('.')) { 1830 return gotoid; 1831 } 1832 var retval = ''; 1833 gotoid = gotoid.toLowerCase(); 1834 if (this.pc.gotos && this.pc.gotos[gotoid]) { 1835 retval = this.pc.gotos[gotoid]; 1836 } else { 1837 if (this.gotoPageUrls[gotoid]) { 1838 retval = this.gotoPageUrls[gotoid]; 1839 } else { 1840 retval = fec.util.getErrorPage('getGoto_'+gotoid); 1841 } 1842 } 1843 return retval; 1844 }, 1845 /** 1846 * Mapping of URLs to Ids 1847 */ 1848 gotoPageUrls: { 1849 page_01_01 : "page0101.html" 1850 }, 1851 /** 1852 * @returns {string} URL where the server should redirect after completion 1853 */ 1854 getGotoPage: function(){ 1855 return (typeof this.pc.gotoPage == 'undefined') ? "" : this.pc.gotoPage; 1856 }, 1857 /** 1858 * @params {string} gotoPage - URL where the server should redirect after 1859 * completion 1860 */ 1861 setGotoPage: function(gotoPage){ 1862 this.pc.gotoPage = gotoPage; 1863 }, 1864 /** 1865 * @returns {string} pageId of pagecontrol or empty-string 1866 */ 1867 getPageId: function() { 1868 return this.pc.pageid ? this.pc.pageid : ""; 1869 }, 1870 /** 1871 * Parse a special error baheviour, given by the pageControl. So you can 1872 * define a special behaviour per page. 1873 */ 1874 processErrorConfig: function() { 1875 if (this.pc.errorconfig) { 1876 var errconfs = $H(this.pc.errorconfig); 1877 errconfs.each(function(err) { 1878 fec.errorConfig[err[0]] = err[1]; 1879 }.bind(this)); 1880 } 1881 } 1882 }); 1883 1884 1885 1886 1887 /* _____________________ CONFIG MISCELLANEOUS _______________________________ */ 1888 1889 /** 1890 * ErrorCode Mapping - Maps an ErrorCode from the response to a text. 1891 * 1892 * @namespace fec.errorCode 1893 */ 1894 fec.errorCode = { 1895 EUNEXP : 'unexpected error' 1896 }; 1897 1898 /** 1899 * ErrorCode behaviour configuration - You can override the default behavior for 1900 * an error. These values are available: 1901 * redir - redirect immediately to redir-URL 1902 * head - title text 1903 * css - css classname 1904 * text - errortext, overrides fec.errorCode 1905 * 1906 * @namespace fec.errorConfig 1907 */ 1908 fec.errorConfig = { 1909 EUNEXP : { 1910 head : 'Hinweis', 1911 css : 'info' 1912 } 1913 }; 1914 1915 /** 1916 * Messages mappings - Inhere you find all text-fragments that will be shown 1917 * to the user. 1918 * 1919 * @namespace fec.msg 1920 */ 1921 fec.msg = { 1922 YES : 'Ja', 1923 NO : 'Nein', 1924 FE001 : 'Es ist ein Fehler aufgetreten!', 1925 FE002 : 'pageControl String nicht valide', 1926 FE003 : 'Bitte korrigieren Sie folgende Eingabefelder', 1927 FE004 : 'Formularfehler', 1928 FE005 : 'ist ein Pflichtfeld.', 1929 FE006 : 'darf maximal ##MAX## Zeichen lang sein.', 1930 FE007 : 'muss mindestens ##MIN## Zeichen lang sein.', 1931 FE008 : 'muss zwischen ##MIN## und ##MAX## Zeichen lang sein.', 1932 FE009 : 'Achtung, diese Webseite wurde nicht von der vorgesehenen URL "##HOST##" geladen. Bitte geben Sie hier und in den verlinkten Systemen keine persönliche Daten ein.', 1933 FE010 : 'Bitte warten ..' 1934 }; 1935 1936 1937 1938 /* _____________________ EXAMPLE HOOK IMPLEMENTATIONS _______________________ */ 1939 /** 1940 * An example for a special data-value setter. Default behaviour is replacing a 1941 * data value (from the server) with the the corresponding element-id. But if 1942 * this is not enough you can implement special setters. Valuekey is the 1943 * data-key. 1944 * 1945 * @example fecHookSetDataValuekey 1946 */ 1947 function fecHookSetDataValuekey() { 1948 if (fecSetterValue !== undefined) { 1949 // spezial handling of the setterValue 1950 $('elementid').update(fecSetterValue); 1951 } 1952 } 1953 /** 1954 * An example for a form-validation function, that will be called automatically. 1955 * "Scopename" must be the same as the page-scope-name, therefore you can have 1956 * only one check-funktion for every scope you use 1957 * 1958 * @example fecHookCheckFormScopename 1959 */ 1960 function fecHookCheckFormScopename() { 1961 fecForm.initFormValidation(); 1962 var inputs = document.getElementsByTagName('input'), input = false; 1963 for (var i = 0, len = inputs.length; i < len; i++){ 1964 input = inputs[i]; 1965 // different checks for every formfield 1966 if (input.name == fec.util.eleId('html-id-name')) { 1967 if (input.value === '') { 1968 fecForm.setErrorInDom(input,"html-id-name is empty"); 1969 } 1970 } 1971 } 1972 return fecForm.checkIfFormValid(); 1973 } 1974 1975 1976