1/* Prototype JavaScript framework, version 1.6.0.3 2 * (c) 2005-2008 Sam Stephenson 3 * 4 * Prototype is freely distributable under the terms of an MIT-style license. 5 * For details, see the Prototype web site: http://www.prototypejs.org/ 6 * 7 *--------------------------------------------------------------------------*/ 8 9var Prototype = { 10 Version: '1.6.0.3', 11 12 Browser: { 13 IE: !!(window.attachEvent && 14 navigator.userAgent.indexOf('Opera') === -1), 15 Opera: navigator.userAgent.indexOf('Opera') > -1, 16 WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1, 17 Gecko: navigator.userAgent.indexOf('Gecko') > -1 && 18 navigator.userAgent.indexOf('KHTML') === -1, 19 MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/) 20 }, 21 22 BrowserFeatures: { 23 XPath: !!document.evaluate, 24 SelectorsAPI: !!document.querySelector, 25 ElementExtensions: !!window.HTMLElement, 26 SpecificElementExtensions: 27 document.createElement('div')['__proto__'] && 28 document.createElement('div')['__proto__'] !== 29 document.createElement('form')['__proto__'] 30 }, 31 32 ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>', 33 JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, 34 35 emptyFunction: function() { }, 36 K: function(x) { return x } 37}; 38 39if (Prototype.Browser.MobileSafari) 40 Prototype.BrowserFeatures.SpecificElementExtensions = false; 41 42 43/* Based on Alex Arnell's inheritance implementation. */ 44var Class = { 45 create: function() { 46 var parent = null, properties = $A(arguments); 47 if (Object.isFunction(properties[0])) 48 parent = properties.shift(); 49 50 function klass() { 51 this.initialize.apply(this, arguments); 52 } 53 54 Object.extend(klass, Class.Methods); 55 klass.superclass = parent; 56 klass.subclasses = []; 57 58 if (parent) { 59 var subclass = function() { }; 60 subclass.prototype = parent.prototype; 61 klass.prototype = new subclass; 62 parent.subclasses.push(klass); 63 } 64 65 for (var i = 0; i < properties.length; i++) 66 klass.addMethods(properties[i]); 67 68 if (!klass.prototype.initialize) 69 klass.prototype.initialize = Prototype.emptyFunction; 70 71 klass.prototype.constructor = klass; 72 73 return klass; 74 } 75}; 76 77Class.Methods = { 78 addMethods: function(source) { 79 var ancestor = this.superclass && this.superclass.prototype; 80 var properties = Object.keys(source); 81 82 if (!Object.keys({ toString: true }).length) 83 properties.push("toString", "valueOf"); 84 85 for (var i = 0, length = properties.length; i < length; i++) { 86 var property = properties[i], value = source[property]; 87 if (ancestor && Object.isFunction(value) && 88 value.argumentNames().first() == "$super") { 89 var method = value; 90 value = (function(m) { 91 return function() { return ancestor[m].apply(this, arguments) }; 92 })(property).wrap(method); 93 94 value.valueOf = method.valueOf.bind(method); 95 value.toString = method.toString.bind(method); 96 } 97 this.prototype[property] = value; 98 } 99 100 return this; 101 } 102}; 103 104var Abstract = { }; 105 106Object.extend = function(destination, source) { 107 for (var property in source) 108 destination[property] = source[property]; 109 return destination; 110}; 111 112Object.extend(Object, { 113 inspect: function(object) { 114 try { 115 if (Object.isUndefined(object)) return 'undefined'; 116 if (object === null) return 'null'; 117 return object.inspect ? object.inspect() : String(object); 118 } catch (e) { 119 if (e instanceof RangeError) return '...'; 120 throw e; 121 } 122 }, 123 124 toJSON: function(object) { 125 var type = typeof object; 126 switch (type) { 127 case 'undefined': 128 case 'function': 129 case 'unknown': return; 130 case 'boolean': return object.toString(); 131 } 132 133 if (object === null) return 'null'; 134 if (object.toJSON) return object.toJSON(); 135 if (Object.isElement(object)) return; 136 137 var results = []; 138 for (var property in object) { 139 var value = Object.toJSON(object[property]); 140 if (!Object.isUndefined(value)) 141 results.push(property.toJSON() + ': ' + value); 142 } 143 144 return '{' + results.join(', ') + '}'; 145 }, 146 147 toQueryString: function(object) { 148 return $H(object).toQueryString(); 149 }, 150 151 toHTML: function(object) { 152 return object && object.toHTML ? object.toHTML() : String.interpret(object); 153 }, 154 155 keys: function(object) { 156 var keys = []; 157 for (var property in object) 158 keys.push(property); 159 return keys; 160 }, 161 162 values: function(object) { 163 var values = []; 164 for (var property in object) 165 values.push(object[property]); 166 return values; 167 }, 168 169 clone: function(object) { 170 return Object.extend({ }, object); 171 }, 172 173 isElement: function(object) { 174 return !!(object && object.nodeType == 1); 175 }, 176 177 isArray: function(object) { 178 return object != null && typeof object == "object" && 179 'splice' in object && 'join' in object; 180 }, 181 182 isHash: function(object) { 183 return object instanceof Hash; 184 }, 185 186 isFunction: function(object) { 187 return typeof object == "function"; 188 }, 189 190 isString: function(object) { 191 return typeof object == "string"; 192 }, 193 194 isNumber: function(object) { 195 return typeof object == "number"; 196 }, 197 198 isUndefined: function(object) { 199 return typeof object == "undefined"; 200 } 201}); 202 203Object.extend(Function.prototype, { 204 argumentNames: function() {
205 var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1] 206 .replace(/\s+/g, '').split(','); 207 return names.length == 1 && !names[0] ? [] : names; 208 }, 209 210 bind: function() { 211 if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; 212 var __method = this, args = $A(arguments), object = args.shift(); 213 return function() { 214 return __method.apply(object, args.concat($A(arguments))); 215 } 216 }, 217 218 bindAsEventListener: function() { 219 var __method = this, args = $A(arguments), object = args.shift(); 220 return function(event) { 221 return __method.apply(object, [event || window.event].concat(args)); 222 } 223 }, 224 225 curry: function() { 226 if (!arguments.length) return this; 227 var __method = this, args = $A(arguments); 228 return function() { 229 return __method.apply(this, args.concat($A(arguments))); 230 } 231 }, 232 233 delay: function() { 234 var __method = this, args = $A(arguments), timeout = args.shift() * 1000; 235 return window.setTimeout(function() { 236 return __method.apply(__method, args); 237 }, timeout); 238 }, 239 240 defer: function() { 241 var args = [0.01].concat($A(arguments)); 242 return this.delay.apply(this, args); 243 }, 244 245 wrap: function(wrapper) { 246 var __method = this; 247 return function() { 248 return wrapper.apply(this, [__method.bind(this)].concat($A(arguments))); 249 } 250 }, 251 252 methodize: function() { 253 if (this._methodized) return this._methodized; 254 var __method = this; 255 return this._methodized = function() { 256 return __method.apply(null, [this].concat($A(arguments))); 257 }; 258 } 259}); 260 261Date.prototype.toJSON = function() { 262 return '"' + this.getUTCFullYear() + '-' + 263 (this.getUTCMonth() + 1).toPaddedString(2) + '-' + 264 this.getUTCDate().toPaddedString(2) + 'T' + 265 this.getUTCHours().toPaddedString(2) + ':' + 266 this.getUTCMinutes().toPaddedString(2) + ':' + 267 this.getUTCSeconds().toPaddedString(2) + 'Z"'; 268}; 269 270var Try = { 271 these: function() { 272 var returnValue; 273 274 for (var i = 0, length = arguments.length; i < length; i++) { 275 var lambda = arguments[i]; 276 try { 277 returnValue = lambda(); 278 break; 279 } catch (e) { } 280 } 281 282 return returnValue; 283 } 284}; 285 286RegExp.prototype.match = RegExp.prototype.test; 287 288RegExp.escape = function(str) { 289 return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); 290}; 291 292/*--------------------------------------------------------------------------*/ 293 294var PeriodicalExecuter = Class.create({ 295 initialize: function(callback, frequency) { 296 this.callback = callback; 297 this.frequency = frequency; 298 this.currentlyExecuting = false; 299 300 this.registerCallback(); 301 }, 302 303 registerCallback: function() { 304 this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); 305 }, 306 307 execute: function() { 308 this.callback(this); 309 }, 310 311 stop: function() { 312 if (!this.timer) return; 313 clearInterval(this.timer); 314 this.timer = null; 315 }, 316 317 onTimerEvent: function() { 318 if (!this.currentlyExecuting) { 319 try { 320 this.currentlyExecuting = true; 321 this.execute(); 322 } finally { 323 this.currentlyExecuting = false; 324 } 325 } 326 } 327}); 328Object.extend(String, { 329 interpret: function(value) { 330 return value == null ? '' : String(value); 331 }, 332 specialChar: { 333 '\b': '\\b', 334 '\t': '\\t', 335 '\n': '\\n', 336 '\f': '\\f', 337 '\r': '\\r', 338 '\\': '\\\\' 339 } 340}); 341 342Object.extend(String.prototype, { 343 gsub: function(pattern, replacement) { 344 var result = '', source = this, match; 345 replacement = arguments.callee.prepareReplacement(replacement); 346 347 while (source.length > 0) { 348 if (match = source.match(pattern)) { 349 result += source.slice(0, match.index); 350 result += String.interpret(replacement(match)); 351 source = source.slice(match.index + match[0].length); 352 } else { 353 result += source, source = ''; 354 } 355 } 356 return result; 357 }, 358 359 sub: function(pattern, replacement, count) { 360 replacement = this.gsub.prepareReplacement(replacement); 361 count = Object.isUndefined(count) ? 1 : count; 362 363 return this.gsub(pattern, function(match) { 364 if (--count < 0) return match[0]; 365 return replacement(match); 366 }); 367 }, 368 369 scan: function(pattern, iterator) { 370 this.gsub(pattern, iterator); 371 return String(this); 372 }, 373 374 truncate: function(length, truncation) { 375 length = length || 30; 376 truncation = Object.isUndefined(truncation) ? '...' : truncation; 377 return this.length > length ? 378 this.slice(0, length - truncation.length) + truncation : String(this); 379 }, 380 381 strip: function() { 382 return this.replace(/^\s+/, '').replace(/\s+$/, ''); 383 }, 384 385 stripTags: function() { 386 return this.replace(/<\/?[^>]+>/gi, ''); 387 }, 388 389 stripScripts: function() { 390 return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); 391 }, 392 393 extractScripts: function() { 394 var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); 395 var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); 396 return (this.match(matchAll) || []).map(function(scriptTag) { 397 return (scriptTag.match(matchOne) || ['', ''])[1]; 398 }); 399 }, 400 401 evalScripts: function() { 402 return this.extractScripts().map(function(script) { return eval(script) }); 403 }, 404 405 escapeHTML: function() { 406 var self = arguments.callee; 407 self.text.data = this; 408 return self.div.innerHTML; 409 }, 410 411 unescapeHTML: function() { 412 var div = new Element('div'); 413 div.innerHTML = this.stripTags(); 414 return div.childNodes[0] ? (div.childNodes.length > 1 ? 415 $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) : 416 div.childNodes[0].nodeValue) : ''; 417 }, 418 419 toQueryParams: function(separator) { 420 var match = this.strip().match(/([^?#]*)(#.*)?$/); 421 if (!match) return { }; 422 423 return match[1].split(separator || '&').inject({ }, function(hash, pair) { 424 if ((pair = pair.split('='))[0]) { 425 var key = decodeURIComponent(pair.shift()); 426 var value = pair.length > 1 ? pair.join('=') : pair[0]; 427 if (value != undefined) value = decodeURIComponent(value); 428 429 if (key in hash) { 430 if (!Object.isArray(hash[key])) hash[key] = [hash[key]]; 431 hash[key].push(value); 432 } 433 else hash[key] = value; 434 } 435 return hash; 436 }); 437 }, 438 439 toArray: function() { 440 return this.split(''); 441 }, 442 443 succ: function() { 444 return this.slice(0, this.length - 1) + 445 String.fromCharCode(this.charCodeAt(this.length - 1) + 1); 446 }, 447 448 times: function(count) { 449 return count < 1 ? '' : new Array(count + 1).join(this); 450 }, 451 452 camelize: function() { 453 var parts = this.split('-'), len = parts.length; 454 if (len == 1) return parts[0]; 455 456 var camelized = this.charAt(0) == '-' 457 ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) 458 : parts[0]; 459 460 for (var i = 1; i < len; i++) 461 camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); 462 463 return camelized; 464 }, 465 466 capitalize: function() { 467 return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); 468 }, 469 470 underscore: function() { 471 return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase(); 472 }, 473 474 dasherize: function() { 475 return this.gsub(/_/,'-'); 476 }, 477 478 inspect: function(useDoubleQuotes) { 479 var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) { 480 var character = String.specialChar[match[0]];
481 return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16); 482 }); 483 if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; 484 return "'" + escapedString.replace(/'/g, '\\\'') + "'"; 485 }, 486 487 toJSON: function() { 488 return this.inspect(true); 489 }, 490 491 unfilterJSON: function(filter) { 492 return this.sub(filter || Prototype.JSONFilter, '#{1}'); 493 }, 494 495 isJSON: function() { 496 var str = this; 497 if (str.blank()) return false; 498 str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''); 499 return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str); 500 }, 501 502 evalJSON: function(sanitize) { 503 var json = this.unfilterJSON(); 504 try { 505 if (!sanitize || json.isJSON()) return eval('(' + json + ')'); 506 } catch (e) { } 507 throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); 508 }, 509 510 include: function(pattern) { 511 return this.indexOf(pattern) > -1; 512 }, 513 514 startsWith: function(pattern) { 515 return this.indexOf(pattern) === 0; 516 }, 517 518 endsWith: function(pattern) { 519 var d = this.length - pattern.length; 520 return d >= 0 && this.lastIndexOf(pattern) === d; 521 }, 522 523 empty: function() { 524 return this == ''; 525 }, 526 527 blank: function() { 528 return /^\s*$/.test(this); 529 }, 530 531 interpolate: function(object, pattern) { 532 return new Template(this, pattern).evaluate(object); 533 } 534}); 535 536if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, { 537 escapeHTML: function() { 538 return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); 539 }, 540 unescapeHTML: function() { 541 return this.stripTags().replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); 542 } 543}); 544 545String.prototype.gsub.prepareReplacement = function(replacement) { 546 if (Object.isFunction(replacement)) return replacement; 547 var template = new Template(replacement); 548 return function(match) { return template.evaluate(match) }; 549}; 550 551String.prototype.parseQuery = String.prototype.toQueryParams; 552 553Object.extend(String.prototype.escapeHTML, { 554 div: document.createElement('div'), 555 text: document.createTextNode('') 556}); 557 558String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text); 559 560var Template = Class.create({ 561 initialize: function(template, pattern) { 562 this.template = template.toString(); 563 this.pattern = pattern || Template.Pattern; 564 }, 565 566 evaluate: function(object) { 567 if (Object.isFunction(object.toTemplateReplacements)) 568 object = object.toTemplateReplacements(); 569 570 return this.template.gsub(this.pattern, function(match) { 571 if (object == null) return ''; 572 573 var before = match[1] || ''; 574 if (before == '\\') return match[2]; 575 576 var ctx = object, expr = match[3]; 577 var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; 578 match = pattern.exec(expr); 579 if (match == null) return before; 580 581 while (match != null) { 582 var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1]; 583 ctx = ctx[comp]; 584 if (null == ctx || '' == match[3]) break; 585 expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); 586 match = pattern.exec(expr); 587 } 588 589 return before + String.interpret(ctx); 590 }); 591 } 592}); 593Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; 594 595var $break = { }; 596 597var Enumerable = { 598 each: function(iterator, context) { 599 var index = 0; 600 try { 601 this._each(function(value) { 602 iterator.call(context, value, index++); 603 }); 604 } catch (e) { 605 if (e != $break) throw e; 606 } 607 return this; 608 }, 609 610 eachSlice: function(number, iterator, context) { 611 var index = -number, slices = [], array = this.toArray(); 612 if (number < 1) return array; 613 while ((index += number) < array.length) 614 slices.push(array.slice(index, index+number)); 615 return slices.collect(iterator, context); 616 }, 617 618 all: function(iterator, context) { 619 iterator = iterator || Prototype.K; 620 var result = true; 621 this.each(function(value, index) { 622 result = result && !!iterator.call(context, value, index); 623 if (!result) throw $break; 624 }); 625 return result; 626 }, 627 628 any: function(iterator, context) { 629 iterator = iterator || Prototype.K; 630 var result = false;
631 this.each(function(value, index) { 632 if (result = !!iterator.call(context, value, index)) 633 throw $break; 634 }); 635 return result; 636 }, 637 638 collect: function(iterator, context) { 639 iterator = iterator || Prototype.K; 640 var results = []; 641 this.each(function(value, index) { 642 results.push(iterator.call(context, value, index)); 643 }); 644 return results; 645 }, 646 647 detect: function(iterator, context) { 648 var result; 649 this.each(function(value, index) { 650 if (iterator.call(context, value, index)) { 651 result = value; 652 throw $break; 653 } 654 }); 655 return result; 656 }, 657 658 findAll: function(iterator, context) { 659 var results = []; 660 this.each(function(value, index) { 661 if (iterator.call(context, value, index)) 662 results.push(value); 663 }); 664 return results; 665 }, 666 667 grep: function(filter, iterator, context) { 668 iterator = iterator || Prototype.K; 669 var results = []; 670 671 if (Object.isString(filter)) 672 filter = new RegExp(filter); 673 674 this.each(function(value, index) { 675 if (filter.match(value)) 676 results.push(iterator.call(context, value, index)); 677 }); 678 return results; 679 }, 680 681 include: function(object) { 682 if (Object.isFunction(this.indexOf)) 683 if (this.indexOf(object) != -1) return true; 684 685 var found = false; 686 this.each(function(value) { 687 if (value == object) { 688 found = true; 689 throw $break; 690 } 691 }); 692 return found; 693 }, 694 695 inGroupsOf: function(number, fillWith) { 696 fillWith = Object.isUndefined(fillWith) ? null : fillWith; 697 return this.eachSlice(number, function(slice) { 698 while(slice.length < number) slice.push(fillWith); 699 return slice; 700 }); 701 }, 702 703 inject: function(memo, iterator, context) { 704 this.each(function(value, index) { 705 memo = iterator.call(context, memo, value, index); 706 }); 707 return memo; 708 }, 709 710 invoke: function(method) { 711 var args = $A(arguments).slice(1); 712 return this.map(function(value) { 713 return value[method].apply(value, args); 714 }); 715 }, 716 717 max: function(iterator, context) { 718 iterator = iterator || Prototype.K; 719 var result; 720 this.each(function(value, index) { 721 value = iterator.call(context, value, index); 722 if (result == null || value >= result) 723 result = value; 724 }); 725 return result; 726 }, 727 728 min: function(iterator, context) { 729 iterator = iterator || Prototype.K; 730 var result; 731 this.each(function(value, index) { 732 value = iterator.call(context, value, index); 733 if (result == null || value < result) 734 result = value; 735 }); 736 return result; 737 }, 738 739 partition: function(iterator, context) { 740 iterator = iterator || Prototype.K; 741 var trues = [], falses = []; 742 this.each(function(value, index) { 743 (iterator.call(context, value, index) ? 744 trues : falses).push(value); 745 }); 746 return [trues, falses]; 747 }, 748 749 pluck: function(property) { 750 var results = []; 751 this.each(function(value) { 752 results.push(value[property]); 753 }); 754 return results; 755 }, 756 757 reject: function(iterator, context) { 758 var results = []; 759 this.each(function(value, index) { 760 if (!iterator.call(context, value, index)) 761 results.push(value); 762 }); 763 return results; 764 }, 765 766 sortBy: function(iterator, context) { 767 return this.map(function(value, index) { 768 return { 769 value: value, 770 criteria: iterator.call(context, value, index) 771 }; 772 }).sort(function(left, right) { 773 var a = left.criteria, b = right.criteria; 774 return a < b ? -1 : a > b ? 1 : 0; 775 }).pluck('value'); 776 }, 777 778 toArray: function() { 779 return this.map(); 780 }, 781 782 zip: function() { 783 var iterator = Prototype.K, args = $A(arguments); 784 if (Object.isFunction(args.last())) 785 iterator = args.pop(); 786 787 var collections = [this].concat(args).map($A); 788 return this.map(function(value, index) { 789 return iterator(collections.pluck(index)); 790 }); 791 }, 792 793 size: function() { 794 return this.toArray().length; 795 }, 796 797 inspect: function() { 798 return '#<Enumerable:' + this.toArray().inspect() + '>'; 799 } 800}; 801 802Object.extend(Enumerable, { 803 map: Enumerable.collect, 804 find: Enumerable.detect, 805 select: Enumerable.findAll, 806 filter: Enumerable.findAll, 807 member: Enumerable.include, 808 entries: Enumerable.toArray, 809 every: Enumerable.all, 810 some: Enumerable.any 811}); 812function $A(iterable) { 813 if (!iterable) return []; 814 if (iterable.toArray) return iterable.toArray(); 815 var length = iterable.length || 0, results = new Array(length); 816 while (length--) results[length] = iterable[length]; 817 return results; 818} 819 820if (Prototype.Browser.WebKit) { 821 $A = function(iterable) { 822 if (!iterable) return []; 823 // In Safari, only use the `toArray` method if it's not a NodeList. 824 // A NodeList is a function, has an function `item` property, and a numeric 825 // `length` property. Adapted from Google Doctype. 826 if (!(typeof iterable === 'function' && typeof iterable.length === 827 'number' && typeof iterable.item === 'function') && iterable.toArray) 828 return iterable.toArray(); 829 var length = iterable.length || 0, results = new Array(length); 830 while (length--) results[length] = iterable[length]; 831 return results; 832 }; 833} 834 835Array.from = $A; 836 837Object.extend(Array.prototype, Enumerable); 838 839if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse; 840 841Object.extend(Array.prototype, { 842 _each: function(iterator) { 843 for (var i = 0, length = this.length; i < length; i++) 844 iterator(this[i]); 845 }, 846 847 clear: function() { 848 this.length = 0; 849 return this; 850 }, 851 852 first: function() { 853 return this[0]; 854 }, 855 856 last: function() { 857 return this[this.length - 1]; 858 }, 859 860 compact: function() { 861 return this.select(function(value) { 862 return value != null; 863 }); 864 }, 865 866 flatten: function() { 867 return this.inject([], function(array, value) { 868 return array.concat(Object.isArray(value) ? 869 value.flatten() : [value]); 870 }); 871 }, 872 873 without: function() { 874 var values = $A(arguments); 875 return this.select(function(value) { 876 return !values.include(value); 877 }); 878 }, 879 880 reverse: function(inline) { 881 return (inline !== false ? this : this.toArray())._reverse(); 882 }, 883 884 reduce: function() { 885 return this.length >
885 1 ? this : this[0]; 886 }, 887 888 uniq: function(sorted) { 889 return this.inject([], function(array, value, index) { 890 if (0 == index || (sorted ? array.last() != value : !array.include(value))) 891 array.push(value); 892 return array; 893 }); 894 }, 895 896 intersect: function(array) { 897 return this.uniq().findAll(function(item) { 898 return array.detect(function(value) { return item === value }); 899 }); 900 }, 901 902 clone: function() { 903 return [].concat(this); 904 }, 905 906 size: function() { 907 return this.length; 908 }, 909 910 inspect: function() { 911 return '[' + this.map(Object.inspect).join(', ') + ']'; 912 }, 913 914 toJSON: function() { 915 var results = []; 916 this.each(function(object) { 917 var value = Object.toJSON(object); 918 if (!Object.isUndefined(value)) results.push(value); 919 }); 920 return '[' + results.join(', ') + ']'; 921 } 922}); 923 924// use native browser JS 1.6 implementation if available 925if (Object.isFunction(Array.prototype.forEach)) 926 Array.prototype._each = Array.prototype.forEach; 927 928if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) { 929 i || (i = 0); 930 var length = this.length; 931 if (i < 0) i = length + i; 932 for (; i < length; i++) 933 if (this[i] === item) return i; 934 return -1; 935}; 936 937if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) { 938 i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1; 939 var n = this.slice(0, i).reverse().indexOf(item); 940 return (n < 0) ? n : i - n - 1; 941}; 942 943Array.prototype.toArray = Array.prototype.clone; 944 945function $w(string) { 946 if (!Object.isString(string)) return []; 947 string = string.strip(); 948 return string ? string.split(/\s+/) : []; 949} 950 951if (Prototype.Browser.Opera){ 952 Array.prototype.concat = function() { 953 var array = []; 954 for (var i = 0, length = this.length; i < length; i++) array.push(this[i]); 955 for (var i = 0, length = arguments.length; i < length; i++) { 956 if (Object.isArray(arguments[i])) { 957 for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++) 958 array.push(arguments[i][j]); 959 } else { 960 array.push(arguments[i]); 961 } 962 } 963 return array; 964 }; 965} 966Object.extend(Number.prototype, { 967 toColorPart: function() { 968 return this.toPaddedString(2, 16); 969 }, 970 971 succ: function() { 972 return this + 1; 973 }, 974 975 times: function(iterator, context) { 976 $R(0, this, true).each(iterator, context); 977 return this; 978 }, 979 980 toPaddedString: function(length, radix) { 981 var string = this.toString(radix || 10); 982 return '0'.times(length - string.length) + string; 983 }, 984 985 toJSON: function() { 986 return isFinite(this) ? this.toString() : 'null'; 987 } 988}); 989 990$w('abs round ceil floor').each(function(method){ 991 Number.prototype[method] = Math[method].methodize(); 992}); 993function $H(object) { 994 return new Hash(object); 995}; 996 997var Hash = Class.create(Enumerable, (function() { 998 999 function toQueryPair(key, value) { 1000 if (Object.isUndefined(value)) return key; 1001 return key + '=' + encodeURIComponent(String.interpret(value)); 1002 } 1003 1004 return { 1005 initialize: function(object) { 1006 this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); 1007 }, 1008 1009 _each: function(iterator) { 1010 for (var key in this._object) { 1011 var value = this._object[key], pair = [key, value]; 1012 pair.key = key; 1013 pair.value = value; 1014 iterator(pair); 1015 } 1016 }, 1017 1018 set: function(key, value) { 1019 return this._object[key] = value; 1020 }, 1021 1022 get: function(key) { 1023 // simulating poorly supported hasOwnProperty 1024 if (this._object[key] !== Object.prototype[key]) 1025 return this._object[key]; 1026 }, 1027 1028 unset: function(key) { 1029 var value = this._object[key]; 1030 delete this._object[key]; 1031 return value; 1032 }, 1033 1034 toObject: function() { 1035 return Object.clone(this._object); 1036 }, 1037 1038 keys: function() { 1039 return this.pluck('key'); 1040 }, 1041 1042 values: function() { 1043 return this.pluck('value'); 1044 }, 1045 1046 index: function(value) { 1047 var match = this.detect(function(pair) { 1048 return pair.value === value; 1049 }); 1050 return match && match.key; 1051 }, 1052 1053 merge: function(object) { 1054 return this.clone().update(object); 1055 }, 1056 1057 update: function(object) { 1058 return new Hash(object).inject(this, function(result, pair) { 1059 result.set(pair.key, pair.value); 1060 return result; 1061 }); 1062 }, 1063 1064 toQueryString: function() { 1065 return this.inject([], function(results, pair) { 1066 var key = encodeURIComponent(pair.key), values = pair.value; 1067 1068 if (values && typeof values == 'object') { 1069 if (Object.isArray(values)) 1070 return results.concat(values.map(toQueryPair.curry(key))); 1071 } else results.push(toQueryPair(key, values)); 1072 return results; 1073 }).join('&'); 1074 }, 1075 1076 inspect: function() { 1077 return '#<Hash:{' + this.map(function(pair) { 1078 return pair.map(Object.inspect).join(': '); 1079 }).join(', ') + '}>'; 1080 }, 1081 1082 toJSON: function() { 1083 return Object.toJSON(this.toObject()); 1084 }, 1085 1086 clone: function() { 1087 return new Hash(this); 1088 } 1089 } 1090})()); 1091 1092Hash.prototype.toTemplateReplacements = Hash.prototype.toObject; 1093Hash.from = $H; 1094var ObjectRange = Class.create(Enumerable, { 1095 initialize: function(start, end, exclusive) { 1096 this.start = start; 1097 this.end = end; 1098 this.exclusive = exclusive; 1099 }, 1100 1101 _each: function(iterator) { 1102 var value = this.start; 1103 while (this.include(value)) { 1104 iterator(value); 1105 value = value.succ(); 1106 } 1107 }, 1108 1109 include: function(value) { 1110 if (value < this.start) 1111 return false;
1112 if (this.exclusive) 1113 return value < this.end; 1114 return value <= this.end; 1115 } 1116}); 1117 1118var $R = function(start, end, exclusive) { 1119 return new ObjectRange(start, end, exclusive); 1120}; 1121 1122var Ajax = { 1123 getTransport: function() { 1124 return Try.these( 1125 function() {return new XMLHttpRequest()}, 1126 function() {return new ActiveXObject('Msxml2.XMLHTTP')}, 1127 function() {return new ActiveXObject('Microsoft.XMLHTTP')} 1128 ) || false; 1129 }, 1130 1131 activeRequestCount: 0 1132}; 1133 1134Ajax.Responders = { 1135 responders: [], 1136 1137 _each: function(iterator) { 1138 this.responders._each(iterator); 1139 }, 1140 1141 register: function(responder) { 1142 if (!this.include(responder)) 1143 this.responders.push(responder); 1144 }, 1145 1146 unregister: function(responder) { 1147 this.responders = this.responders.without(responder); 1148 }, 1149 1150 dispatch: function(callback, request, transport, json) { 1151 this.each(function(responder) { 1152 if (Object.isFunction(responder[callback])) { 1153 try { 1154 responder[callback].apply(responder, [request, transport, json]); 1155 } catch (e) { } 1156 } 1157 }); 1158 } 1159}; 1160 1161Object.extend(Ajax.Responders, Enumerable); 1162 1163Ajax.Responders.register({ 1164 onCreate: function() { Ajax.activeRequestCount++ },
1165 onComplete: function() { Ajax.activeRequestCount-- } 1166}); 1167 1168Ajax.Base = Class.create({ 1169 initialize: function(options) { 1170 this.options = { 1171 method: 'post', 1172 asynchronous: true, 1173 contentType: 'application/x-www-form-urlencoded', 1174 encoding: 'UTF-8', 1175 parameters: '', 1176 evalJSON: true, 1177 evalJS: true 1178 }; 1179 Object.extend(this.options, options || { }); 1180 1181 this.options.method = this.options.method.toLowerCase(); 1182 1183 if (Object.isString(this.options.parameters)) 1184 this.options.parameters = this.options.parameters.toQueryParams(); 1185 else if (Object.isHash(this.options.parameters)) 1186 this.options.parameters = this.options.parameters.toObject(); 1187 } 1188}); 1189 1190Ajax.Request = Class.create(Ajax.Base, { 1191 _complete: false, 1192 1193 initialize: function($super, url, options) { 1194 $super(options); 1195 this.transport = Ajax.getTransport(); 1196 this.request(url); 1197 }, 1198 1199 request: function(url) { 1200 this.url = url; 1201 this.method = this.options.method; 1202 var params = Object.clone(this.options.parameters); 1203 1204 if (!['get', 'post'].include(this.method)) { 1205 // simulate other verbs over post 1206 params['_method'] = this.method; 1207 this.method = 'post'; 1208 } 1209 1210 this.parameters = params; 1211 1212 if (params = Object.toQueryString(params)) { 1213 // when GET, append parameters to URL 1214 if (this.method == 'get') 1215 this.url += (this.url.include('?') ? '&' : '?') + params; 1216 else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) 1217 params += '&_='; 1218 } 1219 1220 try { 1221 var response = new Ajax.Response(this); 1222 if (this.options.onCreate) this.options.onCreate(response); 1223 Ajax.Responders.dispatch('onCreate', this, response); 1224 1225 this.transport.open(this.method.toUpperCase(), this.url, 1226 this.options.asynchronous); 1227 1228 if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); 1229 1230 this.transport.onreadystatechange = this.onStateChange.bind(this); 1231 this.setRequestHeaders(); 1232 1233 this.body = this.method == 'post' ? (this.options.postBody || params) : null; 1234 this.transport.send(this.body); 1235 1236 /* Force Firefox to handle ready state 4 for synchronous requests */ 1237 if (!this.options.asynchronous && this.transport.overrideMimeType) 1238 this.onStateChange(); 1239 1240 } 1241 catch (e) { 1242 this.dispatchException(e); 1243 } 1244 }, 1245 1246 onStateChange: function() { 1247 var readyState = this.transport.readyState; 1248 if (readyState > 1 && !((readyState == 4) && this._complete)) 1249 this.respondToReadyState(this.transport.readyState); 1250 }, 1251 1252 setRequestHeaders: function() { 1253 var headers = { 1254 'X-Requested-With': 'XMLHttpRequest', 1255 'X-Prototype-Version': Prototype.Version, 1256 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' 1257 }; 1258 1259 if (this.method == 'post') { 1260 headers['Content-type'] = this.options.contentType + 1261 (this.options.encoding ? '; charset=' + this.options.encoding : ''); 1262 1263 /* Force "Connection: close" for older Mozilla browsers to work 1264 * around a bug where XMLHttpRequest sends an incorrect 1265 * Content-length header. See Mozilla Bugzilla #246651. 1266 */ 1267 if (this.transport.overrideMimeType && 1268 (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) 1269 headers['Connection'] = 'close'; 1270 } 1271 1272 // user-defined headers 1273 if (typeof this.options.requestHeaders == 'object') { 1274 var extras = this.options.requestHeaders; 1275 1276 if (Object.isFunction(extras.push)) 1277 for (var i = 0, length = extras.length; i < length; i += 2) 1278 headers[extras[i]] = extras[i+1]; 1279 else 1280 $H(extras).each(function(pair) { headers[pair.key] = pair.value }); 1281 } 1282 1283 for (var name in headers) 1284 this.transport.setRequestHeader(name, headers[name]); 1285 }, 1286 1287 success: function() { 1288 var status = this.getStatus(); 1289 return !status || (status >= 200 && status < 300); 1290 }, 1291 1292 getStatus: function() { 1293 try { 1294 return this.transport.status || 0; 1295 } catch (e) { return 0 } 1296 }, 1297 1298 respondToReadyState: function(readyState) { 1299 var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); 1300 1301 if (state == 'Complete') {
1302 try { 1303 this._complete = true; 1304 (this.options['on' + response.status] 1305 || this.options['on' + (this.success() ? 'Success' : 'Failure')] 1306 || Prototype.emptyFunction)(response, response.headerJSON); 1307 } catch (e) { 1308 this.dispatchException(e); 1309 } 1310 1311 var contentType = response.getHeader('Content-type'); 1312 if (this.options.evalJS == 'force' 1313 || (this.options.evalJS && this.isSameOrigin() && contentType 1314 && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) 1315 this.evalResponse(); 1316 } 1317 1318 try { 1319 (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); 1320 Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); 1321 } catch (e) { 1322 this.dispatchException(e); 1323 } 1324 1325 if (state == 'Complete') { 1326 // avoid memory leak in MSIE: clean up 1327 this.transport.onreadystatechange = Prototype.emptyFunction; 1328 } 1329 }, 1330 1331 isSameOrigin: function() { 1332 var m = this.url.match(/^\s*https?:\/\/[^\/]*/); 1333 return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ 1334 protocol: location.protocol, 1335 domain: document.domain, 1336 port: location.port ? ':' + location.port : '' 1337 })); 1338 }, 1339 1340 getHeader: function(name) { 1341 try { 1342 return this.transport.getResponseHeader(name) || null; 1343 } catch (e) { return null } 1344 }, 1345 1346 evalResponse: function() { 1347 try { 1348 return eval((this.transport.responseText || '').unfilterJSON()); 1349 } catch (e) { 1350 this.dispatchException(e); 1351 } 1352 }, 1353 1354 dispatchException: function(exception) { 1355 (this.options.onException || Prototype.emptyFunction)(this, exception); 1356 Ajax.Responders.dispatch('onException', this, exception); 1357 } 1358}); 1359 1360Ajax.Request.Events = 1361 ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; 1362 1363Ajax.Response = Class.create({ 1364 initialize: function(request){ 1365 this.request = request; 1366 var transport = this.transport = request.transport, 1367 readyState = this.readyState = transport.readyState; 1368 1369 if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { 1370 this.status = this.getStatus(); 1371 this.statusText = this.getStatusText(); 1372 this.responseText = String.interpret(transport.responseText); 1373 this.headerJSON = this._getHeaderJSON(); 1374 } 1375 1376 if(readyState == 4) { 1377 var xml = transport.responseXML; 1378 this.responseXML = Object.isUndefined(xml) ? null : xml; 1379 this.responseJSON = this._getResponseJSON(); 1380 } 1381 }, 1382 1383 status: 0, 1384 statusText: '', 1385 1386 getStatus: Ajax.Request.prototype.getStatus, 1387 1388 getStatusText: function() { 1389 try { 1390 return this.transport.statusText || ''; 1391 } catch (e) { return '' } 1392 }, 1393 1394 getHeader: Ajax.Request.prototype.getHeader, 1395 1396 getAllHeaders: function() { 1397 try { 1398 return this.getAllResponseHeaders(); 1399 } catch (e) { return null } 1400 }, 1401 1402 getResponseHeader: function(name) { 1403 return this.transport.getResponseHeader(name); 1404 }, 1405 1406 getAllResponseHeaders: function() { 1407 return this.transport.getAllResponseHeaders(); 1408 }, 1409 1410 _getHeaderJSON: function() { 1411 var json = this.getHeader('X-JSON'); 1412 if (!json) return null; 1413 json = decodeURIComponent(escape(json)); 1414 try { 1415 return json.evalJSON(this.request.options.sanitizeJSON || 1416 !this.request.isSameOrigin()); 1417 } catch (e) { 1418 this.request.dispatchException(e); 1419 } 1420 }, 1421 1422 _getResponseJSON: function() { 1423 var options = this.request.options; 1424 if (!options.evalJSON || (options.evalJSON != 'force' && 1425 !(this.getHeader('Content-type') || '').include('application/json')) || 1426 this.responseText.blank()) 1427 return null; 1428 try { 1429 return this.responseText.evalJSON(options.sanitizeJSON || 1430 !this.request.isSameOrigin()); 1431 } catch (e) { 1432 this.request.dispatchException(e); 1433 } 1434 } 1435}); 1436 1437Ajax.Updater = Class.create(Ajax.Request, { 1438 initialize: function($super, container, url, options) { 1439 this.container = { 1440 success: (container.success || container), 1441 failure: (container.failure || (container.success ? null : container)) 1442 }; 1443 1444 options = Object.clone(options); 1445 var onComplete = options.onComplete; 1446 options.onComplete = (function(response, json) { 1447 this.updateContent(response.responseText); 1448 if (Object.isFunction(onComplete)) onComplete(response, json); 1449 }).bind(this); 1450 1451 $super(url, options); 1452 }, 1453 1454 updateContent: function(responseText) { 1455 var receiver = this.container[this.success() ? 'success' : 'failure'], 1456 options = this.options; 1457 1458 if (!options.evalScripts) responseText = responseText.stripScripts(); 1459 1460 if (receiver = $(receiver)) { 1461 if (options.insertion) { 1462 if (Object.isString(options.insertion)) { 1463 var insertion = { }; insertion[options.insertion] = responseText; 1464 receiver.insert(insertion); 1465 } 1466 else options.insertion(receiver, responseText); 1467 } 1468 else receiver.update(responseText); 1469 } 1470 } 1471}); 1472
1473Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { 1474 initialize: function($super, container, url, options) { 1475 $super(options); 1476 this.onComplete = this.options.onComplete; 1477 1478 this.frequency = (this.options.frequency || 2); 1479 this.decay = (this.options.decay || 1); 1480 1481 this.updater = { }; 1482 this.container = container; 1483 this.url = url; 1484 1485 this.start(); 1486 }, 1487 1488 start: function() { 1489 this.options.onComplete = this.updateComplete.bind(this); 1490 this.onTimerEvent(); 1491 }, 1492 1493 stop: function() { 1494 this.updater.options.onComplete = undefined; 1495 clearTimeout(this.timer); 1496 (this.onComplete || Prototype.emptyFunction).apply(this, arguments); 1497 }, 1498 1499 updateComplete: function(response) { 1500 if (this.options.decay) { 1501 this.decay = (response.responseText == this.lastText ? 1502 this.decay * this.options.decay : 1); 1503 1504 this.lastText = response.responseText; 1505 } 1506 this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); 1507 }, 1508 1509 onTimerEvent: function() { 1510 this.updater = new Ajax.Updater(this.container, this.url, this.options); 1511 } 1512}); 1513function $(element) { 1514 if (arguments.length > 1) { 1515 for (var i = 0, elements = [], length = arguments.length; i < length; i++) 1516 elements.push($(arguments[i])); 1517 return elements; 1518 } 1519 if (Object.isString(element)) 1520 element = document.getElementById(element); 1521 return Element.extend(element); 1522} 1523 1524if (Prototype.BrowserFeatures.XPath) { 1525 document._getElementsByXPath = function(expression, parentElement) { 1526 var results = []; 1527 var query = document.evaluate(expression, $(parentElement) || document, 1528 null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); 1529 for (var i = 0, length = query.snapshotLength; i < length; i++) 1530 results.push(Element.extend(query.snapshotItem(i))); 1531 return results; 1532 }; 1533} 1534 1535/*--------------------------------------------------------------------------*/ 1536 1537if (!window.Node) var Node = { }; 1538 1539if (!Node.ELEMENT_NODE) { 1540 // DOM level 2 ECMAScript Language Binding 1541 Object.extend(Node, { 1542 ELEMENT_NODE: 1, 1543 ATTRIBUTE_NODE: 2, 1544 TEXT_NODE: 3, 1545 CDATA_SECTION_NODE: 4, 1546 ENTITY_REFERENCE_NODE: 5, 1547 ENTITY_NODE: 6, 1548 PROCESSING_INSTRUCTION_NODE: 7, 1549 COMMENT_NODE: 8, 1550 DOCUMENT_NODE: 9, 1551 DOCUMENT_TYPE_NODE: 10, 1552 DOCUMENT_FRAGMENT_NODE: 11, 1553 NOTATION_NODE: 12 1554 }); 1555} 1556 1557(function() { 1558 var element = this.Element; 1559 this.Element = function(tagName, attributes) { 1560 attributes = attributes || { }; 1561 tagName = tagName.toLowerCase(); 1562 var cache = Element.cache; 1563 if (Prototype.Browser.IE && attributes.name) { 1564 tagName = '<' + tagName + ' name="' + attributes.name + '">'; 1565 delete attributes.name; 1566 return Element.writeAttribute(document.createElement(tagName), attributes); 1567 } 1568 if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName)); 1569 return Element.writeAttribute(cache[tagName].cloneNode(false), attributes); 1570 }; 1571 Object.extend(this.Element, element || { }); 1572 if (element) this.Element.prototype = element.prototype; 1573}).call(window); 1574 1575Element.cache = { }; 1576 1577Element.Methods = { 1578 visible: function(element) { 1579 return $(element).style.display != 'none'; 1580 }, 1581 1582 toggle: function(element) { 1583 element = $(element); 1584 Element[Element.visible(element) ? 'hide' : 'show'](element); 1585 return element; 1586 }, 1587 1588 hide: function(element) { 1589 element = $(element); 1590 element.style.display = 'none'; 1591 return element; 1592 }, 1593 1594 show: function(element) { 1595 element = $(element); 1596 element.style.display = ''; 1597 return element; 1598 }, 1599 1600 remove: function(element) { 1601 element = $(element); 1602 element.parentNode.removeChild(element); 1603 return element; 1604 }, 1605 1606 update: function(element, content) { 1607 element = $(element); 1608 if (content && content.toElement) content = content.toElement(); 1609 if (Object.isElement(content)) return element.update().insert(content); 1610 content = Object.toHTML(content); 1611 element.innerHTML = content.stripScripts(); 1612 content.evalScripts.bind(content).defer(); 1613 return element; 1614 }, 1615 1616 replace: function(element, content) { 1617 element = $(element); 1618 if (content && content.toElement) content = content.toElement(); 1619 else if (!Object.isElement(content)) { 1620 content = Object.toHTML(content); 1621 var range = element.ownerDocument.createRange(); 1622 range.selectNode(element); 1623 content.evalScripts.bind(content).defer(); 1624 content = range.createContextualFragment(content.stripScripts()); 1625 } 1626 element.parentNode.replaceChild(content, element); 1627 return element; 1628 }, 1629 1630 insert: function(element, insertions) { 1631 element = $(element); 1632 1633 if (Object.isString(insertions) || Object.isNumber(insertions) || 1634 Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML))) 1635 insertions = {bottom:insertions}; 1636 1637 var content, insert, tagName, childNodes; 1638 1639 for (var position in insertions) { 1640 content = insertions[position]; 1641 position = position.toLowerCase(); 1642 insert = Element._insertionTranslations[position]; 1643 1644 if (content && content.toElement) content = content.toElement(); 1645 if (Object.isElement(content)) { 1646 insert(element, content); 1647 continue; 1648 } 1649 1650 content = Object.toHTML(content); 1651 1652 tagName = ((position == 'before' || position == 'after') 1653 ? element.parentNode : element).tagName.toUpperCase(); 1654 1655 childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); 1656 1657 if (position == 'top' || position == 'after') childNodes.reverse(); 1658 childNodes.each(insert.curry(element)); 1659 1660 content.evalScripts.bind(content).defer(); 1661 } 1662 1663 return element; 1664 }, 1665 1666 wrap: function(element, wrapper, attributes) { 1667 element = $(element); 1668 if (Object.isElement(wrapper)) 1669 $(wrapper).writeAttribute(attributes || { }); 1670 else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes); 1671 else wrapper = new Element('div', wrapper); 1672 if (element.parentNode) 1673 element.parentNode.replaceChild(wrapper, element); 1674 wrapper.appendChild(element); 1675 return wrapper; 1676 }, 1677 1678 inspect: function(element) { 1679 element = $(element); 1680 var result = '<' + element.tagName.toLowerCase(); 1681 $H({'id': 'id', 'className': 'class'}).each(function(pair) { 1682 var property = pair.first(), attribute = pair.last(); 1683 var value = (element[property] || '').toString(); 1684 if (value) result += ' ' + attribute + '=' + value.inspect(true); 1685 }); 1686 return result + '>'; 1687 }, 1688 1689 recursivelyCollect: function(element, property) { 1690 element = $(element); 1691 var elements = []; 1692 while (element = element[property]) 1693 if (element.nodeType == 1) 1694 elements.push(Element.extend(element)); 1695 return elements; 1696 }, 1697 1698 ancestors: function(element) { 1699 return $(element).recursivelyCollect('parentNode'); 1700 }, 1701 1702 descendants: function(element) { 1703 return $(element).select("*"); 1704 }, 1705 1706 firstDescendant: function(element) { 1707 element = $(element).firstChild; 1708 while (element && element.nodeType != 1) element = element.nextSibling; 1709 return $(element); 1710 }, 1711 1712 immediateDescendants: function(element) { 1713 if (!(element = $(element).firstChild)) return []; 1714 while (element && element.nodeType != 1) element = element.nextSibling; 1715 if (element) return [element].concat($(element).nextSiblings()); 1716 return []; 1717 }, 1718 1719 previousSiblings: function(element) { 1720 return $(element).recursivelyCollect('previousSibling'); 1721 }, 1722 1723 nextSiblings: function(element) { 1724 return $(element).recursivelyCollect('nextSibling'); 1725 }, 1726 1727 siblings: function(element) { 1728 element = $(element); 1729 return element.previousSiblings().reverse().concat(element.nextSiblings()); 1730 }, 1731 1732 match: function(element, selector) { 1733 if (Object.isString(selector)) 1734 selector = new Selector(selector); 1735 return selector.match($(element)); 1736 }, 1737 1738 up: function(element, expression, index) { 1739 element = $(element); 1740 if (arguments.length == 1) return $(element.parentNode); 1741 var ancestors = element.ancestors(); 1742 return Object.isNumber(expression) ? ancestors[expression] : 1743 Selector.findElement(ancestors, expression, index); 1744 }, 1745 1746 down: function(element, expression, index) { 1747 element = $(element); 1748 if (arguments.length == 1) return element.firstDescendant(); 1749 return Object.isNumber(expression) ? element.descendants()[expression] : 1750 Element.select(element, expression)[index || 0]; 1751 }, 1752 1753 previous: function(element, expression, index) { 1754 element = $(element); 1755 if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); 1756 var previousSiblings = element.previousSiblings(); 1757 return Object.isNumber(expression) ? previousSiblings[expression] : 1758 Selector.findElement(previousSiblings, expression, index); 1759 }, 1760 1761 next: function(element, expression, index) { 1762 element = $(element); 1763 if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); 1764 var nextSiblings = element.nextSiblings(); 1765 return Object.isNumber(expression) ? nextSiblings[expression] : 1766 Selector.findElement(nextSiblings, expression, index); 1767 }, 1768 1769 select: function() { 1770 var args = $A(arguments), element = $(args.shift()); 1771 return Selector.findChildElements(element, args); 1772 }, 1773 1774 adjacent: function() { 1775 var args = $A(arguments), element = $(args.shift()); 1776 return Selector.findChildElements(element.parentNode, args).without(element); 1777 }, 1778 1779 identify: function(element) { 1780 element = $(element); 1781 var id = element.readAttribute('id'), self = arguments.callee; 1782 if (id) return id; 1783 do { id = 'anonymous_element_' + self.counter++ } while ($(id)); 1784 element.writeAttribute('id', id); 1785 return id; 1786 }, 1787 1788 readAttribute: function(element, name) { 1789 element = $(element); 1790 if (Prototype.Browser.IE) { 1791 var t = Element._attributeTranslations.read; 1792 if (t.values[name]) return t.values[name](element, name); 1793 if (t.names[name]) name = t.names[name]; 1794 if (name.include(':')) { 1795 return (!element.attributes || !element.attributes[name]) ? null : 1796 element.attributes[name].value; 1797 } 1798 } 1799 return element.getAttribute(name); 1800 }, 1801 1802 writeAttribute: function(element, name, value) { 1803 element = $(element); 1804 var attributes = { }, t = Element._attributeTranslations.write; 1805 1806 if (typeof name == 'object') attributes = name; 1807 else attributes[name] = Object.isUndefined(value) ? true : value; 1808 1809 for (var attr in attributes) { 1810 name = t.names[attr] || attr; 1811 value = attributes[attr]; 1812 if (t.values[attr]) name = t.values[attr](element, value); 1813 if (value === false || value === null) 1814 element.removeAttribute(name); 1815 else if (value === true) 1816 element.setAttribute(name, name); 1817 else element.setAttribute(name, value); 1818 } 1819 return element; 1820 }, 1821 1822 getHeight: function(element) { 1823 return $(element).getDimensions().height; 1824 }, 1825 1826 getWidth: function(element) { 1827 return $(element).getDimensions().width; 1828 }, 1829 1830 classNames: function(element) { 1831 return new Element.ClassNames(element); 1832 }, 1833 1834 hasClassName: function(element, className) { 1835 if (!(element = $(element))) return; 1836 var elementClassName = element.className; 1837 return (elementClassName.length > 0 && (elementClassName == className || 1838 new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName))); 1839 }, 1840 1841 addClassName: function(element, className) { 1842 if (!(element = $(element))) return; 1843 if (!element.hasClassName(className)) 1844 element.className += (element.className ? ' ' : '') + className; 1845 return element; 1846 }, 1847 1848 removeClassName: function(element, className) { 1849 if (!(element = $(element))) return; 1850 element.className = element.className.replace( 1851 new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip(); 1852 return element; 1853 }, 1854 1855 toggleClassName: function(element, className) { 1856 if (!(element = $(element))) return; 1857 return element[element.hasClassName(className) ? 1858 'removeClassName' : 'addClassName'](className); 1859 }, 1860 1861 // removes whitespace-only text node children 1862 cleanWhitespace: function(element) { 1863 element = $(element); 1864 var node = element.firstChild; 1865 while (node) { 1866 var nextNode = node.nextSibling; 1867 if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) 1868 element.removeChild(node); 1869 node = nextNode; 1870 } 1871 return element; 1872 }, 1873 1874 empty: function(element) { 1875 return $(element).innerHTML.blank(); 1876 }, 1877 1878 descendantOf: function(element, ancestor) { 1879 element = $(element), ancestor = $(ancestor); 1880 1881 if (element.compareDocumentPosition) 1882 return (element.compareDocumentPosition(ancestor) & 8) === 8; 1883 1884 if (ancestor.contains) 1885 return ancestor.contains(element) && ancestor !== element; 1886 1887 while (element = element.parentNode) 1888 if (element == ancestor) return true; 1889 1890 return false;
1891 }, 1892 1893 scrollTo: function(element) { 1894 element = $(element); 1895 var pos = element.cumulativeOffset(); 1896 window.scrollTo(pos[0], pos[1]); 1897 return element; 1898 }, 1899 1900 getStyle: function(element, style) { 1901 element = $(element); 1902 style = style == 'float' ? 'cssFloat' : style.camelize(); 1903 var value = element.style[style]; 1904 if (!value || value == 'auto') { 1905 var css = document.defaultView.getComputedStyle(element, null); 1906 value = css ? css[style] : null; 1907 } 1908 if (style == 'opacity') return value ? parseFloat(value) : 1.0; 1909 return value == 'auto' ? null : value; 1910 }, 1911 1912 getOpacity: function(element) { 1913 return $(element).getStyle('opacity'); 1914 }, 1915 1916 setStyle: function(element, styles) { 1917 element = $(element); 1918 var elementStyle = element.style, match; 1919 if (Object.isString(styles)) { 1920 element.style.cssText += ';' + styles; 1921 return styles.include('opacity') ? 1922 element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element; 1923 } 1924 for (var property in styles) 1925 if (property == 'opacity') element.setOpacity(styles[property]); 1926 else 1927 elementStyle[(property == 'float' || property == 'cssFloat') ? 1928 (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') : 1929 property] = styles[property]; 1930 1931 return element; 1932 }, 1933 1934 setOpacity: function(element, value) { 1935 element = $(element); 1936 element.style.opacity = (value == 1 || value === '') ? '' : 1937 (value < 0.00001) ? 0 : value; 1938 return element; 1939 }, 1940 1941 getDimensions: function(element) { 1942 element = $(element); 1943 var display = element.getStyle('display'); 1944 if (display != 'none' && display != null) // Safari bug 1945 return {width: element.offsetWidth, height: element.offsetHeight}; 1946 1947 // All *Width and *Height properties give 0 on elements with display none, 1948 // so enable the element temporarily 1949 var els = element.style; 1950 var originalVisibility = els.visibility; 1951 var originalPosition = els.position; 1952 var originalDisplay = els.display; 1953 els.visibility = 'hidden'; 1954 els.position = 'absolute'; 1955 els.display = 'block'; 1956 var originalWidth = element.clientWidth; 1957 var originalHeight = element.clientHeight; 1958 els.display = originalDisplay; 1959 els.position = originalPosition; 1960 els.visibility = originalVisibility; 1961 return {width: originalWidth, height: originalHeight}; 1962 }, 1963 1964 makePositioned: function(element) { 1965 element = $(element); 1966 var pos = Element.getStyle(element, 'position'); 1967 if (pos == 'static' || !pos) { 1968 element._madePositioned = true; 1969 element.style.position = 'relative'; 1970 // Opera returns the offset relative to the positioning context, when an 1971 // element is position relative but top and left have not been defined 1972 if (Prototype.Browser.Opera) { 1973 element.style.top = 0; 1974 element.style.left = 0; 1975 } 1976 } 1977 return element; 1978 }, 1979 1980 undoPositioned: function(element) { 1981 element = $(element); 1982 if (element._madePositioned) { 1983 element._madePositioned = undefined; 1984 element.style.position = 1985 element.style.top = 1986 element.style.left = 1987 element.style.bottom = 1988 element.style.right = ''; 1989 } 1990 return element; 1991 }, 1992 1993 makeClipping: function(element) { 1994 element = $(element); 1995 if (element._overflow) return element; 1996 element._overflow = Element.getStyle(element, 'overflow') || 'auto'; 1997 if (element._overflow !== 'hidden') 1998 element.style.overflow = 'hidden'; 1999 return element; 2000 }, 2001 2002 undoClipping: function(element) { 2003 element = $(element); 2004 if (!element._overflow) return element; 2005 element.style.overflow = element._overflow == 'auto' ? '' : element._overflow; 2006 element._overflow = null; 2007 return element; 2008 }, 2009 2010 cumulativeOffset: function(element) { 2011 var valueT = 0, valueL = 0; 2012 do { 2013 valueT += element.offsetTop || 0; 2014 valueL += element.offsetLeft || 0; 2015 element = element.offsetParent; 2016 } while (element); 2017 return Element._returnOffset(valueL, valueT); 2018 }, 2019 2020 positionedOffset: function(element) { 2021 var valueT = 0, valueL = 0; 2022 do { 2023 valueT += element.offsetTop || 0; 2024 valueL += element.offsetLeft || 0; 2025 element = element.offsetParent; 2026 if (element) { 2027 if (element.tagName.toUpperCase() == 'BODY') break; 2028 var p = Element.getStyle(element, 'position'); 2029 if (p !== 'static') break; 2030 } 2031 } while (element); 2032 return Element._returnOffset(valueL, valueT); 2033 }, 2034 2035 absolutize: function(element) { 2036 element = $(element); 2037 if (element.getStyle('position') == 'absolute') return element; 2038 // Position.prepare(); // To be done manually by Scripty when it needs it. 2039 2040 var offsets = element.positionedOffset(); 2041 var top = offsets[1]; 2042 var left = offsets[0]; 2043 var width = element.clientWidth; 2044 var height = element.clientHeight; 2045 2046 element._originalLeft = left - parseFloat(element.style.left || 0); 2047 element._originalTop = top - parseFloat(element.style.top || 0); 2048 element._originalWidth = element.style.width; 2049 element._originalHeight = element.style.height; 2050 2051 element.style.position = 'absolute'; 2052 element.style.top = top + 'px'; 2053 element.style.left = left + 'px'; 2054 element.style.width = width + 'px'; 2055 element.style.height = height + 'px'; 2056 return element; 2057 }, 2058 2059 relativize: function(element) { 2060 element = $(element); 2061 if (element.getStyle('position') == 'relative') return element; 2062 // Position.prepare(); // To be done manually by Scripty when it needs it. 2063 2064 element.style.position = 'relative'; 2065 var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); 2066 var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); 2067 2068 element.style.top = top + 'px'; 2069 element.style.left = left + 'px'; 2070 element.style.height = element._originalHeight; 2071 element.style.width = element._originalWidth; 2072 return element; 2073 }, 2074 2075 cumulativeScrollOffset: function(element) { 2076 var valueT = 0, valueL = 0; 2077 do { 2078 valueT += element.scrollTop || 0; 2079 valueL += element.scrollLeft || 0; 2080 element = element.parentNode; 2081 } while (element); 2082 return Element._returnOffset(valueL, valueT); 2083 }, 2084 2085 getOffsetParent: function(element) { 2086 if (element.offsetParent) return $(element.offsetParent); 2087 if (element == document.body) return $(element); 2088 if(element.tagName.toUpperCase()=='HTML') //for IE6,7 2089 return $(document.body); // 2090 2091 while ((element = element.parentNode) && element != document.body) 2092 if (Element.getStyle(element, 'position') != 'static') 2093 return $(element); 2094 2095 return $(document.body); 2096 }, 2097 2098 viewportOffset: function(forElement) { 2099 var valueT = 0, valueL = 0; 2100 2101 var element = forElement; 2102 do { 2103 valueT += element.offsetTop || 0; 2104 valueL += element.offsetLeft || 0; 2105 2106 // Safari fix 2107 if (element.offsetParent == document.body && 2108 Element.getStyle(element, 'position') == 'absolute') break; 2109 2110 } while (element = element.offsetParent); 2111 2112 element = forElement; 2113 do { 2114 if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) { 2115 valueT -= element.scrollTop || 0; 2116 valueL -= element.scrollLeft || 0; 2117 } 2118 } while (element = element.parentNode); 2119 2120 return Element._returnOffset(valueL, valueT); 2121 }, 2122 2123 clonePosition: function(element, source) { 2124 var options = Object.extend({ 2125 setLeft: true, 2126 setTop: true, 2127 setWidth: true, 2128 setHeight: true, 2129 offsetTop: 0, 2130 offsetLeft: 0 2131 }, arguments[2] || { }); 2132 2133 // find page position of source 2134 source = $(source); 2135 var p = source.viewportOffset(); 2136 2137 // find coordinate system to use 2138 element = $(element); 2139 var delta = [0, 0]; 2140 var parent = null; 2141 // delta [0,0] will do fine with position: fixed elements, 2142 // position:absolute needs offsetParent deltas 2143 if (Element.getStyle(element, 'position') == 'absolute') { 2144 parent = element.getOffsetParent(); 2145 delta = parent.viewportOffset(); 2146 } 2147 2148 // correct by body offsets (fixes Safari) 2149 if (parent == document.body) { 2150 delta[0] -= document.body.offsetLeft; 2151 delta[1] -= document.body.offsetTop; 2152 } 2153 2154 // set position 2155 if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; 2156 if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; 2157 if (options.setWidth) element.style.width = source.offsetWidth + 'px'; 2158 if (options.setHeight) element.style.height = source.offsetHeight + 'px'; 2159 return element; 2160 } 2161}; 2162 2163Element.Methods.identify.counter = 1; 2164 2165Object.extend(Element.Methods, { 2166 getElementsBySelector: Element.Methods.select, 2167 childElements: Element.Methods.immediateDescendants 2168}); 2169 2170Element._attributeTranslations = { 2171 write: { 2172 names: { 2173 className: 'class', 2174 htmlFor: 'for' 2175 }, 2176 values: { } 2177 } 2178}; 2179 2180if (Prototype.Browser.Opera) { 2181 Element.Methods.getStyle = Element.Methods.getStyle.wrap( 2182 function(proceed, element, style) { 2183 switch (style) { 2184 case 'left': case 'top': case 'right': case 'bottom': 2185 if (proceed(element, 'position') === 'static') return null; 2186 case 'height': case 'width': 2187 // returns '0px' for hidden elements; we want it to return null 2188 if (!Element.visible(element)) return null; 2189 2190 // returns the border-box dimensions rather than the content-box 2191 // dimensions, so we subtract padding and borders from the value 2192 var dim = parseInt(proceed(element, style), 10); 2193 2194 if (dim !== element['offset' + style.capitalize()]) 2195 return dim + 'px'; 2196 2197 var properties; 2198 if (style === 'height') { 2199 properties = ['border-top-width', 'padding-top', 2200 'padding-bottom', 'border-bottom-width']; 2201 } 2202 else { 2203 properties = ['border-left-width', 'padding-left', 2204 'padding-right', 'border-right-width']; 2205 } 2206 return properties.inject(dim, function(memo, property) { 2207 var val = proceed(element, property); 2208 return val === null ? memo : memo - parseInt(val, 10); 2209 }) + 'px'; 2210 default: return proceed(element, style); 2211 } 2212 } 2213 ); 2214 2215 Element.Methods.readAttribute = Element.Methods.readAttribute.wrap( 2216 function(proceed, element, attribute) { 2217 if (attribute === 'title') return element.title; 2218 return proceed(element, attribute); 2219 } 2220 ); 2221} 2222 2223else if (Prototype.Browser.IE) { 2224 // IE doesn't report offsets correctly for static elements, so we change them 2225 // to "relative" to get the values, then change them back. 2226 Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( 2227 function(proceed, element) { 2228 element = $(element); 2229 // IE throws an error if element is not in document
2230 try { element.offsetParent } 2231 catch(e) { return $(document.body) } 2232 var position = element.getStyle('position'); 2233 if (position !== 'static') return proceed(element); 2234 element.setStyle({ position: 'relative' }); 2235 var value = proceed(element); 2236 element.setStyle({ position: position }); 2237 return value; 2238 } 2239 ); 2240 2241 $w('positionedOffset viewportOffset').each(function(method) { 2242 Element.Methods[method] = Element.Methods[method].wrap( 2243 function(proceed, element) { 2244 element = $(element); 2245 try { element.offsetParent } 2246 catch(e) { return Element._returnOffset(0,0) } 2247 var position = element.getStyle('position'); 2248 if (position !== 'static') return proceed(element); 2249 // Trigger hasLayout on the offset parent so that IE6 reports 2250 // accurate offsetTop and offsetLeft values for position: fixed. 2251 var offsetParent = element.getOffsetParent(); 2252 if (offsetParent && offsetParent.getStyle('position') === 'fixed') 2253 offsetParent.setStyle({ zoom: 1 }); 2254 element.setStyle({ position: 'relative' }); 2255 var value = proceed(element); 2256 element.setStyle({ position: position }); 2257 return value; 2258 } 2259 ); 2260 }); 2261 2262 Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap( 2263 function(proceed, element) { 2264 try { element.offsetParent } 2265 catch(e) { return Element._returnOffset(0,0) } 2266 return proceed(element); 2267 } 2268 ); 2269 2270 Element.Methods.getStyle = function(element, style) { 2271 element = $(element); 2272 style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize(); 2273 var value = element.style[style]; 2274 if (!value && element.currentStyle) value = element.currentStyle[style]; 2275 2276 if (style == 'opacity') { 2277 if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/)) 2278 if (value[1]) return parseFloat(value[1]) / 100; 2279 return 1.0; 2280 } 2281 2282 if (value == 'auto') { 2283 if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none')) 2284 return element['offset' + style.capitalize()] + 'px'; 2285 return null; 2286 } 2287 return value; 2288 }; 2289 2290 Element.Methods.setOpacity = function(element, value) { 2291 function stripAlpha(filter){ 2292 return filter.replace(/alpha\([^\)]*\)/gi,''); 2293 } 2294 element = $(element); 2295 var currentStyle = element.currentStyle; 2296 if ((currentStyle && !currentStyle.hasLayout) || 2297 (!currentStyle && element.style.zoom == 'normal')) 2298 element.style.zoom = 1; 2299 2300 var filter = element.getStyle('filter'), style = element.style; 2301 if (value == 1 || value === '') { 2302 (filter = stripAlpha(filter)) ? 2303 style.filter = filter : style.removeAttribute('filter'); 2304 return element; 2305 } else if (value < 0.00001) value = 0; 2306 style.filter = stripAlpha(filter) + 2307 'alpha(opacity=' + (value * 100) + ')'; 2308 return element; 2309 }; 2310 2311 Element._attributeTranslations = { 2312 read: { 2313 names: { 2314 'class': 'className', 2315 'for': 'htmlFor' 2316 }, 2317 values: { 2318 _getAttr: function(element, attribute) { 2319 return element.getAttribute(attribute, 2); 2320 }, 2321 _getAttrNode: function(element, attribute) { 2322 var node = element.getAttributeNode(attribute); 2323 return node ? node.value : ""; 2324 }, 2325 _getEv: function(element, attribute) { 2326 attribute = element.getAttribute(attribute); 2327 return attribute ? attribute.toString().slice(23, -2) : null; 2328 }, 2329 _flag: function(element, attribute) { 2330 return $(element).hasAttribute(attribute) ? attribute : null; 2331 }, 2332 style: function(element) { 2333 return element.style.cssText.toLowerCase(); 2334 }, 2335 title: function(element) { 2336 return element.title; 2337 } 2338 } 2339 } 2340 }; 2341 2342 Element._attributeTranslations.write = { 2343 names: Object.extend({ 2344 cellpadding: 'cellPadding', 2345 cellspacing: 'cellSpacing' 2346 }, Element._attributeTranslations.read.names), 2347 values: { 2348 checked: function(element, value) { 2349 element.checked = !!value; 2350 }, 2351 2352 style: function(element, value) { 2353 element.style.cssText = value ? value : ''; 2354 } 2355 } 2356 }; 2357 2358 Element._attributeTranslations.has = {}; 2359 2360 $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' + 2361 'encType maxLength readOnly longDesc frameBorder').each(function(attr) { 2362 Element._attributeTranslations.write.names[attr.toLowerCase()] = attr; 2363 Element._attributeTranslations.has[attr.toLowerCase()] = attr; 2364 }); 2365 2366 (function(v) { 2367 Object.extend(v, { 2368 href: v._getAttr, 2369 src: v._getAttr, 2370 type: v._getAttr, 2371 action: v._getAttrNode, 2372 disabled: v._flag, 2373 checked: v._flag, 2374 readonly: v._flag, 2375 multiple: v._flag, 2376 onload: v._getEv, 2377 onunload: v._getEv, 2378 onclick: v._getEv, 2379 ondblclick: v._getEv, 2380 onmousedown: v._getEv, 2381 onmouseup: v._getEv, 2382 onmouseover: v._getEv, 2383 onmousemove: v._getEv, 2384 onmouseout: v._getEv, 2385 onfocus: v._getEv, 2386 onblur: v._getEv, 2387 onkeypress: v._getEv, 2388 onkeydown: v._getEv, 2389 onkeyup: v._getEv, 2390 onsubmit: v._getEv,
2391 onreset: v._getEv, 2392 onselect: v._getEv, 2393 onchange: v._getEv 2394 }); 2395 })(Element._attributeTranslations.read.values); 2396} 2397 2398else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) { 2399 Element.Methods.setOpacity = function(element, value) { 2400 element = $(element); 2401 element.style.opacity = (value == 1) ? 0.999999 : 2402 (value === '') ? '' : (value < 0.00001) ? 0 : value; 2403 return element; 2404 }; 2405} 2406 2407else if (Prototype.Browser.WebKit) { 2408 Element.Methods.setOpacity = function(element, value) { 2409 element = $(element); 2410 element.style.opacity = (value == 1 || value === '') ? '' : 2411 (value < 0.00001) ? 0 : value; 2412 2413 if (value == 1) 2414 if(element.tagName.toUpperCase() == 'IMG' && element.width) { 2415 element.width++; element.width--; 2416 } else try { 2417 var n = document.createTextNode(' '); 2418 element.appendChild(n); 2419 element.removeChild(n); 2420 } catch (e) { } 2421 2422 return element; 2423 }; 2424 2425 // Safari returns margins on body which is incorrect if the child is absolutely 2426 // positioned. For performance reasons, redefine Element#cumulativeOffset for 2427 // KHTML/WebKit only. 2428 Element.Methods.cumulativeOffset = function(element) { 2429 var valueT = 0, valueL = 0; 2430 do { 2431 valueT += element.offsetTop || 0; 2432 valueL += element.offsetLeft || 0; 2433 if (element.offsetParent == document.body) 2434 if (Element.getStyle(element, 'position') == 'absolute') break; 2435 2436 element = element.offsetParent; 2437 } while (element); 2438 2439 return Element._returnOffset(valueL, valueT); 2440 }; 2441} 2442 2443if (Prototype.Browser.IE || Prototype.Browser.Opera) { 2444 // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements 2445 Element.Methods.update = function(element, content) { 2446 element = $(element); 2447 2448 if (content && content.toElement) content = content.toElement(); 2449 if (Object.isElement(content)) return element.update().insert(content); 2450 2451 content = Object.toHTML(content); 2452 var tagName = element.tagName.toUpperCase(); 2453 2454 if (tagName in Element._insertionTranslations.tags) { 2455 $A(element.childNodes).each(function(node) { element.removeChild(node) }); 2456 Element._getContentFromAnonymousElement(tagName, content.stripScripts()) 2457 .each(function(node) { element.appendChild(node) }); 2458 } 2459 else element.innerHTML = content.stripScripts(); 2460 2461 content.evalScripts.bind(content).defer(); 2462 return element; 2463 }; 2464} 2465 2466if ('outerHTML' in document.createElement('div')) { 2467 Element.Methods.replace = function(element, content) { 2468 element = $(element); 2469 2470 if (content && content.toElement) content = content.toElement(); 2471 if (Object.isElement(content)) { 2472 element.parentNode.replaceChild(content, element); 2473 return element; 2474 } 2475 2476 content = Object.toHTML(content); 2477 var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); 2478 2479 if (Element._insertionTranslations.tags[tagName]) { 2480 var nextSibling = element.next(); 2481 var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); 2482 parent.removeChild(element); 2483 if (nextSibling) 2484 fragments.each(function(node) { parent.insertBefore(node, nextSibling) }); 2485 else 2486 fragments.each(function(node) { parent.appendChild(node) }); 2487 } 2488 else element.outerHTML = content.stripScripts(); 2489 2490 content.evalScripts.bind(content).defer(); 2491 return element; 2492 }; 2493} 2494 2495Element._returnOffset = function(l, t) { 2496 var result = [l, t]; 2497 result.left = l; 2498 result.top = t; 2499 return result; 2500}; 2501 2502Element._getContentFromAnonymousElement = function(tagName, html) { 2503 var div = new Element('div'), t = Element._insertionTranslations.tags[tagName]; 2504 if (t) { 2505 div.innerHTML = t[0] + html + t[1]; 2506 t[2].times(function() { div = div.firstChild }); 2507 } else div.innerHTML = html; 2508 return $A(div.childNodes); 2509}; 2510 2511Element._insertionTranslations = { 2512 before: function(element, node) { 2513 element.parentNode.insertBefore(node, element); 2514 }, 2515 top: function(element, node) { 2516 element.insertBefore(node, element.firstChild); 2517 }, 2518 bottom: function(element, node) { 2519 element.appendChild(node); 2520 }, 2521 after: function(element, node) { 2522 element.parentNode.insertBefore(node, element.nextSibling); 2523 }, 2524 tags: { 2525 TABLE: ['<table>', '</table>', 1], 2526 TBODY: ['<table><tbody>', '</tbody></table>', 2], 2527 TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3], 2528 TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4], 2529 SELECT: ['<select>', '</select>', 1] 2530 } 2531}; 2532 2533(function() { 2534 Object.extend(this.tags, { 2535 THEAD: this.tags.TBODY, 2536 TFOOT: this.tags.TBODY, 2537 TH: this.tags.TD 2538 }); 2539}).call(Element._insertionTranslations); 2540 2541Element.Methods.Simulated = { 2542 hasAttribute: function(element, attribute) { 2543 attribute = Element._attributeTranslations.has[attribute] || attribute; 2544 var node = $(element).getAttributeNode(attribute); 2545 return !!(node && node.specified); 2546 } 2547}; 2548 2549Element.Methods.ByTag = { }; 2550 2551Object.extend(Element, Element.Methods); 2552 2553if (!Prototype.BrowserFeatures.ElementExtensions && 2554 document.createElement('div')['__proto__']) { 2555 window.HTMLElement = { }; 2556 window.HTMLElement.prototype = document.createElement('div')['__proto__']; 2557 Prototype.BrowserFeatures.ElementExtensions = true; 2558} 2559 2560Element.extend = (function() { 2561 if (Prototype.BrowserFeatures.SpecificElementExtensions) 2562 return Prototype.K; 2563 2564 var Methods = { }, ByTag = Element.Methods.ByTag; 2565 2566 var extend = Object.extend(function(element) { 2567 if (!element || element._extendedByPrototype || 2568 element.nodeType != 1 || element == window) return element; 2569 2570 var methods = Object.clone(Methods), 2571 tagName = element.tagName.toUpperCase(), property, value; 2572 2573 // extend methods for specific tags 2574 if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); 2575 2576 for (property in methods) { 2577 value = methods[property]; 2578 if (Object.isFunction(value) && !(property in element)) 2579 element[property] = value.methodize(); 2580 } 2581 2582 element._extendedByPrototype = Prototype.emptyFunction; 2583 return element; 2584 2585 }, { 2586 refresh: function() { 2587 // extend methods for all tags (Safari doesn't need this) 2588 if (!Prototype.BrowserFeatures.ElementExtensions) { 2589 Object.extend(Methods, Element.Methods); 2590 Object.extend(Methods, Element.Methods.Simulated); 2591 } 2592 } 2593 }); 2594 2595 extend.refresh(); 2596 return extend; 2597})(); 2598 2599Element.hasAttribute = function(element, attribute) { 2600 if (element.hasAttribute) return element.hasAttribute(attribute); 2601 return Element.Methods.Simulated.hasAttribute(element, attribute); 2602}; 2603 2604Element.addMethods = function(methods) { 2605 var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag; 2606 2607 if (!methods) { 2608 Object.extend(Form, Form.Methods);
2609 Object.extend(Form.Element, Form.Element.Methods); 2610 Object.extend(Element.Methods.ByTag, { 2611 "FORM": Object.clone(Form.Methods), 2612 "INPUT": Object.clone(Form.Element.Methods), 2613 "SELECT": Object.clone(Form.Element.Methods), 2614 "TEXTAREA": Object.clone(Form.Element.Methods) 2615 }); 2616 } 2617 2618 if (arguments.length == 2) { 2619 var tagName = methods; 2620 methods = arguments[1]; 2621 } 2622 2623 if (!tagName) Object.extend(Element.Methods, methods || { }); 2624 else { 2625 if (Object.isArray(tagName)) tagName.each(extend); 2626 else extend(tagName); 2627 } 2628 2629 function extend(tagName) { 2630 tagName = tagName.toUpperCase(); 2631 if (!Element.Methods.ByTag[tagName]) 2632 Element.Methods.ByTag[tagName] = { }; 2633 Object.extend(Element.Methods.ByTag[tagName], methods); 2634 } 2635 2636 function copy(methods, destination, onlyIfAbsent) { 2637 onlyIfAbsent = onlyIfAbsent || false; 2638 for (var property in methods) { 2639 var value = methods[property]; 2640 if (!Object.isFunction(value)) continue; 2641 if (!onlyIfAbsent || !(property in destination)) 2642 destination[property] = value.methodize(); 2643 } 2644 } 2645 2646 function findDOMClass(tagName) { 2647 var klass; 2648 var trans = { 2649 "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph", 2650 "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList", 2651 "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading", 2652 "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote", 2653 "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION": 2654 "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD": 2655 "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR": 2656 "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET": 2657 "FrameSet", "IFRAME": "IFrame" 2658 }; 2659 if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element'; 2660 if (window[klass]) return window[klass]; 2661 klass = 'HTML' + tagName + 'Element'; 2662 if (window[klass]) return window[klass]; 2663 klass = 'HTML' + tagName.capitalize() + 'Element'; 2664 if (window[klass]) return window[klass]; 2665 2666 window[klass] = { }; 2667 window[klass].prototype = document.createElement(tagName)['__proto__']; 2668 return window[klass]; 2669 } 2670 2671 if (F.ElementExtensions) { 2672 copy(Element.Methods, HTMLElement.prototype); 2673 copy(Element.Methods.Simulated, HTMLElement.prototype, true); 2674 } 2675 2676 if (F.SpecificElementExtensions) { 2677 for (var tag in Element.Methods.ByTag) { 2678 var klass = findDOMClass(tag); 2679 if (Object.isUndefined(klass)) continue; 2680 copy(T[tag], klass.prototype); 2681 } 2682 } 2683 2684 Object.extend(Element, Element.Methods); 2685 delete Element.ByTag; 2686 2687 if (Element.extend.refresh) Element.extend.refresh(); 2688 Element.cache = { }; 2689}; 2690 2691document.viewport = { 2692 getDimensions: function() { 2693 var dimensions = { }, B = Prototype.Browser; 2694 $w('width height').each(function(d) { 2695 var D = d.capitalize(); 2696 if (B.WebKit && !document.evaluate) { 2697 // Safari <3.0 needs self.innerWidth/Height 2698 dimensions[d] = self['inner' + D]; 2699 } else if (B.Opera && parseFloat(window.opera.version()) < 9.5) { 2700 // Opera <9.5 needs document.body.clientWidth/Height 2701 dimensions[d] = document.body['client' + D] 2702 } else { 2703 dimensions[d] = document.documentElement['client' + D]; 2704 } 2705 }); 2706 return dimensions; 2707 }, 2708 2709 getWidth: function() { 2710 return this.getDimensions().width; 2711 }, 2712 2713 getHeight: function() { 2714 return this.getDimensions().height; 2715 }, 2716 2717 getScrollOffsets: function() { 2718 return Element._returnOffset( 2719 window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft, 2720 window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop); 2721 } 2722}; 2723/* Portions of the Selector class are derived from Jack Slocum's DomQuery, 2724 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style 2725 * license. Please see http://www.yui-ext.com/ for more information. */ 2726 2727var Selector = Class.create({ 2728 initialize: function(expression) { 2729 this.expression = expression.strip(); 2730 2731 if (this.shouldUseSelectorsAPI()) { 2732 this.mode = 'selectorsAPI'; 2733 } else if (this.shouldUseXPath()) { 2734 this.mode = 'xpath'; 2735 this.compileXPathMatcher(); 2736 } else { 2737 this.mode = "normal"; 2738 this.compileMatcher(); 2739 } 2740 2741 }, 2742 2743 shouldUseXPath: function() { 2744 if (!Prototype.BrowserFeatures.XPath) return false;
2745 2746 var e = this.expression; 2747 2748 // Safari 3 chokes on :*-of-type and :empty 2749 if (Prototype.Browser.WebKit && 2750 (e.include("-of-type") || e.include(":empty"))) 2751 return false; 2752 2753 // XPath can't do namespaced attributes, nor can it read 2754 // the "checked" property from DOM nodes 2755 if ((/(\[[\w-]*?:|:checked)/).test(e)) 2756 return false; 2757 2758 return true; 2759 }, 2760 2761 shouldUseSelectorsAPI: function() { 2762 if (!Prototype.BrowserFeatures.SelectorsAPI) return false; 2763 2764 if (!Selector._div) Selector._div = new Element('div'); 2765 2766 // Make sure the browser treats the selector as valid. Test on an 2767 // isolated element to minimize cost of this check. 2768 try { 2769 Selector._div.querySelector(this.expression); 2770 } catch(e) { 2771 return false; 2772 } 2773 2774 return true; 2775 }, 2776 2777 compileMatcher: function() { 2778 var e = this.expression, ps = Selector.patterns, h = Selector.handlers, 2779 c = Selector.criteria, le, p, m; 2780 2781 if (Selector._cache[e]) { 2782 this.matcher = Selector._cache[e]; 2783 return; 2784 } 2785 2786 this.matcher = ["this.matcher = function(root) {", 2787 "var r = root, h = Selector.handlers, c = false, n;"]; 2788 2789 while (e && le != e && (/\S/).test(e)) { 2790 le = e; 2791 for (var i in ps) { 2792 p = ps[i]; 2793 if (m = e.match(p)) { 2794 this.matcher.push(Object.isFunction(c[i]) ? c[i](m) : 2795 new Template(c[i]).evaluate(m)); 2796 e = e.replace(m[0], ''); 2797 break; 2798 } 2799 } 2800 } 2801 2802 this.matcher.push("return h.unique(n);\n}"); 2803 eval(this.matcher.join('\n')); 2804 Selector._cache[this.expression] = this.matcher; 2805 }, 2806 2807 compileXPathMatcher: function() { 2808 var e = this.expression, ps = Selector.patterns, 2809 x = Selector.xpath, le, m; 2810 2811 if (Selector._cache[e]) { 2812 this.xpath = Selector._cache[e]; return; 2813 } 2814 2815 this.matcher = ['.//*']; 2816 while (e && le != e && (/\S/).test(e)) { 2817 le = e; 2818 for (var i in ps) { 2819 if (m = e.match(ps[i])) { 2820 this.matcher.push(Object.isFunction(x[i]) ? x[i](m) : 2821 new Template(x[i]).evaluate(m)); 2822 e = e.replace(m[0], ''); 2823 break; 2824 } 2825 } 2826 } 2827 2828 this.xpath = this.matcher.join(''); 2829 Selector._cache[this.expression] = this.xpath; 2830 }, 2831 2832 findElements: function(root) { 2833 root = root || document; 2834 var e = this.expression, results; 2835 2836 switch (this.mode) { 2837 case 'selectorsAPI': 2838 // querySelectorAll queries document-wide, then filters to descendants 2839 // of the context element. That's not what we want. 2840 // Add an explicit context to the selector if necessary. 2841 if (root !== document) { 2842 var oldId = root.id, id = $(root).identify(); 2843 e = "#" + id + " " + e; 2844 } 2845 2846 results = $A(root.querySelectorAll(e)).map(Element.extend); 2847 root.id = oldId; 2848 2849 return results; 2850 case 'xpath': 2851 return document._getElementsByXPath(this.xpath, root); 2852 default: 2853 return this.matcher(root); 2854 } 2855 }, 2856 2857 match: function(element) { 2858 this.tokens = []; 2859 2860 var e = this.expression, ps = Selector.patterns, as = Selector.assertions; 2861 var le, p, m; 2862 2863 while (e && le !== e && (/\S/).test(e)) { 2864 le = e; 2865 for (var i in ps) { 2866 p = ps[i]; 2867 if (m = e.match(p)) { 2868 // use the Selector.assertions methods unless the selector 2869 // is too complex. 2870 if (as[i]) { 2871 this.tokens.push([i, Object.clone(m)]); 2872 e = e.replace(m[0], ''); 2873 } else { 2874 // reluctantly do a document-wide search 2875 // and look for a match in the array 2876 return this.findElements(document).include(element); 2877 } 2878 } 2879 } 2880 } 2881 2882 var match = true, name, matches; 2883 for (var i = 0, token; token = this.tokens[i]; i++) { 2884 name = token[0], matches = token[1]; 2885 if (!Selector.assertions[name](element, matches)) { 2886 match = false; break; 2887 } 2888 } 2889 2890 return match; 2891 }, 2892 2893 toString: function() { 2894 return this.expression; 2895 }, 2896 2897 inspect: function() { 2898 return "#<Selector:" + this.expression.inspect() + ">"; 2899 } 2900}); 2901 2902Object.extend(Selector, { 2903 _cache: { }, 2904 2905 xpath: { 2906 descendant: "//*", 2907 child: "/*", 2908 adjacent: "/following-sibling::*[1]", 2909 laterSibling: '/following-sibling::*', 2910 tagName: function(m) { 2911 if (m[1] == '*') return ''; 2912 return "[local-name()='" + m[1].toLowerCase() + 2913 "' or local-name()='" + m[1].toUpperCase() + "']"; 2914 }, 2915 className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
2916 id: "[@id='#{1}']", 2917 attrPresence: function(m) { 2918 m[1] = m[1].toLowerCase(); 2919 return new Template("[@#{1}]").evaluate(m); 2920 }, 2921 attr: function(m) { 2922 m[1] = m[1].toLowerCase(); 2923 m[3] = m[5] || m[6]; 2924 return new Template(Selector.xpath.operators[m[2]]).evaluate(m); 2925 }, 2926 pseudo: function(m) { 2927 var h = Selector.xpath.pseudos[m[1]]; 2928 if (!h) return ''; 2929 if (Object.isFunction(h)) return h(m); 2930 return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m); 2931 }, 2932 operators: { 2933 '=': "[@#{1}='#{3}']", 2934 '!=': "[@#{1}!='#{3}']", 2935 '^=': "[starts-with(@#{1}, '#{3}')]", 2936 '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']", 2937 '*=': "[contains(@#{1}, '#{3}')]", 2938 '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]", 2939 '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]" 2940 }, 2941 pseudos: { 2942 'first-child': '[not(preceding-sibling::*)]', 2943 'last-child': '[not(following-sibling::*)]', 2944 'only-child': '[not(preceding-sibling::* or following-sibling::*)]', 2945 'empty': "[count(*) = 0 and (count(text()) = 0)]", 2946 'checked': "[@checked]", 2947 'disabled': "[(@disabled) and (@type!='hidden')]", 2948 'enabled': "[not(@disabled) and (@type!='hidden')]", 2949 'not': function(m) { 2950 var e = m[6], p = Selector.patterns, 2951 x = Selector.xpath, le, v; 2952 2953 var exclusion = []; 2954 while (e && le != e && (/\S/).test(e)) { 2955 le = e; 2956 for (var i in p) { 2957 if (m = e.match(p[i])) { 2958 v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m); 2959 exclusion.push("(" + v.substring(1, v.length - 1) + ")"); 2960 e = e.replace(m[0], ''); 2961 break; 2962 } 2963 } 2964 } 2965 return "[not(" + exclusion.join(" and ") + ")]"; 2966 }, 2967 'nth-child': function(m) { 2968 return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m); 2969 }, 2970 'nth-last-child': function(m) { 2971 return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m); 2972 }, 2973 'nth-of-type': function(m) { 2974 return Selector.xpath.pseudos.nth("position() ", m); 2975 }, 2976 'nth-last-of-type': function(m) { 2977 return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m); 2978 }, 2979 'first-of-type': function(m) { 2980 m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m); 2981 }, 2982 'last-of-type': function(m) { 2983 m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m); 2984 }, 2985 'only-of-type': function(m) { 2986 var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m); 2987 }, 2988 nth: function(fragment, m) { 2989 var mm, formula = m[6], predicate; 2990 if (formula == 'even') formula = '2n+0'; 2991 if (formula == 'odd') formula = '2n+1'; 2992 if (mm = formula.match(/^(\d+)$/)) // digit only 2993 return '[' + fragment + "= " + mm[1] + ']'; 2994 if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b 2995 if (mm[1] == "-") mm[1] = -1; 2996 var a = mm[1] ? Number(mm[1]) : 1; 2997 var b = mm[2] ? Number(mm[2]) : 0; 2998 predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " + 2999 "((#{fragment} - #{b}) div #{a} >= 0)]"; 3000 return new Template(predicate).evaluate({ 3001 fragment: fragment, a: a, b: b }); 3002 } 3003 } 3004 } 3005 }, 3006 3007 criteria: { 3008 tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;', 3009 className: 'n = h.className(n, r, "#{1}", c); c = false;', 3010 id: 'n = h.id(n, r, "#{1}", c); c = false;', 3011 attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;', 3012 attr: function(m) { 3013 m[3] = (m[5] || m[6]); 3014 return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m); 3015 }, 3016 pseudo: function(m) { 3017 if (m[6]) m[6] = m[6].replace(/"/g, '\\"'); 3018 return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m); 3019 }, 3020 descendant: 'c = "descendant";', 3021 child: 'c = "child";', 3022 adjacent: 'c = "adjacent";', 3023 laterSibling: 'c = "laterSibling";' 3024 }, 3025 3026 patterns: {
3027 // combinators must be listed first 3028 // (and descendant needs to be last combinator) 3029 laterSibling: /^\s*~\s*/, 3030 child: /^\s*>\s*/, 3031 adjacent: /^\s*\+\s*/, 3032 descendant: /^\s/, 3033 3034 // selectors follow 3035 tagName: /^\s*(\*|[\w\-]+)(\b|$)?/, 3036 id: /^#([\w\-\*]+)(\b|$)/, 3037 className: /^\.([\w\-\*]+)(\b|$)/, 3038 pseudo: 3039/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/, 3040 attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/, 3041 attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ 3042 }, 3043 3044 // for Selector.match and Element#match 3045 assertions: { 3046 tagName: function(element, matches) { 3047 return matches[1].toUpperCase() == element.tagName.toUpperCase(); 3048 }, 3049 3050 className: function(element, matches) { 3051 return Element.hasClassName(element, matches[1]); 3052 }, 3053 3054 id: function(element, matches) { 3055 return element.id === matches[1]; 3056 }, 3057 3058 attrPresence: function(element, matches) { 3059 return Element.hasAttribute(element, matches[1]); 3060 }, 3061 3062 attr: function(element, matches) { 3063 var nodeValue = Element.readAttribute(element, matches[1]); 3064 return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]); 3065 } 3066 }, 3067 3068 handlers: { 3069 // UTILITY FUNCTIONS 3070 // joins two collections 3071 concat: function(a, b) { 3072 for (var i = 0, node; node = b[i]; i++) 3073 a.push(node); 3074 return a; 3075 }, 3076 3077 // marks an array of nodes for counting 3078 mark: function(nodes) { 3079 var _true = Prototype.emptyFunction; 3080 for (var i = 0, node; node = nodes[i]; i++) 3081 node._countedByPrototype = _true; 3082 return nodes; 3083 }, 3084 3085 unmark: function(nodes) { 3086 for (var i = 0, node; node = nodes[i]; i++) 3087 node._countedByPrototype = undefined; 3088 return nodes; 3089 }, 3090 3091 // mark each child node with its position (for nth calls) 3092 // "ofType" flag indicates whether we're indexing for nth-of-type 3093 // rather than nth-child 3094 index: function(parentNode, reverse, ofType) { 3095 parentNode._countedByPrototype = Prototype.emptyFunction; 3096 if (reverse) { 3097 for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { 3098 var node = nodes[i]; 3099 if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; 3100 } 3101 } else { 3102 for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) 3103 if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; 3104 } 3105 }, 3106 3107 // filters out duplicates and extends all nodes 3108 unique: function(nodes) { 3109 if (nodes.length == 0) return nodes; 3110 var results = [], n; 3111 for (var i = 0, l = nodes.length; i < l; i++) 3112 if (!(n = nodes[i])._countedByPrototype) { 3113 n._countedByPrototype = Prototype.emptyFunction; 3114 results.push(Element.extend(n)); 3115 } 3116 return Selector.handlers.unmark(results); 3117 }, 3118 3119 // COMBINATOR FUNCTIONS 3120 descendant: function(nodes) { 3121 var h = Selector.handlers; 3122 for (var i = 0, results = [], node; node = nodes[i]; i++) 3123 h.concat(results, node.getElementsByTagName('*')); 3124 return results; 3125 }, 3126 3127 child: function(nodes) { 3128 var h = Selector.handlers; 3129 for (var i = 0, results = [], node; node = nodes[i]; i++) { 3130 for (var j = 0, child; child = node.childNodes[j]; j++) 3131 if (child.nodeType == 1 && child.tagName != '!') results.push(child); 3132 } 3133 return results; 3134 }, 3135 3136 adjacent: function(nodes) { 3137 for (var i = 0, results = [], node; node = nodes[i]; i++) { 3138 var next = this.nextElementSibling(node); 3139 if (next) results.push(next); 3140 } 3141 return results; 3142 }, 3143 3144 laterSibling: function(nodes) { 3145 var h = Selector.handlers; 3146 for (var i = 0, results = [], node; node = nodes[i]; i++) 3147 h.concat(results, Element.nextSiblings(node)); 3148 return results; 3149 }, 3150 3151 nextElementSibling: function(node) { 3152 while (node = node.nextSibling) 3153 if (node.nodeType == 1) return node; 3154 return null; 3155 }, 3156 3157 previousElementSibling: function(node) { 3158 while (node = node.previousSibling) 3159 if (node.nodeType == 1) return node; 3160 return null; 3161 }, 3162 3163 // TOKEN FUNCTIONS 3164 tagName: function(nodes, root, tagName, combinator) { 3165 var uTagName = tagName.toUpperCase(); 3166 var results = [], h = Selector.handlers; 3167 if (nodes) { 3168 if (combinator) {
3169 // fastlane for ordinary descendant combinators 3170 if (combinator == "descendant") { 3171 for (var i = 0, node; node = nodes[i]; i++) 3172 h.concat(results, node.getElementsByTagName(tagName)); 3173 return results; 3174 } else nodes = this[combinator](nodes); 3175 if (tagName == "*") return nodes; 3176 } 3177 for (var i = 0, node; node = nodes[i]; i++) 3178 if (node.tagName.toUpperCase() === uTagName) results.push(node); 3179 return results; 3180 } else return root.getElementsByTagName(tagName); 3181 }, 3182 3183 id: function(nodes, root, id, combinator) { 3184 var targetNode = $(id), h = Selector.handlers; 3185 if (!targetNode) return []; 3186 if (!nodes && root == document) return [targetNode]; 3187 if (nodes) { 3188 if (combinator) { 3189 if (combinator == 'child') { 3190 for (var i = 0, node; node = nodes[i]; i++) 3191 if (targetNode.parentNode == node) return [targetNode]; 3192 } else if (combinator == 'descendant') { 3193 for (var i = 0, node; node = nodes[i]; i++) 3194 if (Element.descendantOf(targetNode, node)) return [targetNode]; 3195 } else if (combinator == 'adjacent') { 3196 for (var i = 0, node; node = nodes[i]; i++) 3197 if (Selector.handlers.previousElementSibling(targetNode) == node) 3198 return [targetNode]; 3199 } else nodes = h[combinator](nodes); 3200 } 3201 for (var i = 0, node; node = nodes[i]; i++) 3202 if (node == targetNode) return [targetNode]; 3203 return []; 3204 } 3205 return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : []; 3206 }, 3207 3208 className: function(nodes, root, className, combinator) { 3209 if (nodes && combinator) nodes = this[combinator](nodes); 3210 return Selector.handlers.byClassName(nodes, root, className); 3211 }, 3212 3213 byClassName: function(nodes, root, className) { 3214 if (!nodes) nodes = Selector.handlers.descendant([root]); 3215 var needle = ' ' + className + ' '; 3216 for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) { 3217 nodeClassName = node.className; 3218 if (nodeClassName.length == 0) continue; 3219 if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle)) 3220 results.push(node); 3221 } 3222 return results; 3223 }, 3224 3225 attrPresence: function(nodes, root, attr, combinator) { 3226 if (!nodes) nodes = root.getElementsByTagName("*"); 3227 if (nodes && combinator) nodes = this[combinator](nodes); 3228 var results = []; 3229 for (var i = 0, node; node = nodes[i]; i++) 3230 if (Element.hasAttribute(node, attr)) results.push(node); 3231 return results; 3232 }, 3233 3234 attr: function(nodes, root, attr, value, operator, combinator) { 3235 if (!nodes) nodes = root.getElementsByTagName("*"); 3236 if (nodes && combinator) nodes = this[combinator](nodes); 3237 var handler = Selector.operators[operator], results = []; 3238 for (var i = 0, node; node = nodes[i]; i++) { 3239 var nodeValue = Element.readAttribute(node, attr); 3240 if (nodeValue === null) continue; 3241 if (handler(nodeValue, value)) results.push(node); 3242 } 3243 return results; 3244 }, 3245 3246 pseudo: function(nodes, name, value, root, combinator) { 3247 if (nodes && combinator) nodes = this[combinator](nodes); 3248 if (!nodes) nodes = root.getElementsByTagName("*"); 3249 return Selector.pseudos[name](nodes, value, root); 3250 } 3251 }, 3252 3253 pseudos: { 3254 'first-child': function(nodes, value, root) { 3255 for (var i = 0, results = [], node; node = nodes[i]; i++) { 3256 if (Selector.handlers.previousElementSibling(node)) continue; 3257 results.push(node); 3258 } 3259 return results; 3260 }, 3261 'last-child': function(nodes, value, root) { 3262 for (var i = 0, results = [], node; node = nodes[i]; i++) { 3263 if (Selector.handlers.nextElementSibling(node)) continue; 3264 results.push(node); 3265 } 3266 return results; 3267 }, 3268 'only-child': function(nodes, value, root) { 3269 var h = Selector.handlers; 3270 for (var i = 0, results = [], node; node = nodes[i]; i++) 3271 if (!h.previousElementSibling(node) && !h.nextElementSibling(node)) 3272 results.push(node); 3273 return results; 3274 }, 3275 'nth-child': function(nodes, formula, root) { 3276 return Selector.pseudos.nth(nodes, formula, root); 3277 }, 3278 'nth-last-child': function(nodes, formula, root) { 3279 return Selector.pseudos.nth(nodes, formula, root, true); 3280 }, 3281 'nth-of-type': function(nodes, formula, root) { 3282 return Selector.pseudos.nth(nodes, formula, root, false, true); 3283 }, 3284 'nth-last-of-type': function(nodes, formula, root) { 3285 return Selector.pseudos.nth(nodes, formula, root, true, true); 3286 }, 3287 'first-of-type': function(nodes, formula, root) { 3288 return Selector.pseudos.nth(nodes, "1", root, false, true); 3289 }, 3290 'last-of-type': function(nodes, formula, root) { 3291 return Selector.pseudos.nth(nodes, "1", root, true, true); 3292 }, 3293 'only-of-type': function(nodes, formula, root) { 3294 var p = Selector.pseudos;
3295 return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root); 3296 }, 3297 3298 // handles the an+b logic 3299 getIndices: function(a, b, total) { 3300 if (a == 0) return b > 0 ? [b] : []; 3301 return $R(1, total).inject([], function(memo, i) { 3302 if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i); 3303 return memo; 3304 }); 3305 }, 3306 3307 // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type 3308 nth: function(nodes, formula, root, reverse, ofType) { 3309 if (nodes.length == 0) return []; 3310 if (formula == 'even') formula = '2n+0'; 3311 if (formula == 'odd') formula = '2n+1'; 3312 var h = Selector.handlers, results = [], indexed = [], m; 3313 h.mark(nodes); 3314 for (var i = 0, node; node = nodes[i]; i++) { 3315 if (!node.parentNode._countedByPrototype) { 3316 h.index(node.parentNode, reverse, ofType); 3317 indexed.push(node.parentNode); 3318 } 3319 } 3320 if (formula.match(/^\d+$/)) { // just a number 3321 formula = Number(formula); 3322 for (var i = 0, node; node = nodes[i]; i++) 3323 if (node.nodeIndex == formula) results.push(node); 3324 } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b 3325 if (m[1] == "-") m[1] = -1; 3326 var a = m[1] ? Number(m[1]) : 1; 3327 var b = m[2] ? Number(m[2]) : 0; 3328 var indices = Selector.pseudos.getIndices(a, b, nodes.length); 3329 for (var i = 0, node, l = indices.length; node = nodes[i]; i++) { 3330 for (var j = 0; j < l; j++) 3331 if (node.nodeIndex == indices[j]) results.push(node); 3332 } 3333 } 3334 h.unmark(nodes); 3335 h.unmark(indexed); 3336 return results; 3337 }, 3338 3339 'empty': function(nodes, value, root) { 3340 for (var i = 0, results = [], node; node = nodes[i]; i++) { 3341 // IE treats comments as element nodes 3342 if (node.tagName == '!' || node.firstChild) continue; 3343 results.push(node); 3344 } 3345 return results; 3346 }, 3347 3348 'not': function(nodes, selector, root) { 3349 var h = Selector.handlers, selectorType, m; 3350 var exclusions = new Selector(selector).findElements(root); 3351 h.mark(exclusions); 3352 for (var i = 0, results = [], node; node = nodes[i]; i++) 3353 if (!node._countedByPrototype) results.push(node); 3354 h.unmark(exclusions); 3355 return results; 3356 }, 3357 3358 'enabled': function(nodes, value, root) { 3359 for (var i = 0, results = [], node; node = nodes[i]; i++) 3360 if (!node.disabled && (!node.type || node.type !== 'hidden')) 3361 results.push(node); 3362 return results; 3363 }, 3364 3365 'disabled': function(nodes, value, root) { 3366 for (var i = 0, results = [], node; node = nodes[i]; i++) 3367 if (node.disabled) results.push(node); 3368 return results; 3369 }, 3370 3371 'checked': function(nodes, value, root) { 3372 for (var i = 0, results = [], node; node = nodes[i]; i++) 3373 if (node.checked) results.push(node); 3374 return results; 3375 } 3376 }, 3377 3378 operators: { 3379 '=': function(nv, v) { return nv == v; }, 3380 '!=': function(nv, v) { return nv != v; }, 3381 '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); }, 3382 '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); }, 3383 '*=': function(nv, v) { return nv == v || nv && nv.include(v); }, 3384 '$=': function(nv, v) { return nv.endsWith(v); }, 3385 '*=': function(nv, v) { return nv.include(v); }, 3386 '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); }, 3387 '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() + 3388 '-').include('-' + (v || "").toUpperCase() + '-'); } 3389 }, 3390 3391 split: function(expression) { 3392 var expressions = []; 3393 expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { 3394 expressions.push(m[1].strip()); 3395 }); 3396 return expressions; 3397 }, 3398 3399 matchElements: function(elements, expression) { 3400 var matches = $$(expression), h = Selector.handlers; 3401 h.mark(matches); 3402 for (var i = 0, results = [], element; element = elements[i]; i++) 3403 if (element._countedByPrototype) results.push(element); 3404 h.unmark(matches); 3405 return results; 3406 }, 3407 3408 findElement: function(elements, expression, index) { 3409 if (Object.isNumber(expression)) { 3410 index = expression; expression = false;
3411 } 3412 return Selector.matchElements(elements, expression || '*')[index || 0]; 3413 }, 3414 3415 findChildElements: function(element, expressions) { 3416 expressions = Selector.split(expressions.join(',')); 3417 var results = [], h = Selector.handlers; 3418 for (var i = 0, l = expressions.length, selector; i < l; i++) { 3419 selector = new Selector(expressions[i].strip()); 3420 h.concat(results, selector.findElements(element)); 3421 } 3422 return (l > 1) ? h.unique(results) : results; 3423 } 3424}); 3425 3426if (Prototype.Browser.IE) { 3427 Object.extend(Selector.handlers, { 3428 // IE returns comment nodes on getElementsByTagName("*"). 3429 // Filter them out. 3430 concat: function(a, b) { 3431 for (var i = 0, node; node = b[i]; i++) 3432 if (node.tagName !== "!") a.push(node); 3433 return a; 3434 }, 3435 3436 // IE improperly serializes _countedByPrototype in (inner|outer)HTML. 3437 unmark: function(nodes) { 3438 for (var i = 0, node; node = nodes[i]; i++) 3439 node.removeAttribute('_countedByPrototype'); 3440 return nodes; 3441 } 3442 }); 3443} 3444 3445function $$() { 3446 return Selector.findChildElements(document, $A(arguments)); 3447} 3448var Form = { 3449 reset: function(form) { 3450 $(form).reset(); 3451 return form; 3452 }, 3453 3454 serializeElements: function(elements, options) { 3455 if (typeof options != 'object') options = { hash: !!options }; 3456 else if (Object.isUndefined(options.hash)) options.hash = true; 3457 var key, value, submitted = false, submit = options.submit; 3458 3459 var data = elements.inject({ }, function(result, element) { 3460 if (!element.disabled && element.name) { 3461 key = element.name; value = $(element).getValue(); 3462 if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted && 3463 submit !== false && (!submit || key == submit) && (submitted = true)))) { 3464 if (key in result) { 3465 // a key is already present; construct an array of values 3466 if (!Object.isArray(result[key])) result[key] = [result[key]]; 3467 result[key].push(value); 3468 } 3469 else result[key] = value; 3470 } 3471 } 3472 return result; 3473 }); 3474 3475 return options.hash ? data : Object.toQueryString(data); 3476 } 3477}; 3478 3479Form.Methods = { 3480 serialize: function(form, options) { 3481 return Form.serializeElements(Form.getElements(form), options); 3482 }, 3483 3484 getElements: function(form) { 3485 return $A($(form).getElementsByTagName('*')).inject([], 3486 function(elements, child) { 3487 if (Form.Element.Serializers[child.tagName.toLowerCase()]) 3488 elements.push(Element.extend(child)); 3489 return elements; 3490 } 3491 ); 3492 }, 3493 3494 getInputs: function(form, typeName, name) { 3495 form = $(form); 3496 var inputs = form.getElementsByTagName('input'); 3497 3498 if (!typeName && !name) return $A(inputs).map(Element.extend); 3499 3500 for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { 3501 var input = inputs[i]; 3502 if ((typeName && input.type != typeName) || (name && input.name != name)) 3503 continue; 3504 matchingInputs.push(Element.extend(input)); 3505 } 3506 3507 return matchingInputs; 3508 }, 3509 3510 disable: function(form) { 3511 form = $(form); 3512 Form.getElements(form).invoke('disable'); 3513 return form; 3514 }, 3515 3516 enable: function(form) { 3517 form = $(form); 3518 Form.getElements(form).invoke('enable'); 3519 return form; 3520 }, 3521 3522 findFirstElement: function(form) { 3523 var elements = $(form).getElements().findAll(function(element) { 3524 return 'hidden' != element.type && !element.disabled; 3525 }); 3526 var firstByIndex = elements.findAll(function(element) { 3527 return element.hasAttribute('tabIndex') && element.tabIndex >= 0; 3528 }).sortBy(function(element) { return element.tabIndex }).first(); 3529 3530 return firstByIndex ? firstByIndex : elements.find(function(element) { 3531 return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); 3532 }); 3533 }, 3534 3535 focusFirstElement: function(form) { 3536 form = $(form); 3537 form.findFirstElement().activate(); 3538 return form; 3539 }, 3540 3541 request: function(form, options) { 3542 form = $(form), options = Object.clone(options || { }); 3543 3544 var params = options.parameters, action = form.readAttribute('action') || ''; 3545 if (action.blank()) action = window.location.href; 3546 options.parameters = form.serialize(true); 3547 3548 if (params) { 3549 if (Object.isString(params)) params = params.toQueryParams(); 3550 Object.extend(options.parameters, params); 3551 } 3552 3553 if (form.hasAttribute('method') && !options.method) 3554 options.method = form.method; 3555 3556 return new Ajax.Request(action, options); 3557 } 3558}; 3559 3560/*--------------------------------------------------------------------------*/ 3561 3562Form.Element = { 3563 focus: function(element) { 3564 $(element).focus(); 3565 return element; 3566 }, 3567 3568 select: function(element) { 3569 $(element).select(); 3570 return element; 3571 } 3572}; 3573 3574Form.Element.Methods = { 3575 serialize: function(element) { 3576 element = $(element); 3577 if (!element.disabled && element.name) { 3578 var value = element.getValue(); 3579 if (value != undefined) { 3580 var pair = { }; 3581 pair[element.name] = value; 3582 return Object.toQueryString(pair); 3583 } 3584 } 3585 return ''; 3586 }, 3587 3588 getValue: function(element) { 3589 element = $(element); 3590 var method = element.tagName.toLowerCase(); 3591 return Form.Element.Serializers[method](element); 3592 }, 3593 3594 setValue: function(element, value) { 3595 element = $(element); 3596 var method = element.tagName.toLowerCase(); 3597 Form.Element.Serializers[method](element, value); 3598 return element; 3599 }, 3600 3601 clear: function(element) { 3602 $(element).value = ''; 3603 return element; 3604 }, 3605 3606 present: function(element) { 3607 return $(element).value != ''; 3608 }, 3609 3610 activate: function(element) { 3611 element = $(element); 3612 try { 3613 element.focus(); 3614 if (element.select && (element.tagName.toLowerCase() != 'input' || 3615 !['button', 'reset', 'submit'].include(element.type))) 3616 element.select(); 3617 } catch (e) { } 3618 return element; 3619 }, 3620 3621 disable: function(element) { 3622 element = $(element); 3623 element.disabled = true; 3624 return element; 3625 }, 3626 3627 enable: function(element) { 3628 element = $(element); 3629 element.disabled = false;
3630 return element; 3631 } 3632}; 3633 3634/*--------------------------------------------------------------------------*/ 3635 3636var Field = Form.Element; 3637var $F = Form.Element.Methods.getValue; 3638 3639/*--------------------------------------------------------------------------*/ 3640 3641Form.Element.Serializers = { 3642 input: function(element, value) { 3643 switch (element.type.toLowerCase()) { 3644 case 'checkbox': 3645 case 'radio': 3646 return Form.Element.Serializers.inputSelector(element, value); 3647 default: 3648 return Form.Element.Serializers.textarea(element, value); 3649 } 3650 }, 3651 3652 inputSelector: function(element, value) { 3653 if (Object.isUndefined(value)) return element.checked ? element.value : null; 3654 else element.checked = !!value; 3655 }, 3656 3657 textarea: function(element, value) { 3658 if (Object.isUndefined(value)) return element.value; 3659 else element.value = value; 3660 }, 3661 3662 select: function(element, value) { 3663 if (Object.isUndefined(value)) 3664 return this[element.type == 'select-one' ? 3665 'selectOne' : 'selectMany'](element); 3666 else { 3667 var opt, currentValue, single = !Object.isArray(value); 3668 for (var i = 0, length = element.length; i < length; i++) { 3669 opt = element.options[i]; 3670 currentValue = this.optionValue(opt); 3671 if (single) { 3672 if (currentValue == value) { 3673 opt.selected = true; 3674 return; 3675 } 3676 } 3677 else opt.selected = value.include(currentValue); 3678 } 3679 } 3680 }, 3681 3682 selectOne: function(element) { 3683 var index = element.selectedIndex; 3684 return index >= 0 ? this.optionValue(element.options[index]) : null; 3685 }, 3686 3687 selectMany: function(element) { 3688 var values, length = element.length; 3689 if (!length) return null; 3690 3691 for (var i = 0, values = []; i < length; i++) { 3692 var opt = element.options[i]; 3693 if (opt.selected) values.push(this.optionValue(opt)); 3694 } 3695 return values; 3696 }, 3697 3698 optionValue: function(opt) { 3699 // extend element because hasAttribute may not be native 3700 return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; 3701 } 3702}; 3703 3704/*--------------------------------------------------------------------------*/ 3705 3706Abstract.TimedObserver = Class.create(PeriodicalExecuter, { 3707 initialize: function($super, element, frequency, callback) { 3708 $super(callback, frequency); 3709 this.element = $(element); 3710 this.lastValue = this.getValue(); 3711 }, 3712 3713 execute: function() { 3714 var value = this.getValue(); 3715 if (Object.isString(this.lastValue) && Object.isString(value) ? 3716 this.lastValue != value : String(this.lastValue) != String(value)) { 3717 this.callback(this.element, value); 3718 this.lastValue = value; 3719 } 3720 } 3721}); 3722 3723Form.Element.Observer = Class.create(Abstract.TimedObserver, { 3724 getValue: function() { 3725 return Form.Element.getValue(this.element); 3726 } 3727}); 3728 3729Form.Observer = Class.create(Abstract.TimedObserver, { 3730 getValue: function() { 3731 return Form.serialize(this.element); 3732 } 3733}); 3734 3735/*--------------------------------------------------------------------------*/ 3736 3737Abstract.EventObserver = Class.create({ 3738 initialize: function(element, callback) { 3739 this.element = $(element); 3740 this.callback = callback; 3741 3742 this.lastValue = this.getValue(); 3743 if (this.element.tagName.toLowerCase() == 'form') 3744 this.registerFormCallbacks(); 3745 else 3746 this.registerCallback(this.element); 3747 }, 3748 3749 onElementEvent: function() { 3750 var value = this.getValue(); 3751 if (this.lastValue != value) { 3752 this.callback(this.element, value); 3753 this.lastValue = value; 3754 } 3755 }, 3756 3757 registerFormCallbacks: function() { 3758 Form.getElements(this.element).each(this.registerCallback, this); 3759 }, 3760 3761 registerCallback: function(element) { 3762 if (element.type) { 3763 switch (element.type.toLowerCase()) { 3764 case 'checkbox': 3765 case 'radio': 3766 Event.observe(element, 'click', this.onElementEvent.bind(this)); 3767 break; 3768 default: 3769 Event.observe(element, 'change', this.onElementEvent.bind(this)); 3770 break; 3771 } 3772 } 3773 } 3774}); 3775 3776Form.Element.EventObserver = Class.create(Abstract.EventObserver, { 3777 getValue: function() { 3778 return Form.Element.getValue(this.element); 3779 } 3780}); 3781 3782Form.EventObserver = Class.create(Abstract.EventObserver, { 3783 getValue: function() { 3784 return Form.serialize(this.element); 3785 } 3786}); 3787if (!window.Event) var Event = { }; 3788 3789Object.extend(Event, { 3790 KEY_BACKSPACE: 8, 3791 KEY_TAB: 9, 3792 KEY_RETURN: 13, 3793 KEY_ESC: 27, 3794 KEY_LEFT: 37, 3795 KEY_UP: 38, 3796 KEY_RIGHT: 39, 3797 KEY_DOWN: 40, 3798 KEY_DELETE: 46, 3799 KEY_HOME: 36, 3800 KEY_END: 35, 3801 KEY_PAGEUP: 33, 3802 KEY_PAGEDOWN: 34, 3803 KEY_INSERT: 45, 3804 3805 cache: { }, 3806 3807 relatedTarget: function(event) { 3808 var element; 3809 switch(event.type) { 3810 case 'mouseover': element = event.fromElement; break; 3811 case 'mouseout': element = event.toElement; break; 3812 default: return null; 3813 } 3814 return Element.extend(element); 3815 } 3816}); 3817 3818Event.Methods = (function() { 3819 var isButton; 3820 3821 if (Prototype.Browser.IE) { 3822 var buttonMap = { 0: 1, 1: 4, 2: 2 }; 3823 isButton = function(event, code) { 3824 return event.button == buttonMap[code]; 3825 }; 3826 3827 } else if (Prototype.Browser.WebKit) { 3828 isButton = function(event, code) { 3829 switch (code) { 3830 case 0: return event.which == 1 && !event.metaKey; 3831 case 1: return event.which == 1 && event.metaKey; 3832 default: return false;
3833 } 3834 }; 3835 3836 } else { 3837 isButton = function(event, code) { 3838 return event.which ? (event.which === code + 1) : (event.button === code); 3839 }; 3840 } 3841 3842 return { 3843 isLeftClick: function(event) { return isButton(event, 0) }, 3844 isMiddleClick: function(event) { return isButton(event, 1) }, 3845 isRightClick: function(event) { return isButton(event, 2) }, 3846 3847 element: function(event) { 3848 event = Event.extend(event); 3849 3850 var node = event.target, 3851 type = event.type, 3852 currentTarget = event.currentTarget; 3853 3854 if (currentTarget && currentTarget.tagName) { 3855 // Firefox screws up the "click" event when moving between radio buttons 3856 // via arrow keys. It also screws up the "load" and "error" events on images, 3857 // reporting the document as the target instead of the original image. 3858 if (type === 'load' || type === 'error' || 3859 (type === 'click' && currentTarget.tagName.toLowerCase() === 'input' 3860 && currentTarget.type === 'radio')) 3861 node = currentTarget; 3862 } 3863 if (node) { 3864 if (node.nodeType == Node.TEXT_NODE) node = node.parentNode; 3865 return Element.extend(node); 3866 } else return false; 3867 }, 3868 3869 findElement: function(event, expression) { 3870 var element = Event.element(event); 3871 if (!expression) return element; 3872 var elements = [element].concat(element.ancestors()); 3873 return Selector.findElement(elements, expression, 0); 3874 }, 3875 3876 pointer: function(event) { 3877 var docElement = document.documentElement, 3878 body = document.body || { scrollLeft: 0, scrollTop: 0 }; 3879 return { 3880 x: event.pageX || (event.clientX + 3881 (docElement.scrollLeft || body.scrollLeft) - 3882 (docElement.clientLeft || 0)), 3883 y: event.pageY || (event.clientY + 3884 (docElement.scrollTop || body.scrollTop) - 3885 (docElement.clientTop || 0)) 3886 }; 3887 }, 3888 3889 pointerX: function(event) { return Event.pointer(event).x }, 3890 pointerY: function(event) { return Event.pointer(event).y }, 3891 3892 stop: function(event) { 3893 Event.extend(event); 3894 event.preventDefault(); 3895 event.stopPropagation(); 3896 event.stopped = true; 3897 } 3898 }; 3899})(); 3900 3901Event.extend = (function() { 3902 var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { 3903 m[name] = Event.Methods[name].methodize(); 3904 return m; 3905 }); 3906 3907 if (Prototype.Browser.IE) { 3908 Object.extend(methods, { 3909 stopPropagation: function() { this.cancelBubble = true }, 3910 preventDefault: function() { this.returnValue = false }, 3911 inspect: function() { return "[object Event]" } 3912 }); 3913 3914 return function(event) { 3915 if (!event) return false; 3916 if (event._extendedByPrototype) return event; 3917 3918 event._extendedByPrototype = Prototype.emptyFunction; 3919 var pointer = Event.pointer(event); 3920 Object.extend(event, { 3921 target: event.srcElement, 3922 relatedTarget: Event.relatedTarget(event), 3923 pageX: pointer.x, 3924 pageY: pointer.y 3925 }); 3926 return Object.extend(event, methods); 3927 }; 3928 3929 } else { 3930 Event.prototype = Event.prototype || document.createEvent("HTMLEvents")['__proto__']; 3931 Object.extend(Event.prototype, methods); 3932 return Prototype.K; 3933 } 3934})(); 3935 3936Object.extend(Event, (function() { 3937 var cache = Event.cache; 3938 3939 function getEventID(element) { 3940 try { 3941 if (element._prototypeEventID) return element._prototypeEventID[0]; 3942 arguments.callee.id = arguments.callee.id || 1; 3943 return element._prototypeEventID = [++arguments.callee.id]; 3944 } catch (error) { 3945 return false; 3946 } 3947 } 3948 3949 function getDOMEventName(eventName) { 3950 if (eventName && eventName.include(':')) return "dataavailable"; 3951 return eventName; 3952 } 3953 3954 function getCacheForID(id) { 3955 return cache[id] = cache[id] || { }; 3956 } 3957 3958 function getWrappersForEventName(id, eventName) { 3959 var c = getCacheForID(id); 3960 return c[eventName] = c[eventName] || []; 3961 } 3962 3963 function createWrapper(element, eventName, handler) { 3964 var id = getEventID(element); 3965 var c = getWrappersForEventName(id, eventName); 3966 if (c.pluck("handler").include(handler)) return false;
3967 3968 var wrapper = function(event) { 3969 if (!Event || !Event.extend || 3970 (event.eventName && event.eventName != eventName)) 3971 return false; 3972 3973 Event.extend(event); 3974 handler.call(element, event); 3975 }; 3976 3977 wrapper.handler = handler; 3978 c.push(wrapper); 3979 return wrapper; 3980 } 3981 3982 function findWrapper(id, eventName, handler) { 3983 var c = getWrappersForEventName(id, eventName); 3984 return c.find(function(wrapper) { return wrapper.handler == handler }); 3985 } 3986 3987 function destroyWrapper(id, eventName, handler) { 3988 var c = getCacheForID(id); 3989 if (!c[eventName]) return false; 3990 c[eventName] = c[eventName].without(findWrapper(id, eventName, handler)); 3991 } 3992 3993 function destroyCache() { 3994 for (var id in cache) 3995 for (var eventName in cache[id]) 3996 cache[id][eventName] = null; 3997 } 3998 3999 4000 // Internet Explorer needs to remove event handlers on page unload 4001 // in order to avoid memory leaks. 4002 if (window.attachEvent) { 4003 window.attachEvent("onunload", destroyCache); 4004 } 4005 4006 // Safari has a dummy event handler on page unload so that it won't 4007 // use its bfcache. Safari <= 3.1 has an issue with restoring the "document" 4008 // object when page is returned to via the back button using its bfcache. 4009 if (Prototype.Browser.WebKit) { 4010 window.addEventListener('unload', Prototype.emptyFunction, false); 4011 } 4012 4013 return { 4014 observe: function(element, eventName, handler) { 4015 element = $(element); 4016 var name = getDOMEventName(eventName); 4017 4018 var wrapper = createWrapper(element, eventName, handler); 4019 if (!wrapper) return element; 4020 4021 if (element.addEventListener) { 4022 element.addEventListener(name, wrapper, false); 4023 } else { 4024 element.attachEvent("on" + name, wrapper); 4025 } 4026 4027 return element; 4028 }, 4029 4030 stopObserving: function(element, eventName, handler) { 4031 element = $(element); 4032 var id = getEventID(element), name = getDOMEventName(eventName); 4033 4034 if (!handler && eventName) { 4035 getWrappersForEventName(id, eventName).each(function(wrapper) { 4036 element.stopObserving(eventName, wrapper.handler); 4037 }); 4038 return element; 4039 4040 } else if (!eventName) { 4041 Object.keys(getCacheForID(id)).each(function(eventName) { 4042 element.stopObserving(eventName); 4043 }); 4044 return element; 4045 } 4046 4047 var wrapper = findWrapper(id, eventName, handler); 4048 if (!wrapper) return element; 4049 4050 if (element.removeEventListener) { 4051 element.removeEventListener(name, wrapper, false); 4052 } else { 4053 element.detachEvent("on" + name, wrapper); 4054 } 4055 4056 destroyWrapper(id, eventName, handler); 4057 4058 return element; 4059 }, 4060 4061 fire: function(element, eventName, memo) { 4062 element = $(element); 4063 if (element == document && document.createEvent && !element.dispatchEvent) 4064 element = document.documentElement; 4065 4066 var event; 4067 if (document.createEvent) { 4068 event = document.createEvent("HTMLEvents"); 4069 event.initEvent("dataavailable", true, true); 4070 } else { 4071 event = document.createEventObject(); 4072 event.eventType = "ondataavailable"; 4073 } 4074 4075 event.eventName = eventName; 4076 event.memo = memo || { }; 4077 4078 if (document.createEvent) { 4079 element.dispatchEvent(event); 4080 } else { 4081 element.fireEvent(event.eventType, event); 4082 } 4083 4084 return Event.extend(event); 4085 } 4086 }; 4087})()); 4088 4089Object.extend(Event, Event.Methods); 4090 4091Element.addMethods({ 4092 fire: Event.fire, 4093 observe: Event.observe, 4094 stopObserving: Event.stopObserving 4095}); 4096 4097Object.extend(document, { 4098 fire: Element.Methods.fire.methodize(), 4099 observe: Element.Methods.observe.methodize(), 4100 stopObserving: Element.Methods.stopObserving.methodize(), 4101 loaded: false 4102}); 4103 4104(function() { 4105 /* Support for the DOMContentLoaded event is based on work by Dan Webb, 4106 Matthias Miller, Dean Edwards and John Resig. */ 4107 4108 var timer; 4109 4110 function fireContentLoadedEvent() { 4111 if (document.loaded) return; 4112 if (timer) window.clearInterval(timer); 4113 document.fire("dom:loaded"); 4114 document.loaded = true; 4115 } 4116 4117 if (document.addEventListener) { 4118 if (Prototype.Browser.WebKit) { 4119 timer = window.setInterval(function() { 4120 if (/loaded|complete/.test(document.readyState)) 4121 fireContentLoadedEvent(); 4122 }, 0); 4123 4124 Event.observe(window, "load", fireContentLoadedEvent); 4125 4126 } else { 4127 document.addEventListener("DOMContentLoaded", 4128 fireContentLoadedEvent, false); 4129 } 4130 4131 } else { 4132 document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>"); 4133 $("__onDOMContentLoaded").onreadystatechange = function() { 4134 if (this.readyState == "complete") { 4135 this.onreadystatechange = null; 4136 fireContentLoadedEvent(); 4137 } 4138 }; 4139 } 4140})(); 4141/*------------------------------- DEPRECATED -------------------------------*/ 4142 4143Hash.toQueryString = Object.toQueryString; 4144 4145var Toggle = { display: Element.toggle }; 4146 4147Element.Methods.childOf = Element.Methods.descendantOf; 4148 4149var Insertion = { 4150 Before: function(element, content) { 4151 return Element.insert(element, {before:content}); 4152 }, 4153 4154 Top: function(element, content) { 4155 return Element.insert(element, {top:content}); 4156 }, 4157 4158 Bottom: function(element, content) { 4159 return Element.insert(element, {bottom:content}); 4160 }, 4161 4162 After: function(element, content) { 4163 return Element.insert(element, {after:content}); 4164 } 4165}; 4166 4167var $continue = new Error('"throw $continue" is deprecated, use "return" instead'); 4168 4169// This should be moved to script.aculo.us; notice the deprecated methods 4170// further below, that map to the newer Element methods. 4171var Position = { 4172 // set to true if needed, warning: firefox performance problems 4173 // NOT neeeded for page scrolling, only if draggable contained in 4174 // scrollable elements 4175 includeScrollOffsets: false, 4176 4177 // must be called before calling withinIncludingScrolloffset, every time the 4178 // page is scrolled 4179 prepare: function() { 4180 this.deltaX = window.pageXOffset 4181 || document.documentElement.scrollLeft 4182 || document.body.scrollLeft 4183 || 0; 4184 this.deltaY = window.pageYOffset 4185 || document.documentElement.scrollTop 4186 || document.body.scrollTop 4187 || 0; 4188 }, 4189 4190 // caches x/y coordinate pair to use with overlap 4191 within: function(element, x, y) { 4192 if (this.includeScrollOffsets) 4193 return this.withinIncludingScrolloffsets(element, x, y); 4194 this.xcomp = x; 4195 this.ycomp = y; 4196 this.offset = Element.cumulativeOffset(element); 4197 4198 return (y >= this.offset[1] && 4199 y < this.offset[1] + element.offsetHeight && 4200 x >= this.offset[0] && 4201 x < this.offset[0] + element.offsetWidth); 4202 }, 4203 4204 withinIncludingScrolloffsets: function(element, x, y) { 4205 var offsetcache = Element.cumulativeScrollOffset(element); 4206 4207 this.xcomp = x + offsetcache[0] - this.deltaX; 4208 this.ycomp = y + offsetcache[1] - this.deltaY; 4209 this.offset = Element.cumulativeOffset(element); 4210 4211 return (this.ycomp >= this.offset[1] && 4212 this.ycomp < this.offset[1] + element.offsetHeight && 4213 this.xcomp >= this.offset[0] && 4214 this.xcomp < this.offset[0] + element.offsetWidth); 4215 }, 4216 4217 // within must be called directly before 4218 overlap: function(mode, element) { 4219 if (!mode) return 0; 4220 if (mode == 'vertical') 4221 return ((this.offset[1] + element.offsetHeight) - this.ycomp) / 4222 element.offsetHeight; 4223 if (mode == 'horizontal') 4224 return ((this.offset[0] + element.offsetWidth) - this.xcomp) / 4225 element.offsetWidth; 4226 }, 4227 4228 // Deprecation layer -- use newer Element methods now (1.5.2).
4229 4230 cumulativeOffset: Element.Methods.cumulativeOffset, 4231 4232 positionedOffset: Element.Methods.positionedOffset, 4233 4234 absolutize: function(element) { 4235 Position.prepare(); 4236 return Element.absolutize(element); 4237 }, 4238 4239 relativize: function(element) { 4240 Position.prepare(); 4241 return Element.relativize(element); 4242 }, 4243 4244 realOffset: Element.Methods.cumulativeScrollOffset, 4245 4246 offsetParent: Element.Methods.getOffsetParent, 4247 4248 page: Element.Methods.viewportOffset, 4249 4250 clone: function(source, target, options) { 4251 options = options || { }; 4252 return Element.clonePosition(target, source, options); 4253 } 4254}; 4255 4256/*--------------------------------------------------------------------------*/ 4257 4258if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){ 4259 function iter(name) { 4260 return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]"; 4261 } 4262 4263 instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ? 4264 function(element, className) { 4265 className = className.toString().strip(); 4266 var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className); 4267 return cond ? document._getElementsByXPath('.//*' + cond, element) : []; 4268 } : function(element, className) { 4269 className = className.toString().strip(); 4270 var elements = [], classNames = (/\s/.test(className) ? $w(className) : null); 4271 if (!classNames && !className) return elements; 4272 4273 var nodes = $(element).getElementsByTagName('*'); 4274 className = ' ' + className + ' '; 4275 4276 for (var i = 0, child, cn; child = nodes[i]; i++) { 4277 if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) || 4278 (classNames && classNames.all(function(name) { 4279 return !name.toString().blank() && cn.include(' ' + name + ' '); 4280 })))) 4281 elements.push(Element.extend(child)); 4282 } 4283 return elements; 4284 }; 4285 4286 return function(className, parentElement) { 4287 return $(parentElement || document.body).getElementsByClassName(className); 4288 }; 4289}(Element.Methods); 4290 4291/*--------------------------------------------------------------------------*/ 4292 4293Element.ClassNames = Class.create(); 4294Element.ClassNames.prototype = { 4295 initialize: function(element) { 4296 this.element = $(element); 4297 }, 4298 4299 _each: function(iterator) { 4300 this.element.className.split(/\s+/).select(function(name) { 4301 return name.length > 0; 4302 })._each(iterator); 4303 }, 4304 4305 set: function(className) { 4306 this.element.className = className; 4307 }, 4308 4309 add: function(classNameToAdd) { 4310 if (this.include(classNameToAdd)) return; 4311 this.set($A(this).concat(classNameToAdd).join(' ')); 4312 }, 4313 4314 remove: function(classNameToRemove) { 4315 if (!this.include(classNameToRemove)) return; 4316 this.set($A(this).without(classNameToRemove).join(' ')); 4317 }, 4318 4319 toString: function() { 4320 return $A(this).join(' '); 4321 } 4322}; 4323 4324Object.extend(Element.ClassNames.prototype, Enumerable); 4325 4326/*--------------------------------------------------------------------------*/ 4327 4328Element.addMethods(); 4329/* 4330* Really easy field validation with Prototype 4331* http://tetlaw.id.au/view/javascript/really-easy-field-validation 4332* Andrew Tetlaw 4333* Version 1.5.4.1 (2007-01-05) 4334* 4335* Copyright (c) 2007 Andrew Tetlaw 4336* Permission is hereby granted, free of charge, to any person 4337* obtaining a copy of this software and associated documentation 4338* files (the "Software"), to deal in the Software without 4339* restriction, including without limitation the rights to use, copy, 4340* modify, merge, publish, distribute, sublicense, and/or sell copies 4341* of the Software, and to permit persons to whom the Software is 4342* furnished to do so, subject to the following conditions: 4343* 4344* The above copyright notice and this permission notice shall be 4345* included in all copies or substantial portions of the Software. 4346* 4347* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 4348* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 4349* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 4350* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 4351* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 4352* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 4353* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 4354* SOFTWARE. 4355* 4356*/ 4357var Validator = Class.create(); 4358 4359Validator.prototype = { 4360 initialize : function(className, error, test, options) { 4361 if(typeof test == 'function'){ 4362 this.options = $H(options); 4363 this._test = test; 4364 } else { 4365 this.options = $H(test); 4366 this._test = function(){return true}; 4367 } 4368 this.error = error || 'Validation failed.'; 4369 this.className = className; 4370 }, 4371 test : function(v, elm) { 4372 return (this._test(v,elm) && this.options.all(function(p){ 4373 return Validator.methods[p.key] ? Validator.methods[p.key](v,elm,p.value) : true; 4374 })); 4375 } 4376} 4377Validator.methods = { 4378 pattern : function(v,elm,opt) {return Validation.get('IsEmpty').test(v) || opt.test(v)}, 4379 minLength : function(v,elm,opt) {return v.length >= opt}, 4380 maxLength : function(v,elm,opt) {return v.length <= opt}, 4381 min : function(v,elm,opt) {return v >= parseFloat(opt)}, 4382 max : function(v,elm,opt) {return v <= parseFloat(opt)}, 4383 notOneOf : function(v,elm,opt) {return $A(opt).all(function(value) { 4384 return v != value; 4385 })}, 4386 oneOf : function(v,elm,opt) {return $A(opt).any(function(value) { 4387 return v == value; 4388 })}, 4389 is : function(v,elm,opt) {return v == opt}, 4390 isNot : function(v,elm,opt) {return v != opt}, 4391 equalToField : function(v,elm,opt) {return v == $F(opt)}, 4392 notEqualToField : function(v,elm,opt) {return v != $F(opt)}, 4393 include : function(v,elm,opt) {return $A(opt).all(function(value) { 4394 return Validation.get(value).test(v,elm); 4395 })} 4396} 4397 4398var Validation = Class.create(); 4399Validation.defaultOptions = { 4400 onSubmit : true, 4401 stopOnFirst : false, 4402 immediate : false, 4403 focusOnError : true, 4404 useTitles : false, 4405 addClassNameToContainer: false, 4406 containerClassName: '.input-box', 4407 onFormValidate : function(result, form) {}, 4408 onElementValidate : function(result, elm) {} 4409}; 4410 4411Validation.prototype = { 4412 initialize : function(form, options){ 4413 this.form = $(form); 4414 if (!this.form) { 4415 return; 4416 } 4417 this.options = Object.extend({ 4418 onSubmit : Validation.defaultOptions.onSubmit, 4419 stopOnFirst : Validation.defaultOptions.stopOnFirst, 4420 immediate : Validation.defaultOptions.immediate, 4421 focusOnError : Validation.defaultOptions.focusOnError, 4422 useTitles : Validation.defaultOptions.useTitles, 4423 onFormValidate : Validation.defaultOptions.onFormValidate, 4424 onElementValidate : Validation.defaultOptions.onElementValidate 4425 }, options || {}); 4426 if(this.options.onSubmit) Event.observe(this.form,'submit',this.onSubmit.bind(this),false); 4427 if(this.options.immediate) { 4428 Form.getElements(this.form).each(function(input) { // Thanks Mike! 4429 if (input.tagName.toLowerCase() == 'select') { 4430 Event.observe(input, 'blur', this.onChange.bindAsEventListener(this)); 4431 } 4432 if (input.type.toLowerCase() == 'radio' || input.type.toLowerCase() == 'checkbox') { 4433 Event.observe(input, 'click', this.onChange.bindAsEventListener(this)); 4434 } else { 4435 Event.observe(input, 'change', this.onChange.bindAsEventListener(this)); 4436 } 4437 }, this); 4438 } 4439 }, 4440 onChange : function (ev) { 4441 Validation.isOnChange = true; 4442 Validation.validate(Event.element(ev),{ 4443 useTitle : this.options.useTitles, 4444 onElementValidate : this.options.onElementValidate 4445 }); 4446 Validation.isOnChange = false;
4447 }, 4448 onSubmit : function(ev){ 4449 if(!this.validate()) Event.stop(ev); 4450 }, 4451 validate : function() { 4452 var result = false; 4453 var useTitles = this.options.useTitles; 4454 var callback = this.options.onElementValidate; 4455 try { 4456 if(this.options.stopOnFirst) { 4457 result = Form.getElements(this.form).all(function(elm) { 4458 if (elm.hasClassName('local-validation') && !this.isElementInForm(elm, this.form)) { 4459 return true; 4460 } 4461 return Validation.validate(elm,{useTitle : useTitles, onElementValidate : callback}); 4462 }, this); 4463 } else { 4464 result = Form.getElements(this.form).collect(function(elm) { 4465 if (elm.hasClassName('local-validation') && !this.isElementInForm(elm, this.form)) { 4466 return true; 4467 } 4468 return Validation.validate(elm,{useTitle : useTitles, onElementValidate : callback}); 4469 }, this).all(); 4470 } 4471 } catch (e) { 4472 4473 } 4474 if(!result && this.options.focusOnError) { 4475 try{ 4476 Form.getElements(this.form).findAll(function(elm){return $(elm).hasClassName('validation-failed')}).first().focus() 4477 } 4478 catch(e){ 4479 4480 } 4481 } 4482 this.options.onFormValidate(result, this.form); 4483 return result; 4484 }, 4485 reset : function() { 4486 Form.getElements(this.form).each(Validation.reset); 4487 }, 4488 isElementInForm : function(elm, form) { 4489 var domForm = elm.up('form'); 4490 if (domForm == form) { 4491 return true; 4492 } 4493 return false; 4494 } 4495} 4496 4497Object.extend(Validation, { 4498 validate : function(elm, options){ 4499 options = Object.extend({ 4500 useTitle : false, 4501 onElementValidate : function(result, elm) {} 4502 }, options || {}); 4503 elm = $(elm); 4504 4505 var cn = $w(elm.className); 4506 return result = cn.all(function(value) { 4507 var test = Validation.test(value,elm,options.useTitle); 4508 options.onElementValidate(test, elm); 4509 return test; 4510 }); 4511 }, 4512 insertAdvice : function(elm, advice){ 4513 var container = $(elm).up('.field-row'); 4514 if(container){ 4515 Element.insert(container, {after: advice}); 4516 } else if (elm.up('td.value')) { 4517 elm.up('td.value').insert({bottom: advice}); 4518 } else if (elm.advaiceContainer && $(elm.advaiceContainer)) { 4519 $(elm.advaiceContainer).update(advice); 4520 } 4521 else { 4522 switch (elm.type.toLowerCase()) { 4523 case 'checkbox': 4524 case 'radio': 4525 var p = elm.parentNode; 4526 if(p) { 4527 Element.insert(p, {'bottom': advice}); 4528 } else { 4529 Element.insert(elm, {'after': advice}); 4530 } 4531 break; 4532 default: 4533 Element.insert(elm, {'after': advice}); 4534 } 4535 } 4536 }, 4537 showAdvice : function(elm, advice, adviceName){ 4538 if(!elm.advices){ 4539 elm.advices = new Hash(); 4540 } 4541 else{ 4542 elm.advices.each(function(pair){ 4543 this.hideAdvice(elm, pair.value); 4544 }.bind(this)); 4545 } 4546 elm.advices.set(adviceName, advice); 4547 if(typeof Effect == 'undefined') { 4548 advice.style.display = 'block'; 4549 } else { 4550 if(!advice._adviceAbsolutize) { 4551 new Effect.Appear(advice, {duration : 1 }); 4552 } else { 4553 Position.absolutize(advice); 4554 advice.show(); 4555 advice.setStyle({ 4556 'top':advice._adviceTop, 4557 'left': advice._adviceLeft, 4558 'width': advice._adviceWidth, 4559 'z-index': 1000 4560 }); 4561 advice.addClassName('advice-absolute'); 4562 } 4563 } 4564 }, 4565 hideAdvice : function(elm, advice){ 4566 if(advice != null) advice.hide(); 4567 }, 4568 updateCallback : function(elm, status) { 4569 if (typeof elm.callbackFunction != 'undefined') { 4570 eval(elm.callbackFunction+'(\''+elm.id+'\',\''+status+'\')'); 4571 } 4572 }, 4573 ajaxError : function(elm, errorMsg) { 4574 var name = 'validate-ajax'; 4575 var advice = Validation.getAdvice(name, elm); 4576 if (advice == null) { 4577 advice = this.createAdvice(name, elm, false, errorMsg); 4578 } 4579 this.showAdvice(elm, advice, 'validate-ajax'); 4580 this.updateCallback(elm, 'failed'); 4581 4582 elm.addClassName('validation-failed'); 4583 elm.addClassName('validate-ajax'); 4584 if (Validation.defaultOptions.addClassNameToContainer && Validation.defaultOptions.containerClassName != '') { 4585 var container = elm.up(Validation.defaultOptions.containerClassName); 4586 if (container && this.allowContainerClassName(elm)) { 4587 container.removeClassName('validation-passed'); 4588 container.addClassName('validation-error'); 4589 } 4590 } 4591 }, 4592 allowContainerClassName: function (elm) { 4593 if (elm.type == 'radio' || elm.type == 'checkbox') { 4594 return elm.hasClassName('change-container-classname'); 4595 } 4596 4597 return true; 4598 }, 4599 test : function(name, elm, useTitle) { 4600 var v = Validation.get(name); 4601 var prop = '__advice'+name.camelize(); 4602 try { 4603 if(Validation.isVisible(elm) && !v.test($F(elm), elm)) { 4604 //if(!elm[prop]) { 4605 var advice = Validation.getAdvice(name, elm); 4606 if (advice == null) { 4607 advice = this.createAdvice(name, elm, useTitle); 4608 } 4609 this.showAdvice(elm, advice, name); 4610 this.updateCallback(elm, 'failed'); 4611 //} 4612 elm[prop] = 1; 4613 if (!elm.advaiceContainer) { 4614 elm.removeClassName('validation-passed'); 4615 elm.addClassName('validation-failed'); 4616 } 4617 4618 if (Validation.defaultOptions.addClassNameToContainer && Validation.defaultOptions.containerClassName != '') { 4619 var container = elm.up(Validation.defaultOptions.containerClassName); 4620 if (container && this.allowContainerClassName(elm)) { 4621 container.removeClassName('validation-passed'); 4622 container.addClassName('validation-error'); 4623 } 4624 } 4625 return false;
4626 } else { 4627 var advice = Validation.getAdvice(name, elm); 4628 this.hideAdvice(elm, advice); 4629 this.updateCallback(elm, 'passed'); 4630 elm[prop] = ''; 4631 elm.removeClassName('validation-failed'); 4632 elm.addClassName('validation-passed'); 4633 if (Validation.defaultOptions.addClassNameToContainer && Validation.defaultOptions.containerClassName != '') { 4634 var container = elm.up(Validation.defaultOptions.containerClassName); 4635 if (container && !container.down('.validation-failed') && this.allowContainerClassName(elm)) { 4636 if (!Validation.get('IsEmpty').test(elm.value) || !this.isVisible(elm)) { 4637 container.addClassName('validation-passed'); 4638 } else { 4639 container.removeClassName('validation-passed'); 4640 } 4641 container.removeClassName('validation-error'); 4642 } 4643 } 4644 return true; 4645 } 4646 } catch(e) { 4647 throw(e) 4648 } 4649 }, 4650 isVisible : function(elm) { 4651 while(elm.tagName != 'BODY') { 4652 if(!$(elm).visible()) return false; 4653 elm = elm.parentNode; 4654 } 4655 return true; 4656 }, 4657 getAdvice : function(name, elm) { 4658 return $('advice-' + name + '-' + Validation.getElmID(elm)) || $('advice-' + Validation.getElmID(elm)); 4659 }, 4660 createAdvice : function(name, elm, useTitle, customError) { 4661 var v = Validation.get(name); 4662 var errorMsg = useTitle ? ((elm && elm.title) ? elm.title : v.error) : v.error; 4663 if (customError) { 4664 errorMsg = customError; 4665 } 4666 try { 4667 if (Translator){ 4668 errorMsg = Translator.translate(errorMsg); 4669 } 4670 } 4671 catch(e){} 4672 4673 advice = '<div class="validation-advice" id="advice-' + name + '-' + Validation.getElmID(elm) +'" style="display:none">' + errorMsg + '</div>' 4674 4675 4676 Validation.insertAdvice(elm, advice); 4677 advice = Validation.getAdvice(name, elm); 4678 if($(elm).hasClassName('absolute-advice')) { 4679 var dimensions = $(elm).getDimensions(); 4680 var originalPosition = Position.cumulativeOffset(elm); 4681 4682 advice._adviceTop = (originalPosition[1] + dimensions.height) + 'px'; 4683 advice._adviceLeft = (originalPosition[0]) + 'px'; 4684 advice._adviceWidth = (dimensions.width) + 'px'; 4685 advice._adviceAbsolutize = true; 4686 } 4687 return advice; 4688 }, 4689 getElmID : function(elm) { 4690 return elm.id ? elm.id : elm.name; 4691 }, 4692 reset : function(elm) { 4693 elm = $(elm); 4694 var cn = $w(elm.className); 4695 cn.each(function(value) { 4696 var prop = '__advice'+value.camelize(); 4697 if(elm[prop]) { 4698 var advice = Validation.getAdvice(value, elm); 4699 if (advice) { 4700 advice.hide(); 4701 } 4702 elm[prop] = ''; 4703 } 4704 elm.removeClassName('validation-failed'); 4705 elm.removeClassName('validation-passed'); 4706 if (Validation.defaultOptions.addClassNameToContainer && Validation.defaultOptions.containerClassName != '') { 4707 var container = elm.up(Validation.defaultOptions.containerClassName); 4708 if (container) { 4709 container.removeClassName('validation-passed'); 4710 container.removeClassName('validation-error'); 4711 } 4712 } 4713 }); 4714 }, 4715 add : function(className, error, test, options) { 4716 var nv = {}; 4717 nv[className] = new Validator(className, error, test, options); 4718 Object.extend(Validation.methods, nv); 4719 }, 4720 addAllThese : function(validators) { 4721 var nv = {}; 4722 $A(validators).each(function(value) { 4723 nv[value[0]] = new Validator(value[0], value[1], value[2], (value.length > 3 ? value[3] : {})); 4724 }); 4725 Object.extend(Validation.methods, nv); 4726 }, 4727 get : function(name) { 4728 return Validation.methods[name] ? Validation.methods[name] : Validation.methods['_LikeNoIDIEverSaw_']; 4729 }, 4730 methods : { 4731 '_LikeNoIDIEverSaw_' : new Validator('_LikeNoIDIEverSaw_','',{}) 4732 } 4733}); 4734 4735Validation.add('IsEmpty', '', function(v) { 4736 return (v == '' || (v == null) || (v.length == 0) || /^\s+$/.test(v)); // || /^\s+$/.test(v)); 4737}); 4738 4739Validation.addAllThese([ 4740 ['validate-select', 'Please select an option.', function(v) { 4741 return ((v != "none") && (v != null) && (v.length != 0)); 4742 }], 4743 ['required-entry', 'This is a required field.', function(v) { 4744 return !Validation.get('IsEmpty').test(v); 4745 }], 4746 ['validate-number', 'Please enter a valid number in this field.', function(v) { 4747 return Validation.get('IsEmpty').test(v) || (!isNaN(parseNumber(v)) && !/^\s+$/.test(parseNumber(v))); 4748 }], 4749 ['validate-digits', 'Please use numbers only in this field. please avoid spaces or other characters such as dots or commas.', function(v) { 4750 return Validation.get('IsEmpty').test(v) || !/[^\d]/.test(v); 4751 }], 4752 ['validate-alpha', 'Please use letters only (a-z or A-Z) in this field.', function (v) { 4753 return Validation.get('IsEmpty').test(v) || /^[a-zA-Z]+$/.test(v) 4754 }], 4755 ['validate-code', 'Please use only letters (a-z), numbers (0-9) or underscore(_) in this field, first character should be a letter.', function (v) { 4756 return Validation.get('IsEmpty').test(v) || /^[a-z]+[a-z0-9_]+$/.test(v) 4757 }], 4758 ['validate-alphanum', 'Please use only letters (a-z or A-Z) or numbers (0-9) only in this field. No spaces or other characters are allowed.', function(v) { 4759 return Validation.get('IsEmpty').test(v) || /^[a-zA-Z0-9]+$/.test(v) /*!/\W/.test(v)*/ 4760 }], 4761 ['validate-street', 'Please use only letters (a-z or A-Z) or numbers (0-9) or spaces and # only in this field.', function(v) { 4762 return Validation.get('IsEmpty').test(v) || /^[ \w]{3,}([A-Za-z]\.)?([ \w]*\#\d+)?(\r\n| )[ \w]{3,}/.test(v) 4763 }], 4764 ['validate-phoneStrict', 'Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890.', function(v) { 4765 return Validation.get('IsEmpty').test(v) || /^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/.test(v); 4766 }], 4767 ['validate-phoneLax', 'Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890.', function(v) { 4768 return Validation.get('IsEmpty').test(v) || /^((\d[-. ]?)?((\(\d{3}\))|\d{3}))?[-. ]?\d{3}[-. ]?\d{4}$/.test(v); 4769 }], 4770 ['validate-fax', 'Please enter a valid fax number. For example (123) 456-7890 or 123-456-7890.', function(v) { 4771 return Validation.get('IsEmpty').test(v) || /^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/.test(v); 4772 }], 4773 ['validate-date', 'Please enter a valid date.', function(v) { 4774 var test = new Date(v); 4775 return Validation.get('IsEmpty').test(v) || !isNaN(test); 4776 }], 4777 ['validate-email', 'Please enter a valid email address. For example [email protected].', function (v) { 4778 //return Validation.get('IsEmpty').test(v) || /\w{1,}[@][\w\-]{1,}([.]([\w\-]{1,})){1,3}$/.test(v) 4779 //return Validation.get('IsEmpty').test(v) || /^[\!\#$%\*/?|\^\{\}`~&\'\+\-=_a-z0-9][\!\#$%\*/?|\^\{\}`~&\'\+\-=_a-z0-9\.]{1,30}[\!\#$%\*/?|\^\{\}`~&\'\+\-=_a-z0-9]@([a-z0-9_-]{1,30}\.){1,5}[a-z]{2,4}$/i.test(v) 4780 return Validation.get('IsEmpty').test(v) || /^[a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]+(\.[a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*\.([a-z]{2,})/i.test(v) 4781 }], 4782 ['validate-emailSender', 'Please use only visible characters and spaces.', function (v) { 4783 return Validation.get('IsEmpty').test(v) || /^[\S ]+$/.test(v) 4784 }], 4785 ['validate-password', 'Please enter 6 or more characters. Leading or trailing space
4785s will be ignored.', function(v) { 4786 var pass=v.strip(); /*strip leading and trailing spaces*/ 4787 return !(pass.length>0 && pass.length < 6); 4788 }], 4789 ['validate-admin-password', 'Please enter 7 or more characters. Password should contain both numeric and alphabetic characters.', function(v) { 4790 var pass=v.strip(); 4791 if (0 == pass.length) { 4792 return true; 4793 } 4794 if (!(/[a-z]/i.test(v)) || !(/[0-9]/.test(v))) { 4795 return false; 4796 } 4797 return !(pass.length < 7); 4798 }], 4799 ['validate-cpassword', 'Please make sure your passwords match.', function(v) { 4800 var conf = $('confirmation') ? $('confirmation') : $$('.validate-cpassword')[0]; 4801 var pass = false; 4802 if ($('password')) { 4803 pass = $('password'); 4804 } 4805 var passwordElements = $$('.validate-password'); 4806 for (var i = 0; i < passwordElements.size(); i++) { 4807 var passwordElement = passwordElements[i]; 4808 if (passwordElement.up('form').id == conf.up('form').id) { 4809 pass = passwordElement; 4810 } 4811 } 4812 if ($$('.validate-admin-password').size()) { 4813 pass = $$('.validate-admin-password')[0]; 4814 } 4815 return (pass.value == conf.value); 4816 }], 4817 ['validate-url', 'Please enter a valid URL. http:// is required', function (v) { 4818 return Validation.get('IsEmpty').test(v) || /^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i.test(v) 4819 }], 4820 ['validate-clean-url', 'Please enter a valid URL. For example http://www.example.com or www.example.com', function (v) { 4821 return Validation.get('IsEmpty').test(v) || /^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\d+))?\/?/i.test(v) || /^(www)((\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\d+))?\/?/i.test(v) 4822 }], 4823 ['validate-identifier', 'Please enter a valid URL Key. For example "example-page", "example-page.html" or "anotherlevel/example-page"', function (v) { 4824 return Validation.get('IsEmpty').test(v) || /^[A-Z0-9][A-Z0-9_\/-]+(\.[A-Z0-9_-]+)*$/i.test(v) 4825 }], 4826 ['validate-xml-identifier', 'Please enter a valid XML-identifier. For example something_1, block5, id-4', function (v) { 4827 return Validation.get('IsEmpty').test(v) || /^[A-Z][A-Z0-9_\/-]*$/i.test(v) 4828 }], 4829 ['validate-ssn', 'Please enter a valid social security number. For example 123-45-6789.', function(v) { 4830 return Validation.get('IsEmpty').test(v) || /^\d{3}-?\d{2}-?\d{4}$/.test(v); 4831 }], 4832 ['validate-zip', 'Please enter a valid zip code. For example 90602 or 90602-1234.', function(v) { 4833 return Validation.get('IsEmpty').test(v) || /(^\d{5}$)|(^\d{5}-\d{4}$)/.test(v); 4834 }], 4835 ['validate-zip-international', 'Please enter a valid zip code.', function(v) { 4836 //return Validation.get('IsEmpty').test(v) || /(^[A-z0-9]{2,10}([\s]{0,1}|[\-]{0,1})[A-z0-9]{2,10}$)/.test(v); 4837 return true; 4838 }], 4839 ['validate-date-au', 'Please use this date format: dd/mm/yyyy. For example 17/03/2006 for the 17th of March, 2006.', function(v) { 4840 if(Validation.get('IsEmpty').test(v)) return true; 4841 var regex = /^(\d{2})\/(\d{2})\/(\d{4})$/; 4842 if(!regex.test(v)) return false; 4843 var d = new Date(v.replace(regex, '$2/$1/$3')); 4844 return ( parseInt(RegExp.$2, 10) == (1+d.getMonth()) ) && 4845 (parseInt(RegExp.$1, 10) == d.getDate()) && 4846 (parseInt(RegExp.$3, 10) == d.getFullYear() ); 4847 }], 4848 ['validate-currency-dollar', 'Please enter a valid $ amount. For example $100.00.', function(v) { 4849 // [$]1[##][,###]+[.##] 4850 // [$]1###+[.##] 4851 // [$]0.## 4852 // [$].## 4853 return Validation.get('IsEmpty').test(v) || /^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/.test(v) 4854 }], 4855 ['validate-one-required', 'Please select one of the above options.', function (v,elm) { 4856 var p = elm.parentNode; 4857 var options = p.getElementsByTagName('INPUT'); 4858 return $A(options).any(function(elm) { 4859 return $F(elm); 4860 }); 4861 }], 4862 ['validate-one-required-by-name', 'Please select one of the options.', function (v,elm) { 4863 var inputs = $$('input[name="' + elm.name.replace(/([\\"])/g, '\\$1') + '"]'); 4864 4865 var error = 1; 4866 for(var i=0;i<inputs.length;i++) { 4867 if((inputs[i].type == 'checkbox' || inputs[i].type == 'radio') && inputs[i].checked == true) { 4868 error = 0; 4869 } 4870 4871 if(Validation.isOnChange && (inputs[i].type == 'checkbox' || inputs[i].type == 'radio')) { 4872 Validation.reset(inputs[i]); 4873 } 4874 } 4875 4876 if( error == 0 ) { 4877 return true; 4878 } else { 4879 return false;
4880 } 4881 }], 4882 ['validate-not-negative-number', 'Please enter a valid number in this field.', function(v) { 4883 v = parseNumber(v); 4884 return (!isNaN(v) && v>=0); 4885 }], 4886 ['validate-state', 'Please select State/Province.', function(v) { 4887 return (v!=0 || v == ''); 4888 }], 4889 4890 ['validate-new-password', 'Please enter 6 or more characters. Leading or trailing spaces will be ignored.', function(v) { 4891 if (!Validation.get('validate-password').test(v)) return false; 4892 if (Validation.get('IsEmpty').test(v) && v != '') return false; 4893 return true; 4894 }], 4895 ['validate-greater-than-zero', 'Please enter a number greater than 0 in this field.', function(v) { 4896 if(v.length) 4897 return parseFloat(v) > 0; 4898 else 4899 return true; 4900 }], 4901 ['validate-zero-or-greater', 'Please enter a number 0 or greater in this field.', function(v) { 4902 if(v.length) 4903 return parseFloat(v) >= 0; 4904 else 4905 return true; 4906 }], 4907 ['validate-cc-number', 'Please enter a valid credit card number.', function(v, elm) { 4908 // remove non-numerics 4909 var ccTypeContainer = $(elm.id.substr(0,elm.id.indexOf('_cc_number')) + '_cc_type'); 4910 if (ccTypeContainer && typeof Validation.creditCartTypes.get(ccTypeContainer.value) != 'undefined' 4911 && Validation.creditCartTypes.get(ccTypeContainer.value)[2] == false) { 4912 if (!Validation.get('IsEmpty').test(v) && Validation.get('validate-digits').test(v)) { 4913 return true; 4914 } else { 4915 return false; 4916 } 4917 } 4918 return validateCreditCard(v); 4919 }], 4920 ['validate-cc-type', 'Credit card number doesn\'t match credit card type', function(v, elm) { 4921 // remove credit card number delimiters such as "-" and space 4922 elm.value = removeDelimiters(elm.value); 4923 v = removeDelimiters(v); 4924 4925 var ccTypeContainer = $(elm.id.substr(0,elm.id.indexOf('_cc_number')) + '_cc_type'); 4926 if (!ccTypeContainer) { 4927 return true; 4928 } 4929 var ccType = ccTypeContainer.value; 4930 4931 if (typeof Validation.creditCartTypes.get(ccType) == 'undefined') { 4932 return false; 4933 } 4934 4935 // Other card type or switch or solo card 4936 if (Validation.creditCartTypes.get(ccType)[0]==false) { 4937 return true; 4938 } 4939 4940 // Matched credit card type 4941 var ccMatchedType = ''; 4942 4943 Validation.creditCartTypes.each(function (pair) { 4944 if (pair.value[0] && v.match(pair.value[0])) { 4945 ccMatchedType = pair.key; 4946 throw $break; 4947 } 4948 }); 4949 4950 if(ccMatchedType != ccType) { 4951 return false; 4952 } 4953 4954 if (ccTypeContainer.hasClassName('validation-failed') && Validation.isOnChange) { 4955 Validation.validate(ccTypeContainer); 4956 } 4957 4958 return true; 4959 }], 4960 ['validate-cc-type-select', 'Card type doesn\'t match credit card number', function(v, elm) { 4961 var ccNumberContainer = $(elm.id.substr(0,elm.id.indexOf('_cc_type')) + '_cc_number'); 4962 if (Validation.isOnChange && Validation.get('IsEmpty').test(ccNumberContainer.value)) { 4963 return true; 4964 } 4965 if (Validation.get('validate-cc-type').test(ccNumberContainer.value, ccNumberContainer)) { 4966 Validation.validate(ccNumberContainer); 4967 } 4968 return Validation.get('validate-cc-type').test(ccNumberContainer.value, ccNumberContainer); 4969 }], 4970 ['validate-cc-exp', 'Incorrect credit card expiration date', function(v, elm) {
4971 var ccExpMonth = v; 4972 var ccExpYear = $(elm.id.substr(0,elm.id.indexOf('_expiration')) + '_expiration_yr').value; 4973 var currentTime = new Date(); 4974 var currentMonth = currentTime.getMonth() + 1; 4975 var currentYear = currentTime.getFullYear(); 4976 if (ccExpMonth < currentMonth && ccExpYear == currentYear) { 4977 return false; 4978 } 4979 return true; 4980 }], 4981 ['validate-cc-cvn', 'Please enter a valid credit card verification number.', function(v, elm) { 4982 var ccTypeContainer = $(elm.id.substr(0,elm.id.indexOf('_cc_cid')) + '_cc_type'); 4983 if (!ccTypeContainer) { 4984 return true; 4985 } 4986 var ccType = ccTypeContainer.value; 4987 4988 if (typeof Validation.creditCartTypes.get(ccType) == 'undefined') { 4989 return false; 4990 } 4991 4992 var re = Validation.creditCartTypes.get(ccType)[1]; 4993 4994 if (v.match(re)) { 4995 return true; 4996 } 4997 4998 return false; 4999 }], 5000 ['validate-ajax', '', function(v, elm) { return true; }], 5001 ['validate-data', 'Please use only letters (a-z or A-Z), numbers (0-9) or underscore(_) in this field, first character should be a letter.', function (v) { 5002 if(v != '' && v) { 5003 return /^[A-Za-z]+[A-Za-z0-9_]+$/.test(v); 5004 } 5005 return true; 5006 }], 5007 ['validate-css-length', 'Please input a valid CSS-length. For example 100px or 77pt or 20em or .5ex or 50%', function (v) { 5008 if (v != '' && v) { 5009 return /^[0-9\.]+(px|pt|em|ex|%)?$/.test(v) && (!(/\..*\./.test(v))) && !(/\.$/.test(v)); 5010 } 5011 return true; 5012 }], 5013 ['validate-length', 'Maximum length exceeded.', function (v, elm) { 5014 var re = new RegExp(/^maximum-length-[0-9]+$/); 5015 var result = true; 5016 $w(elm.className).each(function(name, index) { 5017 if (name.match(re) && result) { 5018 var length = name.split('-')[2]; 5019 result = (v.length <= length); 5020 } 5021 }); 5022 return result; 5023 }], 5024 ['validate-percents', 'Please enter a number lower than 100', {max:100}], 5025 ['validate-captcha', 'Incorrect solution, please try again.', function (v, elm) { 5026 var res = $$('label[for=captcha]')[0].innerText.match(/Solve the problem: (\d) \+ (\d)/); 5027 return parseInt(v) == parseInt(res[1]) + parseInt(res[2]); 5028 }] 5029]); 5030 5031// Credit Card Validation Javascript 5032// copyright 12th May 2003, by Stephen Chapman, Felgall Pty Ltd 5033 5034// You have permission to copy and use this javascript provided that 5035// the content of the script is not changed in any way. 5036 5037function validateCreditCard(s) { 5038 // remove non-numerics 5039 var v = "0123456789"; 5040 var w = ""; 5041 for (i=0; i < s.length; i++) { 5042 x = s.charAt(i); 5043 if (v.indexOf(x,0) != -1) 5044 w += x; 5045 } 5046 // validate number 5047 j = w.length / 2; 5048 k = Math.floor(j); 5049 m = Math.ceil(j) - k; 5050 c = 0; 5051 for (i=0; i<k; i++) { 5052 a = w.charAt(i*2+m) * 2; 5053 c += a > 9 ? Math.floor(a/10 + a%10) : a; 5054 } 5055 for (i=0; i<k+m; i++) c += w.charAt(i*2+1-m) * 1; 5056 return (c%10 == 0); 5057} 5058 5059function removeDelimiters (v) { 5060 v = v.replace(/\s/g, ''); 5061 v = v.replace(/\-/g, ''); 5062 return v; 5063} 5064 5065function parseNumber(v) 5066{ 5067 if (typeof v != 'string') { 5068 return parseFloat(v); 5069 } 5070 5071 var isDot = v.indexOf('.'); 5072 var isComa = v.indexOf(','); 5073 5074 if (isDot != -1 && isComa != -1) { 5075 if (isComa > isDot) { 5076 v = v.replace('.', '').replace(',', '.'); 5077 } 5078 else { 5079 v = v.replace(',', ''); 5080 } 5081 } 5082 else if (isComa != -1) { 5083 v = v.replace(',', '.'); 5084 } 5085 5086 return parseFloat(v); 5087} 5088 5089/** 5090 * Hash with credit card types wich can be simply extended in payment modules 5091 * 0 - regexp for card number 5092 * 1 - regexp for cvn 5093 * 2 - check or not credit card number trough Luhn algorithm by 5094 * function validateCreditCard wich you can find above in this file 5095 */ 5096Validation.creditCartTypes = $H({ 5097 'VI': [new RegExp('^4[0-9]{12}([0-9]{3})?$'), new RegExp('^[0-9]{3}$'), true],
5098 'MC': [new RegExp('^(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))$'), new RegExp('^[0-9]{3}$'), true], 5099 'AE': [new RegExp('^3[47][0-9]{13}$'), new RegExp('^[0-9]{4}$'), true], 5100 'DI': [new RegExp('^6011[0-9]{12}$'), new RegExp('^[0-9]{3}$'), true], 5101 'SS': [new RegExp('^((6759[0-9]{12})|(49[013][1356][0-9]{13})|(633[34][0-9]{12})|(633110[0-9]{10})|(564182[0-9]{10}))([0-9]{2,3})?$'), new RegExp('^([0-9]{3}|[0-9]{4})?$'), true], 5102 'OT': [false, new RegExp('^([0-9]{3}|[0-9]{4})?$'), false] 5103}); 5104 5105 5106 5107// script.aculo.us builder.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008 5108 5109// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) 5110// 5111// script.aculo.us is freely distributable under the terms of an MIT-style license. 5112// For details, see the script.aculo.us web site: http://script.aculo.us/ 5113 5114var Builder = { 5115 NODEMAP: { 5116 AREA: 'map', 5117 CAPTION: 'table', 5118 COL: 'table', 5119 COLGROUP: 'table', 5120 LEGEND: 'fieldset', 5121 OPTGROUP: 'select', 5122 OPTION: 'select', 5123 PARAM: 'object', 5124 TBODY: 'table', 5125 TD: 'table', 5126 TFOOT: 'table', 5127 TH: 'table', 5128 THEAD: 'table', 5129 TR: 'table' 5130 }, 5131 // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken, 5132 // due to a Firefox bug 5133 node: function(elementName) { 5134 elementName = elementName.toUpperCase(); 5135 5136 // try innerHTML approach 5137 var parentTag = this.NODEMAP[elementName] || 'div'; 5138 var parentElement = document.createElement(parentTag); 5139 try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707 5140 parentElement.innerHTML = "<" + elementName + "></" + elementName + ">"; 5141 } catch(e) {} 5142 var element = parentElement.firstChild || null; 5143 5144 // see if browser added wrapping tags 5145 if(element && (element.tagName.toUpperCase() != elementName)) 5146 element = element.getElementsByTagName(elementName)[0]; 5147 5148 // fallback to createElement approach 5149 if(!element) element = document.createElement(elementName); 5150 5151 // abort if nothing could be created 5152 if(!element) return; 5153 5154 // attributes (or text) 5155 if(arguments[1]) 5156 if(this._isStringOrNumber(arguments[1]) || 5157 (arguments[1] instanceof Array) || 5158 arguments[1].tagName) { 5159 this._children(element, arguments[1]); 5160 } else { 5161 var attrs = this._attributes(arguments[1]); 5162 if(attrs.length) { 5163 try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707 5164 parentElement.innerHTML = "<" +elementName + " " + 5165 attrs + "></" + elementName + ">"; 5166 } catch(e) {} 5167 element = parentElement.firstChild || null; 5168 // workaround firefox 1.0.X bug 5169 if(!element) { 5170 element = document.createElement(elementName); 5171 for(attr in arguments[1]) 5172 element[attr == 'class' ? 'className' : attr] = arguments[1][attr]; 5173 } 5174 if(element.tagName.toUpperCase() != elementName) 5175 element = parentElement.getElementsByTagName(elementName)[0]; 5176 } 5177 } 5178 5179 // text, or array of children 5180 if(arguments[2]) 5181 this._children(element, arguments[2]); 5182 5183 return $(element); 5184 }, 5185 _text: function(text) { 5186 return document.createTextNode(text); 5187 }, 5188 5189 ATTR_MAP: { 5190 'className': 'class', 5191 'htmlFor': 'for' 5192 }, 5193 5194 _attributes: function(attributes) { 5195 var attrs = []; 5196 for(attribute in attributes) 5197 attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) + 5198 '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'"') + '"'); 5199 return attrs.join(" "); 5200 }, 5201 _children: function(element, children) { 5202 if(children.tagName) { 5203 element.appendChild(children); 5204 return; 5205 } 5206 if(typeof children=='object') { // array can hold nodes and text 5207 children.flatten().each( function(e) { 5208 if(typeof e=='object') 5209 element.appendChild(e); 5210 else 5211 if(Builder._isStringOrNumber(e)) 5212 element.appendChild(Builder._text(e)); 5213 }); 5214 } else 5215 if(Builder._isStringOrNumber(children)) 5216 element.appendChild(Builder._text(children)); 5217 }, 5218 _isStringOrNumber: function(param) {
5219 return(typeof param=='string' || typeof param=='number'); 5220 }, 5221 build: function(html) { 5222 var element = this.node('div'); 5223 $(element).update(html.strip()); 5224 return element.down(); 5225 }, 5226 dump: function(scope) { 5227 if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope 5228 5229 var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " + 5230 "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " + 5231 "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+ 5232 "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+ 5233 "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+ 5234 "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/); 5235 5236 tags.each( function(tag){ 5237 scope[tag] = function() { 5238 return Builder.node.apply(Builder, [tag].concat($A(arguments))); 5239 }; 5240 }); 5241 } 5242}; 5243// script.aculo.us effects.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008 5244 5245// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) 5246// Contributors: 5247// Justin Palmer (http://encytemedia.com/) 5248// Mark Pilgrim (http://diveintomark.org/) 5249// Martin Bialasinki 5250// 5251// script.aculo.us is freely distributable under the terms of an MIT-style license. 5252// For details, see the script.aculo.us web site: http://script.aculo.us/ 5253 5254// converts rgb() and #xxx to #xxxxxx format, 5255// returns self (or first argument) if not convertable 5256String.prototype.parseColor = function() { 5257 var color = '#'; 5258 if (this.slice(0,4) == 'rgb(') { 5259 var cols = this.slice(4,this.length-1).split(','); 5260 var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); 5261 } else { 5262 if (this.slice(0,1) == '#') { 5263 if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); 5264 if (this.length==7) color = this.toLowerCase(); 5265 } 5266 } 5267 return (color.length==7 ? color : (arguments[0] || this)); 5268}; 5269 5270/*--------------------------------------------------------------------------*/ 5271 5272Element.collectTextNodes = function(element) { 5273 return $A($(element).childNodes).collect( function(node) { 5274 return (node.nodeType==3 ? node.nodeValue : 5275 (node.hasChildNodes() ? Element.collectTextNodes(node) : '')); 5276 }).flatten().join(''); 5277}; 5278 5279Element.collectTextNodesIgnoreClass = function(element, className) { 5280 return $A($(element).childNodes).collect( function(node) { 5281 return (node.nodeType==3 ? node.nodeValue : 5282 ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 5283 Element.collectTextNodesIgnoreClass(node, className) : '')); 5284 }).flatten().join(''); 5285}; 5286 5287Element.setContentZoom = function(element, percent) { 5288 element = $(element); 5289 element.setStyle({fontSize: (percent/100) + 'em'}); 5290 if (Prototype.Browser.WebKit) window.scrollBy(0,0); 5291 return element; 5292}; 5293 5294Element.getInlineOpacity = function(element){ 5295 return $(element).style.opacity || ''; 5296}; 5297 5298Element.forceRerendering = function(element) { 5299 try { 5300 element = $(element); 5301 var n = document.createTextNode(' '); 5302 element.appendChild(n); 5303 element.removeChild(n); 5304 } catch(e) { } 5305}; 5306 5307/*--------------------------------------------------------------------------*/ 5308 5309var Effect = { 5310 _elementDoesNotExistError: { 5311 name: 'ElementDoesNotExistError', 5312 message: 'The specified DOM element does not exist, but is required for this effect to operate' 5313 }, 5314 Transitions: { 5315 linear: Prototype.K, 5316 sinoidal: function(pos) { 5317 return (-Math.cos(pos*Math.PI)/2) + .5; 5318 }, 5319 reverse: function(pos) { 5320 return 1-pos; 5321 }, 5322 flicker: function(pos) { 5323 var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4; 5324 return pos > 1 ? 1 : pos; 5325 }, 5326 wobble: function(pos) { 5327 return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5; 5328 }, 5329 pulse: function(pos, pulses) { 5330 return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5; 5331 }, 5332 spring: function(pos) { 5333 return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); 5334 }, 5335 none: function(pos) { 5336 return 0; 5337 }, 5338 full: function(pos) { 5339 return 1; 5340 } 5341 }, 5342 DefaultOptions: { 5343 duration: 1.0, // seconds 5344 fps: 100, // 100= assume 66fps max. 5345 sync: false, // true for combining 5346 from: 0.0, 5347 to: 1.0, 5348 delay: 0.0, 5349 queue: 'parallel' 5350 }, 5351 tagifyText: function(element) { 5352 var tagifyStyle = 'position:relative'; 5353 if (Prototype.Browser.IE) tagifyStyle += ';zoom:1'; 5354 5355 element = $(element); 5356 $A(element.childNodes).each( function(child) { 5357 if (child.nodeType==3) {
5358 child.nodeValue.toArray().each( function(character) { 5359 element.insertBefore( 5360 new Element('span', {style: tagifyStyle}).update( 5361 character == ' ' ? String.fromCharCode(160) : character), 5362 child); 5363 }); 5364 Element.remove(child); 5365 } 5366 }); 5367 }, 5368 multiple: function(element, effect) { 5369 var elements; 5370 if (((typeof element == 'object') || 5371 Object.isFunction(element)) && 5372 (element.length)) 5373 elements = element; 5374 else 5375 elements = $(element).childNodes; 5376 5377 var options = Object.extend({ 5378 speed: 0.1, 5379 delay: 0.0 5380 }, arguments[2] || { }); 5381 var masterDelay = options.delay; 5382 5383 $A(elements).each( function(element, index) { 5384 new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay })); 5385 }); 5386 }, 5387 PAIRS: { 5388 'slide': ['SlideDown','SlideUp'], 5389 'blind': ['BlindDown','BlindUp'], 5390 'appear': ['Appear','Fade'] 5391 }, 5392 toggle: function(element, effect) { 5393 element = $(element); 5394 effect = (effect || 'appear').toLowerCase(); 5395 var options = Object.extend({ 5396 queue: { position:'end', scope:(element.id || 'global'), limit: 1 } 5397 }, arguments[2] || { }); 5398 Effect[element.visible() ? 5399 Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options); 5400 } 5401}; 5402 5403Effect.DefaultOptions.transition = Effect.Transitions.sinoidal; 5404 5405/* ------------- core effects ------------- */ 5406 5407Effect.ScopedQueue = Class.create(Enumerable, { 5408 initialize: function() { 5409 this.effects = []; 5410 this.interval = null; 5411 }, 5412 _each: function(iterator) { 5413 this.effects._each(iterator); 5414 }, 5415 add: function(effect) { 5416 var timestamp = new Date().getTime(); 5417 5418 var position = Object.isString(effect.options.queue) ? 5419 effect.options.queue : effect.options.queue.position; 5420 5421 switch(position) { 5422 case 'front': 5423 // move unstarted effects after this effect 5424 this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { 5425 e.startOn += effect.finishOn; 5426 e.finishOn += effect.finishOn; 5427 }); 5428 break; 5429 case 'with-last': 5430 timestamp = this.effects.pluck('startOn').max() || timestamp; 5431 break; 5432 case 'end': 5433 // start effect after last queued effect has finished 5434 timestamp = this.effects.pluck('finishOn').max() || timestamp; 5435 break; 5436 } 5437 5438 effect.startOn += timestamp; 5439 effect.finishOn += timestamp; 5440 5441 if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) 5442 this.effects.push(effect); 5443 5444 if (!this.interval) 5445 this.interval = setInterval(this.loop.bind(this), 15); 5446 }, 5447 remove: function(effect) { 5448 this.effects = this.effects.reject(function(e) { return e==effect }); 5449 if (this.effects.length == 0) { 5450 clearInterval(this.interval); 5451 this.interval = null; 5452 } 5453 }, 5454 loop: function() { 5455 var timePos = new Date().getTime(); 5456 for(var i=0, len=this.effects.length;i<len;i++) 5457 this.effects[i] && this.effects[i].loop(timePos); 5458 } 5459}); 5460 5461Effect.Queues = { 5462 instances: $H(), 5463 get: function(queueName) { 5464 if (!Object.isString(queueName)) return queueName; 5465 5466 return this.instances.get(queueName) || 5467 this.instances.set(queueName, new Effect.ScopedQueue()); 5468 } 5469}; 5470Effect.Queue = Effect.Queues.get('global'); 5471 5472Effect.Base = Class.create({ 5473 position: null, 5474 start: function(options) { 5475 function codeForEvent(options,eventName){ 5476 return ( 5477 (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') + 5478 (options[eventName] ? 'this.options.'+eventName+'(this);' : '') 5479 ); 5480 } 5481 if (options && options.transition === false) options.transition = Effect.Transitions.linear; 5482 this.options = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { }); 5483 this.currentFrame = 0; 5484 this.state = 'idle'; 5485 this.startOn = this.options.delay*1000; 5486 this.finishOn = this.startOn+(this.options.duration*1000); 5487 this.fromToDelta = this.options.to-this.options.from; 5488 this.totalTime = this.finishOn-this.startOn; 5489 this.totalFrames = this.options.fps*this.options.duration; 5490 5491 this.render = (function() { 5492 function dispatch(effect, eventName) { 5493 if (effect.options[eventName + 'Internal']) 5494 effect.options[eventName + 'Internal'](effect); 5495 if (effect.options[eventName]) 5496 effect.options[eventName](effect); 5497 } 5498 5499 return function(pos) {
5500 if (this.state === "idle") { 5501 this.state = "running"; 5502 dispatch(this, 'beforeSetup'); 5503 if (this.setup) this.setup(); 5504 dispatch(this, 'afterSetup'); 5505 } 5506 if (this.state === "running") { 5507 pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from; 5508 this.position = pos; 5509 dispatch(this, 'beforeUpdate'); 5510 if (this.update) this.update(pos); 5511 dispatch(this, 'afterUpdate'); 5512 } 5513 }; 5514 })(); 5515 5516 this.event('beforeStart'); 5517 if (!this.options.sync) 5518 Effect.Queues.get(Object.isString(this.options.queue) ? 5519 'global' : this.options.queue.scope).add(this); 5520 }, 5521 loop: function(timePos) { 5522 if (timePos >= this.startOn) { 5523 if (timePos >= this.finishOn) { 5524 this.render(1.0); 5525 this.cancel(); 5526 this.event('beforeFinish'); 5527 if (this.finish) this.finish(); 5528 this.event('afterFinish'); 5529 return; 5530 } 5531 var pos = (timePos - this.startOn) / this.totalTime, 5532 frame = (pos * this.totalFrames).round(); 5533 if (frame > this.currentFrame) { 5534 this.render(pos); 5535 this.currentFrame = frame; 5536 } 5537 } 5538 }, 5539 cancel: function() { 5540 if (!this.options.sync) 5541 Effect.Queues.get(Object.isString(this.options.queue) ? 5542 'global' : this.options.queue.scope).remove(this); 5543 this.state = 'finished'; 5544 }, 5545 event: function(eventName) { 5546 if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); 5547 if (this.options[eventName]) this.options[eventName](this); 5548 }, 5549 inspect: function() { 5550 var data = $H(); 5551 for(property in this) 5552 if (!Object.isFunction(this[property])) data.set(property, this[property]); 5553 return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>'; 5554 } 5555}); 5556 5557Effect.Parallel = Class.create(Effect.Base, { 5558 initialize: function(effects) { 5559 this.effects = effects || []; 5560 this.start(arguments[1]); 5561 }, 5562 update: function(position) { 5563 this.effects.invoke('render', position); 5564 }, 5565 finish: function(position) { 5566 this.effects.each( function(effect) { 5567 effect.render(1.0); 5568 effect.cancel(); 5569 effect.event('beforeFinish'); 5570 if (effect.finish) effect.finish(position); 5571 effect.event('afterFinish'); 5572 }); 5573 } 5574}); 5575 5576Effect.Tween = Class.create(Effect.Base, { 5577 initialize: function(object, from, to) { 5578 object = Object.isString(object) ? $(object) : object; 5579 var args = $A(arguments), method = args.last(), 5580 options = args.length == 5 ? args[3] : null; 5581 this.method = Object.isFunction(method) ? method.bind(object) : 5582 Object.isFunction(object[method]) ? object[method].bind(object) : 5583 function(value) { object[method] = value }; 5584 this.start(Object.extend({ from: from, to: to }, options || { })); 5585 }, 5586 update: function(position) { 5587 this.method(position); 5588 } 5589}); 5590 5591Effect.Event = Class.create(Effect.Base, { 5592 initialize: function() { 5593 this.start(Object.extend({ duration: 0 }, arguments[0] || { })); 5594 }, 5595 update: Prototype.emptyFunction 5596}); 5597 5598Effect.Opacity = Class.create(Effect.Base, { 5599 initialize: function(element) { 5600 this.element = $(element); 5601 if (!this.element) throw(Effect._elementDoesNotExistError); 5602 // make this work on IE on elements without 'layout' 5603 if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) 5604 this.element.setStyle({zoom: 1}); 5605 var options = Object.extend({ 5606 from: this.element.getOpacity() || 0.0, 5607 to: 1.0 5608 }, arguments[1] || { }); 5609 this.start(options); 5610 }, 5611 update: function(position) { 5612 this.element.setOpacity(position); 5613 } 5614}); 5615 5616Effect.Move = Class.create(Effect.Base, { 5617 initialize: function(element) { 5618 this.element = $(element); 5619 if (!this.element) throw(Effect._elementDoesNotExistError); 5620 var options = Object.extend({ 5621 x: 0, 5622 y: 0, 5623 mode: 'relative' 5624 }, arguments[1] || { }); 5625 this.start(options); 5626 }, 5627 setup: function() { 5628 this.element.makePositioned(); 5629 this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); 5630 this.originalTop = parseFloat(this.element.getStyle('top') || '0'); 5631 if (this.options.mode == 'absolute') { 5632 this.options.x = this.options.x - this.originalLeft; 5633 this.options.y = this.options.y - this.originalTop; 5634 } 5635 }, 5636 update: function(position) { 5637 this.element.setStyle({ 5638 left: (this.options.x * position + this.originalLeft).round() + 'px', 5639 top: (this.options.y * position + this.originalTop).round() + 'px' 5640 }); 5641 } 5642}); 5643 5644// for backwards compatibility 5645Effect.MoveBy = function(element, toTop, toLeft) { 5646 return new Effect.Move(element,
5647 Object.extend({ x: toLeft, y: toTop }, arguments[3] || { })); 5648}; 5649 5650Effect.Scale = Class.create(Effect.Base, { 5651 initialize: function(element, percent) { 5652 this.element = $(element); 5653 if (!this.element) throw(Effect._elementDoesNotExistError); 5654 var options = Object.extend({ 5655 scaleX: true, 5656 scaleY: true, 5657 scaleContent: true, 5658 scaleFromCenter: false, 5659 scaleMode: 'box', // 'box' or 'contents' or { } with provided values 5660 scaleFrom: 100.0, 5661 scaleTo: percent 5662 }, arguments[2] || { }); 5663 this.start(options); 5664 }, 5665 setup: function() { 5666 this.restoreAfterFinish = this.options.restoreAfterFinish || false; 5667 this.elementPositioning = this.element.getStyle('position'); 5668 5669 this.originalStyle = { }; 5670 ['top','left','width','height','fontSize'].each( function(k) { 5671 this.originalStyle[k] = this.element.style[k]; 5672 }.bind(this)); 5673 5674 this.originalTop = this.element.offsetTop; 5675 this.originalLeft = this.element.offsetLeft; 5676 5677 var fontSize = this.element.getStyle('font-size') || '100%'; 5678 ['em','px','%','pt'].each( function(fontSizeType) { 5679 if (fontSize.indexOf(fontSizeType)>0) { 5680 this.fontSize = parseFloat(fontSize); 5681 this.fontSizeType = fontSizeType; 5682 } 5683 }.bind(this)); 5684 5685 this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; 5686 5687 this.dims = null; 5688 if (this.options.scaleMode=='box') 5689 this.dims = [this.element.offsetHeight, this.element.offsetWidth]; 5690 if (/^content/.test(this.options.scaleMode)) 5691 this.dims = [this.element.scrollHeight, this.element.scrollWidth]; 5692 if (!this.dims) 5693 this.dims = [this.options.scaleMode.originalHeight, 5694 this.options.scaleMode.originalWidth]; 5695 }, 5696 update: function(position) { 5697 var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); 5698 if (this.options.scaleContent && this.fontSize) 5699 this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType }); 5700 this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); 5701 }, 5702 finish: function(position) { 5703 if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle); 5704 }, 5705 setDimensions: function(height, width) { 5706 var d = { }; 5707 if (this.options.scaleX) d.width = width.round() + 'px'; 5708 if (this.options.scaleY) d.height = height.round() + 'px'; 5709 if (this.options.scaleFromCenter) { 5710 var topd = (height - this.dims[0])/2; 5711 var leftd = (width - this.dims[1])/2; 5712 if (this.elementPositioning == 'absolute') { 5713 if (this.options.scaleY) d.top = this.originalTop-topd + 'px'; 5714 if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px'; 5715 } else { 5716 if (this.options.scaleY) d.top = -topd + 'px'; 5717 if (this.options.scaleX) d.left = -leftd + 'px'; 5718 } 5719 } 5720 this.element.setStyle(d); 5721 } 5722}); 5723 5724Effect.Highlight = Class.create(Effect.Base, { 5725 initialize: function(element) { 5726 this.element = $(element); 5727 if (!this.element) throw(Effect._elementDoesNotExistError); 5728 var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { }); 5729 this.start(options); 5730 }, 5731 setup: function() { 5732 // Prevent executing on elements not in the layout flow 5733 if (this.element.getStyle('display')=='none') { this.cancel(); return; } 5734 // Disable background image during the effect 5735 this.oldStyle = { }; 5736 if (!this.options.keepBackgroundImage) { 5737 this.oldStyle.backgroundImage = this.element.getStyle('background-image'); 5738 this.element.setStyle({backgroundImage: 'none'}); 5739 } 5740 if (!this.options.endcolor) 5741 this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff'); 5742 if (!this.options.restorecolor) 5743 this.options.restorecolor = this.element.getStyle('background-color'); 5744 // init color calculations 5745 this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this)); 5746 this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this)); 5747 }, 5748 update: function(position) { 5749 this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){ 5750 return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) }); 5751 }, 5752 finish: function() { 5753 this.element.setStyle(Object.extend(this.oldStyle, { 5754 backgroundColor: this.options.restorecolor 5755 })); 5756 } 5757}); 5758 5759Effect.ScrollTo = function(element) { 5760 var options = arguments[1] || { }, 5761 scrollOffsets = document.viewport.getScrollOffsets(), 5762 elementOffsets = $(element).cumulativeOffset(); 5763 5764 if (options.offset) elementOffsets[1] += options.offset; 5765 5766 return new Effect.Tween(null, 5767 scrollOffsets.top, 5768 elementOffsets[1], 5769 options, 5770 function(p){ scrollTo(scrollOffsets.left, p.round()); } 5771 ); 5772}; 5773 5774/* ------------- combination effects ------------- */ 5775 5776Effect.Fade = function(element) { 5777 element = $(element); 5778 var oldOpacity = element.getInlineOpacity(); 5779 var options = Object.extend({ 5780 from: element.getOpacity() || 1.0, 5781 to: 0.0, 5782 afterFinishInternal: function(effect) { 5783 if (effect.options.to!=0) return; 5784 effect.element.hide().setStyle({opacity: oldOpacity}); 5785 } 5786 }, arguments[1] || { }); 5787 return new Effect.Opacity(element,options); 5788}; 5789 5790Effect.Appear = function(element) { 5791 element = $(element); 5792 var options = Object.extend({ 5793 from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0), 5794 to: 1.0, 5795 // force Safari to render floated elements properly 5796 afterFinishInternal: function(effect) { 5797 effect.element.forceRerendering(); 5798 }, 5799 beforeSetup: function(effect) { 5800 effect.element.setOpacity(effect.options.from).show(); 5801 }}, arguments[1] || { }); 5802 return new Effect.Opacity(element,options); 5803}; 5804 5805Effect.Puff = function(element) { 5806 element = $(element); 5807 var oldStyle = { 5808 opacity: element.getInlineOpacity(), 5809 position: element.getStyle('position'), 5810 top: element.style.top, 5811 left: element.style.left, 5812 width: element.style.width, 5813 height: element.style.height 5814 }; 5815 return new Effect.Parallel( 5816 [ new Effect.Scale(element, 200, 5817 { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 5818 new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 5819 Object.extend({ duration: 1.0, 5820 beforeSetupInternal: function(effect) { 5821 Position.absolutize(effect.effects[0].element); 5822 }, 5823 afterFinishInternal: function(effect) { 5824 effect.effects[0].element.hide().setStyle(oldStyle); } 5825 }, arguments[1] || { }) 5826 ); 5827}; 5828 5829Effect.BlindUp = function(element) { 5830 element = $(element); 5831 element.makeClipping(); 5832 return new Effect.Scale(element, 0, 5833 Object.extend({ scaleContent: false, 5834 scaleX: false, 5835 restoreAfterFinish: true, 5836 afterFinishInternal: function(effect) { 5837 effect.element.hide().undoClipping(); 5838 } 5839 }, arguments[1] || { }) 5840 ); 5841}; 5842 5843Effect.BlindDown = function(element) { 5844 element = $(element); 5845 var elementDimensions = element.getDimensions(); 5846 return new Effect.Scale(element, 100, Object.extend({ 5847 scaleContent: false, 5848 scaleX: false, 5849 scaleFrom: 0, 5850 scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, 5851 restoreAfterFinish: true, 5852 afterSetup: function(effect) { 5853 effect.element.makeClipping().setStyle({height: '0px'}).show(); 5854 }, 5855 afterFinishInternal: function(effect) { 5856 effect.element.undoClipping(); 5857 } 5858 }, arguments[1] || { })); 5859}; 5860 5861Effect.SwitchOff = function(element) { 5862 element = $(element); 5863 var oldOpacity = element.getInlineOpacity(); 5864 return new Effect.Appear(element, Object.extend({ 5865 duration: 0.4, 5866 from: 0, 5867 transition: Effect.Transitions.flicker, 5868 afterFinishInternal: function(effect) { 5869 new Effect.Scale(effect.element, 1, { 5870 duration: 0.3, scaleFromCenter: true, 5871 scaleX: false, scaleContent: false, restoreAfterFinish: true, 5872 beforeSetup: function(effect) { 5873 effect.element.makePositioned().makeClipping(); 5874 }, 5875 afterFinishInternal: function(effect) { 5876 effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity}); 5877 } 5878 }); 5879 } 5880 }, arguments[1] || { })); 5881}; 5882 5883Effect.DropOut = function(element) { 5884 element = $(element); 5885 var oldStyle = { 5886 top: element.getStyle('top'), 5887 left: element.getStyle('left'), 5888 opacity: element.getInlineOpacity() }; 5889 return new Effect.Parallel( 5890 [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 5891 new Effect.Opacity(element, { sync: true, to: 0.0 }) ], 5892 Object.extend( 5893 { duration: 0.5, 5894 beforeSetup: function(effect) { 5895 effect.effects[0].element.makePositioned(); 5896 }, 5897 afterFinishInternal: function(effect) { 5898 effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle); 5899 } 5900 }, arguments[1] || { })); 5901}; 5902 5903Effect.Shake = function(element) { 5904 element = $(element); 5905 var options = Object.extend({ 5906 distance: 20, 5907 duration: 0.5 5908 }, arguments[1] || {}); 5909 var distance = parseFloat(options.distance); 5910 var split = parseFloat(options.duration) / 10.0; 5911 var oldStyle = { 5912 top: element.getStyle('top'), 5913 left: element.getStyle('left') }; 5914 return new Effect.Move(element, 5915 { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) { 5916 new Effect.Move(effect.element, 5917 { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { 5918 new Effect.Move(effect.element, 5919 { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { 5920 new Effect.Move(effect.element, 5921 { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { 5922 new Effect.Move(effect.element, 5923 { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { 5924 new Effect.Move(effect.element, 5925 { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) { 5926 effect.element.undoPositioned().setStyle(oldStyle); 5927 }}); }}); }}); }}); }}); }}); 5928}; 5929 5930Effect.SlideDown = function(element) { 5931 element = $(element).cleanWhitespace(); 5932 // SlideDown need to have the content of the element wrapped in a container element with fixed height! 5933 var oldInnerBottom = element.down().getStyle('bottom'); 5934 var elementDimensions = element.getDimensions(); 5935 return new Effect.Scale(element, 100, Object.extend({ 5936 scaleContent: false, 5937 scaleX: false, 5938 scaleFrom: window.opera ? 0 : 1, 5939 scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, 5940 restoreAfterFinish: true, 5941 afterSetup: function(effect) { 5942 effect.element.makePositioned(); 5943 effect.element.down().makePositioned(); 5944 if (window.opera) effect.element.setStyle({top: ''}); 5945 effect.element.makeClipping().setStyle({height: '0px'}).show(); 5946 }, 5947 afterUpdateInternal: function(effect) { 5948 effect.element.down().setStyle({bottom: 5949 (effect.dims[0] - effect.element.clientHeight) + 'px' }); 5950 }, 5951 afterFinishInternal: function(effect) { 5952 effect.element.undoClipping().undoPositioned(); 5953 effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); } 5954 }, arguments[1] || { }) 5955 ); 5956}; 5957 5958Effect.SlideUp = function(element) { 5959 element = $(element).cleanWhitespace(); 5960 var oldInnerBottom = element.down().getStyle('bottom'); 5961 var elementDimensions = element.getDimensions(); 5962 return new Effect.Scale(element, window.opera ? 0 : 1, 5963 Object.extend({ scaleContent: false, 5964 scaleX: false, 5965 scaleMode: 'box', 5966 scaleFrom: 100, 5967 scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, 5968 restoreAfterFinish: true, 5969 afterSetup: function(effect) { 5970 effect.element.makePositioned(); 5971 effect.element.down().makePositioned(); 5972 if (window.opera) effect.element.setStyle({top: ''}); 5973 effect.element.makeClipping().show(); 5974 }, 5975 afterUpdateInternal: function(effect) { 5976 effect.element.down().setStyle({bottom: 5977 (effect.dims[0] - effect.element.clientHeight) + 'px' }); 5978 }, 5979 afterFinishInternal: function(effect) { 5980 effect.element.hide().undoClipping().undoPositioned(); 5981 effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); 5982 } 5983 }, arguments[1] || { }) 5984 ); 5985}; 5986 5987// Bug in opera makes the TD containing this element expand for a instance after finish 5988Effect.Squish = function(element) { 5989 return new Effect.Scale(element, window.opera ? 1 : 0, { 5990 restoreAfterFinish: true, 5991 beforeSetup: function(effect) { 5992 effect.element.makeClipping(); 5993 }, 5994 afterFinishInternal: function(effect) { 5995 effect.element.hide().undoClipping(); 5996 } 5997 }); 5998}; 5999 6000Effect.Grow = function(element) { 6001 element = $(element); 6002 var options = Object.extend({ 6003 direction: 'center', 6004 moveTransition: Effect.Transitions.sinoidal, 6005 scaleTransition: Effect.Transitions.sinoidal, 6006 opacityTransition: Effect.Transitions.full 6007 }, arguments[1] || { }); 6008 var oldStyle = { 6009 top: element.style.top, 6010 left: element.style.left, 6011 height: element.style.height, 6012 width: element.style.width, 6013 opacity: element.getInlineOpacity() }; 6014 6015 var dims = element.getDimensions(); 6016 var initialMoveX, initialMoveY; 6017 var moveX, moveY; 6018 6019 switch (options.direction) { 6020 case 'top-left': 6021 initialMoveX = initialMoveY = moveX = moveY = 0; 6022 break; 6023 case 'top-right': 6024 initialMoveX = dims.width; 6025 initialMoveY = moveY = 0; 6026 moveX = -dims.width; 6027 break; 6028 case 'bottom-left': 6029 initialMoveX = moveX = 0; 6030 initialMoveY = dims.height; 6031 moveY = -dims.height; 6032 break; 6033 case 'bottom-right': 6034 initialMoveX = dims.width; 6035 initialMoveY = dims.height; 6036 moveX = -dims.width; 6037 moveY = -dims.height; 6038 break; 6039 case 'center': 6040 initialMoveX = dims.width / 2; 6041 initialMoveY = dims.height / 2; 6042 moveX = -dims.width / 2; 6043 moveY = -dims.height / 2; 6044 break; 6045 } 6046 6047 return new Effect.Move(element, { 6048 x: initialMoveX, 6049 y: initialMoveY, 6050 duration: 0.01, 6051 beforeSetup: function(effect) { 6052 effect.element.hide().makeClipping().makePositioned(); 6053 }, 6054 afterFinishInternal: function(effect) { 6055 new Effect.Parallel( 6056 [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }), 6057 new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }), 6058 new Effect.Scale(effect.element, 100, { 6059 scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 6060 sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true}) 6061 ], Object.extend({ 6062 beforeSetup: function(effect) { 6063 effect.effects[0].element.setStyle({height: '0px'}).show(); 6064 }, 6065 afterFinishInternal: function(effect) { 6066 effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); 6067 } 6068 }, options) 6069 ); 6070 } 6071 }); 6072}; 6073 6074Effect.Shrink = function(element) { 6075 element = $(element); 6076 var options = Object.extend({ 6077 direction: 'center', 6078 moveTransition: Effect.Transitions.sinoidal, 6079 scaleTransition: Effect.Transitions.sinoidal, 6080 opacityTransition: Effect.Transitions.none 6081 }, arguments[1] || { }); 6082 var oldStyle = { 6083 top: element.style.top, 6084 left: element.style.left, 6085 height: element.style.height, 6086 width: element.style.width, 6087 opacity: element.getInlineOpacity() }; 6088 6089 var dims = element.getDimensions(); 6090 var moveX, moveY; 6091 6092 switch (options.direction) { 6093 case 'top-left': 6094 moveX = moveY = 0; 6095 break; 6096 case 'top-right': 6097 moveX = dims.width; 6098 moveY = 0; 6099 break; 6100 case 'bottom-left': 6101 moveX = 0; 6102 moveY = dims.height; 6103 break; 6104 case 'bottom-right': 6105 moveX = dims.width; 6106 moveY = dims.height; 6107 break; 6108 case 'center': 6109 moveX = dims.width / 2; 6110 moveY = dims.height / 2; 6111 break; 6112 } 6113 6114 return new Effect.Parallel( 6115 [ new Effect.Opacity(elemen
6115t, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }), 6116 new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}), 6117 new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }) 6118 ], Object.extend({ 6119 beforeStartInternal: function(effect) { 6120 effect.effects[0].element.makePositioned().makeClipping(); 6121 }, 6122 afterFinishInternal: function(effect) { 6123 effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); } 6124 }, options) 6125 ); 6126}; 6127 6128Effect.Pulsate = function(element) { 6129 element = $(element); 6130 var options = arguments[1] || { }, 6131 oldOpacity = element.getInlineOpacity(), 6132 transition = options.transition || Effect.Transitions.linear, 6133 reverser = function(pos){ 6134 return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5); 6135 }; 6136 6137 return new Effect.Opacity(element, 6138 Object.extend(Object.extend({ duration: 2.0, from: 0, 6139 afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); } 6140 }, options), {transition: reverser})); 6141}; 6142 6143Effect.Fold = function(element) { 6144 element = $(element); 6145 var oldStyle = { 6146 top: element.style.top, 6147 left: element.style.left, 6148 width: element.style.width, 6149 height: element.style.height }; 6150 element.makeClipping(); 6151 return new Effect.Scale(element, 5, Object.extend({ 6152 scaleContent: false, 6153 scaleX: false, 6154 afterFinishInternal: function(effect) { 6155 new Effect.Scale(element, 1, { 6156 scaleContent: false, 6157 scaleY: false, 6158 afterFinishInternal: function(effect) { 6159 effect.element.hide().undoClipping().setStyle(oldStyle); 6160 } }); 6161 }}, arguments[1] || { })); 6162}; 6163 6164Effect.Morph = Class.create(Effect.Base, { 6165 initialize: function(element) { 6166 this.element = $(element); 6167 if (!this.element) throw(Effect._elementDoesNotExistError); 6168 var options = Object.extend({ 6169 style: { } 6170 }, arguments[1] || { }); 6171 6172 if (!Object.isString(options.style)) this.style = $H(options.style); 6173 else { 6174 if (options.style.include(':')) 6175 this.style = options.style.parseStyle(); 6176 else { 6177 this.element.addClassName(options.style); 6178 this.style = $H(this.element.getStyles()); 6179 this.element.removeClassName(options.style); 6180 var css = this.element.getStyles(); 6181 this.style = this.style.reject(function(style) { 6182 return style.value == css[style.key]; 6183 }); 6184 options.afterFinishInternal = function(effect) { 6185 effect.element.addClassName(effect.options.style); 6186 effect.transforms.each(function(transform) { 6187 effect.element.style[transform.style] = ''; 6188 }); 6189 }; 6190 } 6191 } 6192 this.start(options); 6193 }, 6194 6195 setup: function(){ 6196 function parseColor(color){ 6197 if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff'; 6198 color = color.parseColor(); 6199 return $R(0,2).map(function(i){ 6200 return parseInt( color.slice(i*2+1,i*2+3), 16 ); 6201 }); 6202 } 6203 this.transforms = this.style.map(function(pair){ 6204 var property = pair[0], value = pair[1], unit = null; 6205 6206 if (value.parseColor('#zzzzzz') != '#zzzzzz') { 6207 value = value.parseColor(); 6208 unit = 'color'; 6209 } else if (property == 'opacity') { 6210 value = parseFloat(value); 6211 if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) 6212 this.element.setStyle({zoom: 1}); 6213 } else if (Element.CSS_LENGTH.test(value)) { 6214 var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/); 6215 value = parseFloat(components[1]); 6216 unit = (components.length == 3) ? components[2] : null; 6217 } 6218 6219 var originalValue = this.element.getStyle(property); 6220 return { 6221 style: property.camelize(), 6222 originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), 6223 targetValue: unit=='color' ? parseColor(value) : value, 6224 unit: unit 6225 }; 6226 }.bind(this)).reject(function(transform){ 6227 return ( 6228 (transform.originalValue == transform.targetValue) || 6229 ( 6230 transform.unit != 'color' && 6231 (isNaN(transform.originalValue) || isNaN(transform.targetValue)) 6232 ) 6233 ); 6234 }); 6235 }, 6236 update: function(position) { 6237 var style = { }, transform, i = this.transforms.length; 6238 while(i--) 6239 style[(transform = this.transforms[i]).style] = 6240 transform.unit=='color' ? '#'+ 6241 (Math.round(transform.originalValue[0]+ 6242 (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() + 6243 (Math.round(transform.originalValue[1]+ 6244 (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() + 6245 (Math.round(transform.originalValue[2]+ 6246 (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() : 6247 (transform.originalValue + 6248 (transform.targetValue - transform.originalValue) * position).toFixed(3) + 6249 (transform.unit === null ? '' : transform.unit); 6250 this.element.setStyle(style, true); 6251 } 6252}); 6253 6254Effect.Transform = Class.create({ 6255 initialize: function(tracks){ 6256 this.tracks = []; 6257 this.options = arguments[1] || { }; 6258 this.addTracks(tracks); 6259 }, 6260 addTracks: function(tracks){ 6261 tracks.each(function(track){ 6262 track = $H(track); 6263 var data = track.values().first(); 6264 this.tracks.push($H({ 6265 ids: track.keys().first(), 6266 effect: Effect.Morph, 6267 options: { style: data } 6268 })); 6269 }.bind(this)); 6270 return this; 6271 }, 6272 play: function(){ 6273 return new Effect.Parallel( 6274 this.tracks.map(function(track){ 6275 var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options'); 6276 var elements = [$(ids) || $$(ids)].flatten(); 6277 return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) }); 6278 }).flatten(), 6279 this.options 6280 ); 6281 } 6282}); 6283 6284Element.CSS_PROPERTIES = $w( 6285 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + 6286 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' + 6287 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' + 6288 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' + 6289 'fontSize fontWeight height left letterSpacing lineHeight ' + 6290 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+ 6291 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' + 6292 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' + 6293 'right textIndent top width wordSpacing zIndex'); 6294 6295Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/; 6296 6297String.__parseStyleElement = document.createElement('div'); 6298String.prototype.parseStyle = function(){ 6299 var style, styleRules = $H(); 6300 if (Prototype.Browser.WebKit) 6301 style = new Element('div',{style:this}).style; 6302 else { 6303 String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>'; 6304 style = String.__parseStyleElement.childNodes[0].style; 6305 } 6306 6307 Element.CSS_PROPERTIES.each(function(property){ 6308 if (style[property]) styleRules.set(property, style[property]); 6309 }); 6310 6311 if (Prototype.Browser.IE && this.include('opacity')) 6312 styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]); 6313 6314 return styleRules; 6315}; 6316 6317if (document.defaultView && document.defaultView.getComputedStyle) { 6318 Element.getStyles = function(element) { 6319 var css = document.defaultView.getComputedStyle($(element), null); 6320 return Element.CSS_PROPERTIES.inject({ }, function(styles, property) { 6321 styles[property] = css[property]; 6322 return styles; 6323 }); 6324 }; 6325} else { 6326 Element.getStyles = function(element) { 6327 element = $(element); 6328 var css = element.currentStyle, styles; 6329 styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) { 6330 results[property] = css[property]; 6331 return results; 6332 }); 6333 if (!styles.opacity) styles.opacity = element.getOpacity(); 6334 return styles; 6335 }; 6336} 6337 6338Effect.Methods = { 6339 morph: function(element, style) { 6340 element = $(element); 6341 new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { })); 6342 return element; 6343 }, 6344 visualEffect: function(element, effect, options) { 6345 element = $(element); 6346 var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1); 6347 new Effect[klass](element, options); 6348 return element; 6349 }, 6350 highlight: function(element, options) { 6351 element = $(element); 6352 new Effect.Highlight(element, options); 6353 return element; 6354 } 6355}; 6356 6357$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+ 6358 'pulsate shake puff squish switchOff dropOut').each( 6359 function(effect) { 6360 Effect.Methods[effect] = function(element, options){ 6361 element = $(element);
6362 Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options); 6363 return element; 6364 }; 6365 } 6366); 6367 6368$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( 6369 function(f) { Effect.Methods[f] = Element[f]; } 6370); 6371 6372Element.addMethods(Effect.Methods); 6373// script.aculo.us dragdrop.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008 6374 6375// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) 6376// (c) 2005-2008 Sammi Williams (http://www.oriontransfer.co.nz, [email protected]) 6377// 6378// script.aculo.us is freely distributable under the terms of an MIT-style license. 6379// For details, see the script.aculo.us web site: http://script.aculo.us/ 6380 6381if(Object.isUndefined(Effect)) 6382 throw("dragdrop.js requires including script.aculo.us' effects.js library"); 6383 6384var Droppables = { 6385 drops: [], 6386 6387 remove: function(element) { 6388 this.drops = this.drops.reject(function(d) { return d.element==$(element) }); 6389 }, 6390 6391 add: function(element) { 6392 element = $(element); 6393 var options = Object.extend({ 6394 greedy: true, 6395 hoverclass: null, 6396 tree: false 6397 }, arguments[1] || { }); 6398
6399 // cache containers 6400 if(options.containment) { 6401 options._containers = []; 6402 var containment = options.containment; 6403 if(Object.isArray(containment)) { 6404 containment.each( function(c) { options._containers.push($(c)) }); 6405 } else { 6406 options._containers.push($(containment)); 6407 } 6408 } 6409 6410 if(options.accept) options.accept = [options.accept].flatten(); 6411 6412 Element.makePositioned(element); // fix IE 6413 options.element = element; 6414 6415 this.drops.push(options); 6416 }, 6417 6418 findDeepestChild: function(drops) { 6419 deepest = drops[0]; 6420 6421 for (i = 1; i < drops.length; ++i) 6422 if (Element.isParent(drops[i].element, deepest.element)) 6423 deepest = drops[i]; 6424 6425 return deepest; 6426 }, 6427 6428 isContained: function(element, drop) { 6429 var containmentNode; 6430 if(drop.tree) { 6431 containmentNode = element.treeNode; 6432 } else { 6433 containmentNode = element.parentNode; 6434 } 6435 return drop._containers.detect(function(c) { return containmentNode == c }); 6436 }, 6437 6438 isAffected: function(point, element, drop) { 6439 return ( 6440 (drop.element!=element) && 6441 ((!drop._containers) || 6442 this.isContained(element, drop)) && 6443 ((!drop.accept) || 6444 (Element.classNames(element).detect( 6445 function(v) { return drop.accept.include(v) } ) )) && 6446 Position.within(drop.element, point[0], point[1]) ); 6447 }, 6448 6449 deactivate: function(drop) { 6450 if(drop.hoverclass) 6451 Element.removeClassName(drop.element, drop.hoverclass); 6452 this.last_active = null; 6453 }, 6454 6455 activate: function(drop) { 6456 if(drop.hoverclass) 6457 Element.addClassName(drop.element, drop.hoverclass); 6458 this.last_active = drop; 6459 }, 6460 6461 show: function(point, element) { 6462 if(!this.drops.length) return; 6463 var drop, affected = []; 6464 6465 this.drops.each( function(drop) { 6466 if(Droppables.isAffected(point, element, drop)) 6467 affected.push(drop); 6468 }); 6469 6470 if(affected.length>0) 6471 drop = Droppables.findDeepestChild(affected); 6472 6473 if(this.last_active && this.last_active != drop) this.deactivate(this.last_active); 6474 if (drop) { 6475 Position.within(drop.element, point[0], point[1]); 6476 if(drop.onHover) 6477 drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); 6478 6479 if (drop != this.last_active) Droppables.activate(drop); 6480 } 6481 }, 6482 6483 fire: function(event, element) { 6484 if(!this.last_active) return; 6485 Position.prepare(); 6486 6487 if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active)) 6488 if (this.last_active.onDrop) { 6489 this.last_active.onDrop(element, this.last_active.element, event); 6490 return true; 6491 } 6492 }, 6493 6494 reset: function() { 6495 if(this.last_active) 6496 this.deactivate(this.last_active); 6497 } 6498}; 6499 6500var Draggables = { 6501 drags: [], 6502 observers: [], 6503 6504 register: function(draggable) { 6505 if(this.drags.length == 0) { 6506 this.eventMouseUp = this.endDrag.bindAsEventListener(this); 6507 this.eventMouseMove = this.updateDrag.bindAsEventListener(this); 6508 this.eventKeypress = this.keyPress.bindAsEventListener(this); 6509 6510 Event.observe(document, "mouseup", this.eventMouseUp); 6511 Event.observe(draggable.element, "mousemove", this.eventMouseMove); 6512 Event.observe(document, "keypress", this.eventKeypress); 6513 } 6514 this.drags.push(draggable); 6515 }, 6516 6517 unregister: function(draggable) { 6518 this.drags = this.drags.reject(function(d) { return d==draggable }); 6519 if(this.drags.length == 0) { 6520 Event.stopObserving(document, "mouseup", this.eventMouseUp); 6521 Event.stopObserving(draggable.element, "mousemove", this.eventMouseMove); 6522 Event.stopObserving(document, "keypress", this.eventKeypress); 6523 } 6524 }, 6525 6526 activate: function(draggable) { 6527 if(draggable.options.delay) { 6528 this._timeout = setTimeout(function() { 6529 Draggables._timeout = null; 6530 window.focus(); 6531 Draggables.activeDraggable = draggable; 6532 }.bind(this), draggable.options.delay); 6533 } else { 6534 window.focus(); // allows keypress events if window isn't currently focused, fails for Safari 6535 this.activeDraggable = draggable; 6536 } 6537 }, 6538 6539 deactivate: function() { 6540 this.activeDraggable = null; 6541 }, 6542 6543 updateDrag: function(event) { 6544 if(!this.activeDraggable) return; 6545 var pointer = [Event.pointerX(event), Event.pointerY(event)]; 6546 // Mozilla-based browsers fire successive mousemove events with 6547 // the same coordinates, prevent needless redrawing (moz bug?) 6548 if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return; 6549 this._lastPointer = pointer; 6550 6551 this.activeDraggable.updateDrag(event, pointer); 6552 }, 6553 6554 endDrag: function(event) { 6555 if(this._timeout) { 6556 clearTimeout(this._timeout); 6557 this._timeout = null; 6558 } 6559 if(!this.activeDraggable) return; 6560 this._lastPointer = null; 6561 this.activeDraggable.endDrag(event); 6562 this.activeDraggable = null; 6563 }, 6564 6565 keyPress: function(event) { 6566 if(this.activeDraggable) 6567 this.activeDraggable.keyPress(event); 6568 }, 6569 6570 addObserver: function(observer) { 6571 this.observers.push(observer); 6572 this._cacheObserverCallbacks(); 6573 }, 6574 6575 removeObserver: function(element) { // element instead of observer fixes mem leaks 6576 this.observers = this.observers.reject( function(o) { return o.element==element }); 6577 this._cacheObserverCallbacks(); 6578 }, 6579 6580 notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag' 6581 if(this[eventName+'Count'] > 0) 6582 this.observers.each( function(o) { 6583 if(o[eventName]) o[eventName](eventName, draggable, event); 6584 });
6585 if(draggable.options[eventName]) draggable.options[eventName](draggable, event); 6586 }, 6587 6588 _cacheObserverCallbacks: function() { 6589 ['onStart','onEnd','onDrag'].each( function(eventName) { 6590 Draggables[eventName+'Count'] = Draggables.observers.select( 6591 function(o) { return o[eventName]; } 6592 ).length; 6593 }); 6594 } 6595}; 6596 6597/*--------------------------------------------------------------------------*/ 6598 6599var Draggable = Class.create({ 6600 initialize: function(element) { 6601 var defaults = { 6602 handle: false, 6603 reverteffect: function(element, top_offset, left_offset) { 6604 var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02; 6605 new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur, 6606 queue: {scope:'_draggable', position:'end'} 6607 }); 6608 }, 6609 endeffect: function(element) { 6610 var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0; 6611 new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, 6612 queue: {scope:'_draggable', position:'end'}, 6613 afterFinish: function(){ 6614 Draggable._dragging[element] = false 6615 } 6616 }); 6617 }, 6618 zindex: 1000, 6619 revert: false, 6620 quiet: false, 6621 scroll: false, 6622 scrollSensitivity: 20, 6623 scrollSpeed: 15, 6624 snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] } 6625 delay: 0 6626 }; 6627 6628 if(!arguments[1] || Object.isUndefined(arguments[1].endeffect)) 6629 Object.extend(defaults, { 6630 starteffect: function(element) { 6631 element._opacity = Element.getOpacity(element); 6632 Draggable._dragging[element] = true; 6633 new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); 6634 } 6635 }); 6636 6637 var options = Object.extend(defaults, arguments[1] || { }); 6638 6639 this.element = $(element); 6640 6641 if(options.handle && Object.isString(options.handle)) 6642 this.handle = this.element.down('.'+options.handle, 0); 6643 6644 if(!this.handle) this.handle = $(options.handle); 6645 if(!this.handle) this.handle = this.element; 6646 6647 if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) { 6648 options.scroll = $(options.scroll); 6649 this._isScrollChild = Element.childOf(this.element, options.scroll); 6650 } 6651 6652 Element.makePositioned(this.element); // fix IE 6653 6654 this.options = options; 6655 this.dragging = false; 6656 6657 this.eventMouseDown = this.initDrag.bindAsEventListener(this); 6658 Event.observe(this.handle, "mousedown", this.eventMouseDown); 6659 6660 Draggables.register(this); 6661 }, 6662 6663 destroy: function() { 6664 Event.stopObserving(this.handle, "mousedown", this.eventMouseDown); 6665 Draggables.unregister(this); 6666 }, 6667 6668 currentDelta: function() { 6669 return([ 6670 parseInt(Element.getStyle(this.element,'left') || '0'), 6671 parseInt(Element.getStyle(this.element,'top') || '0')]); 6672 }, 6673 6674 initDrag: function(event) { 6675 if(!Object.isUndefined(Draggable._dragging[this.element]) && 6676 Draggable._dragging[this.element]) return; 6677 if(Event.isLeftClick(event)) { 6678 // abort on form elements, fixes a Firefox issue 6679 var src = Event.element(event); 6680 if((tag_name = src.tagName.toUpperCase()) && ( 6681 tag_name=='INPUT' || 6682 tag_name=='SELECT' || 6683 tag_name=='OPTION' || 6684 tag_name=='BUTTON' || 6685 tag_name=='TEXTAREA')) return; 6686 6687 var pointer = [Event.pointerX(event), Event.pointerY(event)]; 6688 var pos = Position.cumulativeOffset(this.element); 6689 this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) }); 6690 6691 Draggables.activate(this); 6692 Event.stop(event); 6693 } 6694 }, 6695 6696 startDrag: function(event) { 6697 this.dragging = true; 6698 if(!this.delta) 6699 this.delta = this.currentDelta(); 6700 6701 if(this.options.zindex) { 6702 this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); 6703 this.element.style.zIndex = this.options.zindex; 6704 } 6705 6706 if(this.options.ghosting) { 6707 this._clone = this.element.cloneNode(true); 6708 this._originallyAbsolute = (this.element.getStyle('position') == 'absolute'); 6709 if (!this._originallyAbsolute) 6710 Position.absolutize(this.element); 6711 this.element.parentNode.insertBefore(this._clone, this.element); 6712 } 6713 6714 if(this.options.scroll) { 6715 if (this.options.scroll == window) { 6716 var where = this._getWindowScroll(this.options.scroll); 6717 this.originalScrollLeft = where.left; 6718 this.originalScrollTop = where.top; 6719 } else {
6720 this.originalScrollLeft = this.options.scroll.scrollLeft; 6721 this.originalScrollTop = this.options.scroll.scrollTop; 6722 } 6723 } 6724 6725 Draggables.notify('onStart', this, event); 6726 6727 if(this.options.starteffect) this.options.starteffect(this.element); 6728 }, 6729 6730 updateDrag: function(event, pointer) { 6731 if(!this.dragging) this.startDrag(event); 6732 6733 if(!this.options.quiet){ 6734 Position.prepare(); 6735 Droppables.show(pointer, this.element); 6736 } 6737 6738 Draggables.notify('onDrag', this, event); 6739 6740 this.draw(pointer); 6741 if(this.options.change) this.options.change(this); 6742 6743 if(this.options.scroll) { 6744 this.stopScrolling(); 6745 6746 var p; 6747 if (this.options.scroll == window) { 6748 with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; } 6749 } else { 6750 p = Position.page(this.options.scroll); 6751 p[0] += this.options.scroll.scrollLeft + Position.deltaX; 6752 p[1] += this.options.scroll.scrollTop + Position.deltaY; 6753 p.push(p[0]+this.options.scroll.offsetWidth); 6754 p.push(p[1]+this.options.scroll.offsetHeight); 6755 } 6756 var speed = [0,0]; 6757 if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity); 6758 if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity); 6759 if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity); 6760 if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity); 6761 this.startScrolling(speed); 6762 } 6763 6764 // fix AppleWebKit rendering 6765 if(Prototype.Browser.WebKit) window.scrollBy(0,0); 6766 6767 Event.stop(event); 6768 }, 6769 6770 finishDrag: function(event, success) { 6771 this.dragging = false; 6772 6773 if(this.options.quiet){ 6774 Position.prepare(); 6775 var pointer = [Event.pointerX(event), Event.pointerY(event)]; 6776 Droppables.show(pointer, this.element); 6777 } 6778 6779 if(this.options.ghosting) { 6780 if (!this._originallyAbsolute) 6781 Position.relativize(this.element); 6782 delete this._originallyAbsolute; 6783 Element.remove(this._clone); 6784 this._clone = null; 6785 } 6786 6787 var dropped = false; 6788 if(success) { 6789 dropped = Droppables.fire(event, this.element); 6790 if (!dropped) dropped = false; 6791 } 6792 if(dropped && this.options.onDropped) this.options.onDropped(this.element); 6793 Draggables.notify('onEnd', this, event); 6794 6795 var revert = this.options.revert; 6796 if(revert && Object.isFunction(revert)) revert = revert(this.element); 6797 6798 var d = this.currentDelta(); 6799 if(revert && this.options.reverteffect) { 6800 if (dropped == 0 || revert != 'failure') 6801 this.options.reverteffect(this.element, 6802 d[1]-this.delta[1], d[0]-this.delta[0]); 6803 } else { 6804 this.delta = d; 6805 } 6806 6807 if(this.options.zindex) 6808 this.element.style.zIndex = this.originalZ; 6809 6810 if(this.options.endeffect) 6811 this.options.endeffect(this.element); 6812 6813 Draggables.deactivate(this); 6814 Droppables.reset(); 6815 }, 6816 6817 keyPress: function(event) { 6818 if(event.keyCode!=Event.KEY_ESC) return; 6819 this.finishDrag(event, false); 6820 Event.stop(event); 6821 }, 6822 6823 endDrag: function(event) { 6824 if(!this.dragging) return; 6825 this.stopScrolling(); 6826 this.finishDrag(event, true); 6827 Event.stop(event); 6828 }, 6829 6830 draw: function(point) { 6831 var pos = Position.cumulativeOffset(this.element); 6832 if(this.options.ghosting) { 6833 var r = Position.realOffset(this.element); 6834 pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY; 6835 } 6836 6837 var d = this.currentDelta(); 6838 pos[0] -= d[0]; pos[1] -= d[1]; 6839 6840 if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) { 6841 pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft; 6842 pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop; 6843 } 6844 6845 var p = [0,1].map(function(i){ 6846 return (point[i]-pos[i]-this.offset[i]) 6847 }.bind(this)); 6848 6849 if(this.options.snap) { 6850 if(Object.isFunction(this.options.snap)) { 6851 p = this.options.snap(p[0],p[1],this); 6852 } else { 6853 if(Object.isArray(this.options.snap)) { 6854 p = p.map( function(v, i) { 6855 return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this)); 6856 } else { 6857 p = p.map( function(v) { 6858 return (v/this.options.snap).round()*this.options.snap }.bind(this)); 6859 } 6860 }} 6861 6862 var style = this.element.style; 6863 if((!this.options.constraint) || (this.options.constraint=='horizontal')) 6864 style.left = p[0] + "px"; 6865 if((!this.options.constraint) || (this.options.constraint=='vertical')) 6866 style.top = p[1] + "px"; 6867 6868 if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering 6869 }, 6870 6871 stopScrolling: function() { 6872 if(this.scrollInterval) { 6873 clearInterval(this.scrollInterval); 6874 this.scrollInterval = null; 6875 Draggables._lastScrollPointer = null; 6876 } 6877 }, 6878 6879 startScrolling: function(speed) { 6880 if(!(speed[0] || speed[1])) return; 6881 this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed]; 6882 this.lastScrolled = new Date(); 6883 this.scrollInterval = setInterval(this.scroll.bind(this), 10); 6884 }, 6885 6886 scroll: function() { 6887 var current = new Date(); 6888 var delta = current - this.lastScrolled; 6889 this.lastScrolled = current; 6890 if(this.options.scroll == window) { 6891 with (this._getWindowScroll(this.options.scroll)) { 6892 if (this.scrollSpeed[0] || this.scrollSpeed[1]) { 6893 var d = delta / 1000;
6894 this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] ); 6895 } 6896 } 6897 } else { 6898 this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000; 6899 this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000; 6900 } 6901 6902 Position.prepare(); 6903 Droppables.show(Draggables._lastPointer, this.element); 6904 Draggables.notify('onDrag', this); 6905 if (this._isScrollChild) { 6906 Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer); 6907 Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000; 6908 Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000; 6909 if (Draggables._lastScrollPointer[0] < 0) 6910 Draggables._lastScrollPointer[0] = 0; 6911 if (Draggables._lastScrollPointer[1] < 0) 6912 Draggables._lastScrollPointer[1] = 0; 6913 this.draw(Draggables._lastScrollPointer); 6914 } 6915 6916 if(this.options.change) this.options.change(this); 6917 }, 6918 6919 _getWindowScroll: function(w) { 6920 var T, L, W, H; 6921 with (w.document) { 6922 if (w.document.documentElement && documentElement.scrollTop) { 6923 T = documentElement.scrollTop; 6924 L = documentElement.scrollLeft; 6925 } else if (w.document.body) { 6926 T = body.scrollTop; 6927 L = body.scrollLeft; 6928 } 6929 if (w.innerWidth) { 6930 W = w.innerWidth; 6931 H = w.innerHeight; 6932 } else if (w.document.documentElement && documentElement.clientWidth) { 6933 W = documentElement.clientWidth; 6934 H = documentElement.clientHeight; 6935 } else { 6936 W = body.offsetWidth; 6937 H = body.offsetHeight; 6938 } 6939 } 6940 return { top: T, left: L, width: W, height: H }; 6941 } 6942}); 6943 6944Draggable._dragging = { }; 6945 6946/*--------------------------------------------------------------------------*/ 6947 6948var SortableObserver = Class.create({ 6949 initialize: function(element, observer) { 6950 this.element = $(element); 6951 this.observer = observer; 6952 this.lastValue = Sortable.serialize(this.element); 6953 }, 6954 6955 onStart: function() { 6956 this.lastValue = Sortable.serialize(this.element); 6957 }, 6958 6959 onEnd: function() { 6960 Sortable.unmark(); 6961 if(this.lastValue != Sortable.serialize(this.element)) 6962 this.observer(this.element) 6963 } 6964}); 6965 6966var Sortable = { 6967 SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/, 6968 6969 sortables: { }, 6970 6971 _findRootElement: function(element) { 6972 while (element.tagName.toUpperCase() != "BODY") { 6973 if(element.id && Sortable.sortables[element.id]) return element; 6974 element = element.parentNode; 6975 } 6976 }, 6977 6978 options: function(element) { 6979 element = Sortable._findRootElement($(element)); 6980 if(!element) return; 6981 return Sortable.sortables[element.id]; 6982 }, 6983 6984 destroy: function(element){ 6985 element = $(element); 6986 var s = Sortable.sortables[element.id]; 6987 6988 if(s) { 6989 Draggables.removeObserver(s.element); 6990 s.droppables.each(function(d){ Droppables.remove(d) }); 6991 s.draggables.invoke('destroy'); 6992 6993 delete Sortable.sortables[s.element.id]; 6994 } 6995 }, 6996 6997 create: function(element) { 6998 element = $(element); 6999 var options = Object.extend({ 7000 element: element, 7001 tag: 'li', // assumes li children, override with tag: 'tagname' 7002 dropOnEmpty: false, 7003 tree: false, 7004 treeTag: 'ul', 7005 overlap: 'vertical', // one of 'vertical', 'horizontal' 7006 constraint: 'vertical', // one of 'vertical', 'horizontal', false 7007 containment: element, // also takes array of elements (or id's); or false 7008 handle: false, // or a CSS class 7009 only: false, 7010 delay: 0, 7011 hoverclass: null, 7012 ghosting: false, 7013 quiet: false, 7014 scroll: false, 7015 scrollSensitivity: 20, 7016 scrollSpeed: 15, 7017 format: this.SERIALIZE_RULE, 7018 7019 // these take arrays of elements or ids and can be 7020 // used for better initialization performance 7021 elements: false, 7022 handles: false, 7023 7024 onChange: Prototype.emptyFunction, 7025 onUpdate: Prototype.emptyFunction 7026 }, arguments[1] || { }); 7027 7028 // clear any old sortable with same element 7029 this.destroy(element); 7030 7031 // build options for the draggables 7032 var options_for_draggable = { 7033 revert: true, 7034 quiet: options.quiet, 7035 scroll: options.scroll, 7036 scrollSpeed: options.scrollSpeed,
7037 scrollSensitivity: options.scrollSensitivity, 7038 delay: options.delay, 7039 ghosting: options.ghosting, 7040 constraint: options.constraint, 7041 handle: options.handle }; 7042 7043 if(options.starteffect) 7044 options_for_draggable.starteffect = options.starteffect; 7045 7046 if(options.reverteffect) 7047 options_for_draggable.reverteffect = options.reverteffect; 7048 else 7049 if(options.ghosting) options_for_draggable.reverteffect = function(element) { 7050 element.style.top = 0; 7051 element.style.left = 0; 7052 }; 7053 7054 if(options.endeffect) 7055 options_for_draggable.endeffect = options.endeffect; 7056 7057 if(options.zindex) 7058 options_for_draggable.zindex = options.zindex; 7059 7060 // build options for the droppables 7061 var options_for_droppable = { 7062 overlap: options.overlap, 7063 containment: options.containment, 7064 tree: options.tree, 7065 hoverclass: options.hoverclass, 7066 onHover: Sortable.onHover 7067 }; 7068 7069 var options_for_tree = { 7070 onHover: Sortable.onEmptyHover, 7071 overlap: options.overlap, 7072 containment: options.containment, 7073 hoverclass: options.hoverclass 7074 }; 7075 7076 // fix for gecko engine 7077 Element.cleanWhitespace(element); 7078 7079 options.draggables = []; 7080 options.droppables = []; 7081 7082 // drop on empty handling 7083 if(options.dropOnEmpty || options.tree) { 7084 Droppables.add(element, options_for_tree); 7085 options.droppables.push(element); 7086 } 7087 7088 (options.elements || this.findElements(element, options) || []).each( function(e,i) { 7089 var handle = options.handles ? $(options.handles[i]) : 7090 (options.handle ? $(e).select('.' + options.handle)[0] : e); 7091 options.draggables.push( 7092 new Draggable(e, Object.extend(options_for_draggable, { handle: handle }))); 7093 Droppables.add(e, options_for_droppable); 7094 if(options.tree) e.treeNode = element; 7095 options.droppables.push(e); 7096 }); 7097 7098 if(options.tree) { 7099 (Sortable.findTreeElements(element, options) || []).each( function(e) { 7100 Droppables.add(e, options_for_tree); 7101 e.treeNode = element; 7102 options.droppables.push(e); 7103 }); 7104 } 7105 7106 // keep reference 7107 this.sortables[element.id] = options; 7108 7109 // for onupdate 7110 Draggables.addObserver(new SortableObserver(element, options.onUpdate)); 7111 7112 }, 7113 7114 // return all suitable-for-sortable elements in a guaranteed order 7115 findElements: function(element, options) { 7116 return Element.findChildren( 7117 element, options.only, options.tree ? true : false, options.tag); 7118 }, 7119 7120 findTreeElements: function(element, options) { 7121 return Element.findChildren( 7122 element, options.only, options.tree ? true : false, options.treeTag); 7123 }, 7124 7125 onHover: function(element, dropon, overlap) { 7126 if(Element.isParent(dropon, element)) return; 7127 7128 if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) { 7129 return; 7130 } else if(overlap>0.5) { 7131 Sortable.mark(dropon, 'before'); 7132 if(dropon.previousSibling != element) { 7133 var oldParentNode = element.parentNode; 7134 element.style.visibility = "hidden"; // fix gecko rendering 7135 dropon.parentNode.insertBefore(element, dropon); 7136 if(dropon.parentNode!=oldParentNode) 7137 Sortable.options(oldParentNode).onChange(element); 7138 Sortable.options(dropon.parentNode).onChange(element); 7139 } 7140 } else { 7141 Sortable.mark(dropon, 'after'); 7142 var nextElement = dropon.nextSibling || null; 7143 if(nextElement != element) { 7144 var oldParentNode = element.parentNode; 7145 element.style.visibility = "hidden"; // fix gecko rendering 7146 dropon.parentNode.insertBefore(element, nextElement); 7147 if(dropon.parentNode!=oldParentNode) 7148 Sortable.options(oldParentNode).onChange(element); 7149 Sortable.options(dropon.parentNode).onChange(element); 7150 } 7151 } 7152 }, 7153 7154 onEmptyHover: function(element, dropon, overlap) { 7155 var oldParentNode = element.parentNode; 7156 var droponOptions = Sortable.options(dropon); 7157 7158 if(!Element.isParent(dropon, element)) { 7159 var index; 7160 7161 var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only}); 7162 var child = null; 7163 7164 if(children) { 7165 var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap); 7166 7167 for (index = 0; index < children.length; index += 1) { 7168 if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) { 7169 offset -= Element.offsetSize (children[index], droponOptions.overlap); 7170 } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) { 7171 child = index + 1 < children.length ? children[index + 1] : null; 7172 break; 7173 } else { 7174 child = children[index]; 7175 break; 7176 } 7177 } 7178 } 7179 7180 dropon.insertBefore(element, child); 7181
7182 Sortable.options(oldParentNode).onChange(element); 7183 droponOptions.onChange(element); 7184 } 7185 }, 7186 7187 unmark: function() { 7188 if(Sortable._marker) Sortable._marker.hide(); 7189 }, 7190 7191 mark: function(dropon, position) { 7192 // mark on ghosting only 7193 var sortable = Sortable.options(dropon.parentNode); 7194 if(sortable && !sortable.ghosting) return; 7195 7196 if(!Sortable._marker) { 7197 Sortable._marker = 7198 ($('dropmarker') || Element.extend(document.createElement('DIV'))). 7199 hide().addClassName('dropmarker').setStyle({position:'absolute'}); 7200 document.getElementsByTagName("body").item(0).appendChild(Sortable._marker); 7201 } 7202 var offsets = Position.cumulativeOffset(dropon); 7203 Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'}); 7204 7205 if(position=='after') 7206 if(sortable.overlap == 'horizontal') 7207 Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'}); 7208 else 7209 Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'}); 7210 7211 Sortable._marker.show(); 7212 }, 7213 7214 _tree: function(element, options, parent) { 7215 var children = Sortable.findElements(element, options) || []; 7216 7217 for (var i = 0; i < children.length; ++i) { 7218 var match = children[i].id.match(options.format); 7219 7220 if (!match) continue; 7221 7222 var child = { 7223 id: encodeURIComponent(match ? match[1] : null), 7224 element: element, 7225 parent: parent, 7226 children: [], 7227 position: parent.children.length, 7228 container: $(children[i]).down(options.treeTag) 7229 }; 7230 7231 /* Get the element containing the children and recurse over it */ 7232 if (child.container) 7233 this._tree(child.container, options, child); 7234 7235 parent.children.push (child); 7236 } 7237 7238 return parent; 7239 }, 7240 7241 tree: function(element) { 7242 element = $(element); 7243 var sortableOptions = this.options(element); 7244 var options = Object.extend({ 7245 tag: sortableOptions.tag, 7246 treeTag: sortableOptions.treeTag, 7247 only: sortableOptions.only, 7248 name: element.id, 7249 format: sortableOptions.format 7250 }, arguments[1] || { }); 7251 7252 var root = { 7253 id: null, 7254 parent: null, 7255 children: [], 7256 container: element, 7257 position: 0 7258 }; 7259 7260 return Sortable._tree(element, options, root); 7261 }, 7262 7263 /* Construct a [i] index for a particular node */ 7264 _constructIndex: function(node) { 7265 var index = ''; 7266 do { 7267 if (node.id) index = '[' + node.position + ']' + index; 7268 } while ((node = node.parent) != null); 7269 return index; 7270 }, 7271 7272 sequence: function(element) { 7273 element = $(element); 7274 var options = Object.extend(this.options(element), arguments[1] || { }); 7275 7276 return $(this.findElements(element, options) || []).map( function(item) { 7277 return item.id.match(options.format) ? item.id.match(options.format)[1] : ''; 7278 }); 7279 }, 7280 7281 setSequence: function(element, new_sequence) { 7282 element = $(element); 7283 var options = Object.extend(this.options(element), arguments[2] || { }); 7284 7285 var nodeMap = { }; 7286 this.findElements(element, options).each( function(n) { 7287 if (n.id.match(options.format)) 7288 nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode]; 7289 n.parentNode.removeChild(n); 7290 }); 7291 7292 new_sequence.each(function(ident) { 7293 var n = nodeMap[ident]; 7294 if (n) { 7295 n[1].appendChild(n[0]); 7296 delete nodeMap[ident]; 7297 } 7298 }); 7299 }, 7300 7301 serialize: function(element) { 7302 element = $(element); 7303 var options = Object.extend(Sortable.options(element), arguments[1] || { }); 7304 var name = encodeURIComponent( 7305 (arguments[1] && arguments[1].name) ? arguments[1].name : element.id); 7306 7307 if (options.tree) { 7308 return Sortable.tree(element, arguments[1]).children.map( function (item) { 7309 return [name + Sortable._constructIndex(item) + "[id]=" + 7310 encodeURIComponent(item.id)].concat(item.children.map(arguments.callee)); 7311 }).flatten().join('&'); 7312 } else { 7313 return Sortable.sequence(element, arguments[1]).map( function(item) { 7314 return name + "[]=" + encodeURIComponent(item); 7315 }).join('&'); 7316 } 7317 } 7318}; 7319 7320// Returns true if child is contained within element 7321Element.isParent = function(child, element) { 7322 if (!child.parentNode || child == element) return false;
7323 if (child.parentNode == element) return true; 7324 return Element.isParent(child.parentNode, element); 7325}; 7326 7327Element.findChildren = function(element, only, recursive, tagName) { 7328 if(!element.hasChildNodes()) return null; 7329 tagName = tagName.toUpperCase(); 7330 if(only) only = [only].flatten(); 7331 var elements = []; 7332 $A(element.childNodes).each( function(e) { 7333 if(e.tagName && e.tagName.toUpperCase()==tagName && 7334 (!only || (Element.classNames(e).detect(function(v) { return only.include(v) })))) 7335 elements.push(e); 7336 if(recursive) { 7337 var grandchildren = Element.findChildren(e, only, recursive, tagName); 7338 if(grandchildren) elements.push(grandchildren); 7339 } 7340 }); 7341 7342 return (elements.length>0 ? elements.flatten() : []); 7343}; 7344 7345Element.offsetSize = function (element, type) { 7346 return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')]; 7347}; 7348// script.aculo.us controls.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008 7349 7350// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) 7351// (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan) 7352// (c) 2005-2008 Jon Tirsen (http://www.tirsen.com) 7353// Contributors: 7354// Richard Livsey 7355// Rahul Bhargava 7356// Rob Wills 7357// 7358// script.aculo.us is freely distributable under the terms of an MIT-style license. 7359// For details, see the script.aculo.us web site: http://script.aculo.us/ 7360 7361// Autocompleter.Base handles all the autocompletion functionality 7362// that's independent of the data source for autocompletion. This 7363// includes drawing the autocompletion menu, observing keyboard 7364// and mouse events, and similar. 7365// 7366// Specific autocompleters need to provide, at the very least, 7367// a getUpdatedChoices function that will be invoked every time 7368// the text inside the monitored textbox changes. This method 7369// should get the text for which to provide autocompletion by 7370// invoking this.getToken(), NOT by directly accessing 7371// this.element.value. This is to allow incremental tokenized 7372// autocompletion. Specific auto-completion logic (AJAX, etc) 7373// belongs in getUpdatedChoices. 7374// 7375// Tokenized incremental autocompletion is enabled automatically 7376// when an autocompleter is instantiated with the 'tokens' option 7377// in the options parameter, e.g.: 7378// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' }); 7379// will incrementally autocomplete with a comma as the token. 7380// Additionally, ',' in the above example can be replaced with 7381// a token array, e.g. { tokens: [',', '\n'] } which 7382// enables autocompletion on multiple tokens. This is most 7383// useful when one of the tokens is \n (a newline), as it 7384// allows smart autocompletion after linebreaks. 7385 7386if(typeof Effect == 'undefined') 7387 throw("controls.js requires including script.aculo.us' effects.js library"); 7388 7389var Autocompleter = { }; 7390Autocompleter.Base = Class.create({ 7391 baseInitialize: function(element, update, options) { 7392 element = $(element); 7393 this.element = element; 7394 this.update = $(update); 7395 this.hasFocus = false; 7396 this.changed = false; 7397 this.active = false; 7398 this.index = 0; 7399 this.entryCount = 0; 7400 this.oldElementValue = this.element.value; 7401 7402 if(this.setOptions) 7403 this.setOptions(options); 7404 else 7405 this.options = options || { }; 7406 7407 this.options.paramName = this.options.paramName || this.element.name; 7408 this.options.tokens = this.options.tokens || []; 7409 this.options.frequency = this.options.frequency || 0.4; 7410 this.options.minChars = this.options.minChars || 1; 7411 this.options.onShow = this.options.onShow || 7412 function(element, update){ 7413 if(!update.style.position || update.style.position=='absolute') { 7414 update.style.position = 'absolute'; 7415 Position.clone(element, update, { 7416 setHeight: false, 7417 offsetTop: element.offsetHeight 7418 }); 7419 } 7420 Effect.Appear(update,{duration:0.15}); 7421 }; 7422 this.options.onHide = this.options.onHide || 7423 function(element, update){ new Effect.Fade(update,{duration:0.15}) }; 7424 7425 if(typeof(this.options.tokens) == 'string') 7426 this.options.tokens = new Array(this.options.tokens); 7427 // Force carriage returns as token delimiters anyway 7428 if (!this.options.tokens.include('\n')) 7429 this.options.tokens.push('\n'); 7430 7431 this.observer = null; 7432 7433 this.element.setAttribute('autocomplete','off'); 7434 7435 Element.hide(this.update); 7436 7437 Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this)); 7438 Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this)); 7439 }, 7440 7441 show: function() { 7442 if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update); 7443 if(!this.iefix && 7444 (Prototype.Browser.IE) && 7445 (Element.getStyle(this.update, 'position')=='absolute')) { 7446 new Insertion.After(this.update, 7447 '<iframe id="' + this.update.id + '_iefix" '+ 7448 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' + 7449 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>'); 7450 this.iefix = $(this.update.id+'_iefix'); 7451 } 7452 if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50); 7453 }, 7454 7455 fixIEOverlapping: function() { 7456 Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)}); 7457 this.iefix.style.zIndex = 1; 7458 this.update.style.zIndex = 2; 7459 Element.show(this.iefix); 7460 }, 7461 7462 hide: function() { 7463 this.stopIndicator(); 7464 if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update); 7465 if(this.iefix) Element.hide(this.iefix); 7466 }, 7467 7468 startIndicator: function() { 7469 if(this.options.indicator) Element.show(this.options.indicator); 7470 }, 7471 7472 stopIndicator: function() { 7473 if(this.options.indicator) Element.hide(this.options.indicator); 7474 }, 7475 7476 onKeyPress: function(event) { 7477 if(this.active) 7478 switch(event.keyCode) { 7479 case Event.KEY_TAB: 7480 case Event.KEY_RETURN: 7481 this.selectEntry(); 7482 Event.stop(event); 7483 case Event.KEY_ESC: 7484 this.hide(); 7485 this.active = false;
7486 Event.stop(event); 7487 return; 7488 case Event.KEY_LEFT: 7489 case Event.KEY_RIGHT: 7490 return; 7491 case Event.KEY_UP: 7492 this.markPrevious(); 7493 this.render(); 7494 Event.stop(event); 7495 return; 7496 case Event.KEY_DOWN: 7497 this.markNext(); 7498 this.render(); 7499 Event.stop(event); 7500 return; 7501 } 7502 else 7503 if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 7504 (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return; 7505 7506 this.changed = true; 7507 this.hasFocus = true; 7508 7509 if(this.observer) clearTimeout(this.observer); 7510 this.observer = 7511 setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); 7512 }, 7513 7514 activate: function() { 7515 this.changed = false; 7516 this.hasFocus = true; 7517 this.getUpdatedChoices(); 7518 }, 7519 7520 onHover: function(event) { 7521 var element = Event.findElement(event, 'LI'); 7522 if(this.index != element.autocompleteIndex) 7523 { 7524 this.index = element.autocompleteIndex; 7525 this.render(); 7526 } 7527 Event.stop(event); 7528 }, 7529 7530 onClick: function(event) { 7531 var element = Event.findElement(event, 'LI'); 7532 this.index = element.autocompleteIndex; 7533 this.selectEntry(); 7534 this.hide(); 7535 }, 7536 7537 onBlur: function(event) { 7538 // needed to make click events working 7539 setTimeout(this.hide.bind(this), 250); 7540 this.hasFocus = false; 7541 this.active = false; 7542 }, 7543 7544 render: function() { 7545 if(this.entryCount > 0) { 7546 for (var i = 0; i < this.entryCount; i++) 7547 this.index==i ? 7548 Element.addClassName(this.getEntry(i),"selected") : 7549 Element.removeClassName(this.getEntry(i),"selected"); 7550 if(this.hasFocus) { 7551 this.show(); 7552 this.active = true; 7553 } 7554 } else { 7555 this.active = false; 7556 this.hide(); 7557 } 7558 }, 7559 7560 markPrevious: function() { 7561 if(this.index > 0) this.index--; 7562 else this.index = this.entryCount-1; 7563 //this.getEntry(this.index).scrollIntoView(true); useless 7564 }, 7565 7566 markNext: function() { 7567 if(this.index < this.entryCount-1) this.index++; 7568 else this.index = 0; 7569 this.getEntry(this.index).scrollIntoView(false); 7570 }, 7571 7572 getEntry: function(index) { 7573 return this.update.firstChild.childNodes[index]; 7574 }, 7575 7576 getCurrentEntry: function() { 7577 return this.getEntry(this.index); 7578 }, 7579 7580 selectEntry: function() { 7581 this.active = false; 7582 this.updateElement(this.getCurrentEntry()); 7583 }, 7584 7585 updateElement: function(selectedElement) { 7586 if (this.options.updateElement) { 7587 this.options.updateElement(selectedElement); 7588 return; 7589 } 7590 var value = ''; 7591 if (this.options.select) { 7592 var nodes = $(selectedElement).select('.' + this.options.select) || []; 7593 if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select); 7594 } else 7595 value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); 7596 7597 var bounds = this.getTokenBounds(); 7598 if (bounds[0] != -1) { 7599 var newValue = this.element.value.substr(0, bounds[0]); 7600 var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/); 7601 if (whitespace) 7602 newValue += whitespace[0]; 7603 this.element.value = newValue + value + this.element.value.substr(bounds[1]); 7604 } else { 7605 this.element.value = value; 7606 } 7607 this.oldElementValue = this.element.value; 7608 this.element.focus(); 7609 7610 if (this.options.afterUpdateElement) 7611 this.options.afterUpdateElement(this.element, selectedElement); 7612 }, 7613 7614 updateChoices: function(choices) { 7615 if(!this.changed && this.hasFocus) { 7616 this.update.innerHTML = choices; 7617 Element.cleanWhitespace(this.update); 7618 Element.cleanWhitespace(this.update.down()); 7619 7620 if(this.update.firstChild && this.update.down().childNodes) { 7621 this.entryCount = 7622 this.update.down().childNodes.length; 7623 for (var i = 0; i < this.entryCount; i++) { 7624 var entry = this.getEntry(i); 7625 entry.autocompleteIndex = i; 7626 this.addObservers(entry); 7627 } 7628 } else { 7629 this.entryCount = 0; 7630 } 7631 7632 this.stopIndicator(); 7633 this.index = 0; 7634 7635 if(this.entryCount==1 && this.options.autoSelect) { 7636 this.selectEntry(); 7637 this.hide(); 7638 } else { 7639 this.render(); 7640 } 7641 } 7642 }, 7643 7644 addObservers: function(element) { 7645 Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this)); 7646 Event.observe(element, "click", this.onClick.bindAsEventListener(this)); 7647 }, 7648 7649 onObserverEvent: function() { 7650 this.changed = false;
7651 this.tokenBounds = null; 7652 if(this.getToken().length>=this.options.minChars) { 7653 this.getUpdatedChoices(); 7654 } else { 7655 this.active = false; 7656 this.hide(); 7657 } 7658 this.oldElementValue = this.element.value; 7659 }, 7660 7661 getToken: function() { 7662 var bounds = this.getTokenBounds(); 7663 return this.element.value.substring(bounds[0], bounds[1]).strip(); 7664 }, 7665 7666 getTokenBounds: function() { 7667 if (null != this.tokenBounds) return this.tokenBounds; 7668 var value = this.element.value; 7669 if (value.strip().empty()) return [-1, 0]; 7670 var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue); 7671 var offset = (diff == this.oldElementValue.length ? 1 : 0); 7672 var prevTokenPos = -1, nextTokenPos = value.length; 7673 var tp; 7674 for (var index = 0, l = this.options.tokens.length; index < l; ++index) { 7675 tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1); 7676 if (tp > prevTokenPos) prevTokenPos = tp; 7677 tp = value.indexOf(this.options.tokens[index], diff + offset); 7678 if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp; 7679 } 7680 return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]); 7681 } 7682}); 7683 7684Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) { 7685 var boundary = Math.min(newS.length, oldS.length); 7686 for (var index = 0; index < boundary; ++index) 7687 if (newS[index] != oldS[index]) 7688 return index; 7689 return boundary; 7690}; 7691 7692Ajax.Autocompleter = Class.create(Autocompleter.Base, { 7693 initialize: function(element, update, url, options) { 7694 this.baseInitialize(element, update, options); 7695 this.options.asynchronous = true; 7696 this.options.onComplete = this.onComplete.bind(this); 7697 this.options.defaultParams = this.options.parameters || null; 7698 this.url = url; 7699 }, 7700 7701 getUpdatedChoices: function() { 7702 this.startIndicator(); 7703 7704 var entry = encodeURIComponent(this.options.paramName) + '=' + 7705 encodeURIComponent(this.getToken()); 7706 7707 this.options.parameters = this.options.callback ? 7708 this.options.callback(this.element, entry) : entry; 7709 7710 if(this.options.defaultParams) 7711 this.options.parameters += '&' + this.options.defaultParams; 7712 7713 new Ajax.Request(this.url, this.options); 7714 }, 7715 7716 onComplete: function(request) { 7717 this.updateChoices(request.responseText); 7718 } 7719}); 7720 7721// The local array autocompleter. Used when you'd prefer to 7722// inject an array of autocompletion options into the page, rather 7723// than sending out Ajax queries, which can be quite slow sometimes. 7724// 7725// The constructor takes four parameters. The first two are, as usual, 7726// the id of the monitored textbox, and id of the autocompletion menu. 7727// The third is the array you want to autocomplete from, and the fourth 7728// is the options block. 7729// 7730// Extra local autocompletion options: 7731// - choices - How many autocompletion choices to offer 7732//
7733// - partialSearch - If false, the autocompleter will match entered 7734// text only at the beginning of strings in the 7735// autocomplete array. Defaults to true, which will 7736// match text at the beginning of any *word* in the 7737// strings in the autocomplete array. If you want to 7738// search anywhere in the string, additionally set 7739// the option fullSearch to true (default: off). 7740// 7741// - fullSsearch - Search anywhere in autocomplete array strings. 7742// 7743// - partialChars - How many characters to enter before triggering 7744// a partial match (unlike minChars, which defines 7745// how many characters are required to do any match 7746// at all). Defaults to 2. 7747// 7748// - ignoreCase - Whether to ignore case when autocompleting. 7749// Defaults to true. 7750// 7751// It's possible to pass in a custom function as the 'selector' 7752// option, if you prefer to write your own autocompletion logic. 7753// In that case, the other options above will not apply unless 7754// you support them. 7755 7756Autocompleter.Local = Class.create(Autocompleter.Base, { 7757 initialize: function(element, update, array, options) { 7758 this.baseInitialize(element, update, options); 7759 this.options.array = array; 7760 }, 7761 7762 getUpdatedChoices: function() { 7763 this.updateChoices(this.options.selector(this)); 7764 }, 7765 7766 setOptions: function(options) { 7767 this.options = Object.extend({ 7768 choices: 10, 7769 partialSearch: true, 7770 partialChars: 2, 7771 ignoreCase: true, 7772 fullSearch: false, 7773 selector: function(instance) { 7774 var ret = []; // Beginning matches 7775 var partial = []; // Inside matches 7776 var entry = instance.getToken(); 7777 var count = 0; 7778 7779 for (var i = 0; i < instance.options.array.length && 7780 ret.length < instance.options.choices ; i++) { 7781 7782 var elem = instance.options.array[i]; 7783 var foundPos = instance.options.ignoreCase ? 7784 elem.toLowerCase().indexOf(entry.toLowerCase()) : 7785 elem.indexOf(entry); 7786 7787 while (foundPos != -1) { 7788 if (foundPos == 0 && elem.length != entry.length) { 7789 ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 7790 elem.substr(entry.length) + "</li>"); 7791 break; 7792 } else if (entry.length >= instance.options.partialChars && 7793 instance.options.partialSearch && foundPos != -1) { 7794 if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) { 7795 partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" + 7796 elem.substr(foundPos, entry.length) + "</strong>" + elem.substr( 7797 foundPos + entry.length) + "</li>"); 7798 break; 7799 } 7800 } 7801 7802 foundPos = instance.options.ignoreCase ? 7803 elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 7804 elem.indexOf(entry, foundPos + 1); 7805 7806 } 7807 } 7808 if (partial.length) 7809 ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)); 7810 return "<ul>" + ret.join('') + "</ul>"; 7811 } 7812 }, options || { }); 7813 } 7814}); 7815 7816// AJAX in-place editor and collection editor 7817// Full rewrite by Christophe Porteneuve <[email protected]> (April 2007). 7818 7819// Use this if you notice weird scrolling problems on some browsers, 7820// the DOM might be a bit confused when this gets called so do this 7821// waits 1 ms (with setTimeout) until it does the activation 7822Field.scrollFreeActivate = function(field) { 7823 setTimeout(function() { 7824 Field.activate(field); 7825 }, 1); 7826}; 7827 7828Ajax.InPlaceEditor = Class.create({ 7829 initialize: function(element, url, options) { 7830 this.url = url; 7831 this.element = element = $(element); 7832 this.prepareOptions(); 7833 this._controls = { }; 7834 arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!! 7835 Object.extend(this.options, options || { });
7836 if (!this.options.formId && this.element.id) { 7837 this.options.formId = this.element.id + '-inplaceeditor'; 7838 if ($(this.options.formId)) 7839 this.options.formId = ''; 7840 } 7841 if (this.options.externalControl) 7842 this.options.externalControl = $(this.options.externalControl); 7843 if (!this.options.externalControl) 7844 this.options.externalControlOnly = false; 7845 this._originalBackground = this.element.getStyle('background-color') || 'transparent'; 7846 this.element.title = this.options.clickToEditText; 7847 this._boundCancelHandler = this.handleFormCancellation.bind(this); 7848 this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this); 7849 this._boundFailureHandler = this.handleAJAXFailure.bind(this); 7850 this._boundSubmitHandler = this.handleFormSubmission.bind(this); 7851 this._boundWrapperHandler = this.wrapUp.bind(this); 7852 this.registerListeners(); 7853 }, 7854 checkForEscapeOrReturn: function(e) { 7855 if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return; 7856 if (Event.KEY_ESC == e.keyCode) 7857 this.handleFormCancellation(e); 7858 else if (Event.KEY_RETURN == e.keyCode) 7859 this.handleFormSubmission(e); 7860 }, 7861 createControl: function(mode, handler, extraClasses) { 7862 var control = this.options[mode + 'Control']; 7863 var text = this.options[mode + 'Text']; 7864 if ('button' == control) { 7865 var btn = document.createElement('input'); 7866 btn.type = 'submit'; 7867 btn.value = text; 7868 btn.className = 'editor_' + mode + '_button'; 7869 if ('cancel' == mode) 7870 btn.onclick = this._boundCancelHandler; 7871 this._form.appendChild(btn); 7872 this._controls[mode] = btn; 7873 } else if ('link' == control) { 7874 var link = document.createElement('a'); 7875 link.href = '#'; 7876 link.appendChild(document.createTextNode(text)); 7877 link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler; 7878 link.className = 'editor_' + mode + '_link'; 7879 if (extraClasses) 7880 link.className += ' ' + extraClasses; 7881 this._form.appendChild(link); 7882 this._controls[mode] = link; 7883 } 7884 }, 7885 createEditField: function() { 7886 var text = (this.options.loadTextURL ? this.options.loadingText : this.getText()); 7887 var fld; 7888 if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) { 7889 fld = document.createElement('input'); 7890 fld.type = 'text'; 7891 var size = this.options.size || this.options.cols || 0; 7892 if (0 < size) fld.size = size; 7893 } else { 7894 fld = document.createElement('textarea'); 7895 fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows); 7896 fld.cols = this.options.cols || 40; 7897 } 7898 fld.name = this.options.paramName; 7899 fld.value = text; // No HTML breaks conversion anymore 7900 fld.className = 'editor_field'; 7901 if (this.options.submitOnBlur) 7902 fld.onblur = this._boundSubmitHandler; 7903 this._controls.editor = fld; 7904 if (this.options.loadTextURL) 7905 this.loadExternalText(); 7906 this._form.appendChild(this._controls.editor); 7907 }, 7908 createForm: function() { 7909 var ipe = this; 7910 function addText(mode, condition) { 7911 var text = ipe.options['text' + mode + 'Controls']; 7912 if (!text || condition === false) return; 7913 ipe._form.appendChild(document.createTextNode(text)); 7914 }; 7915 this._form = $(document.createElement('form')); 7916 this._form.id = this.options.formId; 7917 this._form.addClassName(this.options.formClassName); 7918 this._form.onsubmit = this._boundSubmitHandler; 7919 this.createEditField(); 7920 if ('textarea' == this._controls.editor.tagName.toLowerCase()) 7921 this._form.appendChild(document.createElement('br')); 7922 if (this.options.onFormCustomization) 7923 this.options.onFormCustomization(this, this._form); 7924 addText('Before', this.options.okControl || this.options.cancelControl); 7925 this.createControl('ok', this._boundSubmitHandler); 7926 addText('Between', this.options.okControl && this.options.cancelControl); 7927 this.createControl('cancel', this._boundCancelHandler, 'editor_cancel'); 7928 addText('After', this.options.okControl || this.options.cancelControl); 7929 }, 7930 destroy: function() { 7931 if (this._oldInnerHTML) 7932 this.element.innerHTML = this._oldInnerHTML; 7933 this.leaveEditMode(); 7934 this.unregisterListeners(); 7935 }, 7936 enterEditMode: function(e) { 7937 if (this._saving || this._editing) return; 7938 this._editing = true; 7939 this.triggerCallback('onEnterEditMode'); 7940 if (this.options.externalControl) 7941 this.options.externalControl.hide(); 7942 this.element.hide(); 7943 this.createForm(); 7944 this.element.parentNode.insertBefore(this._form, this.element); 7945 if (!this.options.loadTextURL) 7946 this.postProcessEditField();
7947 if (e) Event.stop(e); 7948 }, 7949 enterHover: function(e) { 7950 if (this.options.hoverClassName) 7951 this.element.addClassName(this.options.hoverClassName); 7952 if (this._saving) return; 7953 this.triggerCallback('onEnterHover'); 7954 }, 7955 getText: function() { 7956 return this.element.innerHTML.unescapeHTML(); 7957 }, 7958 handleAJAXFailure: function(transport) { 7959 this.triggerCallback('onFailure', transport); 7960 if (this._oldInnerHTML) { 7961 this.element.innerHTML = this._oldInnerHTML; 7962 this._oldInnerHTML = null; 7963 } 7964 }, 7965 handleFormCancellation: function(e) { 7966 this.wrapUp(); 7967 if (e) Event.stop(e); 7968 }, 7969 handleFormSubmission: function(e) { 7970 var form = this._form; 7971 var value = $F(this._controls.editor); 7972 this.prepareSubmission(); 7973 var params = this.options.callback(form, value) || ''; 7974 if (Object.isString(params)) 7975 params = params.toQueryParams(); 7976 params.editorId = this.element.id; 7977 if (this.options.htmlResponse) { 7978 var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions); 7979 Object.extend(options, { 7980 parameters: params, 7981 onComplete: this._boundWrapperHandler, 7982 onFailure: this._boundFailureHandler 7983 }); 7984 new Ajax.Updater({ success: this.element }, this.url, options); 7985 } else { 7986 var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); 7987 Object.extend(options, { 7988 parameters: params, 7989 onComplete: this._boundWrapperHandler, 7990 onFailure: this._boundFailureHandler 7991 }); 7992 new Ajax.Request(this.url, options); 7993 } 7994 if (e) Event.stop(e); 7995 }, 7996 leaveEditMode: function() { 7997 this.element.removeClassName(this.options.savingClassName); 7998 this.removeForm(); 7999 this.leaveHover(); 8000 this.element.style.backgroundColor = this._originalBackground; 8001 this.element.show(); 8002 if (this.options.externalControl) 8003 this.options.externalControl.show(); 8004 this._saving = false; 8005 this._editing = false; 8006 this._oldInnerHTML = null; 8007 this.triggerCallback('onLeaveEditMode'); 8008 }, 8009 leaveHover: function(e) { 8010 if (this.options.hoverClassName) 8011 this.element.removeClassName(this.options.hoverClassName); 8012 if (this._saving) return; 8013 this.triggerCallback('onLeaveHover'); 8014 }, 8015 loadExternalText: function() { 8016 this._form.addClassName(this.options.loadingClassName); 8017 this._controls.editor.disabled = true; 8018 var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); 8019 Object.extend(options, { 8020 parameters: 'editorId=' + encodeURIComponent(this.element.id), 8021 onComplete: Prototype.emptyFunction, 8022 onSuccess: function(transport) { 8023 this._form.removeClassName(this.options.loadingClassName); 8024 var text = transport.responseText; 8025 if (this.options.stripLoadedTextTags) 8026 text = text.stripTags(); 8027 this._controls.editor.value = text; 8028 this._controls.editor.disabled = false; 8029 this.postProcessEditField(); 8030 }.bind(this), 8031 onFailure: this._boundFailureHandler 8032 }); 8033 new Ajax.Request(this.options.loadTextURL, options); 8034 }, 8035 postProcessEditField: function() { 8036 var fpc = this.options.fieldPostCreation; 8037 if (fpc) 8038 $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate'](); 8039 }, 8040 prepareOptions: function() { 8041 this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions); 8042 Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks); 8043 [this._extraDefaultOptions].flatten().compact().each(function(defs) { 8044 Object.extend(this.options, defs); 8045 }.bind(this)); 8046 }, 8047 prepareSubmission: function() { 8048 this._saving = true; 8049 this.removeForm(); 8050 this.leaveHover(); 8051 this.showSaving(); 8052 }, 8053 registerListeners: function() { 8054 this._listeners = { }; 8055 var listener; 8056 $H(Ajax.InPlaceEditor.Listeners).each(function(pair) { 8057 listener = this[pair.value].bind(this); 8058 this._listeners[pair.key] = listener; 8059 if (!this.options.externalControlOnly) 8060 this.element.observe(pair.key, listener); 8061 if (this.options.externalControl) 8062 this.options.externalControl.observe(pair.key, listener); 8063 }.bind(this)); 8064 }, 8065 removeForm: function() { 8066 if (!this._form) return; 8067 this._form.remove(); 8068 this._form = null; 8069 this._controls = { }; 8070 }, 8071 showSaving: function() { 8072 this._oldInnerHTML = this.element.innerHTML; 8073 this.element.innerHTML = this.options.savingText; 8074 this.element.addClassName(this.options.savingClassName); 8075 this.element.style.backgroundColor = this._originalBackgroun
8075d; 8076 this.element.show(); 8077 }, 8078 triggerCallback: function(cbName, arg) { 8079 if ('function' == typeof this.options[cbName]) { 8080 this.options[cbName](this, arg); 8081 } 8082 }, 8083 unregisterListeners: function() { 8084 $H(this._listeners).each(function(pair) { 8085 if (!this.options.externalControlOnly) 8086 this.element.stopObserving(pair.key, pair.value); 8087 if (this.options.externalControl) 8088 this.options.externalControl.stopObserving(pair.key, pair.value); 8089 }.bind(this)); 8090 }, 8091 wrapUp: function(transport) { 8092 this.leaveEditMode(); 8093 // Can't use triggerCallback due to backward compatibility: requires 8094 // binding + direct element 8095 this._boundComplete(transport, this.element); 8096 } 8097}); 8098 8099Object.extend(Ajax.InPlaceEditor.prototype, { 8100 dispose: Ajax.InPlaceEditor.prototype.destroy 8101}); 8102 8103Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, { 8104 initialize: function($super, element, url, options) { 8105 this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions; 8106 $super(element, url, options); 8107 }, 8108 8109 createEditField: function() { 8110 var list = document.createElement('select'); 8111 list.name = this.options.paramName; 8112 list.size = 1; 8113 this._controls.editor = list; 8114 this._collection = this.options.collection || []; 8115 if (this.options.loadCollectionURL) 8116 this.loadCollection(); 8117 else 8118 this.checkForExternalText(); 8119 this._form.appendChild(this._controls.editor); 8120 }, 8121 8122 loadCollection: function() { 8123 this._form.addClassName(this.options.loadingClassName); 8124 this.showLoadingText(this.options.loadingCollectionText); 8125 var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); 8126 Object.extend(options, { 8127 parameters: 'editorId=' + encodeURIComponent(this.element.id), 8128 onComplete: Prototype.emptyFunction, 8129 onSuccess: function(transport) { 8130 var js = transport.responseText.strip(); 8131 if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check 8132 throw('Server returned an invalid collection representation.'); 8133 this._collection = eval(js); 8134 this.checkForExternalText(); 8135 }.bind(this), 8136 onFailure: this.onFailure 8137 }); 8138 new Ajax.Request(this.options.loadCollectionURL, options); 8139 }, 8140 8141 showLoadingText: function(text) { 8142 this._controls.editor.disabled = true; 8143 var tempOption = this._controls.editor.firstChild; 8144 if (!tempOption) { 8145 tempOption = document.createElement('option'); 8146 tempOption.value = ''; 8147 this._controls.editor.appendChild(tempOption); 8148 tempOption.selected = true; 8149 } 8150 tempOption.update((text || '').stripScripts().stripTags()); 8151 }, 8152 8153 checkForExternalText: function() { 8154 this._text = this.getText(); 8155 if (this.options.loadTextURL) 8156 this.loadExternalText(); 8157 else 8158 this.buildOptionList(); 8159 }, 8160 8161 loadExternalText: function() { 8162 this.showLoadingText(this.options.loadingText); 8163 var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); 8164 Object.extend(options, { 8165 parameters: 'editorId=' + encodeURIComponent(this.element.id), 8166 onComplete: Prototype.emptyFunction, 8167 onSuccess: function(transport) { 8168 this._text = transport.responseText.strip(); 8169 this.buildOptionList(); 8170 }.bind(this), 8171 onFailure: this.onFailure 8172 }); 8173 new Ajax.Request(this.options.loadTextURL, options); 8174 }, 8175 8176 buildOptionList: function() { 8177 this._form.removeClassName(this.options.loadingClassName); 8178 this._collection = this._collection.map(function(entry) { 8179 return 2 === entry.length ? entry : [entry, entry].flatten(); 8180 }); 8181 var marker = ('value' in this.options) ? this.options.value : this._text; 8182 var textFound = this._collection.any(function(entry) { 8183 return entry[0] == marker; 8184 }.bind(this)); 8185 this._controls.editor.update(''); 8186 var option; 8187 this._collection.each(function(entry, index) { 8188 option = document.createElement('option'); 8189 option.value = entry[0]; 8190 option.selected = textFound ? entry[0] == marker : 0 == index; 8191 option.appendChild(document.createTextNode(entry[1])); 8192 this._controls.editor.appendChild(option); 8193 }.bind(this)); 8194 this._controls.editor.disabled = false;
8195 Field.scrollFreeActivate(this._controls.editor); 8196 } 8197}); 8198 8199//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! **** 8200//**** This only exists for a while, in order to let **** 8201//**** users adapt to the new API. Read up on the new **** 8202//**** API and convert your code to it ASAP! **** 8203 8204Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) { 8205 if (!options) return; 8206 function fallback(name, expr) { 8207 if (name in options || expr === undefined) return; 8208 options[name] = expr; 8209 }; 8210 fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' : 8211 options.cancelLink == options.cancelButton == false ? false : undefined))); 8212 fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' : 8213 options.okLink == options.okButton == false ? false : undefined))); 8214 fallback('highlightColor', options.highlightcolor); 8215 fallback('highlightEndColor', options.highlightendcolor); 8216}; 8217 8218Object.extend(Ajax.InPlaceEditor, { 8219 DefaultOptions: { 8220 ajaxOptions: { }, 8221 autoRows: 3, // Use when multi-line w/ rows == 1 8222 cancelControl: 'link', // 'link'|'button'|false 8223 cancelText: 'cancel', 8224 clickToEditText: 'Click to edit', 8225 externalControl: null, // id|elt 8226 externalControlOnly: false, 8227 fieldPostCreation: 'activate', // 'activate'|'focus'|false 8228 formClassName: 'inplaceeditor-form', 8229 formId: null, // id|elt 8230 highlightColor: '#ffff99', 8231 highlightEndColor: '#ffffff', 8232 hoverClassName: '', 8233 htmlResponse: true, 8234 loadingClassName: 'inplaceeditor-loading', 8235 loadingText: 'Loading...', 8236 okControl: 'button', // 'link'|'button'|false 8237 okText: 'ok', 8238 paramName: 'value', 8239 rows: 1, // If 1 and multi-line, uses autoRows 8240 savingClassName: 'inplaceeditor-saving', 8241 savingText: 'Saving...', 8242 size: 0, 8243 stripLoadedTextTags: false, 8244 submitOnBlur: false, 8245 textAfterControls: '', 8246 textBeforeControls: '', 8247 textBetweenControls: '' 8248 }, 8249 DefaultCallbacks: { 8250 callback: function(form) { 8251 return Form.serialize(form); 8252 }, 8253 onComplete: function(transport, element) { 8254 // For backward compatibility, this one is bound to the IPE, and passes 8255 // the element directly. It was too often customized, so we don't break it. 8256 new Effect.Highlight(element, { 8257 startcolor: this.options.highlightColor, keepBackgroundImage: true }); 8258 }, 8259 onEnterEditMode: null, 8260 onEnterHover: function(ipe) { 8261 ipe.element.style.backgroundColor = ipe.options.highlightColor; 8262 if (ipe._effect) 8263 ipe._effect.cancel(); 8264 }, 8265 onFailure: function(transport, ipe) { 8266 alert('Error communication with the server: ' + transport.responseText.stripTags()); 8267 }, 8268 onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls. 8269 onLeaveEditMode: null, 8270 onLeaveHover: function(ipe) { 8271 ipe._effect = new Effect.Highlight(ipe.element, { 8272 startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor, 8273 restorecolor: ipe._originalBackground, keepBackgroundImage: true 8274 }); 8275 } 8276 }, 8277 Listeners: { 8278 click: 'enterEditMode', 8279 keydown: 'checkForEscapeOrReturn', 8280 mouseover: 'enterHover', 8281 mouseout: 'leaveHover' 8282 } 8283}); 8284 8285Ajax.InPlaceCollectionEditor.DefaultOptions = { 8286 loadingCollectionText: 'Loading options...' 8287}; 8288 8289// Delayed observer, like Form.Element.Observer, 8290// but waits for delay after last key input 8291// Ideal for live-search fields 8292 8293Form.Element.DelayedObserver = Class.create({ 8294 initialize: function(element, delay, callback) { 8295 this.delay = delay || 0.5; 8296 this.element = $(element); 8297 this.callback = callback; 8298 this.timer = null; 8299 this.lastValue = $F(this.element); 8300 Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this)); 8301 }, 8302 delayedListener: function(event) { 8303 if(this.lastValue == $F(this.element)) return; 8304 if(this.timer) clearTimeout(this.timer); 8305 this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000); 8306 this.lastValue = $F(this.element); 8307 },
8308 onTimerEvent: function() { 8309 this.timer = null; 8310 this.callback(this.element, $F(this.element)); 8311 } 8312}); 8313// script.aculo.us slider.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008 8314 8315// Copyright (c) 2005-2008 Marty Haught, Thomas Fuchs 8316// 8317// script.aculo.us is freely distributable under the terms of an MIT-style license. 8318// For details, see the script.aculo.us web site: http://script.aculo.us/ 8319 8320if (!Control) var Control = { }; 8321 8322// options: 8323// axis: 'vertical', or 'horizontal' (default) 8324// 8325// callbacks: 8326// onChange(value) 8327// onSlide(value) 8328Control.Slider = Class.create({ 8329 initialize: function(handle, track, options) { 8330 var slider = this; 8331 8332 if (Object.isArray(handle)) { 8333 this.handles = handle.collect( function(e) { return $(e) }); 8334 } else { 8335 this.handles = [$(handle)]; 8336 } 8337 8338 this.track = $(track); 8339 this.options = options || { }; 8340 8341 this.axis = this.options.axis || 'horizontal'; 8342 this.increment = this.options.increment || 1; 8343 this.step = parseInt(this.options.step || '1'); 8344 this.range = this.options.range || $R(0,1); 8345 8346 this.value = 0; // assure backwards compat 8347 this.values = this.handles.map( function() { return 0 }); 8348 this.spans = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false; 8349 this.options.startSpan = $(this.options.startSpan || null); 8350 this.options.endSpan = $(this.options.endSpan || null); 8351 8352 this.restricted = this.options.restricted || false; 8353 8354 this.maximum = this.options.maximum || this.range.end; 8355 this.minimum = this.options.minimum || this.range.start; 8356 8357 // Will be used to align the handle onto the track, if necessary 8358 this.alignX = parseInt(this.options.alignX || '0'); 8359 this.alignY = parseInt(this.options.alignY || '0'); 8360 8361 this.trackLength = this.maximumOffset() - this.minimumOffset(); 8362 8363 this.handleLength = this.isVertical() ? 8364 (this.handles[0].offsetHeight != 0 ? 8365 this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) : 8366 (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth : 8367 this.handles[0].style.width.replace(/px$/,"")); 8368 8369 this.active = false; 8370 this.dragging = false; 8371 this.disabled = false; 8372 8373 if (this.options.disabled) this.setDisabled(); 8374 8375 // Allowed values array 8376 this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false; 8377 if (this.allowedValues) { 8378 this.minimum = this.allowedValues.min(); 8379 this.maximum = this.allowedValues.max(); 8380 } 8381 8382 this.eventMouseDown = this.startDrag.bindAsEventListener(this); 8383 this.eventMouseUp = this.endDrag.bindAsEventListener(this); 8384 this.eventMouseMove = this.update.bindAsEventListener(this); 8385 8386 // Initialize handles in reverse (make sure first handle is active) 8387 this.handles.each( function(h,i) { 8388 i = slider.handles.length-1-i; 8389 slider.setValue(parseFloat( 8390 (Object.isArray(slider.options.sliderValue) ? 8391 slider.options.sliderValue[i] : slider.options.sliderValue) || 8392 slider.range.start), i); 8393 h.makePositioned().observe("mousedown", slider.eventMouseDown); 8394 }); 8395 8396 this.track.observe("mousedown", this.eventMouseDown); 8397 document.observe("mouseup", this.eventMouseUp); 8398 $(this.track.parentNode.parentNode).observe("mousemove", this.eventMouseMove); 8399 8400 8401 this.initialized = true; 8402 }, 8403 dispose: function() { 8404 var slider = this; 8405 Event.stopObserving(this.track, "mousedown", this.eventMouseDown); 8406 Event.stopObserving(document, "mouseup", this.eventMouseUp); 8407 Event.stopObserving(this.track.parentNode.parentNode, "mousemove", this.eventMouseMove); 8408 this.handles.each( function(h) { 8409 Event.stopObserving(h, "mousedown", slider.eventMouseDown); 8410 }); 8411 }, 8412 setDisabled: function(){ 8413 this.disabled = true; 8414 this.track.parentNode.className = this.track.parentNode.className + ' disabled'; 8415 }, 8416 setEnabled: function(){ 8417 this.disabled = false; 8418 }, 8419 getNearestValue: function(value){ 8420 if (this.allowedValues){ 8421 if (value >= this.allowedValues.max()) return(this.allowedValues.max()); 8422 if (value <= this.allowedValues.min()) return(this.allowedValues.min()); 8423 8424 var offset = Math.abs(this.allowedValues[0] - value); 8425 var newValue = this.allowedValues[0]; 8426 this.allowedValues.each( function(v) { 8427 var currentOffset = Math.abs(v - value); 8428 if (currentOffset <= offset){ 8429 newValue = v; 8430 offset = currentOffset; 8431 } 8432 }); 8433 return newValue; 8434 } 8435 if (value > this.range.end) return this.range.end; 8436 if (value < this.range.start) return this.range.start; 8437 return value; 8438 }, 8439 setValue: function(sliderValue, handleIdx){ 8440 if (!this.active) { 8441 this.activeHandleIdx = handleIdx || 0; 8442 this.activeHandle = this.handles[this.activeHandleIdx]; 8443 this.updateStyles(); 8444 } 8445 handleIdx = handleIdx || this.activeHandleIdx || 0; 8446 if (this.initialized && this.restricted) { 8447 if ((handleIdx>0) && (sliderValue<this.values[handleIdx-1])) 8448 sliderValue = this.values[handleIdx-1]; 8449 if ((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1])) 8450 sliderValue = this.values[handleIdx+1]; 8451 } 8452 sliderValue = this.getNearestValue(sliderValue); 8453 this.values[handleIdx] = sliderValue; 8454 this.value = this.values[0]; // assure backwards compat 8455 8456 this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] = 8457 this.translateToPx(sliderValue); 8458 8459 this.drawSpans(); 8460 if (!this.dragging || !this.event) this.updateFinished(); 8461 }, 8462 setValueBy: function(delta, handleIdx) { 8463 this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta, 8464 handleIdx || this.activeHandleIdx || 0); 8465 }, 8466 translateToPx: function(value) { 8467 return Math.round( 8468 ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) * 8469 (value - this.range.start)) + "px"; 8470 }, 8471 translateToValue: function(offset) { 8472 return ((offset/(this.trackLength-this.handleLength) * 8473 (this.range.end-this.range.start)) + this.range.start); 8474 }, 8475 getRange: function(range) { 8476 var v = this.values.sortBy(Prototype.K); 8477 range = range || 0; 8478 return $R(v[range],v[range+1]); 8479 }, 8480 minimumOffset: function(){ 8481 return(this.isVertical() ? this.alignY : this.alignX); 8482 }, 8483 maximumOffset: function(){ 8484 return(this.isVertical() ? 8485 (this.track.offsetHeight != 0 ? this.track.offsetHeight : 8486 this.track.style.height.replace(/px$/,"")) - this.alignY : 8487 (this.track.offsetWidth != 0 ? this.track.offsetWidth : 8488 this.track.style.width.replace(/px$/,"")) - this.alignX); 8489 }, 8490 isVertical: function(){ 8491 return (this.axis == 'vertical'); 8492 }, 8493 drawSpans: function() { 8494 var slider = this; 8495 if (this.spans) 8496 $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) }); 8497 if (this.options.startSpan) 8498 this.setSpan(this.options.startSpan, 8499 $R(0, this.values.length>1 ? this.getRange(0).min() : this.value )); 8500 if (this.options.endSpan) 8501 this.setSpan(this.options.endSpan, 8502 $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum)); 8503 }, 8504 setSpan: function(span, range) { 8505 if (this.isVertical()) { 8506 span.style.top = this.translateToPx(range.start); 8507 span.style.height = this.translateToPx(range.end - range.start + this.range.start); 8508 } else { 8509 span.style.left = this.translateToPx(range.start); 8510 span.style.width = this.translateToPx(range.end - range.start + this.range.start); 8511 } 8512 }, 8513 updateStyles: function() { 8514 this.handles.each( function(h){ Element.removeClassName(h, 'selected') }); 8515 Element.addClassName(this.activeHandle, 'selected'); 8516 }, 8517 startDrag: function(event) { 8518 if (Event.isLeftClick(event)) { 8519 if (!this.disabled){ 8520 this.active = true; 8521 8522 var handle = Event.element(event); 8523 var pointer = [Event.pointerX(event), Event.pointerY(event)]; 8524 var track = handle; 8525 if (track==this.track) { 8526 var offsets = Position.cumulativeOffset(this.track); 8527 this.event = event; 8528 this.setValue(this.translateToValue( 8529 (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2) 8530 )); 8531 var offsets = Position.cumulativeOffset(this.activeHandle); 8532 this.offsetX = (pointer[0] - offsets[0]); 8533 this.offsetY = (pointer[1] - offsets[1]); 8534 } else { 8535 // find the handle (prevents issues with Safari) 8536 while((this.handles.indexOf(handle) == -1) && handle.parentNode) 8537 handle = handle.parentNode; 8538 8539 if (this.handles.indexOf(handle)!=-1) { 8540 this.activeHandle = handle; 8541 this.activeHandleIdx = this.handles.indexOf(this.activeHandle); 8542 this.updateStyles(); 8543 8544 var offsets = Position.cumulativeOffset(this.activeHandle); 8545 this.offsetX = (pointer[0] - offsets[0]); 8546 this.offsetY = (pointer[1] - offsets[1]); 8547 } 8548 } 8549 } 8550 Event.stop(event); 8551 } 8552 }, 8553 update: function(event) { 8554 if (this.active) { 8555 if (!this.dragging) this.dragging = true; 8556 this.draw(event); 8557 if (Prototype.Browser.WebKit) window.scrollBy(0,0); 8558 Event.stop(event); 8559 } 8560 }, 8561 draw: function(event) { 8562 var pointer = [Event.pointerX(event), Event.pointerY(event)]; 8563 var offsets = Position.cumulativeOffset(this.track); 8564 pointer[0] -= this.offsetX + offsets[0]; 8565 pointer[1] -= this.offsetY + offsets[1]; 8566 this.event = event; 8567 this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] )); 8568 if (this.initialized && this.options.onSlide) 8569 this.options.onSlide(this.values.length>1 ? this.values : this.value, this); 8570 }, 8571 endDrag: function(event) { 8572 if (this.active && this.dragging) { 8573 this.finishDrag(event, true); 8574 Event.stop(event); 8575 } 8576 this.active = false;
8577 this.dragging = false; 8578 }, 8579 finishDrag: function(event, success) { 8580 this.active = false; 8581 this.dragging = false; 8582 this.updateFinished(); 8583 }, 8584 updateFinished: function() { 8585 if (this.initialized && this.options.onChange) 8586 this.options.onChange(this.values.length>1 ? this.values : this.value, this); 8587 this.event = null; 8588 } 8589}); 8590/** 8591 * Magento 8592 * 8593 * NOTICE OF LICENSE 8594 * 8595 * This source file is subject to the Academic Free License (AFL 3.0) 8596 * that is bundled with this package in the file LICENSE_AFL.txt. 8597 * It is also available through the world-wide-web at this URL: 8598 * http://opensource.org/licenses/afl-3.0.php 8599 * If you did not receive a copy of the license and are unable to 8600 * obtain it through the world-wide-web, please send an email 8601 * to [email protected] so we can send you a copy immediately. 8602 * 8603 * DISCLAIMER 8604 * 8605 * Do not edit or add to this file if you wish to upgrade Magento to newer 8606 * versions in the future. If you wish to customize Magento for your 8607 * needs please refer to http://www.magentocommerce.com for more information. 8608 * 8609 * @category Varien 8610 * @package js 8611 * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) 8612 * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) 8613 */ 8614 8615function openModalBox(panel,titleText) { 8616 $(panel).style.overflow='auto'; 8617 Shadowbox.open({ 8618 content: $(panel).innerHTML, 8619 player: "html", 8620 title: titleText, 8621 height: 350, 8622 width: 350 8623 }); 8624} 8625 8626function popWin(url,win,para) { 8627 var win = window.open(url,win,para); 8628 win.focus(); 8629} 8630 8631function setLocation(url){ 8632 window.location.href = url; 8633} 8634 8635function setPLocation(url, setFocus){ 8636 if( setFocus ) { 8637 window.opener.focus(); 8638 } 8639 window.opener.location.href = url; 8640} 8641 8642function setLanguageCode(code, fromCode){ 8643 //TODO: javascript cookies have different domain and path than php cookies 8644 var href = window.location.href; 8645 var after = '', dash; 8646 if (dash = href.match(/\#(.*)$/)) { 8647 href = href.replace(/\#(.*)$/, ''); 8648 after = dash[0]; 8649 } 8650 8651 if (href.match(/[?]/)) { 8652 var re = /([?&]store=)[a-z0-9_]*/; 8653 if (href.match(re)) { 8654 href = href.replace(re, '$1'+code); 8655 } else { 8656 href += '&store='+code; 8657 } 8658 8659 var re = /([?&]from_store=)[a-z0-9_]*/; 8660 if (href.match(re)) { 8661 href = href.replace(re, ''); 8662 } 8663 } else { 8664 href += '?store='+code; 8665 } 8666 if (typeof(fromCode) != 'undefined') { 8667 href += '&from_store='+fromCode; 8668 } 8669 href += after; 8670 8671 setLocation(href); 8672} 8673 8674/** 8675 * Add classes to specified elements. 8676 * Supported classes are: 'odd', 'even', 'first', 'last' 8677 * 8678 * @param elements - array of elements to be decorated 8679 * [@param decorateParams] - array of classes to be set. If omitted, all available will be used 8680 */ 8681function decorateGeneric(elements, decorateParams) 8682{ 8683 var allSupportedParams = ['odd', 'even', 'first', 'last']; 8684 var _decorateParams = {}; 8685 var total = elements.length; 8686 8687 if (total) { 8688 // determine params called 8689 if (typeof(decorateParams) == 'undefined') { 8690 decorateParams = allSupportedParams; 8691 } 8692 if (!decorateParams.length) { 8693 return; 8694 } 8695 for (var k in allSupportedParams) { 8696 _decorateParams[allSupportedParams[k]] = false; 8697 } 8698 for (var k in decorateParams) { 8699 _decorateParams[decorateParams[k]] = true; 8700 } 8701 8702 // decorate elements 8703 // elements[0].addClassName('first'); // will cause bug in IE (#5587) 8704 if (_decorateParams.first) { 8705 Element.addClassName(elements[0], 'first'); 8706 } 8707 if (_decorateParams.last) { 8708 Element.addClassName(elements[total-1], 'last'); 8709 } 8710 for (var i = 0; i < total; i++) { 8711 if ((i + 1) % 2 == 0) { 8712 if (_decorateParams.even) { 8713 Element.addClassName(elements[i], 'even'); 8714 } 8715 } 8716 else { 8717 if (_decorateParams.odd) { 8718 Element.addClassName(elements[i], 'odd'); 8719 } 8720 } 8721 } 8722 } 8723} 8724 8725/** 8726 * Decorate table rows and cells, tbody etc 8727 * @see decorateGeneric() 8728 */ 8729function decorateTable(table, options) { 8730 var table = $(table); 8731 if (table) { 8732 // set default options 8733 var _options = { 8734 'tbody' : false, 8735 'tbody tr' : ['odd', 'even', 'first', 'last'], 8736 'thead tr' : ['first', 'last'], 8737 'tfoot tr' : ['first', 'last'], 8738 'tr td' : ['last'] 8739 }; 8740 // overload options 8741 if (typeof(options) != 'undefined') { 8742 for (var k in options) { 8743 _options[k] = options[k]; 8744 } 8745 } 8746 // decorate 8747 if (_options['tbody']) { 8748 decorateGeneric(table.select('tbody'), _options['tbody']); 8749 } 8750 if (_options['tbody tr']) { 8751 decorateGeneric(table.select('tbody tr'), _options['tbody tr']); 8752 } 8753 if (_options['thead tr']) { 8754 decorateGeneric(table.select('thead tr'), _options['thead tr']); 8755 } 8756 if (_options['tfoot tr']) { 8757 decorateGeneric(table.select('tfoot tr'), _options['tfoot tr']); 8758 } 8759 if (_options['tr td']) { 8760 var allRows = table.select('tr'); 8761 if (allRows.length) { 8762 for (var i = 0; i < allRows.length; i++) { 8763 decorateGeneric(allRows[i].getElementsByTagName('TD'), _options['tr td']); 8764 } 8765 } 8766 } 8767 } 8768} 8769 8770/** 8771 * Set "odd", "even" and "last" CSS classes for list items 8772 * @see decorateGeneric() 8773 */ 8774function decorateList(list, nonRecursive) { 8775 if ($(list)) { 8776 if (typeof(nonRecursive) == 'undefined') { 8777 var items = $(list).select('li') 8778 } 8779 else { 8780 var items = $(list).childElements(); 8781 } 8782 decorateGeneric(items, ['odd', 'even', 'last']); 8783 } 8784} 8785 8786/** 8787 * Set "odd", "even" and "last" CSS classes for list items 8788 * @see decorateGeneric() 8789 */ 8790function decorateDataList(list) { 8791 list = $(list); 8792 if (list) { 8793 decorateGeneric(list.select('dt'), ['odd', 'even', 'last']); 8794 decorateGeneric(list.select('dd'), ['odd', 'even', 'last']); 8795 } 8796} 8797 8798/** 8799 * Parse SID and produces the correct URL 8800 */ 8801function parseSidUrl(baseUrl, urlExt) { 8802 sidPos = baseUrl.indexOf('/?SID='); 8803 sid = ''; 8804 urlExt = (urlExt != undefined) ? urlExt : ''; 8805 8806 if(sidPos > -1) { 8807 sid = '?' + baseUrl.substring(sidPos + 2); 8808 baseUrl = baseUrl.substring(0, sidPos + 1); 8809 } 8810 8811 return baseUrl+urlExt+sid; 8812} 8813 8814/** 8815 * Formats currency using patern 8816 * format - JSON (pattern, decimal, decimalsDelimeter, groupsDelimeter) 8817 * showPlus - true (always show '+'or '-'), 8818 * false (never show '-' even if number is negative) 8819 * null (show '-' if number is negative) 8820 */ 8821 8822function formatCurrency(price, format, showPlus){ 8823 precision = isNaN(format.precision = Math.abs(format.precision)) ? 2 : format.precision; 8824 requiredPrecision = isNaN(format.requiredPrecision = Math.abs(format.requiredPrecision)) ? 2 : format.requiredPrecision; 8825 8826 //precision = (precision > requiredPrecision) ? precision : requiredPrecision; 8827 //for now we don't need this difference so precision is requiredPrecision 8828 precision = requiredPrecision; 8829 8830 integerRequired = isNaN(format.integerRequired = Math.abs(format.integerRequired)) ? 1 : format.integerRequired; 8831 8832 decimalSymbol = format.decimalSymbol == undefined ? "," : format.decimalSymbol; 8833 groupSymbol = format.groupSymbol == undefined ? "." : format.groupSymbol; 8834 groupLength = format.groupLength == undefined ? 3 : format.groupLength; 8835 8836 if (showPlus == undefined || showPlus == true) { 8837 s = price < 0 ? "-" : ( showPlus ? "+" : ""); 8838 } else if (showPlus == false) { 8839 s = ''; 8840 } 8841 8842 i = parseInt(price = Math.abs(+price || 0).toFixed(precision)) + ""; 8843 pad = (i.length < integerRequired) ? (integerRequired - i.length) : 0; 8844 while (pad) { i = '0' + i; pad--; } 8845 8846 j = (j = i.length) > groupLength ? j % groupLength : 0; 8847 re = new RegExp("(\\d{" + groupLength + "})(?=\\d)", "g"); 8848 8849 /** 8850 * replace(/-/, 0) is only for fixing Safari bug which appears 8851 * when Math.abs(0).toFixed() executed on "0" number. 8852 * Result is "0.-0" :( 8853 */ 8854 r = (j ? i.substr(0, j) + groupSymbol : "") + i.substr(j).replace(re, "$1" + groupSymbol) + (precision ? decimalSymbol + Math.abs(price - i).toFixed(precision).replace(/-/, 0).slice(2) : "") 8855 8856 if (format.pattern.indexOf('{sign}') == -1) { 8857 pattern = s + format.pattern; 8858 } else { 8859 pattern = format.pattern.replace('{sign}', s); 8860 } 8861 8862 return pattern.replace('%s', r).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); 8863}; 8864 8865function expandDetails(el, childClass) {
8866 if (Element.hasClassName(el,'show-details')) { 8867 $$(childClass).each(function(item){item.hide()}); 8868 Element.removeClassName(el,'show-details'); 8869 } 8870 else { 8871 $$(childClass).each(function(item){item.show()}); 8872 Element.addClassName(el,'show-details'); 8873 } 8874} 8875 8876// Version 1.0 8877var isIE = navigator.appVersion.match(/MSIE/) == "MSIE"; 8878 8879if (!window.Varien) 8880 var Varien = new Object(); 8881 8882Varien.showLoading = function(){ 8883 Element.show('loading-process'); 8884} 8885Varien.hideLoading = function(){ 8886 Element.hide('loading-process'); 8887} 8888Varien.GlobalHandlers = { 8889 onCreate: function() { 8890 Varien.showLoading(); 8891 }, 8892 8893 onComplete: function() { 8894 if(Ajax.activeRequestCount == 0) { 8895 Varien.hideLoading(); 8896 } 8897 } 8898}; 8899 8900Ajax.Responders.register(Varien.GlobalHandlers); 8901 8902/** 8903 * Quick Search form client model 8904 */ 8905Varien.searchForm = Class.create(); 8906Varien.searchForm.prototype = { 8907 initialize : function(form, field, emptyText){ 8908 this.form = $(form); 8909 this.field = $(field); 8910 this.emptyText = emptyText; 8911 8912 Event.observe(this.form, 'submit', this.submit.bind(this)); 8913 Event.observe(this.field, 'focus', this.focus.bind(this)); 8914 Event.observe(this.field, 'blur', this.blur.bind(this)); 8915 this.blur(); 8916 }, 8917 8918 submit : function(event){ 8919 if (this.field.value == this.emptyText || this.field.value == ''){ 8920 Event.stop(event); 8921 return false; 8922 } 8923 return true; 8924 }, 8925 8926 focus : function(event){ 8927 if(this.field.value==this.emptyText){ 8928 this.field.value=''; 8929 } 8930 8931 }, 8932 8933 blur : function(event){ 8934 if(this.field.value==''){ 8935 this.field.value=this.emptyText; 8936 } 8937 }, 8938 8939 initAutocomplete : function(url, destinationElement){ 8940 new Ajax.Autocompleter( 8941 this.field, 8942 destinationElement, 8943 url, 8944 { 8945 paramName: this.field.name, 8946 method: 'get', 8947 minChars: 2, 8948 updateElement: this._selectAutocompleteItem.bind(this), 8949 onShow : function(element, update) { 8950 if(!update.style.position || update.style.position=='absolute') { 8951 update.style.position = 'absolute'; 8952 Position.clone(element, update, { 8953 setHeight: false, 8954 offsetTop: element.offsetHeight 8955 }); 8956 }
8957 Effect.Appear(update,{duration:0}); 8958 } 8959 8960 } 8961 ); 8962 }, 8963 8964 _selectAutocompleteItem : function(element){ 8965 if(element.title){ 8966 this.field.value = element.title; 8967 } 8968 this.form.submit(); 8969 } 8970} 8971 8972Varien.Tabs = Class.create(); 8973Varien.Tabs.prototype = { 8974 initialize: function(selector) { 8975 var self=this; 8976 $$(selector+' a').each(this.initTab.bind(this)); 8977 }, 8978 8979 initTab: function(el) { 8980 el.href = 'javascript:void(0)'; 8981 if ($(el.parentNode).hasClassName('active')) { 8982 this.showContent(el); 8983 } 8984 el.observe('click', this.showContent.bind(this, el)); 8985 }, 8986 8987 showContent: function(a) { 8988 var li = $(a.parentNode), ul = $(li.parentNode); 8989 ul.getElementsBySelector('li', 'ol').each(function(el){ 8990 var contents = $(el.id+'_contents'); 8991 if (el==li) { 8992 el.addClassName('active'); 8993 contents.show(); 8994 } else { 8995 el.removeClassName('active'); 8996 contents.hide(); 8997 } 8998 }); 8999 } 9000} 9001 9002Varien.DOB = Class.create(); 9003Varien.DOB.prototype = { 9004 initialize: function(selector, required, format) { 9005 var el = $$(selector)[0]; 9006 this.day = Element.select($(el), '.dob-day input')[0]; 9007 this.month = Element.select($(el), '.dob-month input')[0]; 9008 this.year = Element.select($(el), '.dob-year input')[0]; 9009 this.dob = Element.select($(el), '.dob-full input')[0]; 9010 this.advice = Element.select($(el), '.validation-advice')[0]; 9011 this.required = required; 9012 this.format = format; 9013 9014 this.day.validate = this.validate.bind(this); 9015 this.month.validate = this.validate.bind(this); 9016 this.year.validate = this.validate.bind(this); 9017 9018 this.advice.hide(); 9019 }, 9020 9021 validate: function() { 9022 var error = false; 9023 9024 if (this.day.value=='' && this.month.value=='' && this.year.value=='') { 9025 if (this.required) { 9026 error = 'This date is a required value.'; 9027 } else { 9028 this.dob.value = ''; 9029 } 9030 } else if (this.day.value=='' || this.month.value=='' || this.year.value=='') { 9031 error = 'Please enter a valid full date.'; 9032 } else { 9033 var date = new Date(); 9034 if (this.day.value<1 || this.day.value>31) { 9035 error = 'Please enter a valid day (1-31).'; 9036 } else if (this.month.value<1 || this.month.value>12) { 9037 error = 'Please enter a valid month (1-12).'; 9038 } else if (this.year.value<1900 || this.year.value>date.getFullYear()) { 9039 error = 'Please enter a valid year (1900-'+date.getFullYear()+').'; 9040 } else { 9041 this.dob.value = this.format.replace(/(%m|%b)/i, this.month.value).replace(/(%d|%e)/i, this.day.value).replace(/%y/i, this.year.value); 9042 var testDOB = this.month.value + '/' + this.day.value + '/'+ this.year.value; 9043 var test = new Date(testDOB); 9044 if (isNaN(test)) { 9045 error = 'Please enter a valid date.'; 9046 } 9047 } 9048 } 9049 9050 if (error !== false) { 9051 try { 9052 this.advice.innerHTML = Translator.translate(error); 9053 } 9054 catch (e) { 9055 this.advice.innerHTML = error; 9056 } 9057 this.advice.show(); 9058 return false; 9059 } 9060 9061 this.advice.hide(); 9062 return true; 9063 } 9064} 9065 9066Validation.addAllThese([ 9067 ['validate-custom', ' ', function(v,elm) { 9068 return elm.validate(); 9069 }] 9070]); 9071 9072function truncateOptions() { 9073 $$('.truncated').each(function(element){ 9074 Event.observe(element, 'mouseover', function(){ 9075 if (element.down('div.truncated_full_value')) { 9076 element.down('div.truncated_full_value').addClassName('show') 9077 } 9078 }); 9079 Event.observe(element, 'mouseout', function(){ 9080 if (element.down('div.truncated_full_value')) { 9081 element.down('div.truncated_full_value').removeClassName('show') 9082 } 9083 }); 9084 9085 }); 9086} 9087Event.observe(window, 'load', function(){ 9088 truncateOptions(); 9089}); 9090 9091Element.addMethods({ 9092 getInnerText: function(element) 9093 { 9094 element = $(element); 9095 if(element.innerText && !Prototype.Browser.Opera) { 9096 return element.innerText 9097 } 9098 return element.innerHTML.stripScripts().unescapeHTML().replace(/[\n\r\s]+/g, ' '); 9099 } 9100}); 9101/* 9102if (!("console" in window) || !("firebug" in console)) 9103{ 9104 var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", 9105 "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; 9106 9107 window.console = {}; 9108 for (var i = 0; i < names.length; ++i) 9109 window.console[names[i]] = function() {} 9110} 9111*/ 9112/** 9113 * Magento 9114 * 9115 * NOTICE OF LICENSE 9116 * 9117 * This source file is subject to the Academic Free License (AFL 3.0)
9118 * that is bundled with this package in the file LICENSE_AFL.txt. 9119 * It is also available through the world-wide-web at this URL: 9120 * http://opensource.org/licenses/afl-3.0.php 9121 * If you did not receive a copy of the license and are unable to 9122 * obtain it through the world-wide-web, please send an email 9123 * to [email protected] so we can send you a copy immediately. 9124 * 9125 * DISCLAIMER 9126 * 9127 * Do not edit or add to this file if you wish to upgrade Magento to newer 9128 * versions in the future. If you wish to customize Magento for your 9129 * needs please refer to http://www.magentocommerce.com for more information. 9130 * 9131 * @category Varien 9132 * @package js 9133 * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) 9134 * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) 9135 */ 9136VarienForm = Class.create(); 9137VarienForm.prototype = { 9138 initialize: function(formId, firstFieldFocus){ 9139 this.form = $(formId); 9140 if (!this.form) { 9141 return; 9142 } 9143 this.cache = $A(); 9144 this.currLoader = false;
9145 this.currDataIndex = false; 9146 this.validator = new Validation(this.form); 9147 this.elementFocus = this.elementOnFocus.bindAsEventListener(this); 9148 this.elementBlur = this.elementOnBlur.bindAsEventListener(this); 9149 this.childLoader = this.onChangeChildLoad.bindAsEventListener(this); 9150 this.highlightClass = 'highlight'; 9151 this.extraChildParams = ''; 9152 this.firstFieldFocus= firstFieldFocus || false; 9153 this.bindElements(); 9154 if(this.firstFieldFocus){ 9155 try{ 9156 Form.Element.focus(Form.findFirstElement(this.form)) 9157 } 9158 catch(e){} 9159 } 9160 }, 9161 9162 submit : function(url){ 9163 if(this.validator && this.validator.validate()){ 9164 this.form.submit(); 9165 } 9166 return false; 9167 }, 9168 9169 bindElements:function (){ 9170 var elements = Form.getElements(this.form); 9171 for (var row in elements) { 9172 if (elements[row].id) { 9173 Event.observe(elements[row],'focus',this.elementFocus); 9174 Event.observe(elements[row],'blur',this.elementBlur); 9175 } 9176 } 9177 }, 9178 9179 elementOnFocus: function(event){ 9180 var element = Event.findElement(event, 'fieldset'); 9181 if(element){ 9182 Element.addClassName(element, this.highlightClass); 9183 } 9184 }, 9185 9186 elementOnBlur: function(event){ 9187 var element = Event.findElement(event, 'fieldset'); 9188 if(element){ 9189 Element.removeClassName(element, this.highlightClass); 9190 } 9191 }, 9192 9193 setElementsRelation: function(parent, child, dataUrl, first){ 9194 if (parent=$(parent)) { 9195 // TODO: array of relation and caching 9196 if (!this.cache[parent.id]){ 9197 this.cache[parent.id] = $A(); 9198 this.cache[parent.id]['child'] = child; 9199 this.cache[parent.id]['dataUrl'] = dataUrl; 9200 this.cache[parent.id]['data'] = $A(); 9201 this.cache[parent.id]['first'] = first || false; 9202 } 9203 Event.observe(parent,'change',this.childLoader); 9204 } 9205 }, 9206 9207 onChangeChildLoad: function(event){ 9208 element = Event.element(event); 9209 this.elementChildLoad(element); 9210 }, 9211 9212 elementChildLoad: function(element, callback){ 9213 this.callback = callback || false; 9214 if (element.value) { 9215 this.currLoader = element.id; 9216 this.currDataIndex = element.value; 9217 if (this.cache[element.id]['data'][element.value]) { 9218 this.setDataToChild(this.cache[element.id]['data'][element.value]); 9219 } 9220 else{ 9221 new Ajax.Request(this.cache[this.currLoader]['dataUrl'],{ 9222 method: 'post', 9223 parameters: {"parent":element.value}, 9224 onComplete: this.reloadChildren.bind(this) 9225 }); 9226 } 9227 } 9228 }, 9229 9230 reloadChildren: function(transport){ 9231 var data = eval('(' + transport.responseText + ')'); 9232 this.cache[this.currLoader]['data'][this.currDataIndex] = data; 9233 this.setDataToChild(data); 9234 }, 9235 9236 setDataToChild: function(data){ 9237 if (data.length) { 9238 var child = $(this.cache[this.currLoader]['child']); 9239 if (child){ 9240 var html = '<select name="'+child.name+'" id="'+child.id+'" class="'+child.className+'" title="'+child.title+'" '+this.extraChildParams+'>'; 9241 if(this.cache[this.currLoader]['first']){ 9242 html+= '<option value="">'+this.cache[this.currLoader]['first']+'</option>'; 9243 } 9244 for (var i in data){ 9245 if(data[i].value) { 9246 html+= '<option value="'+data[i].value+'"'; 9247 if(child.value && (child.value == data[i].value || child.value == data[i].label)){ 9248 html+= ' selected'; 9249 } 9250 html+='>'+data[i].label+'</option>'; 9251 } 9252 } 9253 html+= '</select>'; 9254 Element.insert(child, {before: html}); 9255 Element.remove(child); 9256 } 9257 } 9258 else{ 9259 var child = $(this.cache[this.currLoader]['child']); 9260 if (child){ 9261 var html = '<input type="text" name="'+child.name+'" id="'+child.id+'" class="'+child.className+'" title="'+child.title+'" '+this.extraChildParams+'>'; 9262 Element.insert(child, {before: html}); 9263 Element.remove(child); 9264 } 9265 } 9266 9267 this.bindElements(); 9268 if (this.callback) { 9269 this.callback(); 9270 } 9271 } 9272} 9273 9274RegionUpdater = Class.create(); 9275RegionUpdater.prototype = { 9276 initialize: function (countryEl, regionTextEl, regionSelectEl, regions, disableAction, zipEl) 9277 { 9278 this.countryEl = $(countryEl); 9279 this.regionTextEl = $(regionTextEl); 9280 this.regionSelectEl = $(regionSelectEl); 9281 this.zipEl = $(zipEl); 9282 this.regions = regions; 9283 9284 this.disableAction = (typeof disableAction=='undefined') ? 'hide' : disableAction; 9285 this.zipOptions = (typeof zipOptions=='undefined') ? false : zipOptions; 9286 9287 if (this.regionSelectEl.options.length<=1) { 9288 this.update(); 9289 } 9290 9291 Event.observe(this.countryEl, 'change', this.update.bind(this)); 9292 }, 9293 9294 update: function() 9295 { 9296 if (this.regions[this.countryEl.value]) { 9297 var i, option, region, def; 9298 9299 if (this.regionTextEl) { 9300 def = this.regionTextEl.value.toLowerCase(); 9301 this.regionTextEl.value = ''; 9302 } 9303 if (!def) { 9304 def = this.regionSelectEl.getAttribute('defaultValue'); 9305 } 9306 9307 this.regionSelectEl.options.length = 1; 9308 for (regionId in this.regions[this.countryEl.value]) { 9309 region = this.regions[this.countryEl.value][regionId]; 9310 9311 option = document.createElement('OPTION'); 9312 option.value = regionId; 9313 option.text = region.name; 9314 9315 if (this.regionSelectEl.options.add) { 9316 this.regionSelectEl.options.add(option); 9317 } else { 9318 this.regionSelectEl.appendChild(option); 9319 } 9320 9321 if (regionId==def || region.name.toLowerCase()==def || region.code.toLowerCase()==def) { 9322 this.regionSelectEl.value = regionId; 9323 } 9324 } 9325 9326 if (this.disableAction=='hide') { 9327 if (this.regionTextEl) { 9328 this.regionTextEl.style.display = 'none'; 9329 } 9330 9331 this.regionSelectEl.style.display = ''; 9332 } else if (this.disableAction=='disable') { 9333 if (this.regionTextEl) { 9334 this.regionTextEl.disabled = true; 9335 } 9336 this.regionSelectEl.disabled = false;
9337 } 9338 this.setMarkDisplay(this.regionSelectEl, true); 9339 } else { 9340 if (this.disableAction=='hide') { 9341 if (this.regionTextEl) { 9342 this.regionTextEl.style.display = ''; 9343 } 9344 this.regionSelectEl.style.display = 'none'; 9345 Validation.reset(this.regionSelectEl); 9346 } else if (this.disableAction=='disable') { 9347 if (this.regionTextEl) { 9348 this.regionTextEl.disabled = false; 9349 } 9350 this.regionSelectEl.disabled = true; 9351 } else if (this.disableAction=='nullify') { 9352 this.regionSelectEl.options.length = 1; 9353 this.regionSelectEl.value = ''; 9354 this.regionSelectEl.selectedIndex = 0; 9355 this.lastCountryId = ''; 9356 } 9357 this.setMarkDisplay(this.regionSelectEl, false); 9358 } 9359 9360 // Make Zip and its label required/optional 9361 var zipUpdater = new ZipUpdater(this.countryEl.value, this.zipEl); 9362 zipUpdater.update(); 9363 }, 9364 9365 setMarkDisplay: function(elem, display){ 9366 elem = $(elem); 9367 var labelElement = elem.up(0).down('label > span.required') || 9368 elem.up(1).down('label > span.required') || 9369 elem.up(0).down('label.required > em') || 9370 elem.up(1).down('label.required > em'); 9371 if(labelElement) { 9372 inputElement = labelElement.up().next('input'); 9373 if (display) { 9374 labelElement.show(); 9375 if (inputElement) { 9376 inputElement.addClassName('required-entry'); 9377 } 9378 } else { 9379 labelElement.hide(); 9380 if (inputElement) { 9381 inputElement.removeClassName('required-entry'); 9382 } 9383 } 9384 } 9385 } 9386} 9387 9388ZipUpdater = Class.create(); 9389ZipUpdater.prototype = { 9390 initialize: function(country, zipElement) 9391 { 9392 this.country = country; 9393 this.zipElement = $(zipElement); 9394 }, 9395 9396 update: function() 9397 { 9398 // Country ISO 2-letter codes must be pre-defined 9399 if (typeof optionalZipCountries == 'undefined') { 9400 return false; 9401 } 9402 9403 // Ajax-request and normal content load compatibility 9404 if (this.zipElement != undefined) { 9405 this._setPostcodeOptional(); 9406 } else { 9407 Event.observe(window, "load", this._setPostcodeOptional.bind(this)); 9408 } 9409 }, 9410 9411 _setPostcodeOptional: function() 9412 { 9413 this.zipElement = $(this.zipElement); 9414 if (this.zipElement == undefined) { 9415 return false; 9416 } 9417 9418 // find label 9419 var label = $$('label[for="' + this.zipElement.id + '"]')[0]; 9420 if (label != undefined) { 9421 var wildCard = label.down('em') || label.down('span.required'); 9422 } 9423 9424 // Make Zip and its label required/optional 9425 if (optionalZipCountries.indexOf(this.country) != -1) { 9426 while (this.zipElement.hasClassName('required-entry')) { 9427 this.zipElement.removeClassName('required-entry'); 9428 } 9429 if (wildCard != undefined) { 9430 wildCard.hide(); 9431 } 9432 } else { 9433 this.zipElement.addClassName('required-entry'); 9434 if (wildCard != undefined) { 9435 wildCard.show(); 9436 } 9437 } 9438 } 9439} 9440 9441/** 9442 * Magento 9443 * 9444 * NOTICE OF LICENSE 9445 * 9446 * This source file is subject to the Academic Free License (AFL 3.0) 9447 * that is bundled with this package in the file LICENSE_AFL.txt. 9448 * It is also available through the world-wide-web at this URL: 9449 * http://opensource.org/licenses/afl-3.0.php 9450 * If you did not receive a copy of the license and are unable to 9451 * obtain it through the world-wide-web, please send an email 9452 * to [email protected] so we can send you a copy immediately. 9453 * 9454 * DISCLAIMER 9455 * 9456 * Do not edit or add to this file if you wish to upgrade Magento to newer 9457 * versions in the future. If you wish to customize Magento for your 9458 * needs please refer to http://www.magentocommerce.com for more information. 9459 * 9460 * @category Varien 9461 * @package js 9462 * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) 9463 * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) 9464 */ 9465function toggleMenu(el, over) 9466{ 9467 if (over) { 9468 Element.addClassName(el, 'over'); 9469 } 9470 else { 9471 Element.removeClassName(el, 'over'); 9472 } 9473} 9474 9475/** 9476 * Magento 9477 * 9478 * NOTICE OF LICENSE 9479 * 9480 * This source file is subject to the Academic Free License (AFL 3.0)
9481 * that is bundled with this package in the file LICENSE_AFL.txt. 9482 * It is also available through the world-wide-web at this URL: 9483 * http://opensource.org/licenses/afl-3.0.php 9484 * If you did not receive a copy of the license and are unable to 9485 * obtain it through the world-wide-web, please send an email 9486 * to [email protected] so we can send you a copy immediately. 9487 * 9488 * DISCLAIMER 9489 * 9490 * Do not edit or add to this file if you wish to upgrade Magento to newer 9491 * versions in the future. If you wish to customize Magento for your 9492 * needs please refer to http://www.magentocommerce.com for more information. 9493 * 9494 * @category Mage 9495 * @package js 9496 * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) 9497 * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) 9498 */ 9499 9500var Translate = Class.create(); 9501Translate.prototype = { 9502 initialize: function(data){ 9503 this.data = $H(data); 9504 }, 9505 9506 translate : function(){ 9507 var args = arguments; 9508 var text = arguments[0]; 9509 9510 if(this.data.get(text)){ 9511 return this.data.get(text); 9512 } 9513 return text; 9514 }, 9515 add : function() { 9516 if (arguments.length > 1) { 9517 this.data.set(arguments[0], arguments[1]); 9518 } else if (typeof arguments[0] =='object') { 9519 $H(arguments[0]).each(function (pair){ 9520 this.data.set(pair.key, pair.value); 9521 }.bind(this)); 9522 } 9523 } 9524} 9525 9526/** 9527 * Magento 9528 * 9529 * NOTICE OF LICENSE 9530 * 9531 * This source file is subject to the Academic Free License (AFL 3.0) 9532 * that is bundled with this package in the file LICENSE_AFL.txt. 9533 * It is also available through the world-wide-web at this URL: 9534 * http://opensource.org/licenses/afl-3.0.php 9535 * If you did not receive a copy of the license and are unable to 9536 * obtain it through the world-wide-web, please send an email 9537 * to [email protected] so we can send you a copy immediately. 9538 * 9539 * DISCLAIMER 9540 * 9541 * Do not edit or add to this file if you wish to upgrade Magento to newer 9542 * versions in the future. If you wish to customize Magento for your 9543 * needs please refer to http://www.magentocommerce.com for more information. 9544 * 9545 * @category Mage 9546 * @package js 9547 * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) 9548 * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) 9549 */ 9550// old school cookie functions grabbed off the web 9551 9552if (!window.Mage) var Mage = {}; 9553 9554Mage.Cookies = {}; 9555Mage.Cookies.expires = null; 9556Mage.Cookies.path = '/'; 9557Mage.Cookies.domain = null; 9558Mage.Cookies.secure = false; 9559Mage.Cookies.set = function(name, value){ 9560 var argv = arguments; 9561 var argc = arguments.length; 9562 var expires = (argc > 2) ? argv[2] : Mage.Cookies.expires; 9563 var path = (argc > 3) ? argv[3] : Mage.Cookies.path; 9564 var domain = (argc > 4) ? argv[4] : Mage.Cookies.domain; 9565 var secure = (argc > 5) ? argv[5] : Mage.Cookies.secure; 9566 document.cookie = name + "=" + escape (value) + 9567 ((expires == null) ? "" : ("; expires=" + expires.toGMTString())) + 9568 ((path == null) ? "" : ("; path=" + path)) + 9569 ((domain == null) ? "" : ("; domain=" + domain)) + 9570 ((secure == true) ? "; secure" : ""); 9571}; 9572 9573Mage.Cookies.get = function(name){ 9574 var arg = name + "="; 9575 var alen = arg.length; 9576 var clen = document.cookie.length; 9577 var i = 0; 9578 var j = 0; 9579 while(i < clen){ 9580 j = i + alen; 9581 if (document.cookie.substring(i, j) == arg) 9582 return Mage.Cookies.getCookieVal(j); 9583 i = document.cookie.indexOf(" ", i) + 1; 9584 if(i == 0) 9585 break; 9586 } 9587 return null; 9588}; 9589 9590Mage.Cookies.clear = function(name) { 9591 if(Mage.Cookies.get(name)){ 9592 document.cookie = name + "=" + 9593 "; expires=Thu, 01-Jan-70 00:00:01 GMT"; 9594 } 9595}; 9596 9597Mage.Cookies.getCookieVal = function(offset){ 9598 var endstr = document.cookie.indexOf(";", offset); 9599 if(endstr == -1){ 9600 endstr = document.cookie.length; 9601 } 9602 return unescape(document.cookie.substring(offset, endstr)); 9603}; 9604 9605/* 9606 * Shadowbox.js, version 3.0.3 9607 * http://shadowbox-js.com/ 9608 * 9609 * Copyright 2007-2010, Michael J. I. Jackson 9610 * Date: 2011-04-08 01:28:11 +0000 9611 */ 9612(function(T,p){var g={version:"3.0.3"};var Y=navigator.userAgent.toLowerCase();if(Y.indexOf("windows")>-1||Y.indexOf("win32")>-1){g.isWindows=true}else{if(Y.indexOf("macintosh")>-1||Y.indexOf("mac os x")>-1){g.isMac=true}else{if(Y.indexOf("linux")>-1){g.isLinux=true}}}g.isIE=Y.indexOf("msie")>-1;g.isIE6=Y.indexOf("msie 6")>-1;g.isIE7=Y.indexOf("msie 7")>-1;g.isGecko=Y.indexOf("gecko")>-1&&Y.indexOf("safari")==-1;g.isWebKit=Y.indexOf("applewebkit/")>-1;var e=/#(.+)$/,M=/^(light|shadow)box\[(.*?)\]/i,o=/\s*([a-z_]*?)\s*=\s*(.+)\s*/,ar=/[0-9a-z]+$/i,ap=/(.+\/)shadowbox\.js/i;var w=false,m=false,V={},ai=0,O,aa;g.current=-1;g.dimensions=null;g.ease=function(K){return 1+Math.pow(K-1,3)};g.errorInfo={fla:{name:"Flash",url:"http://www.adobe.com/products/flashplayer/"},qt:{name:"QuickTime",url:"http://www.apple.com/quicktime/download/"},wmp:{name:"Windows Media Player",url:"http://www.microsoft.com/windows/windowsmedia/"},f4m:{name:"Flip4Mac",url:"http://www.flip4mac.com/wmv_download.htm"}};g.gallery=[];g.onReady=aj;g.path=null;g.player=null;g.playerId="sb-player";g.options={animate:true,animateFade:true,autoplayMovies:true,continuous:false,enableKeys:true,flashParams:{bgcolor:"#000000",allowfullscreen:true},flashVars:{},flashVersion:"9.0.115",handleOversize:"resize",handleUnsupported:"link",onChange:aj,onClose:aj,onFinish:aj,onOpen:aj,showMovieControls:true,skipSetup:false,slideshowDelay:0,viewportPadding:20};g.getCurrent=function(){return g.current>-1?g.gallery[g.current]:null};g.hasNext=function(){return g.gallery.length>1&&(g.current!=g.gallery.length-1||g.options.continuous)};g.isOpen=function(){return w};g.isPaused=function(){return aa=="pause"};g.applyOptions=function(K){V=ao({},g.options);ao(g.options,K)};g.revertOptions=function(){ao(g.options,V)};
9612g.init=function(au,ax){if(m){return}m=true;if(g.skin.options){ao(g.options,g.skin.options)}if(au){ao(g.options,au)}if(!g.path){var aw,S=document.getElementsByTagName("script");for(var av=0,K=S.length;av<K;++av){aw=ap.exec(S[av].src);if(aw){g.path=aw[1];break}}}if(ax){g.onReady=ax}aq()};g.open=function(S){if(w){return}var K=g.makeGallery(S);g.gallery=K[0];g.current=K[1];S=g.getCurrent();if(S==null){return}g.applyOptions(S.options||{});f();if(g.gallery.length){S=g.getCurrent();if(g.options.onOpen(S)===false){return}w=true;g.skin.onOpen(S,U)}};g.close=function(){if(!w){return}w=false;if(g.player){g.player.remove();g.player=null}if(typeof aa=="number"){clearTimeout(aa);aa=null}ai=0;af(false);g.options.onClose(g.getCurrent());g.skin.onClose();g.revertOptions()};g.play=function(){if(!g.hasNext()){return}if(!ai){ai=g.options.slideshowDelay*1000}if(ai){O=X();aa=setTimeout(function(){ai=O=0;g.next()},ai);if(g.skin.onPlay){g.skin.onPlay()}}};g.pause=function(){if(typeof aa!="number"){return}ai=Math.max(0,ai-(X()-O));if(ai){clearTimeout(aa);aa="pause";if(g.skin.onPause){g.skin.onPause()}}};g.change=function(K){if(!(K in g.gallery)){if(g.options.continuous){K=(K<0?g.gallery.length+K:0);if(!(K in g.gallery)){return}}else{return}}g.current=K;if(typeof aa=="number"){clearTimeout(aa);aa=null;ai=O=0}g.options.onChange(g.getCurrent());U(true)};g.next=function(){g.change(g.current+1)};g.previous=function(){g.change(g.current-1)};g.setDimensions=function(aG,ax,aE,aF,aw,K,aC,az){var aB=aG,av=ax;var aA=2*aC+aw;if(aG+aA>aE){aG=aE-aA}var au=2*aC+K;if(ax+au>aF){ax=aF-au}var S=(aB-aG)/aB,aD=(av-ax)/av,ay=(S>0||aD>0);if(az&&ay){if(S>aD){ax=Math.round((av/aB)*aG)}else{if(aD>S){aG=Math.round((aB/av)*ax)}}}g.dimensions={height:aG+aw,width:ax+K,innerHeight:aG,innerWidth:ax,top:Math.floor((aE-(aG+aA))/2+aC),left:Math.floor((aF-(ax+au))/2+aC),oversized:ay};return g.dimensions};g.makeGallery=function(aw){var K=[],av=-1;if(typeof aw=="string"){aw=[aw]}if(typeof aw.length=="number"){ac(aw,function(ay,az){if(az.content){K[ay]=az}else{K[ay]={content:az}}});av=0}else{if(aw.tagName){var S=g.getCache(aw);aw=S?S:g.makeObject(aw)}if(aw.gallery){K=[];var ax;for(var au in g.cache){ax=g.cache[au];if(ax.gallery&&ax.gallery==aw.gallery){if(av==-1&&ax.content==aw.content){av=K.length}K.push(ax)}}if(av==-1){K.unshift(aw);av=0}}else{K=[aw];av=0}}ac(K,function(ay,az){K[ay]=ao({},az)});return[K,av]};g.makeObject=function(av,au){var aw={content:av.href,title:av.getAttribute("title")||"",link:av};if(au){au=ao({},au);ac(["player","title","height","width","gallery"],function(ax,ay){if(typeof au[ay]!="undefined"){aw[ay]=au[ay];delete au[ay]}});aw.options=au}else{aw.options={}}if(!aw.player){aw.player=g.getPlayer(aw.content)}var K=av.getAttribute("rel");if(K){var S=K.match(M);if(S){aw.gallery=escape(S[2])}ac(K.split(";"),function(ax,ay){S=ay.match(o);if(S){aw[S[1]]=S[2]}})}return aw};g.getPlayer=function(au){if(au.indexOf("#")>-1&&au.indexOf(document.location.href)==0){return"inline"}var av=au.indexOf("?");if(av>-1){au=au.substring(0,av)}var S,K=au.match(ar);if(K){S=K[0].toLowerCase()}if(S){if(g.img&&g.img.ext.indexOf(S)>-1){return"img"}if(g.swf&&g.swf.ext.indexOf(S)>-1){return"swf"}if(g.flv&&g.flv.ext.indexOf(S)>-1){return"flv"}if(g.qt&&g.qt.ext.indexOf(S)>-1){if(g.wmp&&g.wmp.ext.indexOf(S)>-1){return"qtwmp"}else{return"qt"}}if(g.wmp&&g.wmp.ext.indexOf(S)>-1){return"wmp"}}return"iframe"};function f(){var av=g.errorInfo,aw=g.plugins,ay,az,aC,au,aB,S,aA,K;for(var ax=0;ax<g.gallery.length;++ax){ay=g.gallery[ax];az=false;aC=null;switch(ay.player){case"flv":case"swf":if(!aw.fla){aC="fla"}break;case"qt":if(!aw.qt){aC="qt"}break;case"wmp":if(g.isMac){if(aw.qt&&aw.f4m){ay.player="qt"}else{aC="qtf4m"}}else{if(!aw.wmp){aC="wmp"}}break;case"qtwmp":if(aw.qt){ay.player="qt"}else{if(aw.wmp){ay.player="wmp"}else{aC="qtwmp"}}break}if(aC){if(g.options.handleUnsupported=="link"){switch(aC){case"qtf4m":aB="shared";S=[av.qt.url,av.qt.name,av.f4m.url,av.f4m.name];break;case"qtwmp":aB="either";S=[av.qt.url,av.qt.name,av.wmp.url,av.wmp.name];break;default:aB="single";S=[av[aC].url,av[aC].name]}ay.player="html";ay.content='<div class="sb-message">'+s(g.lang.errors[aB],S)+"</div>"}else{az=true}}else{if(ay.player=="inline"){au=e.exec(ay.content);if(au){aA=ag(au[1]);if(aA){ay.content=aA.innerHTML}else{az=true}}else{az=true}}else{if(ay.player=="swf"||ay.player=="flv"){K=(ay.options&&ay.options.flashVersion)||g.options.flashVersion;if(g.flash&&!g.flash.hasFlashPlayerVersion(K)){ay.width=310;ay.height=177}}}}if(az){g.gallery.splice(ax,1);if(ax<g.current){--g.current}else{if(ax==g.current){g.current=ax>0?ax-1:ax}}--ax}}}
9612function af(K){if(!g.options.enableKeys){return}(K?j:a)(document,"keydown",W)}function W(au){if(au.metaKey||au.shiftKey||au.altKey||au.ctrlKey){return}var S=l(au),K;switch(S){case 81:case 88:case 27:K=g.close;break;case 37:K=g.previous;break;case 39:K=g.next;break;case 32:K=typeof aa=="number"?g.pause:g.play;break}if(K){G(au);K()}}function U(ay){af(false);var ax=g.getCurrent();var au=(ax.player=="inline"?"html":ax.player);if(typeof g[au]!="function"){throw"unknown player "+au}if(ay){g.player.remove();g.revertOptions();g.applyOptions(ax.options||{})}g.player=new g[au](ax,g.playerId);if(g.gallery.length>1){var av=g.gallery[g.current+1]||g.gallery[0];if(av.player=="img"){var S=new Image();S.src=av.content}var aw=g.gallery[g.current-1]||g.gallery[g.gallery.length-1];if(aw.player=="img"){var K=new Image();K.src=aw.content}}g.skin.onLoad(ay,r)}function r(){if(!w){return}if(typeof g.player.ready!="undefined"){var K=setInterval(function(){if(w){if(g.player.ready){clearInterval(K);K=null;g.skin.onReady(J)}}else{clearInterval(K);K=null}},10)}else{g.skin.onReady(J)}}function J(){if(!w){return}g.player.append(g.skin.body,g.dimensions);g.skin.onShow(q)}function q(){if(!w){return}if(g.player.onLoad){g.player.onLoad()}g.options.onFinish(g.getCurrent());if(!g.isPaused()){g.play()}af(true)}if(!Array.prototype.indexOf){Array.prototype.indexOf=function(S,au){var K=this.length>>>0;au=au||0;if(au<0){au+=K}for(;au<K;++au){if(au in this&&this[au]===S){return au}}return -1}}function X(){return(new Date).getTime()}function ao(K,au){for(var S in au){K[S]=au[S]}return K}function ac(av,aw){var S=0,K=av.length;for(var au=av[0];S<K&&aw.call(au,S,au)!==false;au=av[++S]){}}function s(S,K){return S.replace(/\{(\w+?)\}/g,function(au,av){return K[av]})}function aj(){}function ag(K){return document.getElementById(K)}function z(K){K.parentNode.removeChild(K)}var ak=true,L=true;function an(){var K=document.body,S=document.createElement("div");ak=typeof S.style.opacity==="string";S.style.position="fixed";S.style.margin=0;S.style.top="20px";K.appendChild(S,K.firstChild);L=S.offsetTop==20;K.removeChild(S)}g.getStyle=(function(){var K=/opacity=([^)]*)/,S=document.defaultView&&document.defaultView.getComputedStyle;return function(ax,aw){var av;if(!ak&&aw=="opacity"&&ax.currentStyle){av=K.test(ax.currentStyle.filter||"")?(parseFloat(RegExp.$1)/100)+"":"";return av===""?"1":av}if(S){var au=S(ax,null);if(au){av=au[aw]}if(aw=="opacity"&&av==""){av="1"}}else{av=ax.currentStyle[aw]}return av}})();g.appendHTML=function(au,S){if(au.insertAdjacentHTML){au.insertAdjacentHTML("BeforeEnd",S)}else{if(au.lastChild){var K=au.ownerDocument.createRange();K.setStartAfter(au.lastChild);var av=K.createContextualFragment(S);au.appendChild(av)}else{au.innerHTML=S}}};g.getWindowSize=function(K){if(document.compatMode==="CSS1Compat"){return document.documentElement["client"+K]}return document.body["client"+K]};g.setOpacity=function(au,K){var S=au.style;if(ak){S.opacity=(K==1?"":K)}else{S.zoom=1;if(K==1){if(typeof S.filter=="string"&&(/alpha/i).test(S.filter)){S.filter=S.filter.replace(/\s*[\w\.]*alpha\([^\)]*\);?/gi,"")}}else{S.filter=(S.filter||"").replace(/\s*[\w\.]*alpha\([^\)]*\)/gi,"")+" alpha(opacity="+(K*100)+")"}}};g.clearOpacity=function(K){g.setOpacity(K,1)};function C(S){var K=S.target?S.target:S.srcElement;return K.nodeType==3?K.parentNode:K}function Q(S){var K=S.pageX||(S.clientX+(document.documentElement.scrollLeft||document.body.scrollLeft)),au=S.pageY||(S.clientY+(document.documentElement.scrollTop||document.body.scrollTop));return[K,au]}function G(K){K.preventDefault()}function l(K){return K.which?K.which:K.keyCode}function j(av,au,S){if(av.addEventListener){av.addEventListener(au,S,false)}else{if(av.nodeType===3||av.nodeType===8){return}if(av.setInterval&&(av!==T&&!av.frameElement)){av=T}if(!S.__guid){S.__guid=j.guid++}if(!av.events){av.events={}}var K=av.events[au];if(!K){K=av.events[au]={};if(av["on"+au]){K[0]=av["on"+au]}}K[S.__guid]=S;av["on"+au]=j.handleEvent}}j.guid=1;j.handleEvent=function(av){var K=true;av=av||j.fixEvent(((this.ownerDocument||this.document||this).parentWindow||T).event);var S=this.events[av.type];for(var au in S){this.__handleEvent=S[au];if(this.__handleEvent(av)===false){K=false}}return K};j.preventDefault=function(){this.returnValue=false};j.stopPropagation=function(){this.cancelBubble=true};j.fixEvent=function(K){K.preventDefault=j.preventDefault;K.stopPropagation=j.stopPropagation;return K};function a(au,S,K){if(au.removeEventListener){au.removeEventListener(S,K,false)}else{if(au.events&&au.events[S]){delete au.events[S][K.__guid]}}}var D=false,N;if(document.addEventListener){N=function(){document.removeEventListener("DOMContentLoaded",N,false);g.load()}}else{if(document.attachEvent){N=function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",N);g.load()}}}}function i(){if(D){return}try{document.documentElement.doScroll("left")}catch(K){setTimeout(i,1);return}g.load()}
9612function aq(){if(document.readyState==="complete"){return g.load()}if(document.addEventListener){document.addEventListener("DOMContentLoaded",N,false);T.addEventListener("load",g.load,false)}else{if(document.attachEvent){document.attachEvent("onreadystatechange",N);T.attachEvent("onload",g.load);var K=false;try{K=T.frameElement===null}catch(S){}if(document.documentElement.doScroll&&K){i()}}}}g.load=function(){if(D){return}if(!document.body){return setTimeout(g.load,13)}D=true;an();g.onReady();if(!g.options.skipSetup){g.setup()}g.skin.init()};g.plugins={};if(navigator.plugins&&navigator.plugins.length){var am=[];ac(navigator.plugins,function(K,S){am.push(S.name)});am=am.join(",");var d=am.indexOf("Flip4Mac")>-1;g.plugins={fla:am.indexOf("Shockwave Flash")>-1,qt:am.indexOf("QuickTime")>-1,wmp:!d&&am.indexOf("Windows Media")>-1,f4m:d}}else{var B=function(K){var S;try{S=new ActiveXObject(K)}catch(au){}return !!S};g.plugins={fla:B("ShockwaveFlash.ShockwaveFlash"),qt:B("QuickTime.QuickTime"),wmp:B("wmplayer.ocx"),f4m:false}}var c=/^(light|shadow)box/i,Z="shadowboxCacheKey",h=1;g.cache={};g.select=function(S){var au=[];if(!S){var K;ac(document.getElementsByTagName("a"),function(ax,ay){K=ay.getAttribute("rel");if(K&&c.test(K)){au.push(ay)}})}else{var aw=S.length;if(aw){if(typeof S=="string"){if(g.find){au=g.find(S)}}else{if(aw==2&&typeof S[0]=="string"&&S[1].nodeType){if(g.find){au=g.find(S[0],S[1])}}else{for(var av=0;av<aw;++av){au[av]=S[av]}}}}else{au.push(S)}}return au};g.setup=function(K,S){ac(g.select(K),function(au,av){g.addCache(av,S)})};g.teardown=function(K){ac(g.select(K),function(S,au){g.removeCache(au)})};g.addCache=function(au,K){var S=au[Z];if(S==p){S=h++;au[Z]=S;j(au,"click",b)}g.cache[S]=g.makeObject(au,K)};g.removeCache=function(K){a(K,"click",b);delete g.cache[K[Z]];K[Z]=null};g.getCache=function(S){var K=S[Z];return(K in g.cache&&g.cache[K])};g.clearCache=function(){for(var K in g.cache){g.removeCache(g.cache[K].link)}g.cache={}};function b(K){g.open(this);if(g.gallery.length){G(K)}}g.lang={code:"en",of:"of",loading:"loading",cancel:"Cancel",next:"Next",previous:"Previous",play:"Play",pause:"Pause",close:"Close",errors:{single:'You must install the <a href="{0}">{1}</a> browser plugin to view this content.',shared:'You must install both the <a href="{0}">{1}</a> and <a href="{2}">{3}</a> browser plugins to view this content.',either:'You must install either the <a href="{0}">{1}</a> or the <a href="{2}">{3}</a> browser plugin to view this content.'}};g.html=function(K,S){this.obj=K;this.id=S;this.height=K.height?parseInt(K.height,10):300;this.width=K.width?parseInt(K.width,10):500};g.html.prototype={append:function(K,S){var au=document.createElement("div");au.id=this.id;au.className="html";au.innerHTML=this.obj.content;K.appendChild(au)},remove:function(){var K=ag(this.id);if(K){z(K)}}};var al=false,A=[],H=["sb-nav-close","sb-nav-next","sb-nav-play","sb-nav-pause","sb-nav-previous"],F,ah,v,P=true;function ae(au,aE,aB,az,aF){var K=(aE=="opacity"),aA=K?g.setOpacity:function(aG,aH){aG.style[aE]=""+aH+"px"};if(az==0||(!K&&!g.options.animate)||(K&&!g.options.animateFade)){aA(au,aB);if(aF){aF()}return}var aC=parseFloat(g.getStyle(au,aE))||0;var aD=aB-aC;if(aD==0){if(aF){aF()}return}az*=1000;var av=X(),ay=g.ease,ax=av+az,aw;var S=setInterval(function(){aw=X();if(aw>=ax){clearInterval(S);S=null;aA(au,aB);if(aF){aF()}}else{aA(au,aC+ay((aw-av)/az)*aD)}},10)}function I(){F.style.height=g.getWindowSize("Height")+"px";F.style.width=g.getWindowSize("Width")+"px"}function ad(){F.style.top=document.documentElement.scrollTop+"px";F.style.left=document.documentElement.scrollLeft+"px"}function y(K){if(K){ac(A,function(S,au){au[0].style.visibility=au[1]||""})}else{A=[];ac(g.options.troubleElements,function(au,S){ac(document.getElementsByTagName(S),function(av,aw){A.push([aw,aw.style.visibility]);aw.style.visibility="hidden"})})}}function x(au,K){var S=ag("sb-nav-"+au);if(S){S.style.display=K?"":"none"}}function n(K,ax){var aw=ag("sb-loading"),au=g.getCurrent().player,av=(au=="img"||au=="html");if(K){g.setOpacity(aw,0);aw.style.display="block";var S=function(){g.clearOpacity(aw);
9612if(ax){ax()}};if(av){ae(aw,"opacity",1,g.options.fadeDuration,S)}else{S()}}else{var S=function(){aw.style.display="none";g.clearOpacity(aw);if(ax){ax()}};if(av){ae(aw,"opacity",0,g.options.fadeDuration,S)}else{S()}}}function at(aC){var ax=g.getCurrent();ag("sb-title-inner").innerHTML=ax.title||"";var aD,az,S,aE,aA;if(g.options.displayNav){aD=true;var aB=g.gallery.length;if(aB>1){if(g.options.continuous){az=aA=true}else{az=(aB-1)>g.current;aA=g.current>0}}if(g.options.slideshowDelay>0&&g.hasNext()){aE=!g.isPaused();S=!aE}}else{aD=az=S=aE=aA=false}x("close",aD);x("next",az);x("play",S);x("pause",aE);x("previous",aA);var K="";if(g.options.displayCounter&&g.gallery.length>1){var aB=g.gallery.length;if(g.options.counterType=="skip"){var aw=0,av=aB,au=parseInt(g.options.counterLimit)||0;if(au<aB&&au>2){var ay=Math.floor(au/2);aw=g.current-ay;if(aw<0){aw+=aB}av=g.current+(au-ay);if(av>aB){av-=aB}}while(aw!=av){if(aw==aB){aw=0}K+='<a onclick="Shadowbox.change('+aw+');"';if(aw==g.current){K+=' class="sb-counter-current"'}K+=">"+(++aw)+"</a>"}}else{K=[g.current+1,g.lang.of,aB].join(" ")}}ag("sb-counter").innerHTML=K;aC()}function u(av){var K=ag("sb-title-inner"),au=ag("sb-info-inner"),S=0.35;K.style.visibility=au.style.visibility="";if(K.innerHTML!=""){ae(K,"marginTop",0,S)}ae(au,"marginTop",0,S,av)}function ab(au,aA){var ay=ag("sb-title"),K=ag("sb-info"),av=ay.offsetHeight,aw=K.offsetHeight,ax=ag("sb-title-inner"),az=ag("sb-info-inner"),S=(au?0.35:0);ae(ax,"marginTop",av,S);ae(az,"marginTop",aw*-1,S,function(){ax.style.visibility=az.style.visibility="hidden";aA()})}function E(K,av,S,ax){var aw=ag("sb-wrapper-inner"),au=(S?g.options.resizeDuration:0);ae(v,"top",av,au);ae(aw,"height",K,au,ax)}function t(K,av,S,aw){var au=(S?g.options.resizeDuration:0);ae(v,"left",av,au);ae(v,"width",K,au,aw)}function R(aA,au){var aw=ag("sb-body-inner"),aA=parseInt(aA),au=parseInt(au),S=v.offsetHeight-aw.offsetHeight,K=v.offsetWidth-aw.offsetWidth,ay=ah.offsetHeight,az=ah.offsetWidth,ax=parseInt(g.options.viewportPadding)||20,av=(g.player&&g.options.handleOversize!="drag");return g.setDimensions(aA,au,ay,az,S,K,ax,av)}var k={};k.markup='<div id="sb-container"><div id="sb-overlay"></div><div id="sb-wrapper"><div id="sb-title"><div id="sb-title-inner"></div></div><div id="sb-wrapper-inner"><div id="sb-body"><div id="sb-body-inner"></div><div id="sb-loading"><div id="sb-loading-inner"><span>{loading}</span></div></div></div></div><div id="sb-info"><div id="sb-info-inner"><div id="sb-counter"></div><div id="sb-nav"><a id="sb-nav-close" title="{close}" onclick="Shadowbox.close()"></a><a id="sb-nav-next" title="{next}" onclick="Shadowbox.next()"></a><a id="sb-nav-play" title="{play}" onclick="Shadowbox.play()"></a><a id="sb-nav-pause" title="{pause}" onclick="Shadowbox.pause()"></a><a id="sb-nav-previous" title="{previous}" onclick="Shadowbox.previous()"></a></div></div></div></div></div>';k.options={animSequence:"sync",counterLimit:10,counterType:"default",displayCounter:true,displayNav:true,fadeDuration:0.35,initialHeight:160,initialWidth:320,modal:false,overlayColor:"#000",overlayOpacity:0.5,resizeDuration:0.35,showOverlay:true,troubleElements:["select","object","embed","canvas"]};k.init=function(){g.appendHTML(document.body,s(k.markup,g.lang));k.body=ag("sb-body-inner");F=ag("sb-container");ah=ag("sb-overlay");v=ag("sb-wrapper");if(!L){F.style.position="absolute"}if(!ak){var au,K,S=/url\("(.*\.png)"\)/;ac(H,function(aw,ax){au=ag(ax);if(au){K=g.getStyle(au,"backgroundImage").match(S);if(K){au.style.backgroundImage="none";au.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true,src="+K[1]+",sizingMethod=scale);"}}})}var av;j(T,"resize",function(){if(av){clearTimeout(av);av=null}if(w){av=setTimeout(k.onWindowResize,10)}})};k.onOpen=function(K,au){P=false;F.style.display="block";I();var S=R(g.options.initialHeight,g.options.initialWidth);E(S.innerHeight,S.top);t(S.width,S.left);if(g.options.showOverlay){ah.style.backgroundColor=g.options.overlayColor;g.setOpacity(ah,0);if(!g.options.modal){j(ah,"click",g.close)}al=true}if(!L){ad();j(T,"scroll",ad)}y();F.style.visibility="visible";if(al){ae(ah,"opacity",g.options.overlayOpacity,g.options.fadeDuration,au)}else{au()}};k.onLoad=function(S,K){n(true);while(k.body.firstChild){z(k.body.firstChild)}ab(S,function(){if(!w){return}if(!S){v.style.visibility="visible"}at(K)})};k.onReady=function(av){if(!w){return}var S=g.player,au=R(S.height,S.width);var K=function(){u(av)};
9612switch(g.options.animSequence){case"hw":E(au.innerHeight,au.top,true,function(){t(au.width,au.left,true,K)});break;case"wh":t(au.width,au.left,true,function(){E(au.innerHeight,au.top,true,K)});break;default:t(au.width,au.left,true);E(au.innerHeight,au.top,true,K)}};k.onShow=function(K){n(false,K);P=true};k.onClose=function(){if(!L){a(T,"scroll",ad)}a(ah,"click",g.close);v.style.visibility="hidden";var K=function(){F.style.visibility="hidden";F.style.display="none";y(true)};if(al){ae(ah,"opacity",0,g.options.fadeDuration,K)}else{K()}};k.onPlay=function(){x("play",false);x("pause",true)};k.onPause=function(){x("pause",false);x("play",true)};k.onWindowResize=function(){if(!P){return}I();var K=g.player,S=R(K.height,K.width);t(S.width,S.left);E(S.innerHeight,S.top);if(K.onWindowResize){K.onWindowResize()}};g.skin=k;T.Shadowbox=g})(window);
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.