PageSourceSearch

https://villatavernasf.com/CMSScripts/CHO/Scripts/utility.js?639257960799345130

js villatavernasf.com collected 2026-09-29 03:46:52 UTC 100,878 bytes, 2,310 lines download raw bytes

1/*-----------------------------------------------------------------------------------------------------------------------\dlo/
2Date:           Sep 10th, 2010
3Revised:        March 4, 2014
4Name:           CHO Prototype Utility.
5Description:    Base script library to use in CHOE3
6Author:         Dmitry Lopyrev
7Note:           Requires Prototype JS library (v.1.7c - modified) 
8
9---------------------------------------------------------------------------------------------------------------------------*/
10
11//------------------------------------------------------------------------------------------------------------------\dlo/
12//Extend object prototypes 
13//----------------------------------------------------------------------------------------------------------------------
14Object.extend(Object, {
15    guid: function () { return new Date().getTime().toPaddedString(10, 16).toUpperCase(); },
16    wrap: Object.extend.wrap(function (proceed, destination, source, properties) {
17        if (!Object.isArray(properties) || !properties.length)
18            return proceed(destination, source);
19        properties.each(function (it) {
20            !Object.isUndefined(source[it]) && (destination[it] = source[it]);
21        });
22        return destination;
23    }),
24    get: function (o, property, def) {
25        if (!o) return def;
26        var properties = (property || '').split('.');
27        property = properties.first();
28        properties = properties.slice(1);
29        o = o[property];
30        if (!Object.isUndefined(o) && properties.length)
31            return arguments.callee(o, properties.join('.'));
32        return o || def;
33    },
34    getValue: function (o, property, def) {
35        o = Object.get(o, property);
36        if (!o)
37            return def;
38        if (Object.isFunction(o.getValue))
39            return o.getValue() || def;
40        return o.value || def;
41    },
42    cast: function (o) {
43        for (it in o) {
44            if (Object.isString(o[it]))
45                o[it] = o[it].cast(true);
46        }
47        return o;
48    },
49    //Temporary
50    isBoolean: function (o) {
51        return typeof o == "boolean";
52    }
53
54});
55
56//----------------------------------------------------------------------------------------------------------------------
57//Extend Window prototype object Doesn't work on Android mobile stock browser
58//----------------------------------------------------------------------------------------------------------------------
59/*
60Object.extend(Window.prototype, {
61    Wait: function (n) {
62        var d = new Date().add('ms', String(n).toNumber() * 1000);
63        while (new Date() <= d) {}
64    }
65});
66*/
67Wait = function (n) {
68    var d = new Date().add('ms', String(n).toNumber() * 1000);
69    while (new Date() <= d) { }
70};
71
72
73//----------------------------------------------------------------------------------------------------------------------
74//Extend Number prototype object
75//----------------------------------------------------------------------------------------------------------------------
76Object.extend(Number.prototype, {
77    toString: Number.prototype.toString.wrap(function (proceed, f) {
78        try {
79            var number = this;
80            if (Object.isString(f) && /(G|C|N|F|P)/g.test(f)) {
81                return isNaN(number) ? "" : String(f || 'G').replace(/(G|C|N|F|P)/g,
82                    function ($1) {
83                        switch ($1) {
84                            case 'C':
85                                return number.toFixed(2);
86                            case 'N':
87                                return number.toFixed(0);
88                            case 'F':
89                                return number.toFixed(2);
90                            case 'P':
91                                return number.toString() + "%";
92                        }
93                        return proceed();
94                    }
95                );
96            } else {
97
98                return proceed.apply(this, $A(arguments).slice(1));
99            }
100        } catch (e) {
101        }
102
103    }),
104    toOrdinal: function () {
105        var s = ["th", "st", "nd", "rd"], v = this % 100;
106        return this + (s[(v - 20) % 10] || s[v] || s[0]);
107    },
108    toNumber: function() {
109        return this;
110    }
111});
112
113Object.extend(Array.prototype, {
114    removeFirst: function (n) {
115        n = Math.abs(String(n).toNumber() || 1);
116        if (this.length < n)
117            return this;
118        return this.splice(this.length - n, n);
119    },
120    removeLast: function (n) {
121        while (this.length && n > 0)
122            this.pop(), n--;
123        return this;
124    },
125    exclude: function(a) {
126        if (Object.isArray(a)) {
127            return this.without.apply(this, $A(a));
128        }
129        return this.without.call(this, arguments);
130    }
131        
132});
133
134
135
136//----------------------------------------------------------------------------------------------------------------------
137//Extend object prototypes 
138//----------------------------------------------------------------------------------------------------------------------
139Object.extend(String.prototype, {
140    toNumber: function (def, radix) {
141        var val = String(this).replace(/([^\-\d\. ])/g, "") || (Object.isNumber(def) ? def.toString() : '0');
142        if (val.indexOf('.') == -1)
143            return parseInt(val, radix || 10);
144        else
145            return parseFloat(val);
146    },
147    cast: function (b) {
148        if (b) {
149            if (/^[-]?[\d ]+[\.]?[\d ]*$/.test(this))
150                return this.toNumber();
151            if (/^(true|false)$/i.test(this))
152                return this.toLowerCase() == 'true';
153        }
154        return this.toString();
155    },
156    include: String.prototype.include.wrap(function (proceed, pattern, ignorecase) {
157        if (ignorecase === true)
158            return this.toLowerCase().indexOf((pattern || '').toLowerCase()) > -1;
159        return proceed(pattern);
160
161    }),
162    addQueryParam: function(o) {
163        if (!o)
164            return this;
165        var p = Object.extend(this.toQueryParams(), Object.isString(o) ? o.toQueryParams(): o);
166        return  this.replace(/[\?#].*/gi, '') +"?" + Object.toQueryString(p);
167       
168    }
169});
170//------------------------------------------------------------------------------------------------------------------\dlo/
171// Extend Date prototype object
172//----------------------------------------------------------------------------------------------------------------------
173Object.extend(Date.prototype, {
174    toString: Date.prototype.toString.wrap(function (proceed, f) {
175        if (Object.isUndefined(f))
176            return proceed();
177        var d = this;
178        return isNaN(this) ? "" : String(f).replace(/(('.+')|yyyy|yy|MMMM|MMM|MM|w|dddd|dd|ds|d|hh|h|mm|m|ss|tt|p)/g,
179            function ($1) {
180                var h;
181                switch ($1) {
182                    case 'yy': return (d.getFullYear() % 100).toPaddedString(2);
183                    case 'yyyy': return d.getFullYear().toPaddedString(4);
184                    case 'MMMM':
185                    case 'MMM': return (['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'][d.getMonth()]).substr(0, $1.length == 3 ? 3 : 20);
186                    case 'MM': return (d.getMonth() + 1).toPaddedString(2);
187                    case 'p': return ['Night', 'Morning', 'Afternoon', 'Evening'][parseInt(d.getHours() / 6)];
188                    case 'dddd':
189                    case 'w': return ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][d.getDay()];
190                    case 'ds': return d.getDate().toOrdinal();
191                    case 'dd': return d.getDate().toPaddedString(2);
192                    case 'd': return d.getDate().toString();
193                    case 'hh': return d.getHours().toPaddedString(2); 											//hour (01-24)
194                    case 'h': return ((h = d.getHours() % 12) ? h 
194: 12).toString(); 			//hour (01-12)
195                    case 'mm': return d.getMinutes().toPaddedString(2);
196                    case 'm': return d.getMinutes().toString();
197                    case 'ss': return d.getSeconds().toPaddedString(2);
198                    case 'tt': return d.getHours() < 12 ? 'am' : 'pm';
199                    default: return $1.replace(/'/g, "");
200                }
201            }
202      );
203    }),
204    CustomFormat: function (f) {
205        return this.toString(f);
206    },
207    add: function (interval, value) {
208        var d = new Date(this.getTime()), b = false;
209        if (!interval || value === 0) return d;
210        if (typeof (value) == 'string') {
211            b = true, value = parseInt('0' + value, 10);
212        }
213        switch (interval.toLowerCase()) {
214            case 'ms':
215                d.setMilliseconds((b ? 0 : this.getMilliseconds()) + value); break;
216            case 'sec':
217                d.setSeconds((b ? 0 : this.getSeconds()) + value); break;
218            case 'min':
219                d.setMinutes((b ? 0 : this.getMinutes()) + value); break;
220            case 'h':
221                d.setHours((b ? 0 : this.getHours()) + value); break;
222            case 'd':
223                d.setDate((b ? 0 : this.getDate()) + value); break;
224            case 'm':
225                d.setMonth((b ? 0 : this.getMonth()) + value); break;
226            case 'y':
227                d.setFullYear((b ? 0 : this.getFullYear()) + value); break;
228        }
229        return d;
230    },
231    toObject: function (f) {
232        var d = this, o = { year: 0, month: 0, day: 0, hour: 0, minute: 0, second: 0, ms: 0 };
233        String(f || 'YMDhmsl').replace(/(Y|M|D|h|m|s|l)/g,
234            function ($1) {
235                switch ($1) {
236                    case 'Y': o.year = d.getFullYear(); break;
237                    case 'M': o.month = d.getMonth(); break;
238                    case 'D': o.day = d.getDate(); break;
239                    case 'h': o.hour = d.getHours(); break;
240                    case 'm': o.minute = d.getMinutes(); break;
241                    case 's': o.second = d.getSeconds(); break;
242                    case 'l': o.ms = d.getMilliseconds(); break;
243                }
244            }
245        );
246        return o;
247    },
248    ticks: function () {
249        function monthToDays(year, month) {
250            var add = 0;
251            var result = 0;
252            if ((year % 4 == 0) && ((year % 100 != 0) || ((year % 100 == 0) && (year % 400 == 0)))) add++;
253
254            switch (month) {
255                case 0: return 0;
256                case 1: result = 31; break;
257                case 2: result = 59; break;
258                case 3: result = 90; break;
259                case 4: result = 120; break;
260                case 5: result = 151; break;
261                case 6: result = 181; break;
262                case 7: result = 212; break;
263                case 8: result = 243; break;
264                case 9: result = 273; break;
265                case 10: result = 304; break;
266                case 11: result = 334; break;
267                case 12: result = 365; break;
268            }
269            if (month > 1) result += add;
270            return result;
271        };
272        function timeToTicks(hour, minute, second) {
273            return (((hour * 3600) + minute * 60) + second) * 10000000;
274        };
275        function dateToTicks(year, month, day) {
276            //number of days in the the years
277            var a = parseInt((year - 1) * 365);
278            //count the leapyear days
279            //A year is a leap year if it can be divided by 4, but can't be divided by 100, except of case when it can be divided by 400. 
280            var b = parseInt((year - 1) / 4);
281            var c = parseInt((year - 1) / 100);
282            var d = parseInt((a + b - c));
283            var e = parseInt((year - 1) / 400);
284            var f = parseInt(d + e);
285            //number of days for complete months.
286            var monthDays = monthToDays(year, month - 1);
287            //add in number of completed days in the month
288            var g = parseInt((f + monthDays) + day - 1);
289            return g * 864000000000;
290        };
291        var o = this.toObject();
292        return dateToTicks(o.year, o.month, o.day) + timeToTicks(o.hour, o.minute, o.second) + (o.ms * 10000);
293    },
294    round: function (n) {
295        var min = this.getHours() * 60 + this.getMinutes();
296        min = Math.floor(min / Math.abs(n) + 0.5) * Math.abs(n);
297        return this.setTime().add('min', min);
298    },
299    clone: function () {
300        return new Date(this.getTime());
301    },
302    diff: function (d, f) {
303        if (d instanceof Date) {
304            switch (String(f)) {
305                case 'y':
306                case 'Y': return this.getFullYear() - d.getFullYear();
307                case 'M': return (this.getMonth() + 12 * this.getFullYear()) - (d.getMonth() + 12 * d.getFullYear());
308                case 'd': return parseInt((this.getTime() - d.getTime()) / (24 * 3600 * 1000));
309                case 'd+': return parseInt((this.getTime() - d.getTime()) / (24 * 3600 * 1000)) + (parseInt((this.getTime() - d.getTime()) % (24 * 3600 * 1000)) >= 12 ? 1 : 0);
310                case 'h': return parseInt((this.getTime() - d.getTime()) / (3600 * 1000));
311                case 'h+': return parseInt((this.getTime() - d.getTime()) / (3600 * 1000)) + (parseInt((this.getTime() - d.getTime()) % (3600 * 1000)) >= 30 ? 1 : 0);
312                case 'm': return parseInt((this.getTime() - d.getTime()) / (60 * 1000));
313                case 'm+': return parseInt((this.getTime() - d.getTime()) / (60 * 1000)) + (parseInt((this.getTime() - d.getTime()) % (60 * 1000)) >= 30 ? 1 : 0);
314                case 's': return parseInt((this.getTime() - d.getTime()) / 1000);
315                default: return this.getTime() - d.getTime();
316            }
317        } else {
318            return 0;
319        }
320
321    },
322    weekend: function () {
323        return this.getDay() == 0 || this.getDay() == 6;
324    },
325    setTime: Date.prototype.setTime.wrap(function (proceed, time) {
326        if (Object.isNumber(time))
327            return proceed(time);
328        time = String(time || '').replace(/(am|pm)/i, function($1) { return ' '+$1});
329        
330        var d = new Date(this.CustomFormat("yyyy/MM/dd ") + time);
331        return d;
332    })
333});
334
335Object.extend(Function.prototype, {
336    argumentValues: function (n) {
337        var names = this.argumentNames().slice(n);
338        var o = {};
339        for (var i = 0; i < this.arguments.length; i++) {
340            if (names[i])
341                o[names[i]] = this.arguments[i];
342            else if (Object.isString(this.arguments[i]) || Object.isDate(this.arguments[i]) || Object.isNumber(this.arguments[i]) || Object.isArray(this.arguments[i]) || Object.isBoolean(this.arguments[i])) {
343                !Object.isArray(o.parameters) && (o.parameters = []);
344                o.parameters.push(this.arguments[i]);
345            } else {
346                Object.extend(o, this.arguments[i]);
347            }
348        }
349        return o;
350    }
351});
352
353//----------------------------------------------------------------------------------------------------------------------
354//- Prototype Library Extesions     
355//----------------------------------------------------------------------------------------------------------------------
356//Added new methods to DOM elements
357Element.addMethods({
358    isDescendantOf: function (element, host) {
359        if (!Object.isElement($p(host)))
360            return false;
361        host = $p(host);
362        var isDecendant = false;
363        //Check if current element is desendant of popup container
364        var b = $p(element).descendantOf(host);
365        while (element.parentNode) {
366            element = element.parentNode;
367            isDecendant |= element === host.up();
368        }
369        //close popup if isn't decendant of host or popup selector
370        return Boolean(isDecendant);
371    },
372    noClick: function (element) {
373        element = $p(element);
374        element.$click = element.onclick ? element.onclick : Prototype.emptyFunction;
375        element.onclick = null;
376        element.on("click", function (e) { e.stop(); }.bindAsEventListener());
377        element.tagName === 'A' && (element.writeAttribute('href', (element.readAttribute('disabled') ? "" : "javascript:void(0);")));
378        return element;
379    },
380    getInnerText: function (element) {
381        return element.innerText || element.textContent;
382    },
383    setInnerText: function (element, txt) {
384        if (element.innerText) element.innerText = txt;
385        else if (element.textContent) element.innerHTML = txt;
386    },
387    //Create and returns an object based on item attributes started with "data-";
388    getActions: function (element, options, def) {
389        def = def || {}, options = options || {};
390        /* 
391        | possible property for options : 
392        |      camelize : convert object's property name to Camelize string (a first letter is capital)
393        |      lower : use lower case in property name
394        |      cast: convert parameter value to type (based on content), overwize value will always be a string
395        */
396        return $A(element.attributes).inject(def, function (o, attr) {
397            if (attr.name.startsWith('data-')) {
398                var ns = attr.name.replace('data-', '').split('-');
399                var result = o;
400                for (var i = 0, it; it = ns[i++];) {
401                    if (options.camelize)
402                        it = ('-' + it).camelize();
403                    if ((options.capitalize))
404                        it = it.capitalize();
405                    if ((options.lower))
406                        it = it.toLowerCase();
407                    !result[it] && (result[it] = {});
408                    if (i == ns.length)
409                        result[it] = attr.value.cast(options.cast);
410                    else
411                        result = result[it];
412                };
413            }
414            return o;
415        });
416    },
417    getFields: function (element, opt) {
418        //returns input elements separated by group name (and only for group specified in "names" parameter)
419        if (!Object.isElement(element))
420            return null;
421        opt = opt || {};
422        var inputs = Object.isArray(opt) ? opt : Form.getInputs(element);
423        if (Object.isString(opt.type)) { //'text|textarea|hidden|checkbox|radio'
424            opt.type = new RegExp(opt.type, 'i');
425        }
426        if (!Object.isArray(inputs))
427            return null;
428        var data = inputs.inject({}, function (o, it) {
429            if (!Object.isElement(it))
430                return o;
431            if (opt.type && !opt.type.test(it.type))
432                return o;
433
434            var data = Object.extend({ field: it.id.split('_').last() }, it.getActions({lower:true}));
435
436            if (Object.isArray(o[data.field]))
437                o[data.field].push(it);
438            else
439                o[data.field] = it;
440            return o;
441        });
442        return data;
443
444    },
445    simulate: function (element, eventName, memo) {
446
447        var eventMatchers = {
448            'HTMLEvents': /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll|change)$/,
449            'MouseEvents': /^(?:click|mouse(?:down|up|over|move|out))$/
450        };
451        var options = {
452            pointerX: 0,
453            pointerY: 0,
454            button: 0,
455            ctrlKey: false,
456            altKey: false,
457            shiftKey: false,
458            metaKey: false,
459            bubbles: true,
460            cancelable: true
461        };
462
463        options = Object.extend(options, arguments[2] || {});
464        var oEvent, eventType = null;
465
466        element = $p(element);
467
468        for (var name in eventMatchers) {
469            if (eventMatchers[name].test(eventName)) {
470                eventType = name;
471                break;
472            }
473        }
474
475        if (!eventType)
476            return undefined;
477
478        if (document.createEvent) {
479            oEvent = document.createEvent(eventType);
480            if (eventType == 'HTMLEvents') {
481                oEvent.initEvent(eventName, options.bubbles, options.cancelable);
482            } else {
483                oEvent.initMouseEvent(eventName, options.bubbles, options.cancelable, document.defaultView,
484                    options.button, options.pointerX, options.pointerY, options.pointerX, options.pointerY,
485                    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, element);
486            }
487            oEvent.memo = memo;
488            element.dispatchEvent(oEvent);
489        } else {
490            options.clientX = options.pointerX;
491            options.clientY = options.pointerY;
492            oEvent = Object.extend(document.createEventObject(), options);
493            oEvent.memo = memo;
494            element.fireEvent('on' + eventName, oEvent);
495        }
496        return element;
497    }
498});
499
500//----------------------------------------------------------------------------------------------------------------------
501//Added new Form methods
502//----------------------------------------------------------------------------------------------------------------------
503Form.Methods.LoadFields = function (form, container, isfullname) {
504    //returns new object for each found input field in form element : 
505    //Properties ::
506    //     field - current input field
507    //     value - current input field's value (can be treat as default value )
508    //     IsChanged(method) -  to return true if input field value has changed
509    // Note :: the name of this property is based on a field name (Input Control ID on server side) 
510    // Optional :: if container passed in it will be updated with new properties 
511    if (form = $p(form), !form)
512        return;
513
514    var elements = Form.getElements(form);
515    elements = elements.length ? elements : [form];
516
517    var o = elements.inject({}, function (o, inp) {
518        if (inp.name) {
519            var name = Object.isString(container) ? container : (isfullname ? inp.name : inp.name.split('$').last());
520            var data = {
521                IsChanged: function () { return Boolean(this.field.getValue() !== this.value); },
522                AcceptChanges: function () { return this.value = this.field.getValue(); },
523                Values: function (flag) {
524                    return $H(this).keys().inject({}, function (values, key) {
525                        if (this[key].field && Object.isFunction(this[key].field.getValue))
526                            if ((flag === true && this[key].IsChanged()) || flag == undefined)
527                                values[key] = this[key].field.getValue();
528                        return values;
529                    }, this);
530                }
531            };
532            if (inp.type == 'radio') {
533                Object.extend(data, { field: $p(inp.id), value: inp.getValue(), options: form.select("[name='" + inp.name + "']") });
534            } else if (inp.type == 'checkbox') {
535                //Do not use Checkbox list: user 
536                Object.extend(data, { field: inp, value: inp.getValue() });
537            } else {
538                Object.extend(data, { field: inp, value: inp.getValue() });
539            }
540
541            if (Object.isArray(o[name]))
542                o[name].push(data);
543            else if (o[name]) {
544                if (inp.type != 'radio')
545                    o[name] = [o[name], data];
546            } else
547                o[name] = data;
548        };
549        return o;
550    }, this);
551    if (Object.isString(container)) {
552        return o[container];
553    }
554    container && (Object.extend(container, o));
555    return o;
556};
557
558//Add new methods to all Prototypes 
559Element.addMethods();
560
561
562/**
563* Ajax.Request.abort
564* extend the prototype.js Ajax.Request object so that it supports an abort method
565*/
566Ajax.Request.prototype.abort = function () {
567    // prevent and state change callbacks from being issued
568    this.transport.onreadystatechange = Prototype.emptyFunction;
569    // abort the XHR
570    this.transport.abort();
571    // update the request counter
572    Ajax.Responders.dispatch('onComplete', this);
573
574    //Ajax.activeRequestCount--;
575    //if (Ajax.activeRequestCount < 0) {
576    //    Ajax.activeRequestCount = 0;
577    //}
578};
579
580
581/*- End of Extensions ---------------------------------------------------------------------------------------------------------*/
582
583var Utility = {
584    Global : {bubbletip:true},
585    UI: {}, //UI classes holder
586    AddLibrary: function (w) {
587        if (w && !w.Prototype) {
588            var newScr = w.document.createElement("SCRIPT");
589            newScr.src = ($$('script').find(function (s) { return /prototype.js/i.test(s.src); }) || {}).src;
590            if (newScr.src) {
591                newScr.type = "text/javascript";
592                w.document.getElementsByTagName("head")[0].appendChild(newScr);
593            }
594        }
595
596    },
597    AddStyle: function () {
598        var b;
599        var links = $$('link').inject([], function (list, it) {
600            if (/App_Themes/i.test(Object.isElement(it) && it['href'])) {
601                list.push(it.href);
602                if (/preloader.css/i.test(it.href))
603                    b = true;
604            }
605            return list;
606        });
607        if (!links.length || b)
608            return;
609        var link = document.createElement("LINK");
610        link.type = 'text/css';
611        link.rel = 'stylesheet';
612        link.href = links[0].substr(0, links[0].toLowerCase().indexOf("app_themes")) + 'app_themes/preloader.css';
613        document.getElementsByTagName("head")[0].appendChild(link);
614
615    }
616};
617
618
619//----------------------------------------------------------------------------------------------------------------------
620//[serfi] $hack-hack$ helper function shaves of HTML from an AJAX invocation supposed to return application/JSON content
621//----------------------------------------------------------------------------------------------------------------------
622function CleanupHtmlJSON(text) {
623    var returnObject = null;
624    var lines = text.split('\r\n');
625    lineCount = lines.length;
626    strJSONSerialized = lines[lineCount - 1];
627    //perhaps the library does this cleansing already but too cryptic for me at this point
628    sanitizedJSON = (strJSONSerialized.replace('"d":"{', '"d":{')).replace('"}"}', '"}}');
629    sanitizedJSON = sanitizedJSON.replace(/\\"/g, '"');
630    sanitizedJSON = sanitizedJSON.replace(/\\\\/g, '\\');
631    try {
632        returnObject = sanitizedJSON.evalJSON();
633    } catch (e) {
634        returnObject = '{ "Title": "Application & Validation Error", "Message": "Unspecified Error." }'.evalJSON();
635    }
636    return returnObject.d;
637}
638
639
640//----------------------------------------------------------------------------------------------------------------------
641//Class to create and handle Web Method Requests
642//----------------------------------------------------------------------------------------------------------------------
643Utility.WebMethodRequest = new Class.create({
644    initialize: function (Caller, options) {
645        this.method = '';
646        this.parameters = {};
647        //update options
648        Object.extend(this, options || {});
649        //current object to hold this WebRequestMethod instance
650        this.Caller = Caller;
651        if (this.Caller.ShowError)
652            this.ShowError = this.Caller.ShowError;
653        if (this.Caller.CompleteRequest)
654            this.CompleteRequest = this.Caller.CompleteRequest;
655    },
656    //Show Error. Executes when WebMethod returns an error 
657    ShowError: function (request) {
658
659        this.StopProgress();
660        var err = { message: 'Error ', title: request.statusText || 'Error', cssButton: 'simple', callBack: this.Caller.onErrorC
660omplete };
661        if (request) {
662            if (request.responseJSON) {
663                err = Object.extend(err, request.responseJSON);
664                //ignore uppercase for properties
665                err.message = err.Message || err.message;
666                err.title = err.Title || err.title;
667                //            } else if (request.status == 500) {
668                //    //the parsing failed: $hack-hack$ - strip off the HTML and reparse: only the last line is a valid JSON serialization 
669                //    var messageJSON = CleanupHtmlJSON(request.responseText);
670                //    err = Object.extend(err, messageJSON);
671                //    err.message = err.Message || err.message;
672                //    err.title = err.Title || err.title;
673            } else if (typeof request.statusText != 'undefined') {
674                //current response text probably is YSofD, so we need to parse if
675                var ysof = /<!--\s*\[(.*?)]:(\s*.*\s(.*[\n\r]*)*?)\s*(at(.*[\n\r]*)*)-->/;
676                var matches = request.responseText.match(/<!--\s*\[(.*?)]:(\s*.*\s(.*[\n\r]*)*?)\s*(at(.*[\n\r]*)*)-->/);
677                if (Object.isArray(matches)) {
678                    err.title = matches[1];
679                    err.message = matches[2];
680                }
681
682                else {
683                    //show fill body of error page
684                    err.message = request.responseText;
685                    err.isHTML = true;
686                }
687            }
688            else if (request.message) {
689                err.message = request.message;
690                err.title;
691            }
692            if (Object.isFunction(this.Caller.onError)) {
693                err = this.Caller.onError.call(this.Caller, err, request);
694            };
695        }
696        if (err)
697            //Show error message
698            $alert(err);
699    },
700    //Called when request is completed
701    CompleteRequest: function (response) {
702        document.title = this.Title;
703        var isexecuted = false; // true - don't execute onComplete!
704
705        //removes current message if exists
706        this.StopProgress();
707
708        if (this.ajaxRequest.success()) {
709            try {
710
711                if (response && response.responseJSON) {
712
713                    //if new update properties passed in apply the properties to current object instance
714                    if (response.responseJSON.properties)
715                        Object.extend(this.Caller, response.responseJSON.properties);
716
717                    //Check Local method. 
718                    var callbackfunction = response.responseJSON.callback || response.responseJSON.method;
719                    var caller;
720                    if (Object.isFunction(this.__callback)) {
721                        isexecuted = true;
722                        if (Object.isArray(response.responseJSON.methodparameters))
723                            this.__callback.apply(caller, response.responseJSON.methodparameters);
724                        else
725                            this.__callback.call(caller, (response.responseJSON.methodparameters || response.responseJSON), response);
726                        delete this.__callback;
727                    }
728                    //if result has a method property and current object has this method, so invoke that method with patameters (optional property)
729                    //NOTE. the callback method can contains full method description like 'host.method'. 
730                    //So, in that case we have to change the caller context
731                    else if (Object.isFunction(func = Object.get(this.Caller, callbackfunction))) {
732                        isexecuted = true;
733                        caller = Object.get(this.Caller, callbackfunction.split('.').removeLast(1).join('.')) || this.Caller;
734                        if (Object.isArray(response.responseJSON.methodparameters))
735                            func.apply(caller, response.responseJSON.methodparameters);
736                        else
737                            func.call(caller, (response.responseJSON.methodparameters || response.responseJSON), response);
738
739                        //Check Global method.
740                        //if result has a method property and current object has this method, so invoke that method with patameters (optional property)
741                    } else if (Object.isFunction(func = Object.get(window, callbackfunction))) {
742                        isexecuted = true;
743                        caller = Object.get(window, callbackfunction.split('.').removeLast(1).join('.')) || window;
744                        if (Object.isArray(response.responseJSON.methodparameters))
745                            func.apply(caller, response.responseJSON.methodparameters.concat([response]));
746                        else
747                            func.call(caller, (Object.isUndefined(response.responseJSON.methodparameters) ? response.responseJSON : response.responseJSON.methodparameters), response);
748                    }
749                }
750                //call complete method if exists and not executed yet
751                if (!isexecuted) {
752                    if (response.responseJSON == null || Object.toJSON(response.responseJSON) == "{}")
753                        return;
754                    if (Object.isFunction(this.Caller.Complete)) {
755                        if (Object.isArray(response.responseJSON.methodparameters))
756                            this.Caller.Complete.apply(this.Caller, response.responseJSON.methodparameters);
757                        else
758                            this.Caller.Complete.call(this.Caller, (response.responseJSON.methodparameters || response.responseJSON), response);
759                    } else if (Object.isString(response.responseJSON))
760                        response.responseJSON.evalScripts();
761                    else
762                        throw new Error("Callback function "+(callbackfunction ? "(" + callbackfunction + ")" : "")+" cannot be executed.");
763                }
764            } catch (err) {
765                this.ShowError(err);
766            }
767        }
768
769        //removes current request
770        //this.ajaxRequest && (delete this.ajaxRequest);
771    },
772
773    //Show a progres message if message object is set
774    //NOTE:: the message object should be an Array with two items: 1- signature of mesage type, 2 - parameters (must have property 'host' set)
775    StartProgress: function (msg) {
776        this.$progressmessage = this.$progressmessage || ( msg ? $progress(msg) : this.Caller.progressmessage);
777        if (this.$progressmessage instanceof Utility.Message) {
778            this.$progressmessage.Show();
779        }
780    },
781
782    //Hides a progress message
783    StopProgress: function () {
784        if (this.$progressmessage instanceof Utility.Message) {
785            this.$progressmessage.Close();
786            delete this.$progressmessage;
787        }
788    },
789
790    //Build and returns current url to request based on parameters
791    GetUrl: function (method) {
792        var url = location.href.replace(/[\?#].*/gi, '').concat(/\/$/gi.test(location.pathname) ? "default.aspx" : "");
793        var data = String(method).split('|').filter(Boolean);
794        if (data.length > 1) {
795            data[0] = data[0] || url;
796            data = [data.join("/"), ''];
797        } else {
798            if (window.pagewebmethodurl)
799                data = [window.pagewebmethodurl , data[0]];
800            else
801                data = [ url , data[0]];
802
803            data[0] = data[0].replace(/\/AjaxMethod$/i, '') + '/AjaxMethod';
804        }
805        if (location.protocol =='https:')
806            data[0] = data[0].replace(/^http:\/\//i, 'https://');
807
808        return data;
809    },
810
811    //Updates container passed in with HTML code returned by Web Method
812    Update: function (method, parameters, container, progress) {
813        try {
814            var f;
815            this.Title = document.title;
816            // the Caller must exists
817            if (!this.Caller || !method)
818                return false;
819
820            //Call onRequest if exists (Allow to modify parameters before request)
821            Object.isFunction(this.Caller.onRequest) && (this.Caller.onRequest.call(this, parameters));
822
823            if (this.stopRequest) {
824                this.stopRequest = false;
825                return;
826            }
827            // WEB-30832 - DEBUGGING ONLY!!!
828            //console.log('method:', method);
829            //if (method === 'BookEvent.ProceedBooking') {
830                //console.log('postData:', JSON.stringify(parameters));
831                //return;
832            //}
833            //Show a Progress message if assigned
834             this.StartProgress(progress);
835
836            //Assign main properties
837            this.parameters = parameters || {};
838            if (Object.isFunction(this.parameters.callback)) {
839                this.__callback = this.parameters.callback;
840                delete this.parameters.callback;
841            }
842            var url = this.GetUrl(method);
843            var opt = {
844                method: 'POST',
845                webMethod: true, // true, if we need to analize result from .NET web service 
846                evalScripts: true,
847                contentType: 'application/json',
848                postBody: Object.toJSON(Boolean(url[1]) ? { methodName: url[1], data: this.parameters } : { data: this.parameters }),
849                onFailure: this.ShowError.bind(this),
850                onComplete: this.CompleteRequest.bind(this)
851            };
852            if (Object.isElement(container))
853                //Create an AJAX Updater to update container
854                this.ajaxRequest = new Ajax.Updater(container, url[0], opt);
855            else
856                //Create an AJAX Request
857                this.ajaxRequest = new Ajax.Request(url[0], opt);
858
859            //Access denied cannot be catch, so check the case and re-throw the error
860            if (this.ajaxRequest.transport.readyState != 1)
861                throw new Error('Access denied.');
862        } catch (e) {
863            this.ShowError(e);
864        }
865
866    },
867
868    Fetch: function (method, parameters) {
869        this.Update(method, parameters);
870    },
871    //Checks if Ajax request is created and running
872    IsProcessing: function () {
873        return this.ajaxRequest && (this.ajaxRequest.transport.readyState > 0 && this.ajaxRequest.transport.readyState < 4);
874    },
875
876    IsPending: function () {
877        return Ajax.activeRequestCount > 0;
878    },
879
880    //Abort ajax request if created.
881    //NOTE: After Web Method request has been created only response on Client side can be aborted.
882    //On server side a web method still processed. 
883    Stop: function () {
884        try {
885            if (this.ajaxRequest) {
886                this.ajaxRequest.abort();
887            }
888        } catch (e) { };
889    }
890
891});
892//----------------------------------------------------------------------------------------------------------------------
893//Base class
894//----------------------------------------------------------------------------------------------------------------------
895Utility.Base = Class.create({
896    initialize: function (host) {
897        //mandatory - DOM container
898        // NOTE: this.host might be set before class initializer call ( on a constructor)
899        this.host = this.host || $p(host) || $$(host);
900
901        if (Object.isElement(this.host)) {
902            this.host.identify();
903            //Apply default settings
904            if (Object.isFunction(this.defaults)) {
905                this.defaults(this.defaults.argumentNames().length ? this.host.getActions() : undefined);
906            } else {
907                Object.extend(this, this.host.getActions());
908            }
909        } else if (this.host instanceof Window) {
910            Object.isFunction(this.defaults) && Object.extend(this, this.defaults());
911        } else if (Object.isArray(this.host) && this.host.length && Object.isElement(this.host[0])) {
912            this.host.invoke('identify');
913            if (Object.isFunction(this.defaults)) {
914                this.defaults();
915            }
916        }
917        if (!this.host)
918            throw new Error('Class cannot be instantiated! <' + host 
918+ '>');
919
920        var $names = this.initialize.argumentNames().slice(2); //skip $super and host arguments
921        var $args = $A(arguments).slice(1);  //skip host argument
922        $args.each(function (it, i) {
923            var name = i < $names.length ? $names[i] : null;
924            if (name != '$super' && name != 'host') {
925                if (name) {
926                    this[name] = Object.extend(this[name] || {}, Object.cast(it));
927                } else if (Object.isString(it) || Object.isDate(it) || Object.isNumber(it) || Object.isArray(it) || Object.isBoolean(it)) {
928                    if (!Object.isArray(this.parameters)) this.parameters = [];
929                    this.parameters.push(it);
930                } else {
931                    Object.extend(this, Object.cast(it));
932                }
933            }
934        }, this);
935
936        this.events = {};
937
938        return this;
939    },
940    run: function (name) {
941        if (name) {
942            var args = $A(arguments).slice(1);
943            //if name is a CSV strip all names and treat each name as a separate function
944
945            var func, caller = name.split('.').removeLast(1).join('.');
946
947            if (Object.isFunction(func = Object.get(this, name))) {
948                caller = Object.get(this, caller) || this;
949                return func.apply(caller, args);
950                //if result has a method property and current object has this method, so invoke that method with patameters (optional property)
951            } else if (Object.isFunction(func = Object.get(self, name))) {
952                caller = Object.get(self, caller) || self;
953                return func.apply(caller, args);
954            }
955        }
956        return undefined;
957    },
958    on: function (eventName, func, fire) {
959        if (!this.host)
960            return this;
961        !this.events && (this.events = {});
962        //Check if Event.Handler passed in as first parameter, add it to event collection
963        if (eventName instanceof Event.Handler) {
964            func = eventName, eventName = eventName.eventName;
965        }
966
967        if (Object.isString(eventName) && func instanceof Event.Handler) {
968            this.events[eventName] = func;
969        } else if (Object.isString(eventName) && this.events[eventName] instanceof Event.Handler) {
970            this.events[eventName].start();
971        } else {
972            Object.isFunction(func) && (this.events[eventName] = this.host.on(eventName, null, func));
973        }
974        fire && this.fire(eventName);
975        return this;
976    },
977    off: function () {
978        var names = !arguments.length ? $H(this.events).keys() : $A(arguments);
979        for (var name in this.events) {
980            if (names.include(name)) {
981                this.events[name].stop();
982                //delete this.events[name];
983            }
984        }
985        return this;
986    },
987    start: function () {
988        var names = !arguments.length ? $H(this.events).keys() : $A(arguments);
989        for (var name in this.events) {
990            if (names.include(name))
991                this.events[name].start();
992        }
993    },
994    stop: function () {
995        var names = !arguments.length ? $H(this.events).keys() : $A(arguments);
996        for (var name in this.events) {
997            if (names.include(name))
998                this.events[name].stop();
999        }
1000    },
1001    fire: function (name) {
1002        if (!this.host.simulate(name))
1003            this.host.fire.apply(this.host, arguments);
1004        return this;
1005    }
1006});
1007//----------------------------------------------------------------------------------------------------------------------
1008//Generic Class to create selectors with server side business actions 
1009//----------------------------------------------------------------------------------------------------------------------
1010Utility.AjaxSelector = new Class.create(Utility.Base, {
1011    initialize: function ($super) {
1012
1013        if (!$super.apply(this, $A(arguments).slice(1)))
1014            return false;
1015
1016        //Verify and add progress information if needed
1017        var progress = Object.get(this, 'parameters.progress') || self.executeprogress;
1018        if (Object.isString(progress))
1019            this.progressmessage = $loading({ host: this.host, message: progress });
1020        else if (progress)
1021            this.progressmessage = self.progressmessage;
1022
1023        //Add new Web Request object
1024        this.WebRequest = new Utility.WebMethodRequest(this);
1025
1026        //Assign an active event to to call server method
1027        if (Object.isString(this.activeevent)) {
1028            this.host.noClick();
1029            this.host[this.activeevent] = this.Call.bindAsEventListener(this);
1030        }
1031        return true;
1032    },
1033
1034    //Request. Usually uses in OnClick event
1035    Call: function (e) {
1036        self.focus();
1037        this.Request(this.method, this.parameters, this.container);
1038        return false;
1039    },
1040
1041    //Call AJAX WebMethod
1042    //argumets   method (required) - method to call 
1043    //           parameters (optional) - parameters (an object) to pass in
1044    //           updateContainer (optional) - DOM element or and object like {success: #DOMELEMENT, failure: #DOMELEMENT} to update on complete.
1045    //                                        If it's not exist (or ommited) use AJAX.Request
1046    Request: function (method, parameters, updateContainer) {
1047        this.WebRequest.Update(method, parameters, updateContainer);
1048    }
1049
1050});
1051
1052
1053//----------------------------------------------------------------------------------------------------------------------
1054//Class to add keyboard support to Selector (select item using keyboard : Up, Down, Home, End, Select, Cancel)
1055//----------------------------------------------------------------------------------------------------------------------
1056Utility.AjaxDropDown = new Class.create(Utility.AjaxSelector, {
1057    defaults: function () {
1058        this.propertynames = ['Text', 'Value', 'Selected'];
1059        this.parameters = {};
1060        this.layout = {};
1061        this.waitmessage = "Loading...";
1062        this.activeevent = "onclick";
1063    },
1064    initialize: function ($super) {
1065
1066        if (!$super.apply(this, $A(arguments).slice(1)))
1067            return false;
1068
1069
1070        if (this.layout.temporary) {
1071            this.Container = this.host;
1072            this.Container.addClassName('ajaxselector');
1073        } else
1074            this.host.wrap(this.Container = new Element('div', { className: 'ajaxselector' }));
1075
1076        //Assign a progress message  to the Host
1077        if (this.waitmessage)
1078            this.progressmessage = $loading({ host: this.Container, message: this.waitmessage });
1079
1080        return this;
1081    },
1082
1083    //Override. Checks and updates popup container. If there is no items to select close popup with delay
1084    Complete: function (response) {
1085        if (response.responseJSON) {
1086            if (this.Selector)
1087                this.DestroySelector();
1088            this.CreateSelector(response.responseJSON);
1089        }
1090    },
1091
1092    //default function to transform option (needs to be overriden on time of Initialization)
1093    //Item transformation method. 
1094    //where: 
1095    //  item - selected DOM element,
1096    //  highlight- search expression, 
1097    //  keys - array of property names
1098    TransformItem: function (item, keys) {
1099        if (item === 'CREATE') //create
1100            return null;
1101
1102        item.$data = $H(item).keys().inject("", function (list, key) {
1103            list += ' data-parameter-' + key.toLowerCase() + '="' + item[key] + '"';
1104            return list;
1105        });
1106        item.$text = item[keys[0]];
1107        return ('<div class="item" #{$data}">#{$text}</div>').interpolate(item);
1108    },
1109
1110
1111    //Load data and update container,
1112    // Data can be present in two formats, 
1113    //  1 - HTML code (formatted text) to update container with
1114    //  2 - Array of objects
1115    CreateSelector: function (data) {
1116        if (!data)
1117            return false;
1118        if (!this.Selector)
1119            //create a popup container
1120            $p(document.body).insert(this.Selector = new Element("div", { className: "ajaxselector selector" }).setStyle({ position: 'absolute', visibility: 'hidden' }));
1121
1122
1123        if (Object.isArray(data)) {
1124
1125            //Calls item transformation to create option container.
1126            //If not handled, this.container will be used a  a container
1127            var parent = this.TransformItem.call(this.container, 'CREATE');
1128            var properties = Object.isArray(this.propertynames) ? this.propertynames : ['Text'];
1129            //transform and add options to container 
1130            for (var i = 0, item; item = data[i++];) {
1131                //call transformation function with two parameters: current item data and text box value 
1132                item.index = i;
1133                (parent || this.Selector).insert(this.TransformItem.call((parent || this.Selector), item, properties));
1134            }
1135        } else if (Object.isString(data)) {
1136            this.Selector.update(data);
1137        }
1138        if (this.items = this.Selector.select('.item'), this.items.length) {
1139            this.items.each(function (item, index) {
1140                item.index = index;
1141                item.onclick = this.Select.bindAsEventListener(this);
1142                item.onmouseover = this.Activate.bind(this, item);
1143            }, this);
1144        }
1145        this.ShowSelector();
1146
1147        return false;
1148    },
1149
1150    ShowSelector: function () {
1151        //set container position
1152        //        var layout = Object.extend({}, this.layout);
1153        this.Selector.clonePosition(this.Container, { setWidth: false, setHeight: false });
1154        this.layout.width = this.layout.width || Math.max(this.Container.measure('width'), this.Selector.measure('width'));
1155        this.layout.height = this.layout.height || Math.min(this.Selector.measure('border-box-height'), this.items[0].measure('border-box-height') * this.items.length);
1156        this.layout.minwidth = this.layout.minwidth || this.layout.width;
1157        this.layout.minheight = this.layout.minheight || this.layout.height;
1158        this.layout.top = this.layout.top || (this.Selector.measure('top') + document.viewport.getScrollOffsets().top);
1159
1160        this.Selector.setStyle({
1161            top: this.layout.top + 'px',
1162            width: this.layout.width + 'px',
1163            height: this.layout.height + 'px',
1164            'min-width': this.layout.minwidth + 'px',
1165            'min-height': this.layout.minheight + 'px',
1166            visibility: 'visible'
1167        });
1168
1169        //Add event handlers
1170        this.on(":exit", this.DestroySelector.bind(this));
1171        this.on(":select", this.Select.bindAsEventListener(this));
1172        this.on(":move", this.Move.bindAsEventListener(this));
1173        this.on(document.on("keyup", this.KeyPress.bindAsEventListener(this)));
1174        this.on(document.on("mousedown", this.Close.bindAsEventListener(this)));
1175        this.on(Event.on(self, "mousewheel", this.Close.bindAsEventListener(this)));
1176        this.on(Event.on(self, "resize", this.Close.bindAsEventListener(this)));
1177
1178        Object.isFunction(this.onShow) && (this.onShow(this.host, this.container));
1179
1180        if (Object.isArray(this.items) && this.items.length)
1181            this.Activate(this.items.first());
1182
1183    },
1184
1185    setFocus: function (index) {
1186        if (Object.isArray(this.items) && this.items.length && (this.items.length > index))            
1187            this.Activate(this.items[index], true);
1188    },
1189
1190    DestroySelector: function (item) {
1191        //destroy event handlers
1192        this.off();
1193
1194        Object.isFunction(this.onHide) && (this.onHide());
1195
1196        //destroy content and item collections
1197        this.Selector.remove();
1198
1199        if (this.layout.temporary) {
1200            this.Container.removeClassName('ajaxselector');
1201        }
1202        delete this.items;
1203        delete this.Selector;
1204
1205    },
1206
1207    Select: function (e) {
1208        Event.stop(e);
1209        if (!Object.isArray(this.items) && Object.isUndefined(this.ActiveIndex))
1210            return false;
1211        var item = this.items[this.ActiveIndex];
1212        var data = item.getActions();
1213        //call onselect event. 
1214        //NOTE.If data is Empty object, you can analize selected item by using 'this'
1215        Object.isFunction(this.onSelect) && (this.onSelect.call(this, this, item, data));
1216        //close selector
1217        this.fire(':exit');
1218    },
1219
1220    Activate: function (item, addExtraBottomPadding) {
1221        if (!item || Object.isUndefined(item.index)) {
1222            return false;
1223        }
1224
1225        if (this.ActiveIndex == item.index)
1226            return true;
1227
1228        var prevIndex = this.ActiveIndex || 0;
1229        this.items[prevIndex].removeClassName("over");
1230        this.items[this.ActiveIndex = item.index].addClassName("over");
1231
1232        !this.items[this.ActiveIndex]._height && (this.items[this.ActiveIndex]._height = this.items[this.ActiveIndex].getHeight());
1233        //we need to calculate top and bottom pixels of active item
1234        var top = this.items[this.ActiveIndex]._height * this.ActiveIndex;
1235        var bottom = this.items[this.ActiveIndex]._height * (this.ActiveIndex + 1);
1236
1237        //do scrolling to make item visible
1238        if (top < this.Selector.scrollTop)
1239            this.Selector.scrollTop = Math.max(this.Selector.scrollTop - top, 0);
1240        else if (bottom > this.Selector.scrollTop + this.Selector.getHeight())
1241        {
1242            if (addExtraBottomPadding) {
1243                // web-117 added extra padding to display focuses item on top
1244                var units = Math.ceil(this.Selector.getHeight() / this.items[this.ActiveIndex]._height);
1245                this.Selector.scrollTop = Math.max(bottom - this.Selector.getHeight() + (this.items[this.ActiveIndex]._height * (units - 3)), 0); 
1246            }
1247            else
1248                this.Selector.scrollTop = Math.max(bottom - this.Selector.getHeight(), 0);
1249        }
1250
1251    },
1252
1253    Close: function (e) {
1254        var el = Event.element(e);
1255        if (!Object.isElement(el) || (el !== this.Selector && !el.descendantOf(this.Selector) && el !== this.host)) {
1256            this.fire(":exit");
1257        }
1258    },
1259
1260    Move: function (e) {
1261        Event.stop(e);
1262
1263        if (!e || Object.isUndefined(e.memo) || Object.isUndefined(e.memo.index) || !this.items.length)
1264            return false;
1265
1266        index = e.memo.index;
1267
1268        //if string - consider it as item position
1269        if (Object.isString(index)) {
1270            if (index == 'BOTTOM')
1271                index = this.items.length - 1;
1272            else // 'TOP' or other
1273                index = 0;
1274        } else if (Object.isNumber(index)) {
1275            if (Object.isUndefined(this.ActiveIndex))
1276                index = index > 0 ? 0 : this.items.length - 1;
1277            else
1278                index = this.ActiveIndex + index;
1279        }
1280
1281        if (index >= 0 && index < this.items.length)
1282            this.Activate(this.items[index]);
1283    },
1284
1285    //Handle Key Press event (IE specific) 
1286    KeyPress: function (e) {
1287
1288        var code = ((e = e || event), e.keyCode || e.which || e.charCode);
1289        Event.stop(e);
1290        switch (code) {
1291            case Event.KEY_ESC:
1292                this.fire(":exit");
1293                return false;
1294            case Event.KEY_RETURN:
1295                this.fire(":select");
1296                return false;
1297            case Event.KEY_UP:
1298            case Event.KEY_DOWN:
1299            case Event.KEY_HOME:
1300            case Event.KEY_END:
1301                return !Prototype.Browser.IE;
1302        }
1303        return false;
1304    }
1305});
1306
1307
1308Utility.Actions = Class.create(Utility.Base, {
1309    SetActions: function(rule) {
1310        this.host.select(rule || '[data-action]').each(function(it) {
1311            if (!it.$click) {
1312                it.data = it.getActions({ cast: true });
1313                it.noClick().on('click', function(e) {
1314                    e.stop();
1315                    this.DoAction(it.data.action.toLowerCase(), it.data, it);
1316                }.bindAsEventListener(this));
1317            }
1318        }, this);
1319    },
1320    DoAction: function(action, it, element) {
1321    }
1322});
1323
1324Utility.Callback = new Class.create(Utility.Base, {
1325    initialize: function ($super, signature, init) {
1326        if (!$super.apply(this, $A(arguments).slice(1)))
1327            return false;
1328        this.signature = signature;
1329
1330        // init might play a role of WebForm_InitCallback
1331        Object.isFunction(init) && (init());
1332    },
1333    DoAssign: function () {
1334        this.controls.each(function (it) {
1335            $$(it.selector).first().on('click', this.RequestWithParam.bindAsEventListener(this, it.prms));
1336        }, this);
1337    },
1338    RequestWithParam: function (e, prms) {
1339        e.stop();
1340        if (!Object.isString(prms))
1341            prms = Object.toJSON(prms);
1342        return this.Request(prms);
1343    },
1344    Request: function (prms) {
1345        WebForm_DoCallback && WebForm_DoCallback(this.signature, prms || '', this.Callback.bind(this), '', null, false);
1346        return false;
1347    },
1348    Callback: function (result) {
1349        try {
1350            if (String(result).isJSON()) {
1351                result = result.evalJSON();
1352                this.run(result.method, result.parameters);
1353            } else {
1354                alert(result);
1355            }
1356        } catch (e) {
1357            alert(e.message);
1358        }
1359    }
1360});
1361
1362
1363Utility.Redirect = function (url, w) {
1364    w = w && w.location ? w : self;
1365    if (url && Object.isString(url))
1366        w.location.href = url;
1367/*
1368    else if (url == "")
1369        w.location.href = w.location.href;
1370*/
1371    else //if (url == "")
1372        w.location.reload(true);
1373
1374};
1375
1376Utility.RedirectNewWindow = function (url, w) {
1377    w = w && w.location ? w : self;
1378    if (url && Object.isString(url))
1379        w.open(url, '_blank')
1380    else
1381        w.location.reload(true);
1382
1383};
1384
1385Ajax.JSONRequest = Class.create(Ajax.Base, (function () {
1386    var head = document.getElementsByTagName('head')[0];
1387    return {
1388        initialize: function ($super, url, id, options) {
1389            $super(options);
1390            this.options.url = url;
1391            this.options.id = id;
1392            this.options.callbackParamName = this.options.callbackParamName || 'callback';
1393            this.options.timeout = this.options.timeout || 10000; // Default timeout: 10 seconds
1394            this.responseJSON = {};
1395            this.request();
1396        },
1397
1398        request: function () {
1399            // Add callback as a parameter and build request URL
1400            if (Object.isString(this.options.parameters))
1401                this.options.parameters = this.options.parameters.toQueryParams();
1402
1403            //Check ID and create a name for callback function
1404            if (this.options.id) {
1405                this._name = '_prototypeJSONPCallback_' + (this.options.id ? String(this.options.id) : '');
1406                this.options.parameters[this.options.callbackParamName] = this._name;
1407            }
1408            this.options.parameters = Object.toQueryString(this.options.parameters);
1409            this.options.url = this.options.url + (this.options.parameters ? ((this.options.url.include('?') ? '&' : '?') + this.options.parameters) : '');
1410            this.script = new Element('script', { type: 'text/javascript', src: this.options.url });
1411
1412            if (this.options.id) {
1413                this.timeout = setTimeout(function () {
1414                    this.cleanup();
1415                    if (Object.isFunction(this.options.onFailure)) {
1416                        this.options.onFailure.call(this, this);
1417                    }
1418                }.bind(this), this.options.timeout);
1419
1420                // Define callback function
1421                window[this._name] = function (json) {
1422                    this.cleanup(); // Garbage collection
1423                    if (Object.isFunction(this.options.onComplete)) {
1424                        this.responseJSON = json;
1425                        this.options.onComplete.call(this, this);
1426                    }
1427                }.bind(this);
1428            }
1429
1430            if (Object.isFunction(this.options.onCreate)) {
1431                this.options.onCreate.call(this, this);
1432            }
1433            if (!$$('head [src="' + this.options.url + '"]').first())
1434                head.insert(this.script);
1435
1436        },
1437        cleanup: function () {
1438            window[this._name] = undefined;
1439            if (this.timeout) {
1440                clearTimeout(this.timeout);
1441                this.timeout = null;
1442            }
1443            if (this.script && Object.isElement(this.script)) {
1444                this.script.remove();
1445                this.script = null;
1446            }
1447        }
1448    };
1449})());
1450
1451
1452
1453
1454//----------------------------------------------------------------------------------------------------------------------
1455//Class to show progress on image load
1456//----------------------------------------------------------------------------------------------------------------------
1457Utility.ImageLoader = Class.create({
1458    initialize: function (img, settings) {
1459        //duration should be in seconds
1460        this.settings = Object.extend({ period: 0.1, duration: 3, loading: 'loading', max: 140 }, settings);
1461
1462        if (this.img = $p(img), !this.img || this.img.tagName != 'IMG' || !this.settings.progress)
1463            return false;
1464
1465        this.dim = this.img.up().getDimensions();
1466        this.settings = Object.extend(this.settings, this.img.getActions());
1467        this.settings.duration *= 10;
1468
1469        this.Run();
1470    },
1471
1472    //Starts counter, checks time every second and update DOM element
1473    Run: function () {
1474        if (this.settings.duration > 0) {
1475            this.img.hide();
1476            this.timer = new PeriodicalExecuter(function (pe) {
1477                try {
1478                    if (this.IsReady()) {
1479                        //reached zero, stop executer
1480                        pe.stop();
1481                        this.Update();
1482                    } else if (--this.settings.duration <= 0) {
1483                        throw new Error('stop');
1484                    } else {
1485                        this.ShowProgress();
1486                    }
1487
1488                    //not reached yet
1489                } catch (e) {
1490                    // Stops executer in case of error
1491                    pe.stop();
1492                    this.Cancel();
1493                }
1494            }.bind(this), this.settings.period); // execute funcion on behalh of current object every second
1495        }
1496        return this;
1497    },
1498    IsReady: function () {
1499        return this.img.complete && this.img.getWidth() > 0 && this.img.getHeight() > 0;
1500
1501    },
1502    ShowProgress: function () {
1503        if (!this.loading) {
1504            this.img.insert({ before: this.loading = new Element('img', { src: this.settings.progress, className: this.settings.loading }) });
1505
1506        }
1507    },
1508
1509    Resize: function () {
1510        //do not resize if settings are not specified
1511        this.settings.width = String(this.settings.width).toNumber();
1512        this.settings.height = String(this.settings.height).toNumber();
1513
1514        if (!this.settings.width && !this.settings.height)
1515            return;
1516
1517        //get image dimensions
1518        var dim = this.img.getDimensions();
1519        //calculate aspect ratio
1520        var ratio = dim.height / dim.width;
1521        //if Height is requested 
1522        if (this.settings.height > this.settings.width && this.settings.width == 0)
1523            this.settings.width = this.settings.height * 1 / ratio;
1524            //if width is requested 
1525        else if ((this.settings.width >= this.settings.height && this.settings.height == 0))
1526            this.settings.height = this.settings.width * ratio;
1527
1528        this.img.setStyle({ width: Math.round(this.settings.width) + 'px', height: Math.round(this.settings.height) + 'px' });
1529    },
1530
1531    HideProgress: function () {
1532        this.loading && (this.loading.remove());
1533        if (!this.settings || !this.settings.hideImage) //sprte - 7514 removing forced inheritance when ImageLoader is loaded
1534            this.img.setStyle('visibility:inherit').show();        
1535    },
1536
1537    Update: function () {
1538        this.HideProgress();
1539        this.Resize();
1540    },
1541
1542    Cancel: function () {
1543        this.HideProgress();
1544
1545    }
1546});
1547
1548//Control's Bubble Tip  (like the element title)
1549Utility.BubbleTip = Class.create(Utility.Base, {
1550    defaults: function (o) {
1551        return this.options = Object.extend({
1552            css: 'bubbletip',
1553            gravity: 'se',
1554            offset: 0,
1555            opacity: 1,
1556            trigger: 'hover'
1557        }, o);
1558    },
1559    initialize: function ($super, host, options) {
1560        try {
1561            if (!$super.apply(this, $A(arguments).slice(1)))
1562                return false;
1563
1564            if (this.getValue()) {
1565                this.on(this.options.trigger == 'hover' ? 'mouseenter' : 'focus', this.enter.bindAsEventListener(this));
1566                this.on(this.options.trigger == 'hover' ? 'mouseleave' : 'blur', this.leave.bindAsEventListener(this));
1567
1568                if (this.host.readAttribute('bubbletip') || this.options.bubbletip)
1569                    this.host.addClassName(this.options.css);
1570                //this.on('mousedown',  this.toggle.bindAsEventListener(this));
1571
1572            }
1573
1574            return this;
1575
1576        }
1577        catch (e) {
1578
1579        }
1580    },
1581    show: function (e) {
1582        if (!(this.content = this.getValue()))
1583            return;
1584        if (this.disabled)
1585            return;
1586
1587        this.host.writeAttribute('title', null);
1588        if (!this.tooltip) {
1589            $p(document.body).insert(this.tooltip = new Element("div", { className: 'ps-tip' }).update('<div class="ps-tip-arrow"></div><div class="ps-tip-inner">#{content}</div></div>'.interpolate(this)));
1590            this.tooltip.setStyle({ top: 0, left: 0, visibility: 'hidden', display: 'block' });
1591        }
1592        var host = Object.extend(this.host.cumulativeOffset(), this.host.getDimensions());
1593        var tip = Object.extend(this.tooltip.getDimensions(), { opacity: this.options.opacity });
1594        var gravity = this.options.gravity;
1595        var i = 0;
1596        do {
1597            i++;
1598            switch (gravity) {
1599                case 'se':
1600                    tip = Object.extend(tip, { top: host.top + host.height / 2, left: host.left + host.width + this.options.offset });
1601                    break;
1602                case 'sw':
1603                    tip = Object.extend(tip, { top: host.top + host.height / 2, left: host.left - tip.width - this.options.offset });
1604                    break;
1605                case 'ne':
1606                    tip = Object.extend(tip, { top: host.top - tip.height, left: host.left + host.width + this.options.offset });
1607                    break;
1608                case 'nw':
1609                    tip = Object.extend(tip, { top: host.top - tip.height, left: host.left - host.width - this.options.offset });
1610                    break;
1611            }
1612            var b = tip.left > 0 && (tip.left + tip.width) < $Screen.viewport.width && tip.top > 0 && (tip.top + tip.height) < $Screen.viewport.width;
1613            if (!b) {
1614                if (gravity == 'se') gravity = 'sw';
1615                else if (gravity == 'sw') gravity = 'ne';
1616                else if (gravity == 'ne') gravity = 'nw';
1617                else gravity = 'se';
1618            }
1619            if (i == 5) gravity = this.options.gravity;
1620
1621        } while (!b && i < 5)
1622        this.tooltip.setStyle("top:#{top}px;left:#{left}px".interpolate(tip)).addClassName('ps-tip-' + gravity);
1623        this.tooltip.setStyle({ visibility: 'visible', opacity: this.options.opacity });
1624
1625    },
1626    getValue: function () {
1627        return unescape((this.host.readAttribute('title') || this.host.readAttribute('bubbletip') || this.options.title || this.options.bubbletip || this.content || '').replace(/(^\s*|\s*$)/, ""));
1628            
1629        ;
1630    },
1631    setValue: function (text) {
1632        if (!text) return;
1633        if (this.content = String(text).replace(/(^\s*|\s*$)/, "")) {
1634            if (this.host.readAttribute('bubbletip') || this.options.bubbletip)
1635                this.host.addClassName(this.options.css);
1636            this.host.writeAttribute('title', null);
1637        }
1638    },
1639    hide: function () {
1640        if (!this.tooltip)
1641            return;
1642        this.tooltip.remove();
1643        delete this.tooltip;
1644    },
1645    enter: function (e) {
1646        if (e.shiftKey)
1647            return;
1648        this.show(e);
1649    },
1650    leave: function (e) {
1651        this.hide();
1652    },
1653    toggle: function (e) {
1654        if (!e.ctrlKey) return;
1655        (this.disabled = !Boolean(this.disabled)) ? this.hide() : this.show();
1656    }
1657});
1658
1659Utility.RGB = {
1660    parse1: function (r, g, b, a) {
1661        if (arguments.length == 1) {
1662            var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(String(arguments[0]));
1663            Object.extend(this, result ? {
1664                R: parseInt(result[1], 16),
1665                G: parseInt(result[2], 16),
1666                B: parseInt(result[3], 16)
1667            } : {});
1668        } else {
1669            this.R = String(r).toNumber();
1670            this.G = String(g).toNumber();
1671            this.B = String(b).toNumber();
1672            this.A = parseFloat(String(a));
1673        }
1674
1675    },
1676    parse: function (r, g, b, a) {
1677        if (arguments.length == 1 && Object.isString(arguments[0])) {
1678            var color = String(arguments[0]).replace(/\s\s*/g, '');// Remove all spaces
1679            var cache, p = parseInt; // Use p as a byte saving reference to parseInt
1680
1681            // Checks for 6 digit hex and converts string to integer
1682            if (cache = /^#([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})/.exec(color))
1683                cache = [p(cache[1], 16), p(cache[2], 16), p(cache[3], 16)];
1684
1685                // Checks for 3 digit hex and converts string to integer
1686            else if (cache = /^#([\da-fA-F])([\da-fA-F])([\da-fA-F])/.exec(color))
1687                cache = [p(cache[1], 16) * 17, p(cache[2], 16) * 17, p(cache[3], 16) * 17];
1688
1689                // Checks for rgba and converts string to
1690                // integer/float using unary + operator to save bytes
1691            else if (cache = /^rgba\(([\d]+),([\d]+),([\d]+),([\d]+|[\d]*.[\d]+)\)/.exec(color))
1692                cache = [+cache[1], +cache[2], +cache[3], +cache[4]];
1693
1694                // Checks for rgb and converts string to
1695                // integer/float using unary + operator to save bytes
1696            else if (cache = /^rgb\(([\d]+),([\d]+),([\d]+)\)/.exec(color))
1697                cache = [+cache[1], +cache[2], +cache[3]];
1698
1699            // Performs RGBA conversion by default
1700            cache && isNaN(cache[3]) && (cache[3] = 1);
1701
1702            return cache;
1703        } else {
1704            return [String(r).toNumber(), String(g).toNumber(), String(b).toNumber(), parseFloat(String(a))];
1705        }
1706            
1707    },
1708    toHex: function () {
1709        var cache = this.parse.apply(this, $A(arguments));
1710        //return ("#" + this.R.toColorPart() + this.G.toColorPart() + this.B.toColorPart()).toUpperCase();
1711        return cache && cache[3] > 0 ? ("#" + cache[0].toColorPart() + cache[1].toColorPart() + cache[2].toColorPart()).toUpperCase() : 'transparent';
1712    },
1713    toRGB: function () {
1714        var cache = this.parse.apply(this, $A(arguments));
1715        return cache && cache[3] > 0 ? "rgb(#{0},#{1},#{2})".interpolate(cache) : 'transparent';
1716    },
1717    toRGBA: function () {
1718        var cache = this.parse.apply(this, $A(arguments));
1719        return cache ? "rgba(#{0},#{1},#{2},#{3})".interpolate(cache) : 'transparent';
1720    },
1721    toContrast: function () {
1722        var cache = this.parse.apply(this, $A(arguments));
1723        return cache ? ((((cache[0] * 299) + (cache[1] * 587) + (cache[2] * 114)) / 1000 >= 128) ? '#000' : '#FFF') : "#000";
1724    }
1725};
1726//----------------------------------------------------------------------------------------------------------------------
1727//Class to create four time of messages:
1728//  - Alert - shows a Dialog window with one button (closes window)
1729//  - Confirm - shows a Dialog with confirm message and two choices: Yes or No
1730//  - Dialog - shows a Dialog with  a close button on header
1731//  - Progress - shows custom progress message (in a center of the window) and disables all client actions
1732//  - Loading - shows a custom progress message linked to given DOM element
1733//----------------------------------------------------------------------------------------------------------------------
1734Utility.Message = Class.create({
1735    defaults: function () {
1736        return {
1737            overlay: true,
1738            ok: 'OK',               // text for the OK button
1739            cancel: 'Cancel',       // text for the Cancel button
1740            controls: true          //allows to show/hide controls (Ok,Cancel) if presented
1741            //omitted acceptable parameters:
1742            //radius: true,
1743            //shadow:true,
1744            //css                // use custom CSS to modify the layout
1745            //callback,             //a call back function on closing message
1746            //width,
1747            //height
1748        };
1749    },
1750    initialize: function (type, options) {
1751
1752        //validate type 
1753        if (!(['alert', 'confirm', 'dialog', 'progress', 'popup', 'loading'].include(this.type = type)))
1754            return false;
1755        this.events = [];
1756        //Load and validate options
1757        this.options = Object.extend(this.defaults(), Object.isString(options.message) ? options : Object.extend(options, options.message));
1758        //host
1759        this.options.host = this.options.host || self;
1760        //title
1761        this.options.title = this.options.title || this.type.capitalize();
1762        //message
1763        this.options.message = this.options.isHTML ? (this.options.message || '') : (this.options.message || '').replace(/\n/g, '<br />');
1764        /*
1765                this.options.message = (this.options.message || '');
1766                this.options.isHTML &&  (this.options.message = this.options.message.replace(/\n/g, '<br/>'));
1767        */
1768        //a callback
1769        this.options.callback = Object.isFunction(this.options.callback) ? this.options.callback : Prototype.emptyFunction;
1770
1771        if (Object.isFunction(this[this.options.auto]))
1772            return this[this.options.auto].apply(this);
1773        return this;
1774    },
1775    HTMLk8: function (data) {
1776        switch (this.type) {
1777            case 'alert':
1778                return ('<div class="message-header-k8"><span class="message-title-k8">#{title}</span><span class="message-tools-k8"><span data-action="discard" class="close-k8"></span></span></div>' +
1779		                '<div class="message-body-k8"><div class="message-k8"></div></div>' +
1780                        '<div class="message-controls-k8"><button data-action="ok" class="ModuleBtn ModuleBtnGray" data-default="true">#{ok}</button</div>').interpolate(data);
1781            case 'confirm':
1782                return ('<div class="message-header-k8"><span class="message-title-k8">#{title}</span><span class="message-tools-k8"><span data-action="discard" class="close-k8"></span></span></div>' +
1783		                '<div class="message-body-k8"><div class="message-k8"></div></div>' +
1784 		                 '<div class="message-controls-k8"><button data-action="ok" class="ModuleBtn ModuleBtnGray" data-default="true">#{ok}</button>&nbsp;&nbsp;<button data-action="cancel" class="ModuleBtn ModuleBtnGray">#{cancel}</button></div>').interpolate(data);
1785            case 'dialog':
1786                return ('<div class="message-header-k8"><span class="message-title-k8">#{title}</span><span class="message-tools-k8"><span data-action="cancel" data-default="true" class="close-k8"></span></span></div>' +
1787		                '<div class="message-body-k8"><div class="message-k8"></div></div>' +
1788                        '<div class="message-controls-k8"><button data-action="ok" class="ModuleBtn ModuleBtnGray" data-default="true">#{ok}</button</div>').interpolate(data);
1789            case 'popup':
1790                return ('<div class="message-header-k8"><span class="message-title-k8">#{title}</span><span class="message-tools-k8"><span data-action="cancel" data-default="true" class="close-k8"></span></span></div>' +
1791                        '<div class="message-body-k8"><div class="message-k8"></div></div>' +
1792                        '<div class="message-controls-k8"><button data-action="ok" class="ModuleBtn ModuleBtnGray" data-default="true">#{ok}</button</div>').interpolate(data);
1793            case 'progress':
1794                return ('<div class="message-body-k8"><div class="message-k8"></div></div>');//.interpolate(data);
1795
1796            case 'loading':
1797            default:
1798                return '<div class="message-k8">#{message}</div>';//.interpolate(data);
1799        }
1800    },
1801    HTML: function (data) {
1802        switch (this.type) {
1803            case 'alert':
1804                return ('<div class="message-header"><span>#{title}</span></div>' +
1805		                '<div class="message-body"><div class="message"></div></div>' +
1806                        '<div class="message-controls"><button data-action="ok" data-default="true">#{ok}</button</div>').interpolate(data);
1807            case 'confirm':
1808                return ('<div class="message-header"><span>#{title}</span><span class="message-tools"><span data-action="discard" class="close"></span></span></div>' +
1809		                '<div class="message-body"><div class="message"></div></div>' +
1810 		                 '<div class="message-controls"><button data-action="ok" data-default="true">#{ok}</button>&nbsp;&nbsp;<button data-action="cancel">#{cancel}</button></div>').interpolate(data);
1811            case 'dialog':
1812                return ('<div class="message-header"><span>#{title}</span><span class="message-tools"><span data-action="cancel" data-default="true" class="close"></span></span></div>' +
1813		                '<div class="message-body"><div class="message"></div></div>' +
1814                        '<div class="message-controls"><button data-action="ok" data-default="true">#{ok}</button</div>').interpolate(data);
1815            case 'popup':
1816                return ('<div class="message-header"><span>#{title}</span><span class="message-tools"><span data-action="cancel" data-default="true" class="close"></span></span></div>' +
1817                        '<div class="message-body"><div class="message"></div></div>' +
1818                        '<div class="message-controls"><button data-action="ok" data-default="true">#{ok}</button</div>').interpolate(data);
1819            case 'progress':
1820                return ('<div class="loader loader-general"><i class="icon-spinner spinning cms-icon-100 loader-icon" aria-hidden="true"></i><span class="loader-text">Loading</span></div>');//.interpolate(data);
1821
1822            case 'loading':
1823            default:
1824                return '<div class="message">#{message}</div>';//.interpolate(data);
1825        }
1826    },
1827    Create: function () {
1828        var messageboxclass = this.options.k8 ? 'message-box-k8 ' : 'message-box ';
1829        var messageheaderclass = this.options.k8 ? '.message-header-k8 ' : '.message-header';
1830        var messagebodyclass = this.options.k8 ? '.message-body-k8 ' : '.message-body';
1831        var messageclass = this.options.k8 ? '.message-k8 ' : '.message';
1832        var messagecontrolsclass = this.options.k8 ? '.message-control
1832s-k8 ' : '.message-controls';
1833
1834
1835        if (this.options.keep !== true)
1836            this.Remove();
1837
1838        if (this.type != 'progress')
1839            $p(document.body).insert(this.container = new Element("div", { 'class': messageboxclass + this.type }));
1840        else 
1841            $p(document.body).insert(this.container = new Element("div", { 'class': 'cms-bootstrap'}));        
1842
1843        this.options.width = this.Calculate(this.options.width, $Screen.viewport.width);
1844        this.options.height = this.Calculate(this.options.height, $Screen.viewport.height);
1845
1846        if (Object.isNumber(this.options.width)) {
1847            this.container.setStyle({ width: this.options.width + 'px' });
1848        }
1849        if (Object.isNumber(this.options.height)) {
1850            this.container.setStyle({ height: this.options.height + 'px' });
1851        }
1852        if (this.options.k8) {
1853            this.container.update(this.HTMLk8(this.options)).addClassName(this.options.css || '').setStyle("position:fixed;padding:0;margin:0;");
1854        } else {
1855            this.container.update(this.HTML(this.options)).addClassName(this.options.css || '').setStyle("position:fixed;padding:0;margin:0;");
1856        }
1857        
1858        this._overlay = this.container.wrap('div', { 'class': 'message-box-overlay' }).setStyle("position:fixed;top:0px;left:0px;width:100%;height:100%;visibility:hidden;");
1859        this.header = this.container.down(messageheaderclass);
1860        if (this.type != 'progress') {
1861            this.messagebody = this.container.down(messagebodyclass);
1862            this.messagecontainer = this.container.down(messageclass);
1863        } else {
1864            this.messagebody = this.container.down('.loader');
1865            this.messagecontainer = this.container.down('.loader-text');
1866        }
1867
1868        this.messagecontrols = this.container.down(messagecontrolsclass);
1869        if (this.messagecontrols) {
1870            !this.options.controls && (this.messagecontrols.remove(), this.messagecontrols = undefined);
1871        }
1872
1873        this.Update({ message: this.options.message });
1874        this.container.select('[data-action]').each(function (it) {
1875            it.on('mousedown', this.DoAction.bind(this));
1876        }, this);
1877        if (!this.options.defaultAction && (this.options.defaultAction = this.container.select('[data-default]').first(), this.options.defaultAction))
1878            this.options.defaultAction = this.options.defaultAction.getActions().action;
1879
1880        if ('loading' == this.type) {
1881            this._overlay.setStyle('background-color:transparent;');
1882        }
1883
1884        this.options.radius && this.container.addClassName('-radius');
1885        this.options.shadow && this.container.addClassName('-shadow');
1886        
1887        // Make draggable. Note:: requires draggables library
1888        if (this.header && Utility.Drag && !this.options.disabledrag) {
1889            try {
1890                new Utility.Drag(this.container, { handle: this.header.setStyle({ cursor: 'move' }) });
1891            } catch (e) { }
1892        }
1893        this.options.host.__message = this;
1894        return this;
1895    },
1896    Update: function (opt) {
1897        if (Object.isUndefined(opt)) return;
1898        opt.title && this.container.down('.message-header span').update(opt.title);
1899        opt.message && this.messagecontainer.update(opt.message);
1900        return this;
1901    },
1902    Indicate: function () {
1903        if (this.type == 'progress') {
1904            var max = 40;
1905            var start = 0;
1906            var el = this.messagebody;
1907            if (el) {
1908                this.events.push(new PeriodicalExecuter(function (pe) {
1909                    try {
1910                        el.setStyle('background-position:#{0}px #{1}px'.interpolate([start, 0]));
1911                        start += start == max ? start * -1 : 1;
1912                    } catch (e) {
1913                        pe.stop();
1914                    }
1915                }, 0.01));
1916            }
1917        }
1918        return this;
1919    },
1920    Show: function (opt) {
1921
1922        if (self.progressmessage instanceof Utility.Message && self.progressmessage.IsShown())
1923            self.progressmessage.Hide();
1924
1925        if (Object.isNumber(opt))
1926            this.Hide.bind(this).delay(opt);
1927        else if (opt)
1928            this.Update(opt);
1929        if (this.options.host.__message !== this)
1930            this.Create();
1931
1932        this.Reposition().Indicate().AddEventHandlers();
1933
1934        self.focus();
1935        return this;
1936    },
1937    IsShown: function () {
1938        return this._overlay && this._overlay.getStyle('visibility') != 'hidden';
1939    },
1940    Hide: function () {
1941        this.Close();
1942    },
1943    Close: function (f) {
1944        //removes all events
1945        this.RemoveEventHandlers();
1946        if (this.options.keep === true) {
1947            this._overlay.setStyle('visibility:hidden');
1948            return this;
1949        }
1950        if (this.container) {
1951            Object.isFunction(this.onHide) && this.onHide();
1952
1953            this.container.remove();
1954            delete this.container;
1955        }
1956        if (this._overlay) {
1957            this._overlay.remove();
1958            delete this._overlay;
1959        }
1960
1961        //remove handler for current message
1962        this.options.host.__message = undefined;
1963        return this;
1964    },
1965    DoAction: function (e) {
1966        var data = e.element().getActions();
1967        if (e.keyCode == Event.KEY_RETURN) {
1968            data.action = this.options.defaultAction;
1969        } else if (e.keyCode == Event.KEY_ESC) {
1970            data.action = 'cancel';
1971        }
1972        switch (data.action) {
1973            case 'ok':
1974                this.Close();
1975                if (this.options.callback !== Prototype.emptyFunction)
1976                    this.options.callback.apply(this, [e.element(), this.messagecontainer]);
1977                else {
1978                    //NOTE: issue with Firefox and .simulate() 
1979                    //                    Object.isElement(this.options.host) && this.options.host.simulate('click');
1980                    if (Object.isElement(this.options.host) && Object.isFunction(this.options.host.onclick))
1981                        this.options.host.onclick();
1982                }
1983                break;
1984            case 'cancel':
1985                this.Close();
1986                this.options.callback.apply(this, [false, this.messagecontainer]);
1987                break;
1988            case 'discard':
1989                this.Close();
1990                this.options.callback.apply(this, [undefined, this.messagecontainer]);
1991                break;
1992            default:
1993                return;
1994        }
1995    },
1996
1997    //Closes previous message and removes existing reference on it
1998    Remove: function () {
1999        // Hide stored message if exists
2000        if (this.options.host.__message)
2001            this.options.host.__message.Close();
2002    },
2003
2004    Calculate: function (value, max) {
2005
2006        //check width and height properties
2007        if (Object.isString(value) && value) {
2008            value = Math.ceil((String(value).indexOf('%') > 0 ? max / 100 : 1) * value.toNumber());
2009        }
2010        if (Object.isNumber(value))
2011            return value;
2012        return undefined;
2013    },
2014
2015
2016    //Maintain position of container
2017    Reposition: function () {
2018        this._overlay.setStyle('visibility:visible');
2019        var pos = {
2020            set: Object.isNumber(this.options.top) && Object.isNumber(this.options.left),
2021            top: String(this.options.left).toNumber(),
2022            left: String(this.options.top).toNumber()
2023        };
2024
2025        if (this.type == 'loading') {
2026            if (this.options.host) {
2027                pos.set = false;
2028                if (!this.options.host.getWidth()) {
2029                    this.container.hide();
2030                } else {
2031                    this.container.clonePosition(this.options.host, { offsetLeft: pos.left, offsetTop: pos.top });
2032                }
2033            }
2034        } else {
2035
2036            var dim = document.viewport.getDimensions();
2037            pos = {
2038                set: true,
2039                top: pos.set ? pos.top : Math.max(((dim.height - this.container.getHeight()) / 2), 0),
2040                left: pos.set ? pos.left : Math.max((dim.width - this.container.getWidth()) / 2, 0)
2041            };
2042        }
2043        if (pos.set)
2044            this.container.setStyle("top:#{top}px;left:#{left}px".interpolate(pos));
2045
2046        if (!(/loading|progress/gi.test(this.type))) {
2047            var dim = {
2048                containerHeight: this.container.measure('padding-box-height'),
2049                messagebodyHeight: this.messagebody.measure('padding-box-height'),
2050                maxHeight: $Screen.viewport.height * .75,
2051                maxWidth: $Screen.viewport.width * .75,
2052                headerHeight: this.header ? this.header.getHeight() : 0,
2053                footerHeight: this.messagecontrols ? this.messagecontrols.getHeight() : (this.options.radius ? 15 : 0),
2054            };
2055            this.messagebody.setStyle({
2056                height: Math.min((this.options.height ? (this.options.height - dim.headerHeight - dim.footerHeight) : dim.messagebodyHeight), dim.maxHeight) + 'px',
2057                marginBottom: dim.footerHeight + 'px'
2058            });
2059            this.messagecontainer.setStyle({
2060                height: Math.min((this.options.height ? (this.options.height - dim.headerHeight - dim.footerHeight - 5) : this.messagebody.clientHeight - 25), this.messagebody.clientHeight - 25) + 'px',
2061            });
2062        }
2063        return this;
2064    },
2065
2066    KeyDown: function (e) {
2067        if ((['alert', 'confirm', 'dialog'].include(this.type) && (e.keyCode == Event.KEY_RETURN || e.keyCode == Event.KEY_ESC)) ||
2068            (/progress/.test(this.type) && e.ctrlKey && e.keyCode == Event.KEY_BACKSPACE)) {
2069            Event.stop(e);
2070            this.DoAction(e);
2071        }
2072    },
2073
2074    AddEventHandlers: function () {
2075        this.events.push(Event.on(top, "resize", this.Reposition.bind(this)));
2076        if (this._overlay) {
2077            this.events.push(Event.on(document, "keydown", this.KeyDown.bindAsEventListener(this)));
2078            if (this.options.overlay) {
2079                this.events.push(Event.on(this._overlay, "mousedown", this.Deny.bindAsEventListener(this)));
2080                this.events.push(Event.on(this.container, "mousewheel", this.Deny.bindAsEventListener(this)));
2081                this.events.push(Event.on(document, "mousewheel", this.Deny.bindAsEventListener(this)));
2082            }
2083        }
2084        return this;
2085    },
2086    Deny: function (e) {
2087        if (e && e.element() === this._overlay) {
2088            e.stop();
2089            return false;
2090        }
2091    },
2092    RemoveEventHandlers: function () {
2093        $A(this.events).invoke('stop');
2094    }
2095});
2096
2097Utility.Drag = Class.create(Utility.Base, {
2098    initialize: function ($super, host, options) {
2099        if (!$super.apply(this, $A(arguments).slice(1)))
2100            return false;
2101        //host must be absolute/fix positioned
2102        this.options.handle.on('mousedown', this.Start.bindAsEventListener(this));
2103    },
2104
2105    Start: function (e) {
2106        this.left = this.host.offsetLeft;
2107        this.top = this.host.offsetTop;
2108        this.X = e.clientX;
2109        this.Y = e.clientY;
2110        this.host.addClassName('dragged');
2111        this.on(Event.on(document, 'mousemove', this.Drag.bindAsEventListener(this)));
2112        this.on(Event.on(document, 'mouseup', this.Release.bindAsEventListener(this)));
2113        return false;
2114    },
2115    Drag: function (e) {
2116
2117        this.Set(e.clientX - this.X, e.clientY - this.Y);
2118        return false;
2119    },
2120    Set: function (dx, dy) {
2121        /*
2122                var left = Math.min(Math.Max(this.left + dx, 0), $Screen.viewport.width);
2123                var y = Math.min(Math.Max(e.clientY - this.Y, 0), $Screen.viewport.height);
2124        */
2125
2126        this.host.setStyle({ left: (this.left + dx) + 'px', top: (this.top + dy) + 'px' });
2127    },
2128    Release: function () {
2129        this.host.removeClassName('dragged');
2130        this.off();
2131    }
2132});
2133
2134Utility.Device = Class.create({
2135    initialize: function () {
2136        this.isTouchDevice = 'ontouchstart' in window;
2137        this.EventName = function (opt) {
2138            return opt ? { 'click': 'click', 'start': 'touchstart', 'move': 'touchmove', 'end': 'touchend', 'resize': 'orientationchange' }
2139                    : { 'click': 'click', 'start': 'mousedown', 'move': 'mousemove', 'end': 'mouseup', 'resize': 'resize' };
2140        }(Boolean('ontouchstart' in window));
2141        Event.observe(window, this.EventName['resize'], this.Refresh.bindAsEventListener(this));
2142        this.viewport = this.GetViewPort();
2143    },
2144    GetViewPort: function () {
2145        var dim = document.viewport.getDimensions();
2146        if (!dim.width || !dim.height)
2147            dim = {
2148                width: window.innerWidth, //Math.max(Math.max(document.documentElement.scrollWidth, document.body.scrollWidth), document.documentElement.clientWidth),
2149                height: window.innerHeight //Math.max(Math.max(document.documentElement.scrollHeight, document.body.scrollHeight), document.documentElement.clientHeight)
2150            };
2151        return dim;
2152    },
2153    GetVisibleArea: function () {
2154        var offsets = document.viewport.getScrollOffsets();
2155        var dim = document.viewport.getDimensions();
2156        return {
2157            top: offsets.top,
2158            bottom: offsets.top + dim.height,
2159            left: offsets.left,
2160            right: offsets.left + dim.width
2161        };
2162    },
2163    IsFullyVisible: function (layout) {
2164        var d = this.GetVisibleArea();
2165        layout = Object.isElement() ? e.getLayout().toObject() : layout;
2166        var b = ((layout.top >= d.top) && ((layout.top + layout.height) <= d.bottom));
2167        return b;// || isAppearingBottom || isAppearingTop;
2168    },
2169    Refresh: function (e) {
2170        this.viewport = this.GetViewPort();
2171        document.fire('::refresh', this.viewport);
2172    }
2173});
2174
2175
2176///*  
2177//This function should be assigned in inline HTML code. In order to show custom Confirm 
2178//before real action (usually placed in  "onclick" event) we need to call this method on event earier, 
2179//like "onmousedown".
2180//Note: A javascript call used on "onmousedown" should ended with "return false;" to prevent event bubbling (call onclick)
2181//NOTE: In all shortcuts arguments can be a List of arguments or an object with specified properties
2182
2183//Example:
2184//<a href='javascript:__doPostBack([elementid], [argument])' onmousedown="javascript:ConfirmOnClick(this,{MESSAGE},{TITLE})"
2185//*/
2186
2187// Aliases 
2188//Redirection  or reloader
2189$redirect = Utility.Redirect;
2190$redirectWindow = Utility.RedirectNewWindow;
2191
2192//Image Loader
2193$ImageLoader = Utility.ImageLoader;
2194
2195$loadscript = Ajax.JSONRequest;
2196
2197//Creates a shortcuts for Alert
2198$alert = function (message, title, callback) {
2199    if (window.useStandart)
2200        return alert(message || o.options.message);
2201    else
2202        return new Utility.Message('alert', Object.extend({ radius: true, shadow: true, auto: 'Show' }, arguments.callee.argumentValues()));
2203};
2204
2205//Creates a shortcuts for INFO
2206$message = function (message, title, callback) {
2207    return $dialog(message, title, callback);
2208};
2209
2210//Creates a shortcuts for Confirm
2211$dialog = function (message, title, callback) {
2212    return new Utility.Message('dialog', Object.extend({ radius: true, shadow: true, isHTML: true, auto: 'Show' }, arguments.callee.argumentValues()));
2213};
2214
2215//Creates a shortcuts for Confirm
2216$confirm = function (message, title, callback) {
2217    return new Utility.Message('confirm', Object.extend({ radius: true, shadow: true, auto: 'Show' }, arguments.callee.argumentValues()));
2218};
2219
2220
2221//Creates a shortcuts for PROGRESS
2222$progress = function (message) {
2223    return new Utility.Message('progress', Object.extend({ radius: true, shadow: true, }, arguments.callee.argumentValues()));
2224};
2225
2226$loading = function (message) {
2227    return new Utility.Message('loading', Object.extend({ overlay: false }, arguments.callee.argumentValues()));
2228};
2229
2230$popupwindow = function (url, title, width, height) {
2231
2232    var prms = Object.isString(url) ? { url: url, title: title, width: width, height: height } : url;
2233
2234    var id = "$" + Object.guid();
2235    prms = Object.extend({
2236        message: '<iframe id="' + id + 'frm" src="' + prms.url + '" marginWidth="0" marginHeight="0" frameBorder="0" scrolling="auto" style="width:100%;height:100%" data-popup="' + id + '" />',
2237        radius: true,
2238        shadow: true,
2239        auto: 'Show'
2240    }, prms);
2241
2242    self[id] = new Utility.Message('popup', prms);
2243    self[id].frm = $p(id + 'frm');
2244
2245    if (Object.isElement(self[id].frm) && self[id].frm.contentWindow) {
2246        self[id].onHide = function () { try { this.frm.contentWindow.document.fire(':close'); } catch (e) { } }.bind(self[id]);
2247        self[id].frm.contentWindow.Close = function () { this.Close(); }.bind(self[id]);
2248    }
2249    return self[id];
2250};
2251
2252
2253$execute = function (host, method, parameters) {
2254    (self.event instanceof Event.Handler) && Event.stop(self.event);
2255    if (!Object.isElement(host)) {
2256        parameters = method, method = host, host = document.body;
2257    }
2258    new Utility.AjaxSelector(host, { method: method, parameters: parameters }).Call();
2259    return false;
2260};
2261
2262window.$isIOSDevice =  /(iPad|iPhone|iPod|Macintosh)/g.test(navigator.userAgent) && !(/(CriOS)/g.test(navigator.userAgent)) && navigator.maxTouchPoints;
2263
2264window.$Screen = window.$Screen || new Utility.Device();
2265
2266document.observe("dom:loaded", function () {
2267
2268
2269
2270    //Add event handler to a document to show a waiting message.
2271    //A wating message could be stored as window variable
2272    //To not show this progress message on each page postback we need to assign this varialble to empty string or null
2273    //window.onbeforeunload = function (e) { debugger; $progress(self.progressmessage); }
2274    //NOTE :: to temporary (one time) disable a progress message on page reload/unload just assign none-string value to self.progressmessage property
2275    //        usage::  OnClientClick="progressmessage={restore:progressmessage};" - to restore message, or -  progressmessage=false - restore 'Please wait...'
2276    //Create default progress message text. 
2277    //To disable  showing the Progress message window assigne this property to null/undefined
2278
2279    self.executeprogress = "Loading...";
2280    Object.isUndefined(self.progressmessage) && (self.progressmessage = $progress({ message: "Loading", auto: 'Create', keep: true }));
2281
2282    Event.observe(document.body, ':unload', function (e) {
2283        if (Object.isUndefined(self.progressmessage))
2284            return;
2285        else if (self.progressmessage instanceof Utility.Message) {
2286            if (self !== top && top.progressmessage instanceof Utility.Message && top.progressmessage.IsShown())
2287                return;
2288            // To hide a progress message on page reload after 20 sec
2289            // IMPORTANT for the cases where external app/page is used (like <a href="..." target="new|..-")
2290            self.progressmessage.Show(10);
2291
2292        } else {
2293            Object.isString(self.progressmessage) ? $progress(self.progressmessage) : (self.progressmessage = (self.progressmessage && self.progressmessage.restore || "Please wait..."));
2294        }
2295    });
2296
2297    Event.observe(window, 'beforeunload', function (e) {
2298        $p(document.body).fire(':unload');
2299    });
2300    //Create all tooltips
2301    !$Screen.isTouchDevice && Utility.Global.bubbletip && ($$('[title]', '[bubbletip]').each(function (it) { new Utility.BubbleTip(it); }));
2302    try {
2303        //Append style
2304        Utility.AddStyle();
2305    }
2306    catch (ex) {
2307        $alert({ title: 'Load error', message: ex.message });
2308    }
2309});
2310

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.