1/* Ginger edit js 2---------------------------------------------------------- 3 A copy of \mod_admin\lib\js\apps\admin-common.js 4 without the 5 $(window).bind('keydown.ctrl_s keydown.meta_s', function(event) { 6 event.preventDefault(); 7 $('#rscform').submit(); 8 }); 9? conflicting with frontend js 10---------------------------------------------------------- */ 11/* Admin Common js 12---------------------------------------------------------- 13 14@package: Zotonic 2009 15@Author: Tim Benniks <[email protected]> 16 17Copyright 2009 Tim Benniks 18Copyright 2012 Arjan Scherpenisse 19 20Licensed under the Apache License, Version 2.0 (the "License"); 21you may not use this file except in compliance with the License. 22You may obtain a copy of the License at 23 24http://www.apache.org/licenses/LICENSE-2.0 25 26Unless required by applicable law or agreed to in writing, software 27distributed under the License is distributed on an "AS IS" BASIS, 28WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 29See the License for the specific language governing permissions and 30limitations under the License. 31 32---------------------------------------------------------- */ 33 34(function($) 35{ 36 $.widget("ui.adminLinkedTable", 37 { 38 _init: function() { 39 var self = this; 40 (self.element.find("tr").each(function() { 41 var href = $(this).attr("data-href"); 42 var event = $(this).attr("data-event"); 43 if (!href && !event) return; 44 if (!href) href = 'javascript:;'; 45 $(this).find("td").each(function() { 46 $(this) 47 .addClass("view-link") 48 .contents() 49 .wrapAll($("<a>") 50 .addClass("view-link") 51 .attr("href", href)); 52 }); 53 if (event) { 54 $(this).find("td a").on("click", function() { 55 z_event(event); 56 }); 57 } 58 59 })); 60 } 61 }); 62 63 $.widget("ui.autofocus", 64 { 65 _init: function() { 66 var self = this; 67 self.element.focus(); 68 } 69 }); 70 71 var scrollTimer = undefined; 72 $(window).scroll(function() { 73 if (scrollTimer) clearTimeout(scrollTimer); 74 scrollTimer = setTimeout(function() { 75 if ($(window).scrollTop() > 118) { 76 $('body').addClass('scrolled'); 77 } else { 78 $('body').removeClass('scrolled'); 79 } 80 }, 200); 81 }); 82 83})(jQuery); 84 85 86window.zConnectDoneReload = function(v) { 87 window.location.reload(); 88}; 89 90window.zAdminConnectDone = function(v) { 91 if (v.is_new) { 92 z_event("links-" + v.subject_id + "-" + v.predicate); 93 } 94}; 95 96window.zAdminLinkDone = function(v) { 97 window.z_zlink(v.url_language, v.title_language); 98 window.zAdminConnectDone(v); 99}; 100 101window.zAdminMediaDone = function(v) { 102 window.z_choose_zmedia(v.object_id); 103 window.zAdminConnectDone(v); 104}; 105 106window.zEditLanguage = function() { 107 return $('.language-tabs li.active').attr('lang'); 108}; 109 110function z_admin_ensure_block_names() { 111 var names = []; 112 $('input.block-name').each(function() { 113 var name = $(this).val(); 114 if (name !== '') { 115 names.push(name); 116 } 117 }); 118 119 $('input.block-name').each(function() { 120 var name = $(this).val(); 121 if (name === '') 122 { 123 var $type = $("input.block-type", $(this).closest(".block")); 124 if ($type.length > 0) { 125 name = $type.val().split("_").pop(); 126 var ct = 1; 127 while (names.indexOf(name+ct) != -1) { 128 ct++; 129 } 130 $(this).val(name+ct); 131 names.push(name+ct); 132 } 133 } 134 }); 135}/* growl notice js 136 ---------------------------------------------------------- 137 138 @package: Zotonic 2009 139 @Author: Tim Benniks <[email protected]> 140 141 Copyright 2009 Tim Benniks 142 143 Licensed under the Apache License, Version 2.0 (the "License");
144 you may not use this file except in compliance with the License. 145 You may obtain a copy of the License at 146 147 http://www.apache.org/licenses/LICENSE-2.0 148 149 Unless required by applicable law or agreed to in writing, software 150 distributed under the License is distributed on an "AS IS" BASIS, 151 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 152 See the License for the specific language governing permissions and 153 limitations under the License. 154 155 ---------------------------------------------------------- */ 156 157(function(jQuery) 158 { 159 jQuery.extend( 160 { 161 noticeAdd: function(options) 162 { 163 var defaults = { 164 inEffect: {opacity: 'show'}, // in effect 165 inEffectDuration: 300, // in effect duration in miliseconds 166 stayTime: 3000, // time in miliseconds before the item has to disappear 167 text: '', // content of the item 168 stay: false, // should the notice item stay or not? 169 type: 'notice' // could also be error, succes 170 }; 171 172 var map = { 173 notice: 'alert-info', 174 error: 'alert-danger' 175 }; 176 177 // declare varaibles 178 var options, noticeWrapAll, noticeItemOuter, noticeItemInner, noticeItemClose; 179 180 options = jQuery.extend({}, defaults, options); 181 noticeWrapAll = (!jQuery('.notice-wrap').length) ? jQuery('<div></div>').addClass('notice-wrap').appendTo('body') : jQuery('.notice-wrap'); 182 noticeItemOuter = jQuery('<div></div>').addClass('notice-item-wrapper'); 183 noticeItemInner = jQuery('<div></div>').hide().addClass('alert ' + map[options.type]).prependTo(noticeWrapAll).html(options.text).animate(options.inEffect, options.inEffectDuration).wrap(noticeItemOuter); 184 noticeItemClose = jQuery('<a>').addClass('close').prependTo(noticeItemInner).html('<span>×</span>').click(function() { jQuery.noticeRemove(noticeItemInner); }); 185 186 // hmmmz, zucht 187 if(navigator.userAgent.match(/MSIE 6/i)) 188 { 189 noticeWrapAll.css({top: document.documentElement.scrollTop}); 190 } 191 192 if(!options.stay) 193 { 194 setTimeout(function() 195 { 196 jQuery.noticeRemove(noticeItemInner); 197 }, 198 options.stayTime); 199 } 200 }, 201 202 noticeRemove: function(obj) 203 { 204 obj.animate({opacity: '0'}, 600, function() 205 { 206 obj.closest(".notice-item-wrapper").animate({height: '0px'}, 300, function() 207 { 208 obj.closest(".notice-item-wrapper").remove(); 209 }); 210 }); 211 } 212 }); 213 })(jQuery); 214/* imageviewer js 215---------------------------------------------------------- 216 217@package: Zotonic 2009 218@Author: Tim Benniks <[email protected]> 219 220Copyright 2009 Tim Benniks 221 222Licensed under the Apache License, Version 2.0 (the "License"); 223you may not use this file except in compliance with the License. 224You may obtain a copy of the License at 225 226http://www.apache.org/licenses/LICENSE-2.0 227 228Unless required by applicable law or agreed to in writing, software 229distributed under the License is distributed on an "AS IS" BASIS, 230WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 231See the License for the specific language governing permissions and 232limitations under the License. 233 234---------------------------------------------------------- */ 235 236$.widget("ui.imageviewer", 237{ 238 _init: function() 239 { 240 this.init(); 241 var imageWrapper, imageMagnifier, bigImg; 242 }, 243 244 init: function() 245 { 246 var ui = this; 247 imageWrapper = jQuery('<div></div>').addClass('image-wrapper'); 248 imageMagnifier = jQuery('<div></div>').addClass('image-magnifier').css({top: this.element.offset().top, left: this.element.offset().left}); 249 bigImg = jQuery('<img alt="'+ui.element.attr('alt')+'" />').hide(); 250 251 this.element.wrap(imageWrapper).after(imageMagnifier).parent().hover(function() 252 { 253 $('.image-magnifier', $(this).parent()).show(150); 254 }, 255 function() 256 { 257 $('.image-magnifier', $(this).parent()).hide(150); 258 }); 259 260 imageMagnifier.after(bigImg).click(function() 261 { 262 ui.loadImage() 263 }); 264 }, 265 266 loadImage: function() 267 { 268 var ui = this; 269 var imageOrigSrc = ui.element.attr('src').split('.'); 270 var imageTempSrc = imageOrigSrc[0].split('/image/'); 271 var imageExt = imageOrigSrc[imageOrigSrc.length - 1]; 272 var imageSrc = '/media/inline/' + imageTempSrc[imageTempSrc.length - 1] + '.' + imageExt; 273 var bigImg = ui.element.siblings('img'); 274 275 var loader = $('<span></span>
275').css({background: '#fff url(/lib/images/spinner.gif) 50% 50% no-repeat', opacity: .5, width: ui.element.width(), height: ui.element.height(), position: "absolute", top: ui.element.offset().top, left: ui.element.offset().left}) 276 277 if(!$('.loaded-bigImage', ui.element.parent()).length) 278 { 279 $(document.body).append(loader); 280 } 281 282 $(bigImg) 283 .load(function() 284 { 285 $(this) 286 .hide() 287 .addClass('loaded-bigImage') 288 .unbind('load'); 289 290 if(!$('.loaded-bigImage', ui.element.parent()).length) 291 { 292 ui.element.after($(this)); 293 } 294 295 loader.remove(); 296 ui.setWidthHeight(); 297 ui.showBig(); 298 }) 299 .attr({src: imageSrc}); 300 }, 301 302 setWidthHeight: function() 303 { 304 $('.loaded-bigImage', this.element.parent()) 305 .attr({ 306 width: jQuery('.loaded-bigImage', this.element.parent()).width(), 307 height: jQuery('.loaded-bigImage', this.element.parent()).height() 308 }); 309 }, 310 311 showBig: function() 312 { 313 var ui = this; 314 var imgObj = jQuery('.loaded-bigImage', ui.element.parent()); 315 var imgWrapper = ui.element.parent(); 316 var zoomImgWidth = imgObj.attr('width'); 317 var zoomImgHeight = imgObj.attr('height'); 318 var fullWidth = zoomImgWidth; 319 var fullHeight = zoomImgHeight; 320 321 if(zoomImgWidth > $(window).width()) 322 { 323 fullWidth = $(window).width() - 40; 324 fullHeight = zoomImgHeight * (fullWidth / zoomImgWidth); 325 } 326 327 if(zoomImgHeight > $(window).height()) 328 { 329 fullHeight = $(window).height() - 40; 330 fullWidth = zoomImgWidth * (fullHeight / zoomImgHeight); 331 } 332 333 leftPos = ($(window).width() / 2) - (fullWidth / 2); 334 topPos = $(window).scrollTop() + ($(window).height() / 2) - (fullHeight / 2); 335 336 $(window).resize(function() 337 { 338 $('.image-magnifier', ui.element.parent()).each(function() 339 { 340 $(this).css({top: $(this).parent().offset().top, left: $(this).parent().offset().left}); 341 }); 342 }); 343 344 if(!$('.popup-overlay').length) 345 { 346 $('<span</span>') 347 .addClass('popup-overlay') 348 .appendTo(document.body) 349 .css({display: 'none', height: $(document).height(), zIndex: 8000}) 350 .animate({opacity: .7}, 200) 351 .click(function() 352 { 353 ui.kill() 354 }); 355 } 356 357 $('.popup-overlay').show(); 358 359 imgObj 360 .css({display: 'none', position: "absolute", zIndex: 9999, width: fullWidth, height: fullHeight, left: leftPos, top: topPos}) 361 .fadeIn(200) 362 .click(function() 363 { 364 ui.kill() 365 }); 366 }, 367 368 kill: function() 369 { 370 jQuery('.popup-overlay').fadeOut(200); 371 jQuery('.loaded-bigImage').fadeOut(200); 372 this.destroy(); 373 } 374});/* dialog js 375 ---------------------------------------------------------- 376 377 @package: Zotonic 2009, 2012, 2015 378 @Author: Tim Benniks <[email protected]> 379 380 Copyright 2009 Tim Benniks 381 Copyright 2012 Arjan Scherpenisse 382 Copyright 2015, 2016 Arthur Clemens 383 384 Licensed under the Apache License, Version 2.0 (the "License");
385 you may not use this file except in compliance with the License. 386 You may obtain a copy of the License at 387 388 http://www.apache.org/licenses/LICENSE-2.0 389 390 Unless required by applicable law or agreed to in writing, software 391 distributed under the License is distributed on an "AS IS" BASIS, 392 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 393 See the License for the specific language governing permissions and 394 limitations under the License. 395 396 ---------------------------------------------------------- */ 397 398(function($) { 399 $.extend({ 400 dialogAdd: function(options) { 401 var width, 402 $title, 403 $body, 404 $text, 405 $modalContent, 406 dialogClass, 407 $modalDialog, 408 $dialog; 409 410 $('#zmodal').remove(); 411 $('.modal-backdrop').remove(); 412 413 options = $.extend({}, $.ui.dialog.defaults, options); 414 415 if (options.backdrop !== 'static') { 416 $title = $('<div>') 417 .addClass('modal-header') 418 .append($('<a>') 419 .addClass('close') 420 .attr('data-dismiss', 'modal') 421 .html('<span>×</span>')) 422 .append($('<h4>') 423 .addClass('modal-title') 424 .html(options.title)); 425 } else { 426 $title = $('<div>') 427 .addClass('modal-header') 428 .append($('<h4>') 429 .addClass('modal-title') 430 .html(options.title)); 431 } 432 $modalContent = $('<div>').addClass('modal-content'); 433 $text = $('<div>').html(options.text); 434 435 // if .modal-body is used in a template, don't add it again 436 if ($text.hasClass('modal-body')) { 437 $body = $text; 438 } else { 439 $body = $('<div>') 440 .addClass('modal-body') 441 .html(options.text); 442 } 443 444 $modalContent = $('<div>') 445 .addClass('modal-content') 446 .append($title) 447 .append($body); 448 449 dialogClass = 'modal'; 450 if (typeof(options.addclass) == 'string') { 451 dialogClass += ' ' + options.addclass; 452 } 453 454 $modalDialog = $('<div>') 455 .addClass('modal-dialog') 456 .append($modalContent); 457 458 width = options.width; 459 if (width) { 460 if (width === 'large') { 461 $modalDialog.addClass('modal-lg'); 462 } else if (width === 'small') { 463 $modalDialog.addClass('modal-sm'); 464 } else { 465 $modalDialog.css({'width': width + 'px'}); 466 } 467 } 468 469 $dialog = $('<div>') 470 .attr('id', 'zmodal') 471 .addClass(dialogClass) 472 .append($modalDialog) 473 .appendTo($('body')); 474 475 $dialog 476 .modal({backdrop: options.backdrop}) 477 .css({'overflow-x': 'hidden', 'overflow-y': 'auto'}); 478 479 480 if (options.center) { 481 $modalDialog.hide(); 482 setTimeout(function() { 483 // $.dialogCenter(); 484 $modalDialog.show(); 485 }, 0); 486 } 487 488 if (typeof($.widgetManager) != 'undefined') { 489 $dialog.widgetManager(); 490 } 491 z_editor_add($dialog); 492 }, 493 494 dialogClose: function() { 495 $('#zmodal').modal('hide'); 496 }, 497 498 dialogRemove: function(obj) { 499 obj = obj || $('#zmodal'); 500 z_editor_remove(obj); 501 obj 502 .draggable('destroy') 503 .resizable('destroy') 504 .fadeOut(300, function() { 505 $(this).remove(); 506 }); 507 }, 508 509 dialogCenter: function() { 510 var $dialog, 511 newMarginTop; 512 $dialog = $('#zmodal:visible').find('.modal-dialog'); 513 newMarginTop = Math.max(0, ($(window).height() - $dialog.height()) / 2); 514 newMarginTop *= .96; // visual coherence 515 newMarginTop = Math.max(newMarginTop, 30); 516 $dialog.css('margin-top', newMarginTop); 517 }, 518 519 dialogScrollTo: function(position) { 520 position = position || 0; 521 $("#zmodal")[0].scrollTop = position 522 } 523 }); 524 525 // $(window).on('resize', function() { 526 // $.dialogCenter(); 527 // }); 528 529 $.widget('ui.show_dialog', { 530 _init: function() { 531 var self = this; 532 this.element.click(function() { 533 $.dialogAdd({ 534 title: self.options.title, 535 text: self.options.text, 536 width: self.options.width, 537 addclass: self.options.addclass, 538 backdrop: self.options.backdrop 539 }); 540 }); 541 } 542 }); 543 544 /* 545 Default dialog parameters: 546 title: text, will be inserted in h4 547 text: text content, may contain html (will be inserted into div) 548 width: (optional) 549 addclass: (optional) classname will be appended to default dialog class 550 backdrop: (optional) boolean (0, 1) or the string 'static' 551 center: (optional) boolean (0, 1); set to 0 to align dialog to the top 552 */ 553 $.ui.dialog.defaults = { 554 title: 'Title', 555 text: 'text', 556 width: undefined, 557 addclass: undefined, 558 backdrop: 1, 559 center: 1 560 }; 561})(jQuery); 562/** 563# qlobber [](https://travis-ci.org/davedoesdev/qlobber) [](
563https://coveralls.io/r/davedoesdev/qlobber?branch=master) [](http://badge.fury.io/js/qlobber) 564 565Node.js globbing for amqp-like topics. 566 567Example: 568 569```javascript 570var Qlobber = require('qlobber').Qlobber; 571var matcher = new Qlobber(); 572matcher.add('foo.*', 'it matched!'); 573assert.deepEqual(matcher.match('foo.bar'), ['it matched!']); 574``` 575 576The API is described [here](#tableofcontents). 577 578qlobber is implemented using a trie, as described in the RabbitMQ blog posts [here](http://www.rabbitmq.com/blog/2010/09/14/very-fast-and-scalable-topic-routing-part-1/) and [here](http://www.rabbitmq.com/blog/2011/03/28/very-fast-and-scalable-topic-routing-part-2/). 579 580## Installation 581 582```shell 583npm install qlobber 584``` 585 586## Another Example 587 588A more advanced example using topics from the [RabbitMQ topic tutorial](http://www.rabbitmq.com/tutorials/tutorial-five-python.html): 589 590```javascript 591var matcher = new Qlobber(); 592matcher.add('*.orange.*', 'Q1'); 593matcher.add('*.*.rabbit', 'Q2'); 594matcher.add('lazy.#', 'Q2'); 595assert.deepEqual(['quick.orange.rabbit', 596 'lazy.orange.elephant', 597 'quick.orange.fox', 598 'lazy.brown.fox', 599 'lazy.pink.rabbit', 600 'quick.brown.fox', 601 'orange', 602 'quick.orange.male.rabbit', 603 'lazy.orange.male.rabbit'].map(function (topic) 604 { 605 return matcher.match(topic).sort(); 606 }), 607 [['Q1', 'Q2'], 608 ['Q1', 'Q2'], 609 ['Q1'], 610 ['Q2'], 611 ['Q2', 'Q2'], 612 [], 613 [], 614 [], 615 ['Q2']]); 616``` 617 618## Licence 619 620[MIT](LICENCE) 621 622## Tests 623 624qlobber passes the [RabbitMQ topic tests](https://github.com/rabbitmq/rabbitmq-server/blob/master/src/rabbit_tests.erl) (I converted them from Erlang to Javascript). 625 626To run the tests: 627 628```javascript 629grunt test 630``` 631 632## Lint 633 634```javascript 635grunt lint 636``` 637 638## Code Coverage 639 640```javascript 641grunt coverage 642``` 643 644[Instanbul](http://gotwarlost.github.io/istanbul/) results are available [here](http://htmlpreview.github.io/?https://github.com/davedoesdev/qlobber/blob/master/coverage/lcov-report/index.html). 645 646Coveralls page is [here](https://coveralls.io/r/davedoesdev/qlobber). 647 648## Benchmarks 649 650```javascript 651grunt bench 652``` 653 654qlobber is also benchmarked in [ascoltatori](https://github.com/mcollina/ascoltatori). 655 656# API 657*/ 658 659/*jslint node: true, nomen: true */ 660"use strict"; 661 662/** 663Creates a new qlobber. 664 665@constructor 666@param {Object} [options] Configures the qlobber. Use the following properties: 667 668- `{String} separator` The character to use for separating words in topics. Defaults to '.'. MQTT uses '/' as the separator, for example. 669 670- `{String} wildcard_one` The character to use for matching exactly one word in a topic. Defaults to '*'. MQTT uses '+', for example. 671 672- `{String} wildcard_some` The character to use for matching zero or more words in a topic. Defaults to '#'. MQTT uses '#' too. 673*/ 674function Qlobber (options) 675{ 676 options = options || {}; 677 678 this._separator = options.separator || '.'; 679 this._wildcard_one = options.wildcard_one || '*'; 680 this._wildcard_some = options.wildcard_some || '#'; 681 this._trie = {}; 682} 683 684Qlobber.prototype._add = function (val, i, words, sub_trie) 685{ 686 var st, word; 687 688 if (i === words.length) 689 { 690 st = sub_trie[this._separator]; 691 692 if (st) 693 { 694 st.push(val); 695 } 696 else 697 { 698 sub_trie[this._separator] = [val]; 699 } 700 701 return; 702 } 703 704 word = words[i]; 705 st = sub_trie[word]; 706 707 if (!st) 708 { 709 st = sub_trie[word] = {}; 710 } 711 712 this._add(val, i + 1, words, st); 713}; 714 715Qlobber.prototype._remove = function (val, i, words, sub_trie) 716{ 717 var st, index, word; 718 719 if (i === words.length) 720 { 721 st = sub_trie[this._separator]; 722 723 if (st) 724 { 725 if (val === undefined) 726 { 727 st = []; 728 } 729 else 730 { 731 index = st.lastIndexOf(val); 732 733 if (index >= 0) 734 { 735 st.splice(index, 1); 736 } 737 } 738 739 if (st.length === 0) 740 { 741 delete sub_trie[this._separator]; 742 } 743 } 744 745 return; 746 } 747 748 word = words[i]; 749 st = sub_trie[word]; 750 751 if (!st) 752 { 753 return; 754 } 755 756 this._remove(val, i + 1, words, st); 757 758 /*jslint forin: true */ 759 for (word in st) 760 { 761 return; 762 } 763 /*jslint forin: false */ 764 765 delete sub_trie[word]; 766}; 767 768Qlobber.prototype._match = function (v, i, words, sub_trie) 769{ 770 var word, st, w, j; 771 772 st = sub_trie[this._wildcard_some]; 773 774 if (st) 775 { 776 // common case: no more levels 777 778 /*jslint forin: true */ 779 for (w in st) 780 { 781 if (w !== this._separator) 782 { 783 for (j = i; j < words.length; j += 1) 784 { 785 v = this._match(v, j, words, st); 786 } 787 788 break; 789 } 790 } 791 /*jslint forin: false */ 792 793 v = this._match(v, words.length, words, st); 794 } 795 796 if (i === words.length) 797 { 798 st = sub_trie[this._separator]; 799 800 if (st) 801 { 802 v = v.concat(st); 803 } 804 } 805 else 806 { 807 word = words[i]; 808 809 if ((word !== this._wildcard_one) && (word !== this._wildcard_some)) 810 { 811 st = sub_trie[word]; 812 813 if (st) 814 { 815 v = this._match(v, i + 1, words, st); 816 } 817 } 818 819 if (word) 820 { 821 st = sub_trie[this._wildcard_one]; 822 823 if (st) 824 { 825 v = this._match(v, i + 1, words, st); 826 } 827 } 828 } 829 830 return v; 831}; 832 833/** 834Add a topic matcher to the qlobber. 835 836Note you can match more than one value against a topic by calling `add` multiple times with the same topic and different values. 837 838@param {String} topic The topic to match against. 839@param {Any} val The value to return if the topic is matched. `undefined` is not supported. 840@return {Qlobber} The qlobber (for chaining). 841*/ 842Qlobber.prototype.add = function (topic, val) 843{ 844 this._add(val, 0, topic.split(this._separator), this._trie); 845 return this; 846}; 847 848/** 849Remove a topic matcher from the qlobber. 850 851@param {String} topic The topic that's being matched against. 852@param {Any} [val] The value that's being matched. If you don't specify `val` then all matchers for `topic` are removed. 853@return {Qlobber} The qlobber (for chaining). 854*/ 855Qlobber.prototype.remove = function (topic, val) 856{ 857 this._remove(val, 0, topic.split(this._separator), this._trie); 858 return this; 859}; 860 861/** 862Match a topic. 863 864@param {String} topic The topic to match against. 865@return {Array} List of values that matched the topic. This may contain duplicates. 866*/ 867Qlobber.prototype.match = function (topic) 868{ 869 return this._match([], 0, topic.split(this._separator), this._trie); 870}; 871 872/** 873Reset the qlobber. 874 875Removes all topic matchers from the qlobber. 876 877@return {Qlobber} The qlobber (for chaining). 878*/ 879Qlobber.prototype.clear = function () 880{ 881 this._trie = {}; 882 return this; 883}; 884 885// for debugging 886Qlobber.prototype.get_trie = function () 887{ 888 return this._trie; 889}; 890/* Zotonic's Pubsub using qmtt over websockets/comet 891 892Special topics: 893 894 "~pagesession" within this page 895 "~site/foo/bar" within the site, handled on the server 896 "foo/bar" the root of the server's mqtt router 897 898TODO: 899 900 * add relay from page topics to server topics 901 * add QoS levels 902 - default level 0 903 - add level 0 and 1, needs local puback 904 - add dup flag to messages 905 * retain local messages for topics 906*/ 907 908function Pubzub () 909{ 910 this._new_matcher = function() { 911 return new Qlobber({ 912 separator: '/', 913 wildcard_one: '+', 914 wildcard_some: '#' 915 }); 916 }; 917 this._matcher = this._new_matcher(); 918 this._subs = {}; 919 this._subs_relayed = {}; 920 this._retained = {}; 921 922 this._will_id = 0; 923 ubf.add_spec("mqtt_msg", [ 924 "retain", "qos", "topic", "dup", "message_id", "payload", 925 "encoder" 926 ]); 927 ubf.add_spec("z_mqtt_payload", [ 928 "site", "user_id", "payload" 929 ]); 930 ubf.add_spec("z_mqtt_cmd", [ 931 "cmd", "topic", "payload", "extra" 932 ]); 933 934 var self = this; 935 z_transport_delegate_register( 936 'mqtt_route', 937 function(mqtt_msg, msg) { 938 self.relayed(mqtt_msg, msg); 939 }); 940} 941 942Pubzub.prototype.me = function () { 943 return "~site/pagesession/" + z_pageid; 944}; 945 946Pubzub.prototype.subscribe_multi = function (topics, fun) { 947 for(var i=topics.length-1; i >= 0; i--) { 948 this.subscribe(topics[i], fun); 949 } 950}; 951 952Pubzub.prototype.subscribe = function (topic, fun, ack) { 953 var id = this.unique_id(); 954 var async_ack = false;
955 this._matcher.add(topic, id); 956 this._subs[id] = {fun: fun, topic: topic}; 957 if (!this.is_local_topic(topic)) { 958 var ct = this._subs_relayed[topic] || 0; 959 if (ct === 0) { 960 async_ack = true; 961 this.transport("subscribe", topic, undefined, undefined, ack); 962 } 963 this._subs_relayed[topic] = ct + 1; 964 } else { 965 this.handle_retained(topic, fun, id); 966 } 967 if(ack && !async_ack) { setTimeout(ack, 0); } 968 return id; 969}; 970 971Pubzub.prototype.unsubscribe = function (id) { 972 if (this._subs[id]) { 973 var sub = this._subs[id]; 974 var topic = sub.topic; 975 976 this._matcher.remove(topic, id); 977 if (!this.is_local_topic(topic)) { 978 var ct = this._subs_relayed[topic] - 1; 979 if (ct === 0) { 980 this.transport("unsubscribe", topic); 981 } 982 this._subs_relayed[topic] = ct; 983 } 984 delete this._subs[id]; 985 } 986}; 987 988Pubzub.prototype.publish = function (topic, message, options) { 989 options = options || {retained: false}; 990 991 if (this.is_local_topic(topic)) { 992 if (message === undefined && options.retained) { 993 delete this._retained[topic]; 994 return; 995 } 996 997 var subs = this._matcher.match(topic); 998 for (var i in subs) { 999 var id = subs[i]; 1000 if (this._subs[id]) { 1001 this._subs[id].fun(topic, message, id); 1002 } 1003 } 1004 1005 if (options.retained) { 1006 this._retained[topic] = message; 1007 } 1008 } else { 1009 if(options.retained) 1010 throw "Error: retained messages are not supported for non-local topics"; 1011 1012 this.transport("publish", topic, message); 1013 } 1014}; 1015 1016Pubzub.prototype.subscribers = function (topic) { 1017 return this._matcher.match(topic); 1018}; 1019 1020Pubzub.prototype.reply_topic = function (sub_topic) { 1021 if (!sub_topic) { 1022 return "~site/reply/"+this.unique_id(); 1023 } else if (sub_topic.substr(0,1) == "/") { 1024 return "~site/reply/"+this.unique_id()+sub_topic; 1025 } else { 1026 return "~site/reply/"+this.unique_id()+"/"+sub_topic; 1027 } 1028}; 1029 1030Pubzub.prototype.lastwill = function (topic, message) { 1031 this._will_id = this.unique_id(); 1032 this.transport("lastwill", topic, message, this._will_id); 1033}; 1034 1035Pubzub.prototype.remove_lastwill = function () { 1036 this.transport("lastwill", undefined, undefined, this._will_id); 1037 this._will_id = undefined; 1038}; 1039 1040Pubzub.prototype.is_local_topic = function (topic) { 1041 var me = this.me(); 1042 1043 return topic == '~pagesession' || 1044 topic == me || 1045 topic.substr(0,13) == "~pagesession/" || 1046 topic.substr(0,me.length+1) == me+"/"; 1047}; 1048 1049Pubzub.prototype.relayed = function (mqtt_msg, _msg) { 1050 var subs = this._matcher.match(mqtt_msg.topic); 1051 for (var i in subs) { 1052 var id = subs[i]; 1053 if (this._subs[id]) { 1054 this._subs[id].fun(mqtt_msg.topic, mqtt_msg.payload, id); 1055 } 1056 } 1057}; 1058 1059Pubzub.prototype.handle_retained = function(topic, fun, id) { 1060 var retained_matcher = this._new_matcher(); 1061 retained_matcher.add(topic, true); 1062 1063 function publish(topic, msg) { 1064 setTimeout(function() { fun(topic, msg, id); }, 0); 1065 } 1066 1067 for(var retained_topic in this._retained) { 1068 for(var matches in retained_matcher.match(retained_topic)) { 1069 if(matches.length) { 1070 publish(retained_topic, this._retained[retained_topic]); 1071 } 1072 } 1073 } 1074}; 1075 1076Pubzub.prototype.transport = function (cmd, topic, payload, extra, ack) { 1077 var msg = { 1078 _record: 'z_mqtt_cmd', 1079 cmd: cmd, 1080 topic: topic, 1081 payload: payload, 1082 extra: extra 1083 }; 1084 var options = { 1085 qos: 1, 1086 ack: ack 1087 }; 1088 z_transport('mqtt', 'ubf', msg, options); 1089}; 1090 1091Pubzub.prototype.unique_id = function () { 1092 var t = (new Date()).getTime() + "-"; 1093 var cs = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 1094 for( var i=0; i < 20; i++ ) 1095 t += cs.charAt(Math.floor(Math.random() * cs.length)); 1096 return t; 1097}; 1098 1099Pubzub.prototype.make_zEvtArgs = function (topic, msg, sub_id) { 1100 if(typeof msg == 'object') { 1101 var args = ensure_name_value(msg); 1102 args.unshift({name: "topic", value: topic}); 1103 args.unshift({name: "sub_id", value: sub_id}); 1104 return args; 1105 } 1106 1107 return undefined; 1108}; 1109 1110(function($) { 1111 window.pubzub = new Pubzub(); 1112})(jQuery); 1113/* live js 1114---------------------------------------------------------- 1115 1116@package: Zotonic 2014 1117@Author: Marc Worrell <[email protected]> 1118 1119Copyright 2014 Marc Worrell 1120 1121Licensed under the Apache License, Version 2.0 (the "License"); 1122you may not use this file except in compliance with the License. 1123You may obtain a copy of the License at 1124 1125http://www.apache.org/licenses/LICENSE-2.0 1126 1127Unless required by applicable law or agreed to in writing, softw
1127are 1128distributed under the License is distributed on an "AS IS" BASIS, 1129WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1130See the License for the specific language governing permissions and 1131limitations under the License. 1132 1133---------------------------------------------------------- */ 1134 1135function ZLive () 1136{ 1137 this._subscriptions = []; 1138 this._prune_timeout = 10000; 1139 var self = this; 1140 setTimeout(function() { self.prune(); }, this._prune_timeout); 1141} 1142 1143ZLive.prototype.subscribe = function(topics, target, postback) { 1144 var self = this; 1145 for(var i=topics.length-1; i >= 0; i--) { 1146 var topic = topics[i]; 1147 var sub_id = pubzub.subscribe( 1148 topic, 1149 function(_topic, msg, id) { 1150 self.update(topic, target, postback, msg, id); 1151 }); 1152 1153 this._subscriptions.push({ 1154 sub_id: sub_id, 1155 topic: topic, 1156 target: target, 1157 postback: postback 1158 }); 1159 } 1160}; 1161 1162ZLive.prototype.update = function(topic, target, postback, payload, sub_id) { 1163 if ($('#'+target).length) { 1164 if (typeof payload._record != 'undefined' && payload._record == 'z_mqtt_payload') { 1165 payload = payload.payload; 1166 } 1167 z_queue_postback(target, postback, {topic: topic, message: payload}); 1168 } else { 1169 this.unsubscribe(sub_id); 1170 } 1171}; 1172 1173ZLive.prototype.unsubscribe = function(sub_id) { 1174 for (var i=0; i<= this._subscriptions.length; i++) { 1175 if (this._subscriptions[i].sub_id == sub_id) { 1176 this._subscriptions.splice(i,1); 1177 break; 1178 } 1179 } 1180 pubzub.unsubscribe(sub_id); 1181}; 1182 1183ZLive.prototype.prune = function() { 1184 for (var i=this._subscriptions.length-1; i >= 0; i--) { 1185 var target = this._subscriptions[i].target; 1186 if ($('#'+target).length === 0) { 1187 pubzub.unsubscribe(this._subscriptions[i].sub_id); 1188 this._subscriptions.splice(i,1); 1189 } 1190 } 1191 var self = this; 1192 setTimeout(function() { self.prune(); }, this._prune_timeout); 1193}; 1194 1195window.z_live = new ZLive(); 1196// LiveValidation 1.3 (standalone version) 1197// Copyright (c) 2007-2008 Alec Hill (www.livevalidation.com) 1198// LiveValidation is licensed under the terms of the MIT License 1199 1200// MW: 20100316: Adapted for async usage with Zotonic.
1201// MW: 20100629: Added support for presence check on radio buttons 1202// MW: 20110329: Dynamically fetch the validation fields from the DOM, this makes it possible to add/remove fields dynamically. 1203// AC: 20150129: Removed unused class names. Changed messageClass to Bootstrap 3 conform "help-block". Replaced hardcoded "control-group" to fieldGroupClass, using Bootstrap 3 "form-group". Replaced hardcoded class "success" in favor of fieldGroupSuccessClass (Bootstrap 3 "has-success"); replaced hardcoded class "error" in favor of fieldGroupErrorClass (Bootstrap 3 "has-error"). 1204 1205 1206/*********************************************** LiveValidation class ***********************************/ 1207 1208 1209function addLiveValidation(element, args) { 1210 if (!$(element).data("z_live_validation")) 1211 $(element).data("z_live_validation", new LiveValidation($(element).attr('id'), args)); 1212} 1213 1214 1215function getLiveValidation(element) { 1216 return $(element).data("z_live_validation"); 1217} 1218 1219 1220/** 1221 * validates a form field in real-time based on validations you assign to it 1222 * 1223 * @var element {mixed} - either a dom element reference or the string id of the element to validate 1224 * @var optionsObj {Object} - general options, see below for details 1225 * 1226 * optionsObj properties: 1227 * validMessage {String} - the message to show when the field passes validation 1228 * (DEFAULT: "Thankyou!") 1229 * onAsync {Function} - function to execute when field passes is waiting for async validation 1230 * (DEFAULT: function(){ this.insertMessage(this.createSpinnerSpan()); this.addFieldClass(); } ) 1231 * onValid {Function} - function to execute when field passes validation 1232 * (DEFAULT: function(){ this.insertMessage(this.createMessageSpan()); this.addFieldClass(); } ) 1233 * onInvalid {Function} - function to execute when field fails validation 1234 * (DEFAULT: function(){ this.insertMessage(this.createMessageSpan()); this.addFieldClass(); }) 1235 * insertAfterWhatNode {Int} - position to insert default message 1236 * (DEFAULT: the field that is being validated) 1237 * onlyOnBlur {Boolean} - whether you want it to validate as you type or only on blur 1238 * (DEFAULT: false) 1239 * wait {Integer} - the time you want it to pause from the last keystroke before it validates (ms) 1240 * (DEFAULT: 0) 1241 * onlyOnSubmit {Boolean} - whether should be validated only when the form it belongs to is submitted 1242 * (DEFAULT: false) 1243 */ 1244 1245var LiveValidation = function(element, optionsObj){ 1246 this.initialize(element, optionsObj); 1247}; 1248 1249LiveValidation.VERSION = '1.3 standalone-zotonic'; 1250 1251/** element types constants ****/ 1252 1253LiveValidation.TEXTAREA = 1; 1254LiveValidation.TEXT = 2; 1255LiveValidation.PASSWORD = 3; 1256LiveValidation.CHECKBOX = 4; 1257LiveValidation.SELECT = 5; 1258LiveValidation.FILE = 6; 1259LiveValidation.RADIO = 7; 1260LiveValidation.FORM = 8; 1261 1262 1263/****** prototype ******/ 1264 1265LiveValidation.prototype = { 1266 1267 validClass: '', // was: z_valid 1268 invalidClass: 'z_invalid', // used ny mod_survey 1269 messageClass: 'z_validation help-block', 1270 validFieldClass: '', // was: z_valid_field 1271 invalidFieldClass: '', // was: form-field-error 1272 asyncFieldClass: '', // was: z_async_validation 1273 fieldGroupClass: 'form-group', 1274 fieldGroupErrorClass: 'has-error', 1275 fieldGroupSuccessClass: 'has-success', 1276 1277 /** 1278 * initialises all of the properties and events 1279 * 1280 * @var - Same as constructor above 1281 */ 1282 initialize: function(element, optionsObj){ 1283 var self = this; 1284 var $form; 1285 1286 if(!element) 1287 throw new Error("LiveValidation::initialize - No element reference or element id has been provided!"); 1288 this.element = element.nodeName ? element : document.getElementById(element); 1289 if(!this.element) 1290 throw new Error("LiveValidation::initialize - No element with reference or id of '" + element + "' exists!"); 1291 // default properties that could not be initialised above 1292 this.validations = []; 1293 this.elementType = this.getElementType(); 1294 var options = optionsObj || {}; 1295 if (this.elementType == LiveValidation.FORM) { 1296 this.form = this.element; 1297 $form = $(this.element); 1298 this.onAsync = options.onAsync || function(){}; 1299 this.onValid = options.onValid || function(){}; 1300 this.onInvalid = options.onInvalid || function(){}; 1301 this.onlyOnBlur = options.onlyOnBlur || false; 1302 this.wait = options.wait || 0; 1303 this.onlyOnSubmit = true; 1304 } else { 1305 this.form = this.element.form; 1306 $form = $(this.element).closest("form"); 1307 this.onAsync = options.onAsync || function(){ this.insertSpinner(this.createSpinnerSpan()); this.addFieldClass(); }; 1308 this.onValid = options.onValid || function(){ this.insertMessage(this.createMessageSpan()); this.addFieldClass(); }; 1309 this.onInvalid = options.onInvalid || function(){ this.insertMessage(this.createMessageSpan()); this.addFieldClass(); }; 1310 this.onlyOnBlur = options.onlyOnBlur || false; 1311 this.wait = options.wait || 0; 1312 this.onlyOnSubmit = options.onlyOnSubmit || false; 1313 } 1314 // options 1315 this.validMessage = options.validMessage || ''; 1316 var node = options.insertAfterWhatNode || this.element; 1317 this.insertAfterWhatNode = node.nodeType ? node : document.getElementById(node); 1318 1319 //if document.getElementById(node) returned null, then set the original element 1320 if(!this.insertAfterWhatNode) 1321 this.insertAfterWhatNode = this.element; 1322 1323 this.validationAsync = false;
1324 1325 // Initialize the form hooks, remember the LiveValidationForm object. 1326 if($form.length){ 1327 this.formObj = LiveValidationForm.getInstance($form[0]); 1328 } 1329 1330 // events 1331 // collect old events 1332 if (this.elementType != LiveValidation.FORM) { 1333 this.oldOnFocus = this.element.onfocus || function(){}; 1334 this.oldOnBlur = this.element.onblur || function(){}; 1335 this.oldOnClick = this.element.onclick || function(){}; 1336 this.oldOnChange = this.element.onchange || function(){}; 1337 this.oldOnKeyup = this.element.onkeyup || function(){}; 1338 this.element.onfocus = function(e){ self.doOnFocus(e); return self.oldOnFocus.call(this, e); }; 1339 if(!this.onlyOnSubmit){ 1340 switch(this.elementType){ 1341 case LiveValidation.RADIO: 1342 case LiveValidation.CHECKBOX: 1343 this.element.onclick = function(e){ self.validate(); return self.oldOnClick.call(this, e); }; 1344 this.element.onchange = function(e){ self.validate(); return self.oldOnChange.call(this, e); }; 1345 break; 1346 case LiveValidation.SELECT: 1347 case LiveValidation.FILE: 1348 this.element.onchange = function(e){ self.validate(); return self.oldOnChange.call(this, e); }; 1349 break; 1350 default: 1351 if(!this.onlyOnBlur) this.element.onkeyup = function(e){ self.deferValidation(); return self.oldOnKeyup.call(this, e); }; 1352 this.element.onblur = function(e){ self.doOnBlur(e); return self.oldOnBlur.call(this, e); }; 1353 break; 1354 } 1355 } 1356 } 1357 }, 1358 1359 /** 1360 * destroys the instance's events (restoring previous ones) and removes it from any LiveValidationForms 1361 */ 1362 destroy: function(){ 1363 // remove events - set them back to the previous events 1364 if (this.elementType != LiveValidation.FORM) { 1365 this.element.onfocus = this.oldOnFocus; 1366 if(!this.onlyOnSubmit){ 1367 switch(this.elementType){ 1368 case LiveValidation.RADIO: 1369 case LiveValidation.CHECKBOX: 1370 this.element.onclick = this.oldOnClick; 1371 this.element.onchange = this.oldOnChange; 1372 break; 1373 case LiveValidation.SELECT: 1374 case LiveValidation.FILE: 1375 this.element.onchange = this.oldOnChange; 1376 break; 1377 default: 1378 if(!this.onlyOnBlur) this.element.onkeyup = this.oldOnKeyup; 1379 this.element.onblur = this.oldOnBlur; 1380 break; 1381 } 1382 } 1383 } 1384 this.validations = []; 1385 this.removeMessageAndFieldClass(); 1386 }, 1387 1388 /** 1389 * Adds a validation to perform to a LiveValidation object 1390 * 1391 * @var validationFunction {Function} - validation function to be used (ie Validate.Presence ) 1392 * @var validationParamsObj {Object} - parameters for doing the validation, if wanted or necessary 1393 * @return {Object} - the LiveValidation object itself so that calls can be chained 1394 */ 1395 add: function(validationFunction, validationParamsObj){ 1396 this.validations.push( {type: validationFunction, params: validationParamsObj || {} } ); 1397 return this; 1398 }, 1399 1400 /** 1401 * Removes a validation from a LiveValidation object - must have exactly the same arguments as used to add it 1402 * 1403 * @var validationFunction {Function} - validation function to be used (ie Validate.Presence ) 1404 * @var validationParamsObj {Object} - parameters for doing the validation, if wanted or necessary 1405 * @return {Object} - the LiveValidation object itself so that calls can be chained 1406 */ 1407 remove: function(validationFunction, validationParamsObj){ 1408 var found = false; 1409 for( var i = 0, len = this.validations.length; i < len; i++ ){ 1410 if( this.validations[i].type == validationFunction ){ 1411 if (this.validations[i].params == validationParamsObj) { 1412 found = true; 1413 break; 1414 } 1415 } 1416 } 1417 if(found) this.validations.splice(i,1); 1418 return this; 1419 }, 1420 1421 1422 /** 1423 * makes the validation wait the alotted time from the last keystroke 1424 */ 1425 deferValidation: function(e){ 1426 if (this.wait >= 300) 1427 this.removeMessageAndFieldClass(); 1428 if (this.timeout) 1429 clearTimeout(this.timeout); 1430 var self = this; 1431 this.timeout = setTimeout( function(){ self.validate(); }, this.wait); 1432 }, 1433 1434 /** 1435 * sets the focused flag to false when field loses focus 1436 */ 1437 doOnBlur: function(e){ 1438 this.focused = false;
1439 this.validate(); 1440 }, 1441 1442 /** 1443 * sets the focused flag to true when field gains focus 1444 */ 1445 doOnFocus: function(e){ 1446 this.focused = true; 1447 this.removeMessageAndFieldClass(); 1448 }, 1449 1450 /** 1451 * gets the type of element, to check whether it is compatible 1452 * 1453 * @var validationFunction {Function} - validation function to be used (ie Validate.Presence ) 1454 * @var validationParamsObj {Object} - parameters for doing the validation, if wanted or necessary 1455 */ 1456 getElementType: function(){ 1457 var nodeName = this.element.nodeName.toUpperCase(); 1458 if (nodeName == 'TEXTAREA') 1459 return LiveValidation.TEXTAREA; 1460 if (nodeName == 'INPUT' && this.element.type.toUpperCase() == 'TEXT') 1461 return LiveValidation.TEXT; 1462 if (nodeName == 'INPUT' && this.element.type.toUpperCase() == 'PASSWORD') 1463 return LiveValidation.PASSWORD; 1464 if (nodeName == 'INPUT' && this.element.type.toUpperCase() == 'CHECKBOX') 1465 return LiveValidation.CHECKBOX; 1466 if (nodeName == 'INPUT' && this.element.type.toUpperCase() == 'FILE') 1467 return LiveValidation.FILE; 1468 if (nodeName == 'INPUT' && this.element.type.toUpperCase() == 'RADIO') 1469 return LiveValidation.RADIO; 1470 if (nodeName == 'INPUT' && this.element.type.toUpperCase() == 'EMAIL') 1471 return LiveValidation.TEXT; 1472 if (nodeName == 'INPUT' && this.element.type.toUpperCase() == 'TEL') 1473 return LiveValidation.TEXT; 1474 if (nodeName == 'INPUT' && this.element.type.toUpperCase() == 'NUMBER') 1475 return LiveValidation.TEXT; 1476 if (nodeName == 'INPUT' && this.element.type.toUpperCase() == 'URL') 1477 return LiveValidation.TEXT; 1478 if (nodeName == 'INPUT' && this.element.type.toUpperCase() == 'HIDDEN') 1479 return LiveValidation.TEXT; 1480 if (nodeName == 'INPUT' && this.element.type.toUpperCase() == 'COLOR') 1481 return LiveValidation.TEXT; 1482 if (nodeName == 'INPUT' && this.element.type.toUpperCase() == 'DATE') 1483 return LiveValidation.TEXT; 1484 if (nodeName == 'INPUT' && this.element.type.toUpperCase() == 'DATETIME-LOCAL') 1485 return LiveValidation.TEXT; 1486 if (nodeName == 'INPUT' && this.element.type.toUpperCase() == 'MONTH') 1487 return LiveValidation.TEXT; 1488 if (nodeName == 'INPUT' && this.element.type.toUpperCase() == 'WEEK') 1489 return LiveValidation.TEXT; 1490 if (nodeName == 'INPUT' && this.element.type.toUpperCase() == 'TIME') 1491 return LiveValidation.TEXT; 1492 if (nodeName == 'INPUT' && this.element.type.toUpperCase() == 'RANGE') 1493 return LiveValidation.TEXT; 1494 if (nodeName == 'INPUT' && this.element.type.toUpperCase() == 'SEARCH') 1495 return LiveValidation.TEXT; 1496 if (nodeName == 'SELECT') 1497 return LiveValidation.SELECT; 1498 if (nodeName == 'FORM') 1499 return LiveValidation.FORM; 1500 if (nodeName == 'INPUT') 1501 throw new Error('LiveValidation::getElementType - Cannot use LiveValidation on an ' + this.element.type + ' input!'); 1502 throw new Error('LiveValidation::getElementType - Element must be an input, select, or textarea!'); 1503 }, 1504 1505 /** 1506 * Loops through all the validations added to the LiveValidation object and checks them one by one 1507 * 1508 * @var validationFunction {Function} - validation function to be used (ie Validate.Presence ) 1509 * @var validationParamsObj {Object} - parameters for doing the validation, if wanted or necessary 1510 * @return {Boolean} - whether the all the validations passed or if one failed 1511 */ 1512 doValidations: function(isSubmit, submitTrigger){ 1513 var result = true; 1514 1515 this.validationFailed = false; 1516 this.validationAsync = false; 1517 for(var i = 0, len = this.validations.length; i < len; ++i){ 1518 var validation = this.validations[i]; 1519 switch(validation.type){ 1520 case Validate.Presence: 1521 case Validate.Confirmation: 1522 case Validate.Acceptance: 1523 case Validate.Custom: 1524 this.displayMessageWhenEmpty = true; 1525 break; 1526 default: 1527 break; 1528 } 1529 var v = this.validateElement(validation.type, validation.params, isSubmit, submitTrigger); 1530 if (v === 'async') { 1531 this.validationAsync = true; 1532 result = 'async'; 1533 } else if (!v) { 1534 this.validationFailed = true; 1535 return false;
1536 } 1537 } 1538 this.message = this.validMessage; 1539 return result; 1540 }, 1541 1542 /** 1543 * Check if there is an async validation. 1544 */ 1545 isAsync: function (){ 1546 for(var i = 0, len = this.validations.length; i < len; ++i) { 1547 var validation = this.validations[i]; 1548 if (validation.type == Validate.Postback || validation.params.isAsync === true) 1549 return true; 1550 } 1551 return false; 1552 }, 1553 1554 /** 1555 * Performs validation on the element and handles any error (validation or otherwise) it throws up 1556 * 1557 * @var validationFunction {Function} - validation function to be used (ie Validate.Presence ) 1558 * @var validationParamsObj {Object} - parameters for doing the validation, if wanted or necessary 1559 * @var isSubmit {Boolean} - is this a form submit or an individual field check 1560 * @var submitTrigger {Object} - the element that triggered the submit 1561 * @return {Boolean} or {"async"} - whether the validation has passed, failed or waits for an async server side check 1562 */ 1563 validateElement: function(validationFunction, validationParamsObj, isSubmit, submitTrigger){ 1564 if (!this.element.disabled) { 1565 var value = this.getValue(); 1566 if(validationFunction == Validate.Acceptance){ 1567 if(this.elementType != LiveValidation.CHECKBOX) 1568 throw new Error('LiveValidation::validateElement - Element to validate acceptance must be a checkbox!'); 1569 value = this.element.checked; 1570 } 1571 var isValid = true; 1572 try { 1573 isValid = validationFunction(value, validationParamsObj, isSubmit, submitTrigger); 1574 if (isValid === 'async') { 1575 this.validationAsync = true; 1576 } 1577 } 1578 catch(error) { 1579 if(error instanceof Validate.Error){ 1580 if( value !== '' || (value === '' && this.displayMessageWhenEmpty) ){ 1581 this.validationFailed = true; 1582 this.message = error.message; 1583 isValid = false; 1584 } 1585 } else { 1586 throw error; 1587 } 1588 } 1589 return isValid; 1590 } else { 1591 return true; 1592 } 1593 }, 1594 1595 1596 getValue: function() { 1597 switch (this.elementType) { 1598 case LiveValidation.SELECT: 1599 if (this.element.selectedIndex >= 0) return this.element.options[this.element.selectedIndex].value; 1600 else return ""; 1601 case LiveValidation.RADIO: 1602 return $('input[name="'+this.element.name+'"]:checked').val(); 1603 case LiveValidation.CHECKBOX: 1604 var val = []; 1605 $('input[name="'+this.element.name+'"]:checked').each(function() { val.push($(this).val()); }); 1606 if (val.length === 0) { 1607 return undefined; 1608 } else { 1609 return val; 1610 } 1611 case LiveValidation.FORM: 1612 return ""; 1613 default: 1614 return this.element.value; 1615 } 1616 }, 1617 1618 /** 1619 * Do all the validations and fires off the onValid or onInvalid callbacks 1620 * 1621 * @var isSubmit {Boolean} - is this a form submit or an individual field check 1622 * @var submitTrigger {Object} - the element that triggered the submit 1623 * @return {Boolean} or "async" - whether all the validations passed or if one failed 1624 */ 1625 validate: function(isSubmit, submitTrigger){ 1626 if(!this.element.disabled) { 1627 var valid = this.doValidations(isSubmit, submitTrigger); 1628 if (valid === 'async') { 1629 this.onAsync(); 1630 return 'async'; 1631 } else if (valid) { 1632 this.onValid(); 1633 return true; 1634 } else { 1635 this.onInvalid(); 1636 return false; 1637 } 1638 } else { 1639 return true; 1640 } 1641 }, 1642 1643 /** 1644 * Called when there is an async validation result. 1645 * The caller has already checked if the current input value hasn't changed. 1646 */ 1647 asyncValidationResult: function(isValid){ 1648 if (this.validationAsync){ 1649 // Find which validation was waiting for async, assume only one async postback per field. 1650 for(var i = 0, len = this.validations.length; i < len; ++i){
1651 var validation = this.validations[i]; 1652 if(validation.type == Validate.Postback || validation.params.isAsync === true) { 1653 // Clear the async wait flag 1654 this.validationAsync = false; 1655 this.validationFailed = !isValid; 1656 if (isValid){ 1657 this.onValid(); 1658 } else { 1659 this.onInvalid(); 1660 } 1661 this.formObj.asyncResult(this, isValid); 1662 } 1663 } 1664 } 1665 }, 1666 1667 /** 1668 * enables the field 1669 * 1670 * @return {LiveValidation} - the LiveValidation object for chaining 1671 */ 1672 enable: function(){ 1673 this.element.disabled = false; 1674 return this; 1675 }, 1676 1677 /** 1678 * disables the field and removes any message and styles associated with the field 1679 * 1680 * @return {LiveValidation} - the LiveValidation object for chaining 1681 */ 1682 disable: function(){ 1683 this.element.disabled = true; 1684 this.removeMessageAndFieldClass(); 1685 return this; 1686 }, 1687 1688 /** Message insertion methods **************************** 1689 * 1690 * These are only used in the onValid and onInvalid callback functions and so if you overide the default callbacks, 1691 * you must either impliment your own functions to do whatever you want, or call some of these from them if you 1692 * want to keep some of the functionality 1693 */ 1694 1695 /** 1696 * makes a span containing a spinner image 1697 * 1698 * @return {HTMLSpanObject} - a span element with the message in it 1699 */ 1700 createSpinnerSpan: function(){ 1701 var span = document.createElement('span'); 1702 span.innerHTML = '<img src="/lib/images/spinner.gif" height="16" width="16" alt="Validating..." />'; 1703 return span; 1704 }, 1705 1706 /** 1707 * makes a span containg the passed or failed message 1708 * 1709 * @return {HTMLSpanObject} - a span element with the message in it 1710 */ 1711 createMessageSpan: function(){ 1712 if (!this.message) return null; 1713 var span = document.createElement('span'); 1714 var textNode = document.createTextNode(this.message); 1715 span.appendChild(textNode); 1716 return span; 1717 }, 1718 1719 /** 1720 * Show an error message 1721 */ 1722 showErrorMessage: function(message){ 1723 this.message = message; 1724 this.onInvalid(); 1725 }, 1726 1727 /** 1728 * Insert a spinner in the message element. 1729 */ 1730 insertSpinner: function (elementToInsert){ 1731 this.removeMessage(); 1732 if( (this.displayMessageWhenEmpty && (this.elementType == LiveValidation.CHECKBOX || this.element.value === '')) 1733 || this.element.value !== '' ){ 1734 1735 elementToInsert.className += ' ' + this.messageClass + ' ' + this.asyncFieldClass; 1736 if(this.insertAfterWhatNode.nextSibling){ 1737 this.insertAfterWhatNode.parentNode.insertBefore(elementToInsert, this.insertAfterWhatNode.nextSibling); 1738 }else{ 1739 this.insertAfterWhatNode.parentNode.appendChild(elementToInsert); 1740 } 1741 } 1742 1743 }, 1744 1745 /** 1746 * inserts the element containing the message in place of the element that already exists (if it does) 1747 * 1748 * @var elementToIsert {HTMLElementObject} - an element node to insert 1749 */ 1750 insertMessage: function(elementToInsert){ 1751 this.removeMessage(); 1752 if (elementToInsert) { 1753 if( (this.displayMessageWhenEmpty && (this.elementType == LiveValidation.CHECKBOX || this.element.value === '')) 1754 || this.element.value !== '' ) { 1755 1756 var className = this.validationFailed ? this.invalidClass : this.validClass; 1757 elementToInsert.className += ' ' + this.messageClass + ' ' + className; 1758 if(this.insertAfterWhatNode.nextSibling){ 1759 this.insertAfterWhatNode.parentNode.insertBefore(elementToInsert, this.insertAfterWhatNode.nextSibling); 1760 }else{ 1761 this.insertAfterWhatNode.parentNode.appendChild(elementToInsert); 1762 } 1763 } 1764 } 1765 }, 1766 1767 1768 /** 1769 * changes the class of the field based on whether it is valid or not 1770 */ 1771 addFieldClass: function(){ 1772 this.removeFieldClass(); 1773 if(!this.validationFailed){ 1774 if(this.displayMessageWhenEmpty || this.element.value !== ''){ 1775 $('input[name="'+this.element.name+'"],select[name="'+this.element.name+'"],textarea[name="'+this.element.name+'"]') 1776 .closest('.' + this.fieldGroupClass).addClass(this.fieldGroupSuccessClass); 1777 switch (this.elementType) { 1778 case LiveValidation.RADIO: 1779 case LiveValidation.CHECKBOX: 1780 $('input[name="'+this.element.name+'"]').closest('label').addClass(this.validFieldClass); 1781 break; 1782 case LiveValidation.FORM: 1783 break; 1784 default: 1785 $(this.element).addClass(this.validFieldClass); 1786 break; 1787 } 1788 } 1789 }else{ 1790 $('input[name="'+this.element.name+'"],select[name="'+this.element.name+'"],textarea[name="'+this.element.name+'"]') 1791 .closest('.' + this.fieldGroupClass).removeClass(this.fieldGroupSuccessClass).addClass(this.fieldGroupErrorClass);
1792 $('label[for="'+this.element.id+'"]').addClass(this.invalidFieldClass); 1793 1794 switch (this.elementType) { 1795 case LiveValidation.RADIO: 1796 case LiveValidation.CHECKBOX: 1797 $('input[name="'+this.element.name+'"]').closest('label').addClass(this.invalidFieldClass); 1798 break; 1799 case LiveValidation.FORM: 1800 break; 1801 default: 1802 $(this.element).addClass(this.invalidFieldClass); 1803 break; 1804 } 1805 } 1806 }, 1807 1808 /** 1809 * removes the message element if it exists, so that the new message will replace it 1810 */ 1811 removeMessage: function(){ 1812 var nextEl; 1813 var el = this.insertAfterWhatNode; 1814 while(el.nextSibling){ 1815 if(el.nextSibling.nodeType === 1){ 1816 nextEl = el.nextSibling; 1817 break; 1818 } 1819 el = el.nextSibling; 1820 } 1821 if(nextEl && nextEl.className.indexOf(this.messageClass) != -1) 1822 this.insertAfterWhatNode.parentNode.removeChild(nextEl); 1823 }, 1824 1825 /** 1826 * removes the class that has been applied to the field to indicate if valid or not 1827 */ 1828 removeFieldClass: function(){ 1829 $('input[name="'+this.element.name+'"],select[name="'+this.element.name+'"],textarea[name="'+this.element.name+'"]') 1830 .closest('.' + this.fieldGroupClass).removeClass(this.fieldGroupSuccessClass).removeClass(this.fieldGroupErrorClass); 1831 $('label[for="'+this.element.id+'"]').removeClass(this.invalidFieldClass); 1832 switch (this.elementType) { 1833 case LiveValidation.RADIO: 1834 case LiveValidation.CHECKBOX: 1835 $('input[name="'+this.element.name+'"]').closest('label').removeClass(this.invalidFieldClass).removeClass(this.validFieldClass); 1836 break; 1837 case LiveValidation.FORM: 1838 break; 1839 default: 1840 $(this.element).removeClass(this.invalidFieldClass).removeClass(this.validFieldClass); 1841 break; 1842 } 1843 }, 1844 1845 /** 1846 * removes the message and the field class 1847 */ 1848 removeMessageAndFieldClass: function(){ 1849 this.removeMessage(); 1850 this.removeFieldClass(); 1851 } 1852 1853}; // end of LiveValidation class 1854 1855 1856 1857 1858/*************************************** LiveValidationForm class ****************************************/ 1859/** 1860 * This class is used internally by LiveValidation class to associate a LiveValidation field with a form it is icontained in one 1861 * 1862 * It will therefore not really ever be needed to be used directly by the developer, unless they want to associate a LiveValidation 1863 * field with a form that it is not a child of 1864 */ 1865 1866/** 1867 * handles validation of LiveValidation fields belonging to this form on its submittal 1868 * 1869 * @var element {HTMLFormElement} - a dom element reference to the form to turn into a LiveValidationForm 1870 */ 1871var LiveValidationForm = function(element){ 1872 this.initialize(element); 1873}; 1874 1875/** 1876 * gets the instance of the LiveValidationForm if it has already been made or creates it if it doesnt exist 1877 * 1878 * @var element {HTMLFormElement} - a dom element reference to a form 1879 */ 1880LiveValidationForm.getInstance = function(element){ 1881 var rand = Math.random() * Math.random(); 1882 if(!$(element).attr("id")) 1883 $(element).attr("id", 'formId_' + rand.toString().replace(/\./, '') + new Date().valueOf()); 1884 var instance = $(element).data("z_live_validation_instance"); 1885 if (!instance) { 1886 instance = new LiveValidationForm(element); 1887 $(element).data("z_live_validation_instance", instance); 1888 } 1889 return instance; 1890}; 1891 1892LiveValidationForm.prototype = { 1893 validFormClass: 'z_form_valid', 1894 invalidFormClass: 'z_form_invalid', 1895 1896 /** 1897 * constructor for LiveValidationForm - handles validation of LiveValidation fields belonging to this form on its submittal 1898 * 1899 * @var element {HTMLFormElement} - a dom element reference to the form to turn into a LiveValidationForm 1900 */ 1901 initialize: function(element){ 1902 this.name = $(element).attr("id"); 1903 this.element = element; 1904 this.skipValidations = 0; 1905 this.submitWaitForAsync = []; 1906 this.isWaitForFormAsync = false;
1907 this.clk = undefined; 1908 1909 // preserve the old onsubmit event 1910 this.oldOnSubmit = this.element.onsubmit || function(){}; 1911 var self = this; 1912 1913 this.onInvalid = function() { 1914 $(this).removeClass(self.validFormClass).addClass(self.invalidFormClass); 1915 $(".z_form_valid", this).hide(); 1916 $(".z_form_invalid", this).fadeIn(); 1917 }; 1918 this.onValid = function() { 1919 $(this).removeClass(self.invalidFormClass).addClass(self.validFormClass); 1920 $(".z_form_invalid", this).hide(); 1921 $(".z_form_valid", this).fadeIn(); 1922 }; 1923 1924 $(element).submit(function(event) { 1925 event.zIsValidated = true; 1926 if (self.skipValidations === 0) { 1927 var result = true; 1928 var is_first = true; 1929 var i, len; 1930 var fields = self.getFields(); 1931 1932 self.submitWaitForAsync = []; 1933 self.isWaitForFormAsync = false; 1934 self.clk = this.clk; 1935 1936 for(i = 0, len = fields.length; i < len; ++i ) { 1937 if (!fields[i].element.disabled) { 1938 var ve = fields[i].validate(true, this.clk); 1939 if (ve === 'async') { 1940 self.submitWaitForAsync.push(fields[i]); 1941 } else if (!ve) { 1942 result = false;
1943 } 1944 } 1945 } 1946 if (result === false) { 1947 self.submitWaitForAsync = []; 1948 } 1949 1950 // Optionally check validations attached to the form itself 1951 // Only done if all other validations are done and passing 1952 if (result && self.submitWaitForAsync.length === 0) { 1953 var formValidation = $(this).data('z_live_validation'); 1954 if (formValidation) { 1955 var vf = formValidation.validate(true, this.clk); 1956 if (vf === 'async') { 1957 self.submitWaitForAsync = [formValidation]; 1958 self.isWaitForFormAsync = true; 1959 } else if (!vf) { 1960 result = false; 1961 } 1962 } 1963 } 1964 if (self.submitWaitForAsync.length > 0) { 1965 event.stopImmediatePropagation(); 1966 return false; 1967 } else if (!result) { 1968 self.onInvalid.call(this); 1969 event.stopImmediatePropagation(); 1970 return false; 1971 } else { 1972 self.onValid.call(this); 1973 return z_form_submit_validated_do(event); 1974 } 1975 } else { 1976 self.skipValidations--; 1977 if (self.skipValidations === 0) { 1978 self.onValid.call(this); 1979 return z_form_submit_validated_do(event); 1980 } else { 1981 return false; 1982 } 1983 } 1984 }); 1985 }, 1986 1987 /** 1988 * destroy this instance and its events 1989 * 1990 * @var force {Boolean} - whether to force the destruction even if there are fields still associated 1991 */ 1992 destroy: function(force){ 1993 if (force || this.getFields().length === 0) { 1994 // remove events - set back to previous events 1995 this.element.onsubmit = this.oldOnSubmit; 1996 // remove from the instances namespace 1997 $(this.element).removeData("z_live_validation_instance"); 1998 return true; 1999 } else { 2000 return false; 2001 } 2002 }, 2003 2004 /** 2005 * get the to-be-validated fields 2006 */ 2007 getFields: function() { 2008 var fields = []; 2009 $("input,select,textarea", this.element).each(function() { 2010 var field = $(this).data('z_live_validation'); 2011 if (field) { 2012 fields.push(field); 2013 } 2014 }); 2015 return fields; 2016 }, 2017 2018 asyncResult: function(Validation, isValid){ 2019 if (isValid){ 2020 var index = $.inArray(Validation, this.submitWaitForAsync); 2021 if (index >= 0){ 2022 this.submitWaitForAsync.splice(index, 1); 2023 if (this.submitWaitForAsync.length === 0){ 2024 // Optionally perform (and wait for) form-level validations 2025 if (!this.isWaitForFormAsync) { 2026 var formValidation = $(this.element).data('z_live_validation'); 2027 if (formValidation) { 2028 var result = formValidation.validate(true, this.clk); 2029 if (result === 'async') { 2030 this.submitWaitForAsync = [formValidation]; 2031 this.isWaitForFormAsync = true; 2032 } else if (!result) { 2033 isValid = false; 2034 } else { 2035 isValid = true; 2036 } 2037 } 2038 } else { 2039 this.isWaitForFormAsync = false; 2040 } 2041 2042 if (!this.isWaitForFormAsync) { 2043 if (isValid){ 2044 // All validations were successful, resubmit (and skip validations for once) 2045 this.skipValidations = 1; 2046 var formObj = this.element; 2047 this.onValid.call(this); 2048 setTimeout(function(){ $(formObj).submit(); }, 0); 2049 } else { 2050 this.onInvalid.call(this); 2051 } 2052 } 2053 } 2054 } 2055 } else { 2056 if (this.submitWaitForAsync.length > 0) { 2057 this.onInvalid.call(this); 2058 } 2059 this.submitWaitForAsync = []; 2060 } 2061 } 2062}; // end of LiveValidationForm prototype 2063 2064 2065 2066 2067/*************************************** Validate class ****************************************/ 2068/** 2069 * This class contains all the methods needed for doing the actual validation itself 2070 * 2071 * All methods are static so that they can be used outside the context of a form field 2072 * as they could be useful for validating stuff anywhere you want really 2073 * 2074 * All of them will return true if the validation is successful, but will raise a ValidationError if 2075 * they fail, so that this can be caught and the message explaining the error can be accessed ( as just 2076 * returning false would leave you a bit in the dark as to why it failed ) 2077 * 2078 * Can use validation methods alone and wrap in a try..catch statement yourself if you want to access the failure 2079 * message and handle the error, or use the Validate::now method if you just want true or false 2080 */ 2081 2082var Validate = { 2083 2084 /** 2085 * validates that the field has been filled in 2086 * 2087 * @var value {mixed} - value to be checked 2088 * @var paramsObj {Object} - parameters for this particular validation, see below for details 2089 * 2090 * paramsObj properties:
2091 * failureMessage {String} - the message to show when the field fails validation 2092 * (DEFAULT: "Can't be empty!") 2093 */ 2094 Presence: function(value, paramsObj){ 2095 paramsObj = paramsObj || {}; 2096 var message = paramsObj.failureMessage || ""; 2097 if(value === '' || value === null || value === undefined){ 2098 Validate.fail(message); 2099 } 2100 return true; 2101 }, 2102 2103 /** 2104 * validates that the value is numeric, does not fall within a given range of numbers 2105 * 2106 * @var value {mixed} - value to be checked 2107 * @var paramsObj {Object} - parameters for this particular validation, see below for details 2108 * 2109 * paramsObj properties: 2110 * notANumberMessage {String} - the message to show when the validation fails when value is not a number 2111 * (DEFAULT: "Must be a number!") 2112 * notAnIntegerMessage {String} - the message to show when the validation fails when value is not an integer 2113 * (DEFAULT: "Must be a number!") 2114 * wrongNumberMessage {String} - the message to show when the validation fails when is param is used 2115 * (DEFAULT: "Must be {is}!") 2116 * tooLowMessage {String} - the message to show when the validation fails when minimum param is used 2117 * (DEFAULT: "Must not be less than {minimum}!") 2118 * tooHighMessage {String} - the message to show when the validation fails when maximum param is used 2119 * (DEFAULT: "Must not be more than {maximum}!") 2120 * is {Int} - the length must be this long 2121 * minimum {Int} - the minimum length allowed 2122 * maximum {Int} - the maximum length allowed 2123 * onlyInteger {Boolean} - if true will only allow integers to be valid 2124 * (DEFAULT: false) 2125 * 2126 * NB. can be checked if it is within a range by specifying both a minimum and a maximum 2127 * NB. will evaluate numbers represented in scientific form (ie 2e10) correctly as numbers 2128 */ 2129 Numericality: function(value, paramsObj){ 2130 var suppliedValue = value; 2131 value = Number(value); 2132 paramsObj = paramsObj || {}; 2133 var minimum = ((paramsObj.minimum) || (paramsObj.minimum === 0)) ? paramsObj.minimum : null; 2134 var maximum = ((paramsObj.maximum) || (paramsObj.maximum === 0)) ? paramsObj.maximum : null; 2135 var is = ((paramsObj.is) || (paramsObj.is === 0)) ? paramsObj.is : null; 2136 var notANumberMessage = paramsObj.notANumberMessage || "Must be a number."; 2137 var notAnIntegerMessage = paramsObj.notAnIntegerMessage || "Must be an integer."; 2138 var wrongNumberMessage = paramsObj.wrongNumberMessage || "Must be " + is + "."; 2139 var tooLowMessage = paramsObj.tooLowMessage || "Must not be less than " + minimum + "."; 2140 var tooHighMessage = paramsObj.tooHighMessage || "Must not be more than " + maximum + "."; 2141 2142 if (!isFinite(value)) 2143 Validate.fail(notANumberMessage); 2144 if (paramsObj.onlyInteger && (/\.0+$|\.$/.test(String(suppliedValue)) || value != parseInt(value,10)) ) 2145 Validate.fail(notAnIntegerMessage); 2146 switch(true){ 2147 case (is !== null): 2148 if( value != Number(is) ) Validate.fail(wrongNumberMessage); 2149 break; 2150 case (minimum !== null && maximum !== null): 2151 Validate.Numericality(value, {tooLowMessage: tooLowMessage, minimum: minimum}); 2152 Validate.Numericality(value, {tooHighMessage: tooHighMessage, maximum: maximum}); 2153 break; 2154 case (minimum !== null): 2155 if( value < Number(minimum) ) Validate.fail(tooLowMessage); 2156 break; 2157 case (maximum !== null): 2158 if( value > Number(maximum) ) Validate.fail(tooHighMessage); 2159 break; 2160 } 2161 return true; 2162 }, 2163 2164 /** 2165 * validates against a RegExp pattern 2166 * 2167 * @var value {mixed} - value to be checked 2168 * @var paramsObj {Object} - parameters for this particular validation, see below for details 2169 * 2170 * paramsObj properties:
2171 * failureMessage {String} - the message to show when the field fails validation 2172 * (DEFAULT: "") 2173 * pattern {RegExp} - the regular expression pattern 2174 * (DEFAULT: /./) 2175 * negate {Boolean} - if set to true, will validate true if the pattern is not matched 2176 * (DEFAULT: false) 2177 * 2178 * NB. will return true for an empty string, to allow for non-required, empty fields to validate. 2179 * If you do not want this to be the case then you must either add a LiveValidation.PRESENCE validation 2180 * or build it into the regular expression pattern 2181 */ 2182 Format: function(value, paramsObj){ 2183 value = String(value); 2184 paramsObj = paramsObj || {}; 2185 var message = paramsObj.failureMessage || ""; 2186 var pattern = paramsObj.pattern || /./; 2187 var negate = paramsObj.negate || false; 2188 if(!negate && !pattern.test(value)) Validate.fail(message); // normal 2189 if(negate && pattern.test(value)) Validate.fail(message); // negated 2190 return true; 2191 }, 2192 2193 /** 2194 * validates that the field contains a valid email address 2195 * 2196 * @var value {mixed} - value to be checked 2197 * @var paramsObj {Object} - parameters for this particular validation, see below for details 2198 * 2199 * paramsObj properties: 2200 * failureMessage {String} - the message to show when the field fails validation 2201 * (DEFAULT: "Must be a number!" or "Must be an integer!") 2202 */ 2203 Email: function(value, paramsObj){ 2204 paramsObj = paramsObj || {}; 2205 var message = paramsObj.failureMessage || ""; 2206 value = $.trim(value); 2207 // see validator_base_email.erl:43 2208 var re = /^$|^(("[^"\f\n\r\t\v\b]+")|([\w\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+(\.[\w\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+)*))@((([A-Za-z0-9\-])+\.)+[A-Za-z\-]{2,})$/; 2209 Validate.Format(value, { failureMessage: message, pattern: re } ); 2210 return true; 2211 }, 2212 2213 /* 2214 * validates that the field contains a valid date 2215 * 2216 * @var value {mixed} - value to be checked 2217 * @var paramsObj {Object} - parameters for this particular validation, see below for details 2218 * 2219 * paramsObj properties: 2220 * failureMessage {String} - the message to show when the field fails validation 2221 * (DEFAULT: "Incorrect Date") 2222 * format {String} - l, m,b endian 2223 * (DEFAULT: "l") 2224 * separator {String} - a character which is not a number 2225 * (DEFAULT: "-") 2226 * 2227 */ 2228 2229 Date: function(value, paramsObj){ 2230 function to_integer(value) { 2231 if (parseInt(value, 10) == value) { 2232 return parseInt(value, 10); 2233 } else { 2234 return parseInt("NaN", 10); 2235 } 2236 } 2237 2238 paramsObj = paramsObj || {}; 2239 var message = paramsObj.failureMessage || "Incorrect Date"; 2240 var format = paramsObj.format || "l"; 2241 var separator = paramsObj.separator || "-"; 2242 value = $.trim(value); 2243 2244 var date_components = value.split(separator); 2245 2246 if (date_components.length != 3) { 2247 Validate.fail(message); 2248 } else { 2249 var day; 2250 var month; 2251 var year; 2252 2253 not_a_number = to_integer(separator); 2254 if (!isNaN(not_a_number)) { 2255 throw "Seperator cannot be a number!"; 2256 } 2257 if (format == 'l') { 2258 day = to_integer(date_components[0]); 2259 month = to_integer(date_components[1]); 2260 year = to_integer(date_components[2]); 2261 } else if (format == 'b') { 2262 day = to_integer(date_components[2]); 2263 month = to_integer(date_components[1]); 2264 year = to_integer(date_components[0]); 2265 } else if (format == 'm') { 2266 day = to_integer(date_components[1]); 2267 month = to_integer(date_components[0]); 2268 year = to_integer(date_components[2]); 2269 } else { 2270 throw "Bad date format error!"; 2271 } 2272 var date_object = new Date(year, month-1, day); 2273 if (!((date_object.getDate() == day) && (date_object.getMonth()+1 == month) && (date_object.getFullYear() == year))) { 2274 Validate.fail(message); 2275 } 2276 } 2277 return true; 2278 }, 2279 2280 /** 2281 * validates the length of the value 2282 * 2283 * @var value {mixed} - value to be checked 2284 * @var paramsObj {Object} - parameters for this particular validation, see below for details 2285 * 2286 * paramsObj properties:
2287 * wrongLengthMessage {String} - the message to show when the fails when is param is used 2288 * (DEFAULT: "Must be {is} characters long!") 2289 * tooShortMessage {String} - the message to show when the fails when minimum param is used 2290 * (DEFAULT: "Must not be less than {minimum} characters long!") 2291 * tooLongMessage {String} - the message to show when the fails when maximum param is used 2292 * (DEFAULT: "Must not be more than {maximum} characters long!") 2293 * is {Int} - the length must be this long 2294 * minimum {Int} - the minimum length allowed 2295 * maximum {Int} - the maximum length allowed 2296 * 2297 * NB. can be checked if it is within a range by specifying both a minimum and a maximum 2298 */ 2299 Length: function(value, paramsObj){ 2300 value = String(value); 2301 paramsObj = paramsObj || {}; 2302 var minimum = ((paramsObj.minimum) || (paramsObj.minimum === 0)) ? paramsObj.minimum : null; 2303 var maximum = ((paramsObj.maximum) || (paramsObj.maximum === 0)) ? paramsObj.maximum : null; 2304 var is = ((paramsObj.is) || (paramsObj.is === 0)) ? paramsObj.is : null; 2305 var wrongLengthMessage = paramsObj.wrongLengthMessage || "Must be " + is + " characters long."; 2306 var tooShortMessage = paramsObj.tooShortMessage || "Must not be less than " + minimum + " characters long."; 2307 var tooLongMessage = paramsObj.tooLongMessage || "Must not be more than " + maximum + " characters long."; 2308 switch(true){ 2309 case (is !== null): 2310 if( value.length != Number(is) ) Validate.fail(wrongLengthMessage); 2311 break; 2312 case (minimum !== null && maximum !== null): 2313 Validate.Length(value, {tooShortMessage: tooShortMessage, minimum: minimum}); 2314 Validate.Length(value, {tooLongMessage: tooLongMessage, maximum: maximum}); 2315 break; 2316 case (minimum !== null): 2317 if( value.length < Number(minimum) ) Validate.fail(tooShortMessage); 2318 break; 2319 case (maximum !== null): 2320 if( value.length > Number(maximum) ) Validate.fail(tooLongMessage); 2321 break; 2322 default: 2323 throw new Error("Validate::Length - Length(s) to validate against must be provided"); 2324 } 2325 return true; 2326 }, 2327 2328 /** 2329 * validates that the value falls within a given set of values 2330 * 2331 * @var value {mixed} - value to be checked 2332 * @var paramsObj {Object} - parameters for this particular validation, see below for details 2333 * 2334 * paramsObj properties: 2335 * failureMessage {String} - the message to show when the field fails validation 2336 * (DEFAULT: "Must be included in the list!") 2337 * within {Array} - an array of values that the value should fall in 2338 * (DEFAULT: []) 2339 * allowNull {Bool} - if true, and a null value is passed in, validates as true 2340 * (DEFAULT: false) 2341 * partialMatch {Bool} - if true, will not only validate against the whole value to check but also if it is a substring of the value 2342 * (DEFAULT: false) 2343 * caseSensitive {Bool}
2343 - if false will compare strings case insensitively 2344 * (DEFAULT: true) 2345 * negate {Bool} - if true, will validate that the value is not within the given set of values 2346 * (DEFAULT: false) 2347 */ 2348 Inclusion: function(value, paramsObj){ 2349 paramsObj = paramsObj || {}; 2350 var message = paramsObj.failureMessage || "Must be included in the list!"; 2351 var caseSensitive = (paramsObj.caseSensitive === false) ? false : true; 2352 if(paramsObj.allowNull && value === null) 2353 return true; 2354 if(!paramsObj.allowNull && value === null) 2355 Validate.fail(message); 2356 var within = paramsObj.within || []; 2357 var length; 2358 2359 //if case insensitive, make all strings in the array lowercase, and the value too 2360 if(!caseSensitive){ 2361 var lowerWithin = []; 2362 length = within.length; 2363 for(var j = 0; j < length; ++j){ 2364 var item = within[j]; 2365 if(typeof item == 'string') 2366 item = item.toLowerCase(); 2367 lowerWithin.push(item); 2368 } 2369 within = lowerWithin; 2370 if(typeof value == 'string') 2371 value = value.toLowerCase(); 2372 } 2373 var found = false; 2374 length = within.length; 2375 for(var i = 0; i < length; ++i){ 2376 if(within[i] == value) found = true; 2377 if(paramsObj.partialMatch){ 2378 if(value.indexOf(within[i]) != -1) found = true; 2379 } 2380 } 2381 if( (!paramsObj.negate && !found) || (paramsObj.negate && found) ) 2382 Validate.fail(message); 2383 return true; 2384 }, 2385 2386 /** 2387 * validates that the value does not fall within a given set of values 2388 * 2389 * @var value {mixed} - value to be checked 2390 * @var paramsObj {Object} - parameters for this particular validation, see below for details 2391 * 2392 * paramsObj properties: 2393 * failureMessage {String} - the message to show when the field fails validation 2394 * (DEFAULT: "Must not be included in the list!") 2395 * within {Array} - an array of values that the value should not fall in 2396 * (DEFAULT: []) 2397 * allowNull {Bool} - if true, and a null value is passed in, validates as true 2398 * (DEFAULT: false) 2399 * partialMatch {Bool} - if true, will not only validate against the whole value to check but also if it is a substring of the value 2400 * (DEFAULT: false) 2401 * caseSensitive {Bool}
2401 - if false will compare strings case insensitively 2402 * (DEFAULT: true) 2403 */ 2404 Exclusion: function(value, paramsObj){ 2405 paramsObj = paramsObj || {}; 2406 paramsObj.failureMessage = paramsObj.failureMessage || "Must not be included in the list"; 2407 paramsObj.negate = true; 2408 Validate.Inclusion(value, paramsObj); 2409 return true; 2410 }, 2411 2412 /** 2413 * validates that the value matches that in another field 2414 * 2415 * @var value {mixed} - value to be checked 2416 * @var paramsObj {Object} - parameters for this particular validation, see below for details 2417 * 2418 * paramsObj properties: 2419 * failureMessage {String} - the message to show when the field fails validation 2420 * (DEFAULT: "Does not match!") 2421 * match {String} - id of the field that this one should match 2422 */ 2423 Confirmation: function(value, paramsObj){ 2424 if(!paramsObj.match) 2425 throw new Error("Validate::Confirmation - Error validating confirmation: Id of element to match must be provided"); 2426 paramsObj = paramsObj || {}; 2427 var message = paramsObj.failureMessage || "Does not match."; 2428 var match = paramsObj.match.nodeName ? paramsObj.match : document.getElementById(paramsObj.match); 2429 if(!match) 2430 throw new Error("Validate::Confirmation - There is no reference with name of, or element with id of '" + paramsObj.match + "'"); 2431 if(value != match.value){ 2432 Validate.fail(message); 2433 } 2434 return true; 2435 }, 2436 2437 /** 2438 * validates that the value is true (for use primarily in detemining if a checkbox has been checked) 2439 * 2440 * @var value {mixed} - value to be checked if true or not (usually a boolean from the checked value of a checkbox) 2441 * @var paramsObj {Object} - parameters for this particular validation, see below for details 2442 * 2443 * paramsObj properties: 2444 * failureMessage {String} - the message to show when the field fails validation 2445 * (DEFAULT: "Must be accepted!") 2446 */ 2447 Acceptance: function(value, paramsObj){ 2448 paramsObj = paramsObj || {}; 2449 var message = paramsObj.failureMessage || "Must be accepted."; 2450 if(!value){ 2451 Validate.fail(message); 2452 } 2453 return true; 2454 }, 2455 2456 /** 2457 * validates against a custom function that returns true or false (or throws a Validate.Error) when passed the value 2458 * 2459 * @var value {mixed} - value to be checked 2460 * @var paramsObj {Object} - parameters for this particular validation, see below for details 2461 * 2462 * paramsObj properties: 2463 * failureMessage {String} - the message to show when the field fails validation 2464 * (DEFAULT: "") 2465 * against {Function} - a function that will take the value and object of arguments and return true or false 2466 * (DEFAULT: function(){ return true; }) 2467 * args {Object} - an object of named arguments that will be passed to the custom function so are accessible through this object within it 2468 * (DEFAULT: {}) 2469 */ 2470 Custom: function(value, paramsObj, isSubmit, submitTrigger){ 2471 paramsObj = paramsObj || {}; 2472 var against = paramsObj.against || function(){ return true; }; 2473 var args = paramsObj.args || {}; 2474 var message = paramsObj.failureMessage || ""; 2475 var result; 2476 2477 if (typeof against == "string") { 2478 result = z_call_function_by_name(against, window, value, args, isSubmit, submitTrigger); 2479 } else { 2480 result = against(value, args, isSubmit, submitTrigger); 2481 } 2482 if (result === 'async') { 2483 return 'async'; 2484 } else { 2485 if(!result) Validate.fail(message); 2486 return true; 2487 } 2488 }, 2489 2490 2491 /** 2492 * Performs a postback, delays the check till the postback is returned. till then a spinner is shown 2493 * next to the input element. 2494 */ 2495 Postback: function(value, paramsObj, isSubmit, submitTrigger) { 2496 paramsObj = paramsObj || {}; 2497 var against = paramsObj.against || function(){ return true; }; 2498 var args = paramsObj.args || {}; 2499 var message = paramsObj.failureMessage || ""; 2500 2501 if (!against(value, args, isSubmit, submitTrigger)) { 2502 Validate.fail(message); 2503 } else if (paramsObj.z_postback) { 2504 // Perform the async postback 2505 extraParams = []; 2506 if (isSubmit) { 2507 extraParams.push({name: 'z_submitter', value: (submitTrigger && submitTrigger.name) ? submitTrigger.name : ''}); 2508 } 2509 z_queue_postback(paramsObj.z_id, paramsObj.z_postback, extraParams); 2510 return 'async'; 2511 } else { 2512 return true; 2513 } 2514 }, 2515 2516 2517 /** 2518 * validates whatever it is you pass in, and handles the validation error for you so it gives a nice true or false reply 2519 * 2520 * @var validationFunction {Function} - validation function to be used (ie Validation.validatePresence ) 2521 * @var value {mixed} - value to be checked if true or not (usually a boolean from the checked value of a checkbox) 2522 * @var validationParamsObj {Object} - parameters for doing the validation, if wanted or necessary 2523 */
2524 now: function(validationFunction, value, validationParamsObj){ 2525 if(!validationFunction) 2526 throw new Error("Validate::now - Validation function must be provided!"); 2527 var isValid = true; 2528 try { 2529 validationFunction(value, validationParamsObj || {}); 2530 } catch(error) { 2531 if (error instanceof Validate.Error) { 2532 isValid = false; 2533 } else { 2534 throw error; 2535 } 2536 } finally { 2537 return isValid; 2538 } 2539 }, 2540 2541 /** 2542 * shortcut for failing throwing a validation error 2543 * 2544 * @var errorMessage {String} - message to display 2545 */ 2546 fail: function(errorMessage){ 2547 throw new Validate.Error(errorMessage); 2548 }, 2549 2550 Error: function(errorMessage){ 2551 this.message = errorMessage; 2552 this.name = 'ValidationError'; 2553 } 2554 2555}; 2556/* inputoverlay js 2557---------------------------------------------------------- 2558 2559@package: Zotonic 2010 2560@Author: Marc Worrell <[email protected]> 2561 2562Copyright 2010 Marc Worrell 2563 2564Licensed under the Apache License, Version 2.0 (the "License"); 2565you may not use this file except in compliance with the License. 2566You may obtain a copy of the License at 2567 2568http://www.apache.org/licenses/LICENSE-2.0 2569 2570Unless required by applicable law or agreed to in writing, software 2571distributed under the License is distributed on an "AS IS" BASIS, 2572WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2573See the License for the specific language governing permissions and 2574limitations under the License. 2575 2576---------------------------------------------------------- */ 2577 2578/* 2579This widget overlays a label field with an input field. When the input 2580is empty the label is visible. When there is content then the label is hidden. 2581 2582HTML: 2583 2584<p class="do_inputoverlay"> 2585 <span>Username</span> 2586 <input type="text" id="username" name="username" value="" /> 2587</p> 2588 2589CSS: 2590 2591p.do_inputoverlay { 2592 margin: 0px; 2593 padding: 0px; 2594 position: relative; 2595 height: 40px; 2596 font-size: 18px; 2597} 2598 2599p.do_inputoverlay input { 2600 position: absolute; 2601 left: 0px; 2602 background: none; 2603 font-size: 18px; 2604} 2605 2606p.do_inputoverlay span { 2607 position: absolute; 2608 left: 8px; 2609 top: 5px; 2610 color: #aaa; 2611} 2612 2613p.do_inputoverlay span.focus { 2614 color: #d8d8d8; 2615} 2616 2617p.do_inputoverlay span.hidden { 2618 display: none; 2619} 2620 2621*/ 2622 2623$.widget("ui.inputoverlay", 2624{ 2625 _init: function() 2626 { 2627 var self = this; 2628 var obj = this.element; 2629 var input = $('input', obj); 2630 var span = $('span', obj); 2631 2632 if (!input.length) { 2633 input = $('textarea', obj); 2634 } 2635 if ($(input).val() != "") { 2636 $(span).addClass('hidden'); 2637 } 2638 2639 var func = function(focus) { 2640 if ($(input).val() == "") { 2641 if (focus) { 2642 $(span).removeClass('hidden').addClass('focus'); 2643 } else { 2644 $(span).removeClass('hidden').removeClass('focus'); 2645 } 2646 } else { 2647 $(span).removeClass('focus').addClass('hidden'); 2648 } 2649 }; 2650 2651 input.change(function() { 2652 func(true); 2653 }).focus(function() { 2654 func(true); 2655 }).blur(function() { 2656 func(false); 2657 }).keydown(function() { 2658 setTimeout(function(){func(true);},10); 2659 }).keyup(function() { 2660 func(true); 2661 }); 2662 2663 input.closest("form").bind("reset", function() { 2664 setTimeout(function(){func(true);},10); 2665 }); 2666 2667 span.click(function() { 2668 input.focus(); 2669 }); 2670 2671 if (input.attr('autocomplete') == 'on') { 2672 setInterval(function() { 2673 if ($(input).val() == "") { 2674 $(span).removeClass('hidden'); 2675 } else { 2676 $(span).addClass('hidden'); 2677 } 2678 }, 100); 2679 } 2680 } 2681}); 2682/** 2683 * Copyright (c) 2009 Sergiy Kovalchuk ([email protected]) 2684 * 2685 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 2686 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. 2687 * 2688 * Following code is based on Element.mask() implementation from ExtJS framework (http://extjs.com/) 2689 * 2690 */ 2691;(function($){ 2692 2693 /** 2694 * Displays loading mask over selected element(s). Accepts both single and multiple selectors. 2695 * 2696 * @param label Text message that will be displayed on top of the mask besides a spinner (optional). 2697 * If not provided only mask will be displayed without a label or a spinner. 2698 * @param delay Delay in milliseconds before element is masked (optional). If unmask() is called 2699 * before the delay times out, no mask is displayed. This can be used to prevent unnecessary 2700 * mask display for quick processes. 2701 */ 2702 $.fn.mask = function(label, delay){ 2703 $(this).each(function() { 2704 if(delay !== undefined && delay > 0) { 2705 var element = $(this); 2706 element.data("_mask_timeout", setTimeout(function() { $.maskElement(element, label)}, delay)); 2707 } else { 2708 $.maskElement($(this), label); 2709 } 2710 }); 2711 }; 2712 2713 /**
2714 * Removes mask from the element(s). Accepts both single and multiple selectors. 2715 */ 2716 $.fn.unmask = function(){ 2717 $(this).each(function() { 2718 $.unmaskElement($(this)); 2719 }); 2720 }; 2721 2722 /** 2723 * Checks if a single element is masked. Returns false if mask is delayed or not displayed. 2724 */ 2725 $.fn.isMasked = function(){ 2726 return this.hasClass("masked"); 2727 }; 2728 2729 /** 2730 * Show or update the upload progressbar 2731 */ 2732 $.fn.maskProgress = function(value){ 2733 $(this).each(function() { 2734 $.maskProgress($(this), value); 2735 }); 2736 }; 2737 2738 $.maskElement = function(element, label){ 2739 2740 //if this element has delayed mask scheduled then remove it and display the new one 2741 if (element.data("_mask_timeout") !== undefined) { 2742 clearTimeout(element.data("_mask_timeout")); 2743 element.removeData("_mask_timeout"); 2744 } 2745 2746 if(element.isMasked()) { 2747 $.unmaskElement(element); 2748 } 2749 2750 if(element.css("position") == "static") { 2751 element.addClass("masked-relative"); 2752 } 2753 2754 element.addClass("masked"); 2755 2756 var maskDiv = $('<div class="loadmask"></div>'); 2757 2758 //auto height fix for IE 2759 if(navigator.userAgent.toLowerCase().indexOf("msie") > -1){ 2760 maskDiv.height(element.height() + parseInt(element.css("padding-top")) + parseInt(element.css("padding-bottom"))); 2761 maskDiv.width(element.width() + parseInt(element.css("padding-left")) + parseInt(element.css("padding-right"))); 2762 } 2763 2764 //fix for z-index bug with selects in IE6 2765 if(navigator.userAgent.toLowerCase().indexOf("msie 6") > -1){ 2766 element.find("select").addClass("masked-hidden"); 2767 } 2768 2769 element.append(maskDiv); 2770 2771 if ($(element).progressbar != undefined) { 2772 var maskProgressDiv = $('<div class="loadmask-progress" style="display:none;"></div>'); 2773 element.append(maskProgressDiv); 2774 element.find(".loadmask-progress").progressbar({value: 0}); 2775 } 2776 if(label !== undefined) { 2777 var maskMsgDiv = $('<div class="loadmask-msg" style="display:none;"></div>'); 2778 maskMsgDiv.append('<div>' + label + '</div>'); 2779 element.append(maskMsgDiv); 2780 2781 //calculate center position 2782 maskMsgDiv.css("top", Math.round(element.height() / 2 - (maskMsgDiv.height() - parseInt(maskMsgDiv.css("padding-top")) - parseInt(maskMsgDiv.css("padding-bottom"))) / 2)+"px"); 2783 maskMsgDiv.css("left", Math.round(element.width() / 2 - (maskMsgDiv.width() - parseInt(maskMsgDiv.css("padding-left")) - parseInt(maskMsgDiv.css("padding-right"))) / 2)+"px"); 2784 2785 maskMsgDiv.show(); 2786 } 2787 }; 2788 2789 $.maskProgress = function(element, value){ 2790 if ($(element).progressbar != undefined) { 2791 element.find(".loadmask-progress").show().progressbar('option', 'value', value); 2792 element.find(".loadmask-msg").hide(); 2793 } 2794 }; 2795 2796 $.unmaskElement = function(element){ 2797 //if this element has delayed mask scheduled then remove it 2798 if (element.data("_mask_timeout") !== undefined) { 2799 clearTimeout(element.data("_mask_timeout")); 2800 element.removeData("_mask_timeout"); 2801 } 2802 2803 element.find(".loadmask-msg,.loadmask-progress,.loadmask").remove(); 2804 element.removeClass("masked"); 2805 element.removeClass("masked-relative"); 2806 element.find("select").removeClass("masked-hidden"); 2807 }; 2808 2809})(jQuery);/* feedback js 2810---------------------------------------------------------- 2811 2812@package: Zotonic 2011 2813@Author: Marc Worrell <[email protected]> 2814 2815Copyright 2011 Marc Worrell 2816 2817Licensed under the Apache License, Version 2.0 (the "License"); 2818you may not use this file except in compliance with the License. 2819You may obtain a copy of the License at 2820 2821http://www.apache.org/licenses/LICENSE-2.0 2822 2823Unless required by applicable law or agreed to in writing, software 2824distributed under the License is distributed on an "AS IS" BASIS, 2825WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2826See the License for the specific language governing permissions and 2827limitations under the License. 2828 2829---------------------------------------------------------- */ 2830 2831$.widget("ui.feedback", 2832{ 2833 _init: function() 2834 { 2835 var self = this; 2836 z_ensure_id(this.element); 2837 $('#'+this.options.trigger) 2838 .on('keyup', function() { self.update(); }) 2839 .on('change', function() { self.update(); }); 2840 }, 2841 2842 update: function() 2843 { 2844 if (this.input_updater != undefined) 2845 { 2846 clearInterval(this.input_updater); 2847 this.input_updater = undefined; 2848 } 2849 2850 var self = this; 2851 this.input_updater = setInterval(function() 2852 { 2853 // Fetch the update 2854 var args = []; 2855 var trigger = $('#'+self.options.trigger); 2856 if (trigger.length == 0) { 2857 clearInterval(self.input_updater); 2858 self.input_updater = undefined; 2859 } else { 2860 if (trigger.prop('tagName').toLowerCase() == 'form') { 2861 args = trigger.formToArray({ all: true }); 2862 } else { 2863 var v = $.fieldValue(trigger.get(0), true); 2864 if (v && v.constructor == Array) { 2865 for(var j=0, jmax=v.length; j < jmax; j++) 2866 args.push({name: "triggervalue", value: v[j]}); 2867 } 2868 else if (v !== null && typeof v != 'undefined') 2869 args.push({name: "triggervalue", value: v}); 2870 } 2871 2872 // Stop when there is an 'in-flight' update with the same args 2873 if (self.element.hasClass('loading')) { 2874 if (self.last_args == $.param(args)) { 2875 clearInterval(self.input_updater); 2876 self.input_updater = undefined; 2877 } 2878 } else { 2879 self.last_args = $.param(args); 2880 clearInterval(self.input_updater); 2881 2882 // Form changed, post it to the server 2883 var notify_args = {}; 2884 for (var i=0; i<args.length; i++) { 2885 notify_args[args[i].name] = args[i].value; 2886 } 2887 notify_args.z_trigger_id = self.options.trigger; 2888 notify_args.z_target_id = $(self.element).attr('id'); 2889 notify_args.z_delegate = self.options.delegate; 2890 for (var k in self.options) { 2891 if (k != "delegate" && k != "timeout" && k != "delegate") { 2892 notify_args[k] = self.options[k]; 2893 } 2894 } 2895 2896 if (typeof self.previous_feedback == 'undefined' || !is_equal(self.previous_feedback, notify_args)) { 2897 self.element.addClass('loading'); 2898 self.previous_feedback = notify_args; 2899 z_notify("feedback", notify_args); 2900 } 2901 } 2902 } 2903 }, self.options.timeout); 2904 } 2905}); 2906 2907$.ui.feedback.defaults = { 2908 delegate: undefined, 2909 trigger: undefined, 2910 timeout: 400 2911} 2912/* z.popupwindow.js 2913---------------------------------------------------------- 2914 2915@author Cody Lindley 2916@author Marc Worrell <[email protected]> 2917 2918http://code.google.com/p/jquery-swip/ 2919 2920Copyright 2009 Cody Lindley 2921Copyright 2011 Marc Worrell 2922 2923Licensed under the Apache License, Version 2.0 (the "License"); 2924you may not use this file except in compliance with the License. 2925You may obtain a copy of the License at 2926 2927http://www.apache.org/licenses/LICENSE-2.0 2928 2929Unless required by applicable law or agreed to in writing, softw
2929are 2930distributed under the License is distributed on an "AS IS" BASIS, 2931WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2932See the License for the specific language governing permissions and 2933limitations under the License. 2934 2935---------------------------------------------------------- */ 2936 2937$.widget("ui.popupwindow", { 2938 _init: function() { 2939 var self = this; 2940 $(this.element).click(function(e) { 2941 e.preventDefault(); 2942 if (self.options.full) { 2943 self.options.toolbar = 1; 2944 self.options.scrollbars = 1; 2945 self.options.location = 1; 2946 self.options.menubar = 1; 2947 self.options.centerScreen = 1; 2948 self.options.centerBrowser = 0; 2949 self.options.width = screen.width-100; 2950 self.options.height = screen.height-100; 2951 } 2952 var windowFeatures = 'height=' + self.options.height + 2953 ',width=' + self.options.width + 2954 ',toolbar=' + (self.options.toolbar?'yes':'no') + 2955 ',scrollbars=' + (self.options.scrollbars?'yes':'no') + 2956 ',status=' + (self.options.status?'yes':'no') + 2957 ',resizable=' + (self.options.resizable?'yes':'no') + 2958 ',location=' + (self.options.location?'yes':'no') + 2959 ',menubar=' + (self.options.menubar?'yes':'no'); 2960 2961 var windowName = self.options.windowName; 2962 var windowURL = this.href || self.options.windowURL; 2963 var top, left; 2964 2965 if (self.options.centerBrowser && !self.options.centerScreen) { 2966 top = window.screenY + (((window.outerHeight/2) - (self.options.height/2))); 2967 left = window.screenX + (((window.outerWidth/2) - (self.options.width/2))); 2968 } else if (self.options.centerScreen) { 2969 top = (screen.height - self.options.height)/2; 2970 left = (screen.width - self.options.width)/2; 2971 } else { 2972 top = self.options.top; 2973 left = self.options.left; 2974 } 2975 2976 /* Remove non-alphanumeric characters for msie */ 2977 if (windowName) { 2978 windowName = windowName.replace(/[^a-zA-Z0-9]/g,'_'); 2979 } 2980 var w = window.open(windowURL, windowName, windowFeatures+',left='+Math.ceil(left)+',top='+Math.ceil(top)); 2981 setTimeout(function() { 2982 if (w.innerWidth != undefined && w.innerWidth > 0) { 2983 w.resizeBy(self.options.width - w.innerWidth, self.options.height - w.innerHeight); 2984 } 2985 }, 500); 2986 w.focus(); 2987 }); 2988 } 2989}); 2990 2991$.ui.popupwindow.defaults = { 2992 full:0, // set the height/width to the current window, show scrollbars etc. 2993 centerBrowser:1, // center window over browser window? {1 (YES) or 0 (NO)}. overrides top and left 2994 centerScreen:0, // center window over entire screen? {1 (YES) or 0 (NO)}. overrides top and left 2995 height:500, // sets the height in pixels of the window. 2996 left:0, // left position when the window appears. 2997 location:0, // determines whether the address bar is displayed {1 (YES) or 0 (NO)}. 2998 menubar:0, // determines whether the menu bar is displayed {1 (YES) or 0 (NO)}. 2999 resizable:0, // whether the window can be resized {1 (YES) or 0 (NO)}. Can also be overloaded using resizable. 3000 scrollbars:0, // determines whether scrollbars appear on the window {1 (YES) or 0 (NO)}. 3001 status:0, // whether a status line appears at the bottom of the window {1 (YES) or 0 (NO)}. 3002 width:500, // sets the width in pixels of the window. 3003 windowName:null, // name of window 3004 windowURL:null, // url used for the popup 3005 top:0, // top position when the window appears. 3006 toolbar:0 // determines whether a toolbar (includes the forward and back buttons) is displayed {1 (YES) or 0 (NO)}. 3007}; 3008
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.