PageSourceSearch

https://hart.amsterdam/lib/js/ginger_edit~modules/z.notice~z.image…mask~z.feedback~z.popupwindow~3727188113.js

js hart.amsterdam collected 2026-10-02 11:37:49 UTC 106,206 bytes, 3,008 lines download raw bytes

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>&times;</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>&times;</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&nbsp;&nbsp;&nbsp;[![Build Status](https://travis-ci.org/davedoesdev/qlobber.png)](https://travis-ci.org/davedoesdev/qlobber) [![Coverage Status](https://coveralls.io/repos/davedoesdev/qlobber/badge.png?branch=master)](
563https://coveralls.io/r/davedoesdev/qlobber?branch=master) [![NPM version](https://badge.fury.io/js/qlobber.png)](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.