").attr({id:i,role:"tooltip"}).addClass("ui-tooltip ui-widget ui-corner-all ui-widget-content "+(this.options.tooltipClass||""));return t("
").addClass("ui-tooltip-content").appendTo(n),n.appendTo(this.document[0].body),this.tooltips[i]=e,n},_find:function(e){var i=e.data("ui-tooltip-id");return i?t("#"+i):t()},_removeTooltip:function(t){t.remove(),delete this.tooltips[t.attr("id")]},_destroy:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event("blur");n.target=n.currentTarget=s[0],e.close(n,!0),t("#"+i).remove(),s.data("ui-tooltip-title")&&(s.attr("title",s.data("ui-tooltip-title")),s.removeData("ui-tooltip-title"))})}})})(jQuery);
/* /jquery-ui/jquery-ui.min.js end */
/* /js/tmpl.min.js begin */
!function(a){"use strict";var b=function(a,c){var d=/[^\w\-\.:]/.test(a)?new Function(b.arg+",tmpl","var _e=tmpl.encode"+b.helper+",_s='"+a.replace(b.regexp,b.func)+"';return _s;"):b.cache[a]=b.cache[a]||b(b.load(a));return c?d(c,b):function(a){return d(a,b)}};b.cache={},b.load=function(a){return document.getElementById(a).innerHTML},b.regexp=/([\s'\\])(?!(?:[^{]|\{(?!%))*%\})|(?:\{%(=|#)([\s\S]+?)%\})|(\{%)|(%\})/g,b.func=function(a,b,c,d,e,f){return b?{"\n":"\\n","\r":"\\r"," ":"\\t"," ":" "}[b]||"\\"+b:c?"="===c?"'+_e("+d+")+'":"'+("+d+"==null?'':"+d+")+'":e?"';":f?"_s+='":void 0},b.encReg=/[<>&"'\x00]/g,b.encMap={"<":"<",">":">","&":"&",'"':""","'":"'"},b.encode=function(a){return(null==a?"":""+a).replace(b.encReg,function(a){return b.encMap[a]||""})},b.arg="o",b.helper=",print=function(s,e){_s+=e?(s==null?'':s):_e(s);},include=function(s,d){_s+=tmpl(s,d);}","function"==typeof define&&define.amd?define(function(){return b}):a.tmpl=b}(this);
/* /js/tmpl.min.js end */
/* /jquery-ui/i18n/datepicker-ru.js begin */
/* Russian (UTF-8) initialisation for the jQuery UI date picker plugin. */
/* Written by Andrew Stromnov (stromnov@gmail.com). */
(function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define([ "../datepicker" ], factory );
} else {
// Browser globals
factory( jQuery.datepicker );
}
}(function( datepicker ) {
datepicker.regional['ru'] = {
closeText: 'Закрыть',
prevText: '<Пред',
nextText: 'След>',
currentText: 'Сегодня',
monthNames: ['январь','февраль','март','апрель','май','июнь','июль','август','сентябрь','октябрь','ноябрь','декабрь'],
monthNamesGenitive: ['января','февраля','марта','апреля','мая','июня','июля','августа','сентября','октября','ноября','декабря'],
monthNamesShort: ['янв','фев','мар','апр','мая','июн','июл','авг','сен','окт','ноя','дек'],
dayNames: ['воскресенье','понедельник','вторник','среда','четверг','пятница','суббота'],
dayNamesShort: ['вск','пнд','втр','срд','чтв','птн','сбт'],
dayNamesMin: ['Вс','Пн','Вт','Ср','Чт','Пт','Сб'],
weekHeader: 'Нед',
dateFormat: 'dd.mm.yy',
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: ''};
datepicker.setDefaults(datepicker.regional['ru']);
return datepicker.regional['ru'];
}));
/* /jquery-ui/i18n/datepicker-ru.js end */
/* /js/star-rating/jquery.rating.js begin */
/*
### jQuery Star Rating Plugin v4.10 - 2013-03-12 ###
* Home: http://www.fyneworks.com/jquery/star-rating/
* Code: http://code.google.com/p/jquery-star-rating-plugin/
*
* Licensed under http://en.wikipedia.org/wiki/MIT_License
###
*/
/*# AVOID COLLISIONS #*/
;if(window.jQuery) (function($){
/*# AVOID COLLISIONS #*/
// IE6 Background Image Fix
if ((!$.support.opacity && !$.support.style)) try { document.execCommand("BackgroundImageCache", false, true)} catch(e) { };
// Thanks to http://www.visualjquery.com/rating/rating_redux.html
// plugin initialization
$.fn.rating = function(options){
if(this.length==0) return this; // quick fail
// Handle API methods
if(typeof arguments[0]=='string'){
// Perform API methods on individual elements
if(this.length>1){
var args = arguments;
return this.each(function(){
$.fn.rating.apply($(this), args);
});
};
// Invoke API method handler
$.fn.rating[arguments[0]].apply(this, $.makeArray(arguments).slice(1) || []);
// Quick exit...
return this;
};
// Initialize options for this call
var options = $.extend(
{}/* new object */,
$.fn.rating.options/* default options */,
options || {} /* just-in-time options */
);
// Allow multiple controls with the same name by making each call unique
$.fn.rating.calls++;
// loop through each matched element
this
.not('.star-rating-applied')
.addClass('star-rating-applied')
.each(function(){
// Load control parameters / find context / etc
var control, input = $(this);
var eid = (this.name || 'unnamed-rating').replace(/\[|\]/g, '_').replace(/^\_+|\_+$/g,'');
var context = $(this.form || document.body);
// FIX: http://code.google.com/p/jquery-star-rating-plugin/issues/detail?id=23
var raters = context.data('rating');
if(!raters || raters.call!=$.fn.rating.calls) raters = { count:0, call:$.fn.rating.calls };
var rater = raters[eid] || context.data('rating'+eid);
// if rater is available, verify that the control still exists
if(rater) control = rater.data('rating');
if(rater && control)//{// save a byte!
// add star to control if rater is available and the same control still exists
control.count++;
//}// save a byte!
else{
// create new control if first star or control element was removed/replaced
// Initialize options for this rater
control = $.extend(
{}/* new object */,
options || {} /* current call options */,
($.metadata? input.metadata(): ($.meta?input.data():null)) || {}, /* metadata options */
{ count:0, stars: [], inputs: [] }
);
// increment number of rating controls
control.serial = raters.count++;
// create rating element
rater = $('
');
input.before(rater);
// Mark element for initialization (once all stars are ready)
rater.addClass('rating-to-be-drawn');
// Accept readOnly setting from 'disabled' property
if(input.attr('disabled') || input.hasClass('disabled')) control.readOnly = true;
// Accept required setting from class property (class='required')
if(input.hasClass('required')) control.required = true;
// Create 'cancel' button
rater.append(
control.cancel = $('
')
.on('mouseover',function(){
$(this).rating('drain');
$(this).addClass('star-rating-hover');
//$(this).rating('focus');
})
.on('mouseout',function(){
$(this).rating('draw');
$(this).removeClass('star-rating-hover');
//$(this).rating('blur');
})
.on('click',function(){
$(this).rating('select');
})
.data('rating', control)
);
}; // first element of group
// insert rating star (thanks Jan Fanslau rev125 for blind support https://code.google.com/p/jquery-star-rating-plugin/issues/detail?id=125)
var star = $('
');
rater.append(star);
// inherit attributes from input element
if(this.id) star.attr('id', this.id);
if(this.className) star.addClass(this.className);
// Half-stars?
if(control.half) control.split = 2;
// Prepare division control
if(typeof control.split=='number' && control.split>0){
var stw = ($.fn.width ? star.width() : 0) || control.starWidth;
var spi = (control.count % control.split), spw = Math.floor(stw/control.split);
star
// restrict star's width and hide overflow (already in CSS)
.width(spw)
// move the star left by using a negative margin
// this is work-around to IE's stupid box model (position:relative doesn't work)
.find('a').css({ 'margin-left':'-'+ (spi*spw) +'px' })
};
// readOnly?
if(control.readOnly)//{ //save a byte!
// Mark star as readOnly so user can customize display
star.addClass('star-rating-readonly');
//} //save a byte!
else//{ //save a byte!
// Enable hover css effects
star.addClass('star-rating-live')
// Attach mouse events
.on('mouseover',function(){
$(this).rating('fill');
$(this).rating('focus');
})
.on('mouseout',function(){
$(this).rating('draw');
$(this).rating('blur');
})
.on('click',function(){
$(this).rating('select');
})
;
//}; //save a byte!
// set current selection
if(this.checked) control.current = star;
// set current select for links
if(this.nodeName=="A"){
if($(this).hasClass('selected'))
control.current = star;
};
// hide input element
input.hide();
// backward compatibility, form element to plugin
input.on('change.rating',function(event){
if(event.selfTriggered) return false;
$(this).rating('select');
});
// attach reference to star to input element and vice-versa
star.data('rating.input', input.data('rating.star', star));
// store control information in form (or body when form not available)
control.stars[control.stars.length] = star[0];
control.inputs[control.inputs.length] = input[0];
control.rater = raters[eid] = rater;
control.context = context;
input.data('rating', control);
rater.data('rating', control);
star.data('rating', control);
context.data('rating', raters);
context.data('rating'+eid, rater); // required for ajax forms
}); // each element
// Initialize ratings (first draw)
$('.rating-to-be-drawn').rating('draw').removeClass('rating-to-be-drawn');
return this; // don't break the chain...
};
/*--------------------------------------------------------*/
/*
### Core functionality and API ###
*/
$.extend($.fn.rating, {
// Used to append a unique serial number to internal control ID
// each time the plugin is invoked so same name controls can co-exist
calls: 0,
focus: function(){
var control = this.data('rating'); if(!control) return this;
if(!control.focus) return this; // quick fail if not required
// find data for event
var input = $(this).data('rating.input') || $( this.tagName=='INPUT' ? this : null );
// focus handler, as requested by focusdigital.co.uk
if(control.focus) control.focus.apply(input[0], [input.val(), $('a', input.data('rating.star'))[0]]);
}, // $.fn.rating.focus
blur: function(){
var control = this.data('rating'); if(!control) return this;
if(!control.blur) return this; // quick fail if not required
// find data for event
var input = $(this).data('rating.input') || $( this.tagName=='INPUT' ? this : null );
// blur handler, as requested by focusdigital.co.uk
if(control.blur) control.blur.apply(input[0], [input.val(), $('a', input.data('rating.star'))[0]]);
}, // $.fn.rating.blur
fill: function(){ // fill to the current mouse position.
var control = this.data('rating'); if(!control) return this;
// do not execute when control is in read-only mode
if(control.readOnly) return;
// Reset all stars and highlight them up to this element
this.rating('drain');
this.prevAll().addBack().filter('.rater-'+ control.serial).addClass('star-rating-hover');
},// $.fn.rating.fill
drain: function() { // drain all the stars.
var control = this.data('rating'); if(!control) return this;
// do not execute when control is in read-only mode
if(control.readOnly) return;
// Reset all stars
control.rater.children().filter('.rater-'+ control.serial).removeClass('star-rating-on').removeClass('star-rating-hover');
},// $.fn.rating.drain
draw: function(){ // set value and stars to reflect current selection
var control = this.data('rating'); if(!control) return this;
// Clear all stars
this.rating('drain');
// Set control value
var current = $( control.current );//? control.current.data('rating.input') : null );
var starson = current.length ? current.prevAll().addBack().filter('.rater-'+ control.serial) : null;
if(starson) starson.addClass('star-rating-on');
// Show/hide 'cancel' button
control.cancel[control.readOnly || control.required?'hide':'show']();
// Add/remove read-only classes to remove hand pointer
this.siblings()[control.readOnly?'addClass':'removeClass']('star-rating-readonly');
},// $.fn.rating.draw
select: function(value,wantCallBack){ // select a value
var control = this.data('rating'); if(!control) return this;
// do not execute when control is in read-only mode
if(control.readOnly) return;
// clear selection
control.current = null;
// programmatically (based on user input)
if(typeof value!='undefined' || this.length>1){
// select by index (0 based)
if(typeof value=='number')
return $(control.stars[value]).rating('select',undefined,wantCallBack);
// select by literal value (must be passed as a string
if(typeof value=='string'){
//return
$.each(control.stars, function(){
//console.log($(this).data('rating.input'), $(this).data('rating.input').val(), value, $(this).data('rating.input').val()==value?'BINGO!':'');
if($(this).data('rating.input').val()==value) $(this).rating('select',undefined,wantCallBack);
});
// don't break the chain
return this;
};
}
else{
control.current = this[0].tagName=='INPUT' ?
this.data('rating.star') :
(this.is('.rater-'+ control.serial) ? this : null);
};
// Update rating control state
this.data('rating', control);
// Update display
this.rating('draw');
// find current input and its sibblings
var current = $( control.current ? control.current.data('rating.input') : null );
var lastipt = $( control.inputs ).filter(':checked');
var deadipt = $( control.inputs ).not(current);
// check and uncheck elements as required
deadipt.prop('checked',false);//.removeAttr('checked');
current.prop('checked',true);//.attr('checked','checked');
// trigger change on current or last selected input
$(current.length? current : lastipt ).trigger({ type:'change', selfTriggered:true });
// click callback, as requested here: http://plugins.jquery.com/node/1655
if((wantCallBack ||wantCallBack == undefined) && control.callback) control.callback.apply(input[0], [input.val(), $('a', control.current)[0]]);// callback event
// don't break the chain
return this;
},// $.fn.rating.select
readOnly: function(toggle, disable){ // make the control read-only (still submits value)
var control = this.data('rating'); if(!control) return this;
// setread-only status
control.readOnly = toggle || toggle==undefined ? true : false;
// enable/disable control value submission
if(disable) $(control.inputs).attr("disabled", "disabled");
else $(control.inputs).removeAttr("disabled");
// Update rating control state
this.data('rating', control);
// Update display
this.rating('draw');
},// $.fn.rating.readOnly
disable: function(){ // make read-only and never submit value
this.rating('readOnly', true, true);
},// $.fn.rating.disable
enable: function(){ // make read/write and submit value
this.rating('readOnly', false, false);
}// $.fn.rating.select
});
/*--------------------------------------------------------*/
/*
### Default Settings ###
eg.: You can override default control like this:
$.fn.rating.options.cancel = 'Clear';
*/
$.fn.rating.options = { //$.extend($.fn.rating, { options: {
cancel: 'Cancel Rating', // advisory title for the 'cancel' link
cancelValue: '', // value to submit when user click the 'cancel' link
split: 0, // split the star into how many parts?
// Width of star image in case the plugin can't work it out. This can happen if
// the jQuery.dimensions plugin is not available OR the image is hidden at installation
starWidth: 16//,
//NB.: These don't need to be pre-defined (can be undefined/null) so let's save some code!
//half: false, // just a shortcut to control.split = 2
//required: false, // disables the 'cancel' button so user can only select one of the specified values
//readOnly: false, // disable rating plugin interaction/ values cannot be.one('change', //focus: function(){}, // executed when stars are focused
//blur: function(){}, // executed when stars are focused
//callback: function(){}, // executed when a star is clicked
}; //} });
/*--------------------------------------------------------*/
// auto-initialize plugin
$(function(){
$('input[type=radio].star').rating();
});
/*# AVOID COLLISIONS #*/
})(jQuery);
/*# AVOID COLLISIONS #*/
/* /js/star-rating/jquery.rating.js end */
/* /ubb/bookingWidgetTop.js begin */
String.prototype.ucFirst = function() {
var str = this;
if(str.length) {
str = str.charAt(0).toUpperCase() + str.slice(1);
}
return str;
};
var bookingWidget = {
iata : null,
destination: null,
isClosed: true,
initedElements: {
ticket: false,
hotel: false,
car: false,
insurance: false,
tour: false
},
current: null,
loading: false,
levelTravelCountriesIso: [],
useLevelTravel: false,
staticServer: '',
isFixed: false,
castInt: function(value, min){
value = parseInt(value);
if (isNaN(value) || value < min) value = min;
return value;
},
init : function(type) {
var _this = this;
_this.initDefaultValues();
_this.initVisual();
_this.setActive(type);
_this.initButtons();
var max = $('#ubbCnt').offset().top;
$(window).scroll(function(){
_this.fixed(max);
});
},
fixed: function(max){
var top = $(document).scrollTop();
if (top >= max) {
$('#ubbCnt').addClass('fixed').fadeIn();
this.isFixed = true;
} else {
$('#ubbCnt').removeClass('fixed').removeAttr('style');
this.isFixed = false;
}
},
initDefaultValues: function() {
$(function(){
if (typeof window.JSVARS == 'object') {
var jsvars = window.JSVARS;
if (jsvars['ubb_ticket_dest_iata'] && jsvars['ubb_ticket_dest_title']) {
$('#ubb_ticket_AirportDestination').val(jsvars['ubb_ticket_dest_title']);
$('#ubb_ticket_AirportDestinationHidden').val(jsvars['ubb_ticket_dest_iata']);
}
}
})
},
initVisual: function() {
$('#ubb').show();
},
initButtons : function() {
var _this = this;
$('#ubb_tabs .tab').bind('click', function() {
var id = $(this).attr('id');
var tmp = id.split('_');
id = tmp[1];
if(_this.current != id) {
_this.setActive(id);
}
});
},
initWidget : function(type) {
var method = 'init'+ type.ucFirst();
if(typeof this[method] == 'function') {
this.current = type;
return this[method]();
} else {
return false;
}
},
setActive : function(type) {
$('#ubb_tabs .tab.act').removeClass('act');
$('#ubbTab_'+ type).addClass('act');
$(".ubb_form").not(':hidden').toggleClass('hidden').removeClass('act');
$('#ubbForm_' + type).toggleClass('hidden').addClass('act');
this.initWidget(type);
},
initTicket : function() {
if(this.initedElements[this.current]) return true;
var _this = this;
if(this.destination) {
// requesting IATA data for inputs
$.ajax({
url: '/ajax/ubb/ticket/iataDataFor/'+ _this.destination._id,
success: function(data, textStatus, jqXHR) {
if(data.result && data.result.title && data.result.IATA && data.result.IATA.length) {
$('#ubb_ticket_AirportDestination').val(data.result.title);
$('#ubb_ticket_AirportDestinationHidden').val(data.result.IATA);
}
},
error: function(jqXHR, textStatus, errorThrown) {
//some error occured
}
});
}
var dest = { title: '', iata: '' };
$("#ubb_ticket_AirportDeparture, #ubb_ticket_AirportDestination").each(function() {
var selector = '#'+ $(this).attr('id');
var selectorHidden = selector +'Hidden';
$(this).autocomplete({
appendTo: $(this).data('autocomplete'),
source: function( request, response ) {
$.ajax({
url: "/ajax/tickets/search",
dataType: "json",
data: {
query: request.term
},
success: function( data ) {
response( $.map( data.result, function( item ) {
return {
label: item.title,
value: item.iata
}
}));
$(selector).removeClass("notact");
}
});
},
minLength: 2,
select: function( event, ui ) {
dest.title = ui.item.label;
dest.iata = ui.item.value;
$(selectorHidden).val(ui.item.value);
$(selector).val(ui.item.label);
$(selector).parent().removeClass('ym-error');
return false;
},
open: function() {
$(this).autocomplete('widget').css('z-index', 999);
},
close: function() {
$(selectorHidden).val(dest.iata);
if(dest.title.length > 0)
$(selector).val(dest.title);
if (dest.iata == '')
$(selector).addClass("notact");
else
$(selector).removeClass("notact");
}
});
});
var dd = $('#ubb_ticket_DepartureDate'),
rd = $('#ubb_ticket_ReturnDate');
$.datepicker.setDefaults( $.datepicker.regional[ "ru" ] );
var datepickerRange = {
startDate: (dd.val() ? new Date(dd.val().replace(/(\d+).(\d+).(\d+)/, '$3/$2/$1')) : null),
endDate: (rd.val() ? new Date(rd.val().replace(/(\d+).(\d+).(\d+)/, '$3/$2/$1')) : null),
currentDate: new Date(),
selectCount: 0,
inp: $('#ubb_ticket_when'),
getSelectedDate: function(inst){
return new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay);
},
getFormatDatePeriod: function(){
var t = this,
currYear = new Date().getFullYear();
if(t.startDate != null && t.endDate != null){
if(t.startDate.getMonth() == t.endDate.getMonth()){
if(t.startDate.getFullYear() == t.endDate.getFullYear() && t.startDate.getFullYear() == currYear)
return t.startDate.getDate() + ' - ' + $.datepicker.formatDate("d MM", t.endDate, { monthNames: $.datepicker.regional[ "ru" ].monthNamesGenitive } );
else
return t.startDate.getDate() + ' - ' + $.datepicker.formatDate("d MM yy", t.endDate, { monthNames: $.datepicker.regional[ "ru" ].monthNamesGenitive });
}else if(t.startDate.getFullYear() == t.endDate.getFullYear() && t.startDate.getFullYear() == currYear){
return $.datepicker.formatDate("d MM", t.startDate, { monthNames: $.datepicker.regional[ "ru" ].monthNamesGenitive }) + ' - ' + $.datepicker.formatDate("d MM", t.endDate, { monthNames: $.datepicker.regional[ "ru" ].monthNamesGenitive });
}else{
return $.datepicker.formatDate("d M yy", t.startDate) + ' - ' + $.datepicker.formatDate("d M yy", t.endDate);
}
}else if(t.startDate != null){
if(t.startDate.getFullYear() == currYear)
return $.datepicker.formatDate("d MM", t.startDate, { monthNames: $.datepicker.regional[ "ru" ].monthNamesGenitive });
else
return $.datepicker.formatDate("d MM yy", t.startDate, { monthNames: $.datepicker.regional[ "ru" ].monthNamesGenitive });
}
return;
},
setDatepickerHeader: function(){
setTimeout(function () {
var departDate = 'Вылет: ' + $.datepicker.formatDate("d M yy", datepickerRange.startDate);
var returnDate = datepickerRange.endDate != null ? '
' + $.datepicker.formatDate("d M yy", datepickerRange.endDate) : '
- ';
var header = $('#ubbTicketDates');
if(header.size() > 0){
header.html(departDate + returnDate);
}else{
var datepickerHeader = $(this).datepicker("widget").find('.ui-datepicker-group-first');
datepickerHeader.before('
' + departDate + returnDate +'
');
}
}, 3);
},
setDates: function(){
var t = this;
t.inp.val(datepickerRange.getFormatDatePeriod());
dd.val($.datepicker.formatDate("dd.mm.yy", t.startDate));
rd.val( t.endDate != null ? $.datepicker.formatDate("dd.mm.yy", t.endDate) : '');
}
};
$('#ubb_ticket_when').datepicker({
'dateFormat': 'dd.mm.yy',
'defaultDate': new Date(),
'minDate': new Date(),
'showOn': 'both',
'buttonImage': '/ubb/i_calendar.png',
'buttonImageOnly': true,
'numberOfMonths': 2,
'selectOtherMonths': true,
beforeShow: function(input, inst){
var cal = inst.dpDiv;
var top = (bookingWidget.isFixed) ? '38px' : $(this).offset().top,
left = $(this).offset().left,
right = 0;
var w = $(window).width();
if(w < 1252){
right = Math.round(w - $(input).offset().left - $(input).parent().width());
}
datepickerRange.setDatepickerHeader();
setTimeout(function () {
if(right > 0)
cal.css({
'top' : top,
'right': right
}).css('left', '');
else if(bookingWidget.isFixed)
cal.css({
'top' : top
});
else
cal.css({
'top' : top,
'left': left
}).css('right', '');
}, 3);
},
onSelect: function(dateText, inst) {
datepickerRange.selectCount++;
datepickerRange.currentDate = datepickerRange.getSelectedDate(inst);
if(datepickerRange.selectCount < 2){
datepickerRange.startDate = datepickerRange.getSelectedDate(inst);
datepickerRange.endDate = null;
inst.inline = $('#ubb_ticket_oneway').prop('checked') ? true : false;
}else if(datepickerRange.currentDate.getTime() == datepickerRange.startDate.getTime()){
return false;
}else{
datepickerRange.selectCount = 0;
datepickerRange.endDate = datepickerRange.getSelectedDate(inst);
if(datepickerRange.startDate.getTime() > datepickerRange.endDate.getTime()){
datepickerRange.endDate = datepickerRange.startDate;
datepickerRange.startDate = datepickerRange.currentDate;
}
inst.inline = false;
}
datepickerRange.setDatepickerHeader();
},
onChangeMonthYear: function(year, month, inst) {
datepickerRange.currentDate = datepickerRange.getSelectedDate(inst);
datepickerRange.setDatepickerHeader();
},
onClose: function(dateText, inst) {
if(inst.inline){
inst.inline = false;
}
datepickerRange.setDates();
}
});
$('#ubb_ticket_when').datepicker('option', 'beforeShowDay', function (currDate) {
if(datepickerRange.startDate && datepickerRange.endDate && currDate.getTime() >= datepickerRange.startDate.getTime() && currDate.getTime() <= datepickerRange.endDate.getTime()){
return [true, "ui-state-active", ""]; // ubb-period_act
} else if(datepickerRange.startDate && currDate.getTime() == datepickerRange.startDate.getTime()){
return [true, "ui-state-active", ""];
} else
return [true, "", ""];
});
$(document).on('mouseover mouseenter', '#ui-datepicker-div *[data-handler = "selectDay"] a.ui-state-default', function(){
var t = $(this),
td = t.parent(),
currDate = new Date(parseInt(td.data("year")), parseInt(td.data("month")), parseInt(t.html())),
startDate = datepickerRange.startDate;
if(startDate && currDate && !datepickerRange.endDate && !oneway){
$('#ui-datepicker-div *[data-handler = "selectDay"]:not(".ui-datepicker-current-day") a.ui-state-default').each(function(el, pos){
var t = $(this),
td = t.parent();
var d = new Date(parseInt(td.data("year")), parseInt(td.data("month")), parseInt(t.html()));
if((d > startDate && d < currDate) || (d < startDate && d > currDate))
t.removeClass('ui-state-hover').addClass('ui-state-active');
else
t.removeClass('ui-state-active');
});
}
});
var oneway = false;
$(document).on('click','#ubb_ticket_oneway', function() {
if($(this).is(":checked")) {
var cookieRd = $.cookie('ubb_ticket_rd');
if(cookieRd != '' && cookieRd != undefined){
datepickerRange.endDate = new Date(cookieRd);
}
oneway = false;
} else {
$.cookie('ubb_ticket_rd', $.datepicker.formatDate("yy-mm-dd", datepickerRange.endDate));
datepickerRange.endDate = null;
oneway = true;
}
$( "#ubb_ticket_when" ).datepicker( "refresh" );
datepickerRange.setDatepickerHeader();
datepickerRange.setDates();
});
$(window).scroll(function(){
$('#ubb_ticket_when').datepicker('hide');
$('#ubb_ticket_when').blur();
});
$(window).resize(function(){
$('#ubb_ticket_when').datepicker('hide');
$('#ubb_ticket_when').blur();
});
$('#ubb_ticket_when').val(datepickerRange.getFormatDatePeriod());
$("#ubb_ticket_SearchButton").click(function(){
var inps = [$('#ubb_ticket_AirportDeparture'),$('#ubb_ticket_AirportDestination')],
error = false;
$(inps).each(function(){
var t = $(this);
if(t.val() == ''){
t.parent().addClass('ym-error');
error = true;
}
});
if(error) return false;
var family = $.cookie('family');
var urlParams = {
origin_iata : $("#ubb_ticket_AirportDepartureHidden").val(),
destination_iata : $("#ubb_ticket_AirportDestinationHidden").val(),
depart_date : $("#ubb_ticket_DepartureDate").val(),
adults : 1,
children : 0,
infants : 0,
trip_class : 0
};
if(family !== undefined && family !== '') {
family = JSON.parse(family);
if(family.adults !== undefined) {
urlParams.adults = family.adults;
}
if(family.kids_ages.length) {
family.kids_ages.forEach(function(el, index, kids_ages){
if (el < 12 && el >= 2) {
urlParams.infants++;
} else if(el < 2){
urlParams.children++;
} else {
urlParams.adults++;
}
});
}
}
var return_date = $("#ubb_ticket_ReturnDate").val();
if (return_date != ''){
urlParams["return_date"] = return_date;
} else {
urlParams["oneway"] = 1;
}
var url = "http://aviasales.redigo.ru/searches/new?marker=13909";
if ($("#ubb_ticket_Referer").length)
url += "." + $("#ubb_ticket_Referer").val();
url += "&with_request=true";
for(var i in urlParams) url += "&" + i + "=" + urlParams[i];
if (_gaq) _gaq.push(['_trackEvent', 'BookingBlockAvia', 'SearchAviaDealButton']);
window.open(url, "_blank");
});
this.initedElements[this.current] = true;
return true;
},
initHotel : function() {
if(this.initedElements[this.current]) return true;
var _this = this;
var destId = _this.destination ? _this.destination._id : 0;
var onlyOneItem = false;
var selectedItem = null;
$('#ubb_hotel_adults .adult').rating();
$.ajax({
url: '/ajax/ubb/hotel/destinationData/'+ destId,
success: function(data, textStatus, jqXHR) {
if(data.result && data.result.destination) {
var obj = data.result.destination;
$('#ubb_hotel_Destination').val(obj.label);
$('#ubb_hotel_DestinationHidden').val(obj.search);
if(data.result.destination.title_genetive.length > 0)
$('#ubb_hotel_DestinationPreposition').html(data.result.destination.title_genetive);
selectedItem = obj;
}
},
error: function(jqXHR, textStatus, errorThrown) {
//some error occured
}
});
var checkin = $('#ubb_hotel_CheckinDate'),
checkout = $('#ubb_hotel_CheckoutDate');
var datepickerRange = {
startDate: (checkin.val() ? new Date(checkin.val().replace(/(\d+).(\d+).(\d+)/, '$3/$2/$1')) : null),
endDate: (checkout.val() ? new Date(checkout.val().replace(/(\d+).(\d+).(\d+)/, '$3/$2/$1')) : null),
currentDate: new Date(),
selectCount: 0,
inp: $('#ubb_hotel_when'),
getSelectedDate: function(inst){
return new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay);
},
getFormatDatePeriod: function(){
var t = this,
currYear = new Date().getFullYear();
if(t.startDate != null && t.endDate != null){
if(t.startDate.getMonth() == t.endDate.getMonth()){
if(t.startDate.getFullYear() == t.endDate.getFullYear() && t.startDate.getFullYear() == currYear){
return t.startDate.getDate() + ' - ' + $.datepicker.formatDate("d MM", t.endDate, { monthNames: $.datepicker.regional[ "ru" ].monthNamesGenitive });
}else{
return t.startDate.getDate() + ' - ' + $.datepicker.formatDate("d MM yy", t.endDate, { monthNames: $.datepicker.regional[ "ru" ].monthNamesGenitive });
}
}else if(t.startDate.getFullYear() == t.endDate.getFullYear() && t.startDate.getFullYear() == currYear){
return $.datepicker.formatDate("d MM", t.startDate, { monthNames: $.datepicker.regional[ "ru" ].monthNamesGenitive }) + ' - ' + $.datepicker.formatDate("d MM", t.endDate, { monthNames: $.datepicker.regional[ "ru" ].monthNamesGenitive });
}else{
return $.datepicker.formatDate("d M yy", t.startDate) + ' - ' + $.datepicker.formatDate("d M yy", t.endDate);
}
}
return;
},
setDatepickerHeader: function(){
setTimeout(function () {
var datepickerHeader = $(this).datepicker("widget").find('.ui-datepicker-group-first');
var departDate = 'Заезд: ' + (datepickerRange.startDate != null ? $.datepicker.formatDate("d M yy", datepickerRange.startDate) : ' - ');
var returnDate = '
Отъезд: ' + (datepickerRange.endDate != null ? $.datepicker.formatDate("d M yy", datepickerRange.endDate) : ' - ') + '';
datepickerHeader.before('
' + departDate + returnDate +'
');
}, 3);
},
setDates: function(){
var t = this;
t.inp.val(datepickerRange.getFormatDatePeriod());
checkin.val($.datepicker.formatDate("dd", t.startDate) + ',' + $.datepicker.formatDate("mm_yy", t.startDate));
checkout.val($.datepicker.formatDate("dd", t.endDate) + ',' + $.datepicker.formatDate("mm_yy", t.endDate));
}
};
$('#ubb_hotel_when').datepicker({
'dateFormat': 'dd.mm.yy',
'defaultDate': new Date(),
'minDate': new Date(),
'showOn': 'both',
'buttonImage': '/ubb/i_calendar.png',
'buttonImageOnly': true,
'numberOfMonths': 2,
'selectOtherMonths': true,
beforeShow: function(input, inst){
var cal = inst.dpDiv;
var top = (bookingWidget.isFixed) ? '38px' : $(this).offset().top,
left = $(this).offset().left,
right = 0;
var w = $(window).width();
if(w < 1252){
right = Math.round(w - $(input).offset().left - $(input).parent().width());
}
datepickerRange.setDatepickerHeader();
setTimeout(function () {
if(right > 0)
cal.css({
'top' : top,
'right': right
}).css('left', '');
else if(bookingWidget.isFixed)
cal.css({
'top' : top
});
else
cal.css({
'top' : top,
'left': left
}).css('right', '');
}, 3);
},
onSelect: function(dateText, inst) {
datepickerRange.selectCount++;
datepickerRange.currentDate = datepickerRange.getSelectedDate(inst);
if(datepickerRange.selectCount < 2){
datepickerRange.startDate = datepickerRange.getSelectedDate(inst);
datepickerRange.endDate = null;
inst.inline = true;
}else if(datepickerRange.currentDate.getTime() == datepickerRange.startDate.getTime()){
return false;
}else{
datepickerRange.selectCount = 0;
datepickerRange.endDate = datepickerRange.getSelectedDate(inst);
if(datepickerRange.startDate.getTime() > datepickerRange.endDate.getTime()){
datepickerRange.endDate = datepickerRange.startDate;
datepickerRange.startDate = datepickerRange.currentDate;
}
inst.inline = false;
}
datepickerRange.setDates();
datepickerRange.setDatepickerHeader();
},
onChangeMonthYear: function(year, month, inst) {
datepickerRange.currentDate = datepickerRange.getSelectedDate(inst);
datepickerRange.setDatepickerHeader();
},
onClose: function(dateText, inst) {
if(inst.inline){
inst.inline = false;
}
$('#ubb_hotel_when_div').removeClass('ym-error');
}
});
$('#ubb_hotel_when').datepicker('option', 'beforeShowDay', function (currDate) {
if(datepickerRange.startDate && datepickerRange.endDate && currDate.getTime() >= datepickerRange.startDate.getTime() && currDate.getTime() <= datepickerRange.endDate.getTime()){
return [true, "ui-state-active", ""]; // ubb-period_act
} else
return [true, "", ""];
});
$(document).on('mouseover mouseenter', '#ui-datepicker-div *[data-handler = "selectDay"] a.ui-state-default', function(){
var t = $(this),
td = t.parent(),
currDate = new Date(parseInt(td.data("year")), parseInt(td.data("month")), parseInt(t.html())),
startDate = datepickerRange.startDate;
if(startDate && currDate && !datepickerRange.endDate){
$('#ui-datepicker-div *[data-handler = "selectDay"]:not(".ui-datepicker-current-day") a.ui-state-default').each(function(el, pos){
var t = $(this),
td = t.parent();
var d = new Date(parseInt(td.data("year")), parseInt(td.data("month")), parseInt(t.html()));
if((d > startDate && d < currDate) || (d < startDate && d > currDate))
t.removeClass('ui-state-hover').addClass('ui-state-active');
else
t.removeClass('ui-state-active');
});
}
});
$('#ubb_hotel_when').val(datepickerRange.getFormatDatePeriod());
$("#ubb_hotel_Destination").autocomplete({
source: function( request, response ) {
selectedItem = null;
onlyOneItem = false;
$.ajax({
url: '/ajax/ubb/hotel/geo/suggest',
dataType: "json",
data: {
query: request.term
},
success: function( data ) {
response($.map( data.result.destinations, function(item) {
if(data.result.destinations.length == 1) {
selectedItem = item;
onlyOneItem = true;
}
return item;
}));
}
});
},
appendTo: $('#ubb_hotel_Destination').data('autocomplete'),
minLength: 2,
select: function( event, ui ) {
$('#ubb_hotel_DestinationHidden').val(ui.item.search);
$('#ubb_hotel_Destination').val(ui.item.label);
selectedItem = ui.item;
$('#ubb_hotel_Destination_div').removeClass('ym-error');
return false;
},
open: function(event, ui) { },
close: function() {
if(onlyOneItem) {
$('#ubb_hotel_DestinationHidden').val(selectedItem.search);
$('#ubb_hotel_Destination').val(selectedItem.label);
}
},
change: function( event, ui ) {
if((!ui.item && !onlyOneItem) || !$('#ubb_hotel_Destination').val().length) {
selectedItem = null;
onlyOneItem = false;
}
}
});
function encodeQueryData(data) {
var ret = [];
for (var d in data)
ret.push(encodeURIComponent(d) + "=" + encodeURIComponent(data[d]));
return ret.join("&");
}
$("#ubb_hotel_SearchButton").click(function(){
$('#ubbForm_hotel .ym-error').removeClass('ym-error');
var error = false;
if (!selectedItem) {
$('#ubb_hotel_DestinationHidden').val('');
$('#ubb_hotel_Destination').val('');
$('#ubb_hotel_Destination_div').addClass('ym-error');
error = true;
}
if (!datepickerRange.startDate || !datepickerRange.endDate) {
$('#ubb_hotel_when_div').addClass('ym-error');
error = true;
}
if (error) {
return false;
}
var adults = $('#ubb_hotel_adults input[name="adults"]:checked');
//kids = $('input[name="kids"]:checked');
var params = {
txtDestino: selectedItem.value,
list_id: selectedItem.id,
D1: $.datepicker.formatDate('dd', datepickerRange.startDate),
MA1: $.datepicker.formatDate('mm_yy', datepickerRange.startDate),
D2: $.datepicker.formatDate('dd', datepickerRange.endDate),
MA2: $.datepicker.formatDate('mm_yy', datepickerRange.endDate),
H: 1,
O1: (adults.size() > 0 ? adults.val() : 2) + 'A0N'
};
var url = 'http://hotels.redigo.ru/resultadosBusqueda.php?' + encodeQueryData(params);
if (_gaq) _gaq.push(['_trackEvent', 'BookingBlockHotel', 'SearchHotelButton']);
window.open(url, "_blank");
return false;
});
this.initedElements[this.current] = true;
return true;
},
addChildAgeField: function(childCnt, agesCnt, ages){
var childCnt = $(childCnt);
var childInps = $('.child', childCnt);
var ages = $(ages),
agesCnt = $(agesCnt);
ages.before('
');
var _currInp = $('input[name="kids"]:checked', childCnt);
if(_currInp.size() > 0)
_currInp.click();
childInps.click(function(){
var i = 0,
fields = '',
currInp = $('input[name="kids"]:checked', childCnt);
ages.html('');
if(currInp.val() == 1){
$('label', agesCnt).html('Возраст ребенка');
}else{
$('label', agesCnt).html('Возраст детей');
}
if(currInp.val() == 1 && currInp.data('act') == 1){
currInp.data('act', 0);
currInp.prop('checked', false);
$('.rating-cancel', childCnt).trigger('click');
agesCnt.hide();
}else{
var count = parseInt(currInp.val());
currInp.data('act', 1);
if(count > 0){
agesCnt.show();
while(i < count){
fields += '
';
i++;
}
ages.append(fields).slideDown();
}
}
});
},
loadSelect : function(selectId, data, valueField, titleField, selValue, setClass, addEvents) {
var inp = $('#' + selectId),
tooltip = $('#' + selectId + '_tooltip');
tooltip.html('');
if(data) {
var total = data.length;
var cols = (total > 10) ? (total > 20 ? (total > 60 ? 5 : 3) : 2) : 1;
var rows = Math.ceil(total/cols);
var table = $('
');
for(var j = 0; j < cols; j++){
var items = data.slice((j * rows), (j * rows + rows));
var col = $('
| ');
$.each(items, function (i, el) {
var _id = this[valueField],
_title = this[titleField];
var elem = $('
' + _title + '
');
if(selValue != null && _id == selValue){
(selectId == 'ubb_tour_departureCities' || selectId == 'ubb_tour_countries') ? elem.addClass('act') : elem.addClass('act-resort');
inp.val(_title).data('id', selValue);
}else if(el.selected){
elem.addClass('act');
inp.val(_title).data('id', _id);
}
col.append(elem);
});
table.append(col);
}
tooltip.append(table);
if(addEvents){
if(selectId == 'ubb_tour_departureCities'){
inp.click(function(){
tooltip.show();
firstShowCountries = true;
});
}
tooltip.mouseleave(function(){
$(this).hide();
});
}
}
},
initTour : function() {
if(this.initedElements[this.current])
return true;
var _this = this;
var inited = true;
var destId = _this.destination ? _this.destination._id : 0;
$('#ubb_tour_adults .adult, #ubb_tour_kids .child').rating();
_this.addChildAgeField('#ubb_tour_kids', '#ubb_tour_ages', '#ubb_tour_kidsAges');
$.ajax({
url: '/ajax/ubb/tour/tourData/'+ destId,
data: {ajax:true},
success: function(data, textStatus, jqXHR) {
if(data.result) {
if(data.result.departureCities)
_this.loadSelect('ubb_tour_departureCities', data.result.departureCities, '_id', 'title', null, 'js-tourCity ubb_tour_city', true);
if(data.result.countries)
_this.loadSelect('ubb_tour_countries', data.result.countries, 'id', 'name', null, 'js-tourCountry ubb_tour_country', true);
if(data.result.resorts){
var res = data.result.resorts.filter(function(el){
return el.selected;
});
if(res.length > 0){
$('#ubb_tour_resort').val(res[0].id);
$('#ubb_tour_countries').val($('#ubb_tour_countries').val() + ', ' + res[0].name);
}
}
if(data.result.levelTravelCountriesIso)
_this.levelTravelCountriesIso = data.result.levelTravelCountriesIso;
}
},
error: function(jqXHR, textStatus, errorThrown) {
inited = false;
}
});
var firstShowCountries = true;
$('#ubb_tour_countries').click(function(){
$('#ubb_tour_countries_tooltip').show();
if(firstShowCountries){
$('#ubb_tour_countries_tooltip .act .js-tourCountry').click();
firstShowCountries = false;
}else{
$('#ubb_tour_countries_tooltip .current .ubb_tour_tooltip').hide();
$('#ubb_tour_countries_tooltip .current').removeClass('current');
$('#ubb_tour_countries_tooltip .act .ubb_tour_tooltip').show();
$('#ubb_tour_countries_tooltip .act').addClass('current');
}
var resortId = $('#ubb_tour_resort').val();
if(resortId != '')
$('*[data-id="' + resortId + '"]').parent().addClass('act-resort');
});
$(document).on('click', '.js-tourCity', function() {
var t = $(this),
cityInp = $('#ubb_tour_departureCities'),
cityID = t.data('id'),
countryID = $('#ubb_tour_countries').data('id');
cityInp.val(t.html()).data('id', t.data('id'));
$('#ubb_tour_departureCities_tooltip .act').removeClass('act');
t.parent().addClass('act');
$('#ubb_tour_departureCities_tooltip').hide();
$('#ubb_tour_countries').attr('disabled', 'yes');
firstShowCountries = true;
$.ajax({
url: '/ajax/ubb/tour/countriesForCity/' + cityID,
success: function(data, textStatus, jqXHR) {
if(data.result)
_this.loadSelect('ubb_tour_countries', data.result.countries ? data.result.countries : null, 'id', 'name', countryID, 'js-tourCountry ubb_tour_country', true);
var el = $('#ubb_tour_countries_tooltip *[data-id="' + countryID + '"]');
if(el.size() > 0){
el.parent().addClass('act');
}else{
$('#ubb_tour_countries').val('');
}
},
error: function(jqXHR, textStatus, errorThrown) {
},
complete: function(jqXHR, textStatus) {
$('#ubb_tour_countries').removeAttr('disabled');
}
});
});
$(document).on('click', '.js-tourCountry', function() {
var t = $(this),
cityInp = $('#ubb_tour_departureCities'),
cityID = cityInp.data('id'),
countryInp = $('#ubb_tour_countries'),
countryID = t.data('id'),
tooltip = $('#ubb_tour_resort_' + countryID + '_tooltip');
$('.ubb_tour_tooltip', $('#ubb_tour_countries_tooltip .current')).hide();
$('#ubb_tour_countries_tooltip .current').removeClass('current');
countryInp.data('country', t.html()).data('id', countryID);
if(tooltip.size() > 0){
tooltip.show();
t.parent().addClass('current');
countryInp.parent().removeClass('ym-error');
}else if(t.data('notooltip')){
countryInp.parent().removeClass('ym-error');
_countryNoTooltipClick(t, countryID);
}else if (countryID) {
var tooltip = $('
');
$.ajax({
url: '/ajax/ubb/tour/resortsForCountry/' + countryID,
success: function(data, textStatus, jqXHR) {
if(data.result.resorts){
t.parent().append(tooltip);
var resortId = $('#ubb_tour_resort').val();
_this.loadSelect('ubb_tour_resort_' + countryID, data.result.resorts, 'id', 'name', (resortId != '' ? resortId : null), 'js-tourResort ubb_tour_resort', false);
t.parent().addClass('current');
countryInp.parent().removeClass('ym-error');
tooltip.append('
');
}else{
t.data('notooltip', true);
_countryNoTooltipClick(t, countryID);
}
},
error: function(jqXHR, textStatus, errorThrown) {
},
complete: function(jqXHR, textStatus) {
tooltip.show();
}
});
}
});
$(document).on('click', '.ubb_close .iClose', function(){
$(this).parents('.ubb_tour_tooltip.resort').hide().parent().removeClass('current');
});
function _countryNoTooltipClick(el, id){
$('#ubb_tour_countries').val(el.html()).data('id', id);
$('#ubb_tour_countries_tooltip .act').removeClass('act');
el.parent().addClass('act');
if(!firstShowCountries)
$('#ubb_tour_countries_tooltip').hide();
}
$(document).on('click', '.js-tourResort', function() {
var t = $(this),
parent = t.parent(),
countryInp = $('#ubb_tour_countries'),
resortInp = $('#ubb_tour_resort');
if(parent.hasClass('act-resort')){
countryInp.val(countryInp.data('country'));
resortInp.val('');
parent.removeClass('act-resort');
}else{
countryInp.val(countryInp.data('country') + ', ' + t.html());
resortInp.val(t.data('id'));
$('.act-resort').removeClass('act-resort');
t.parent().addClass('act-resort');
$('#ubb_tour_countries_tooltip .act').removeClass('act');
t.parents('.ubb_tour_tooltip').parent().addClass('act');
$('#ubb_tour_countries_tooltip').hide();
}
});
$("#ubb_tour_startDate").datepicker({
'dateFormat': 'dd.mm.yy',
'defaultDate': new Date(),
'minDate': new Date(),
'firstDay': 1,
'nextText': 'Вперед',
'prevText': 'Назад',
'showOn': 'both',
'buttonImage': '/ubb/i_calendar.png',
'buttonImageOnly': true,
beforeShow: function(input, inst) {
var cal = inst.dpDiv;
var top = (bookingWidget.isFixed) ? '38px' : $(this).offset().top,
left = $(this).offset().left,
right = 0;
var w = $(window).width();
if(w < 1252){
right = Math.round(w - $(input).offset().left - $(input).parent().width());
}
setTimeout(function () {
if(right > 0)
cal.css({
'top' : top,
'right': right
}).css('left', '');
else if(bookingWidget.isFixed)
cal.css({
'top' : top
});
else
cal.css({
'top' : top,
'left': left
}).css('right', '');
}, 3);
},
onClose: function(dateText, inst) {
var d = new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay),
currYear = new Date().getFullYear(),
t = $(this);
if(d.getFullYear() == currYear){
t.val($.datepicker.formatDate("d MM", d, { monthNames: $.datepicker.regional[ "ru" ].monthNamesGenitive }));
}else{
t.val($.datepicker.formatDate("d MM yy", d, { monthNames: $.datepicker.regional[ "ru" ].monthNamesGenitive }));
}
t.data('date', $.datepicker.formatDate("dd.mm.yy", d));
t.parent().removeClass('ym-error');
}
});
$(window).scroll(function(){
$('#ubb_tour_startDate').datepicker('hide');
$('#ubb_tour_startDate').blur();
});
$(window).resize(function(){
$('#ubb_tour_startDate').datepicker('hide');
$('#ubb_tour_startDate').blur();
});
$('#ubb_tour_nf').change(function(){
var t = $(this);
if(t.val() != '')
t.parent().removeClass('ym-error');
});
$('#proceed_tour').bind('click', function() {
var inps = [$('#ubb_tour_departureCities'),$('#ubb_tour_countries'),$('#ubb_tour_nf'), $('#ubb_tour_startDate')],
error = false;
$(inps).each(function(){
var t = $(this);
if(t.val() == ''){
t.parent().addClass('ym-error');
error = true;
}
});
if(error) return false;
var url = '/tours/search?ti=yes';
url += '&co=' + $('#ubb_tour_countries').data('id');
url += '&ct=' + $('#ubb_tour_departureCities').data('id');
url += '&re=' + $('#ubb_tour_resort').val();
url += '&df=' + $('#ubb_tour_startDate').data('date');
url += '&nf=' + $('#ubb_tour_nf').val();
// url += '&nt=' + $('#ubb_tour_nt').val();
url += '&adults=' + $('#ubb_tour_adults input[name="adults"]:checked').val();
var kids = $('#ubb_tour_kids input[name="kids"]:checked');
if(kids.size() > 0)
url += '&kids=' + kids.val();
var kids_ages = $('#ubb_tour_kidsAges input[name="kids_ages[]"]');
if(kids_ages.size() > 0){
kids_ages.each(function(){
url += '&kids_ages[]=' + $(this).val();
})
}
if (_gaq) {
if (_this.useLevelTravel) _gaq.push(['_trackEvent', 'BookingBlockTour', 'SearchTourButton', 'LevelTravel']);
else _gaq.push(['_trackEvent', 'BookingBlockTour', 'SearchTourButton', 'LightSoft']);
}
window.open(url, "_blank");
return false;
});
if(inited)
this.initedElements[this.current] = true;
return true;
}
};
if(jQuery) {
$.event.trigger('load.bookingWidget.js');
}
/* /ubb/bookingWidgetTop.js end */
/* /js/errorReport.js begin */
$(function() {
$('body').append($('#errorReport'));
$('#send_report').bind('click', function() {
if($('#reportText').val().length) {
var params = $('#reportForm').serializeArray();
$.ajax({
url: '/ajax/sendreport',
type : 'POST',
data : $.param(params),
complete: function() {
$('#errorReportSuccessMess').show();
$('#reportForm').hide();
}
});
} else {
$('#errorReport .errorBox').html('Введите текст комментария!').slideDown();
setTimeout(function(){ $('#errorReport .errorBox').html('').slideUp(); },2000);
return false;
}
});
$('#errorReportLink').bind('click',function() {
$('#reportForm').show();
$('#errorReportSuccessMess').hide();
$('#errorReport').show();
//$('body').addClass('noscroll');
bodyFreezing.freeze();
});
$('#error_close').bind('click',function() {
$('#errorReport').hide();
//$('body').removeClass('noscroll');
bodyFreezing.unfreeze();
});
});
/* /js/errorReport.js end */
/* /js/popularHotelsForDestination.js begin */
$(function(){
$('.popularHotelsForDestination').mouseover(function(){
$(this).find('.popularHotelsForDestination_item:hidden').slideDown();
})
})
/* /js/popularHotelsForDestination.js end */
/* /js/jquery.googleMapV3.js begin */
/**
* Created by JetBrains PhpStorm.
* User: IKamenev
* Date: 20.12.11
* Time: 17:58
* To change this template use File | Settings | File Templates.
*/
jQuery.fn.googleMapV3 = function(options, mapOptions) {
var options = jQuery.extend(
{
centerLat: null,
centerLng: null,
zoomLevel: null,
map: null,
dragEndEvent: function() {},
zoomEndEvent: function() {},
tilesLoadedEvent: function () {},
points: []
},
options
);
return this.each(function() {
var id = jQuery(this).attr("id");
var latlng = new google.maps.LatLng(options.centerLat || 0, options.centerLng || 0);
var myOptions = {
zoom: options.zoomLevel,
minZoom: 3,
center: latlng,
mapTypeId: (typeof options.mapTypeId == 'undefined') ? google.maps.MapTypeId.ROADMAP : options.mapTypeId,
styles: [
{
featureType: "poi",
elementType: "labels",
stylers: [
{ visibility: "off" }
]
}
]
};
if (mapOptions)
myOptions = jQuery.extend (myOptions, mapOptions);
this.googleMapObject = new google.maps.Map(jQuery(this)[0], myOptions);
google.maps.event.addListener(this.googleMapObject, 'zoom_changed', function() {
options.zoomEndEvent();
});
google.maps.event.addListener(this.googleMapObject, 'dragend', function() {
options.dragEndEvent();
});
google.maps.event.addListener(this.googleMapObject, 'tilesloaded', function() {
options.tilesLoadedEvent();
});
this.items = {};
this.infoWindow = new google.maps.InfoWindow;
this.addItem = function(item) {
if (!this.items[item.id]) {
this.items[item.id] = item;
var t = this;
google.maps.event.addListener(item.marker, 'click', function() {
// показываем новый маркер
item.click(t.googleMapObject, t.infoWindow);
});
google.maps.event.addListener(item.marker, 'mouseover', function() {
item.mouseOver(t.googleMapObject, t.infoWindow);
});
google.maps.event.addListener(item.marker, 'mouseout', function() {
item.mouseOut(t.googleMapObject, t.infoWindow);
});
return item;
} else
return false;
};
this.getMarkersByTag = function(tag) {
var items = new Array();
for (i in this.items) {
if (this.items[i].tags.indexOf(tag) !== -1) {
items.push(this.items[i].marker);
}
}
return items;
};
this.getMarkersByTagPrefix = function(prefix, onlyVisible) {
var items = new Array();
var item;
for(i in this.items) {
item = this.items[i];
for (j in item.tags) {
if (item.tags[j].indexOf(prefix) === 0) {
if (onlyVisible && item.marker.visible != true) continue;
items.push(item.marker);
break;
}
}
}
return items;
};
this.showItems = function(param) {
var bounds = this.googleMapObject.getBounds();
if (!bounds) {
var t = this;
setTimeout(function() {
t.showItems(param);
}, 100);
return;
}
for (var i in this.items) {
var item = this.items[i];
var inBounds = bounds.contains(item.marker.getPosition());
if (!param || param.type == 'all') {
item.marker.setMap(this.googleMapObject);
item.visible = true;
} else if ((item.type == param.type) || (param.tag && item.tags.indexOf(param.tag) !== -1)) {
if (!item.visible && inBounds) {
item.marker.setMap(this.googleMapObject);
item.visible = true;
}
else if (item.visible && !inBounds) {
item.marker.setMap(null);
item.visible = false;
} else {
}
}
}
};
this.hideItems = function(param) {
var hide;
for (i in this.items) {
var item = this.items[i];
hide = false;
if (param.type == 'all') {
hide = true;
} else if (item.type == param.type) {
hide = true;
}
if (param.tag && item.tags.indexOf(param.tag) !== -1) hide = true;
if (param.tags) {
if (intersect_safe(param.tags, item.tags) === []) {
hide = true;
}
}
if (hide) {
item.marker.setMap(null);
item.visible = false;
}
}
}
});
};
function CustomOverlay() {}
CustomOverlay.prototype = new google.maps.OverlayView();
CustomOverlay.prototype.onAdd = function() {
var div = document.createElement('DIV');
div.style.border = "0px";
div.style.position = "absolute";
div.style.cursor = "default";
$(div).bind("mouseover mouseout mousemove click mousedown mouseup", function(event){
event.stopPropagation();
});
$(div).html(this._html);
this._div = div;
var panes = this.getPanes();
$(panes.floatPane).append(this._div);
};
CustomOverlay.prototype.draw = function() {
var overlayProjection = this.getProjection();
var point = overlayProjection.fromLatLngToDivPixel(this._marker.getPosition());
var bounds = this.getMap().getBounds();
var neb = bounds.getNorthEast();
var swb = bounds.getSouthWest();
var ne = overlayProjection.fromLatLngToDivPixel(neb);
var sw = overlayProjection.fromLatLngToDivPixel(swb);
var div = this._div;
var left = (point.x - this._width/2);
var top = (point.y - div.clientHeight - this._verticalOffset);
if (left < sw.x)
left = sw.x+10;
if (left+this._width > ne.x)
left = ne.x - this._width - 10;
div.style.left = left + 'px';
div.style.top = top + 'px';
div.style.width = (this._width) + 'px';
// по какой-то причине, сразу clientHeight - показывает непраивльные значения
// пока вот такой вот хак
div.style.visibility = "hidden";
var t = this;
setTimeout(function() {
var top = (point.y - div.clientHeight - t._verticalOffset);
if (top < ne.y)
top = point.y-5;
div.style.top = top + "px";
div.style.visibility = "visible";
$(".hotel_ballon_wrap .del").click(function() {
t._marker.makeInactive();
t._marker.closeMarker();
});
$(".ballon_close").click(function() {
t._markerItem.makeInactive();
t._markerItem.closeMarker();
$('.point_item.act').removeClass('act');
});
}, 5);
};
CustomOverlay.prototype.onRemove = function() {
this._div.parentNode.removeChild(this._div);
this._div = null;
}
CustomOverlay.prototype.hide = function() {
this.setMap(null);
}
CustomOverlay.prototype.show = function() {
if (this._div) {
this._div.style.visibility = "visible";
}
}
/* finds the intersection of
* two arrays in a simple fashion.
*
* PARAMS
* a - first array, must already be sorted
* b - second array, must already be sorted
*
* NOTES
*
* Should have O(n) operations, where n is
* n = MIN(a.length(), b.length())
*/
function intersect_safe(a, b)
{
var ai=0, bi=0;
var result = new Array();
while( ai < a.length && bi < b.length )
{
if (a[ai] < b[bi] ){ ai++; }
else if (a[ai] > b[bi] ){ bi++; }
else /* they're equal */
{
result.push(a[ai]);
ai++;
bi++;
}
}
return result;
}
/* /js/jquery.googleMapV3.js end */
/* /js/photoUploader.js begin */
function photoUploader(previewSelector, inputSelector) {
var preview = $(previewSelector);
var input = $(inputSelector);
var hasFormData = !!window.FormData;
var hasFileReader = !!window.FileReader;
var hasFile = !!window.FileList && !!window.File;
var inputFieldName = 'file';
var photoUploadURL = '/saveUserPhoto';
var maxFileSize = 5242880;
var fileLimit = 10;
var indexId = 0;
var retry = 3;
var customData = {};
var filesList = {};
var uploadResponse;
var successHandler = function(){};
var errorHandler = function(errorText, errorCode){};
var deleteHandler = function(){};
var addHandler = function(file){};
var model;
var objectId;
var uploadStartFlag = false;
var frameHelper, formHelper;
var allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp'];
function fileWrapper(htmlInputElement) {
this.name = htmlInputElement.value.split(/(\\|\/)/g).pop();
this.size = false;
this.type = false;
}
if (hasFile && hasFormData) {
input.prop('multiple', true);
}
input.change(function(){
var files = hasFile ? this.files : [new fileWrapper(this)];
var el = $(this);
var tmpInput = el.clone(true);
var parentInput = el.parent();
el.removeAttr('id').hide().off('change').appendTo('body');
parentInput.append(tmpInput);
$.each(files, function(){
if (count() >= fileLimit) return error("Можно загрузить за один раз только " + fileLimit + " файлов.", 102);
var file = this;
var indexKey = 'id' + indexId;
filesList[indexKey] = hasFormData ? file : el;
if(hasFileReader) {
var reader = new FileReader();
reader.onload = function(e) {
addFile(file, indexKey, e.target.result);
};
reader.readAsDataURL(this);
} else {
addFile(file, indexKey);
}
indexId++;
});
});
// Private methods
function error(text, code) {
errorHandler(text, code);
uploadStartFlag = false;
// if ("console" in window && typeof console == "object") console.log(text);
return false;
}
function checkFile(file) {
if (file.type) {
if (!(/^image\/.+$/).test(file.type)) return error("Недопустимый файл " + file.name + ". Для загрузки разрешены следующие типы файлов: " + allowedExtensions.join(', ') + ".", 100);
} else {
var ext = file.name.split('.').pop().toLowerCase();
if ($.inArray(ext, allowedExtensions) == -1) return error("Недопустимый файл " + file.name + ". Для загрузки разрешены следующие типы файлов: " + allowedExtensions.join(', ') + ".", 100);
}
if (file.size && file.size > maxFileSize) return error("Файл " + file.name + " превышает допустимый размер в 5MB.", 101);
return true;
}
function addFile(file, indexKey, previewData) {
if (!checkFile(file) || addHandler(file) === false) {
delete filesList[indexKey];
return;
}
var previewDiv = $('
').attr('data-id', indexKey),
previewImg;
if (previewData) {
previewDiv.addClass("up_preview");
previewImg = $('
').attr({
src: previewData,
title: file.name
});
} else {
previewDiv.addClass("up_previewName");
previewImg = file.name;
}
var previewDel = $('
').click(function(){
var div = $(this).parent('div');
var key = div.data('id');
delete filesList[key];
div.remove();
deleteHandler();
});
previewDiv.append(previewImg);
previewDiv.append(previewDel);
preview.append(previewDiv);
}
function upload(internal) {
if (uploadStartFlag && !internal) return;
var firstKey;
for(firstKey in filesList) {if (filesList.hasOwnProperty(firstKey)) break;}
if (!firstKey) {
successHandler();
uploadStartFlag = false;
return;
}
uploadStartFlag = true;
if (hasFormData) {
upload_ajax(firstKey);
} else {
upload_form(firstKey);
}
}
function completeUploadFile(key, _data) {
var data = $(_data);
var json = data.is('textarea') ? data.val() : $('textarea', data).val();
try {
var response = eval("(" + json + ")");
} catch(err){
return error("Не удалось распарсить json", 201);
}
if (typeof response != 'object') return error("Не удалось распарсить json", 202);
if (response.c) return error(response.e, response.c);
uploadResponse = response;
$('[data-id=' + key +'] > .iDel').remove();
delete filesList[key];
upload(true);
}
function upload_ajax(key) {
var formData = new FormData();
formData.append(inputFieldName, filesList[key]);
formData.append('model', model);
formData.append('id', objectId);
$.each(customData, function(name, value) {
formData.append(name, value);
});
$.ajax({
url: photoUploadURL,
data: formData,
contentType: false,
processData: false,
type: 'POST',
success: function(data) {
completeUploadFile(key, data);
},
error: function(jqXHR, textStatus) {
return error(textStatus, 200);
}
})
}
function getUploadHelpers() {
if (!frameHelper){
frameHelper = $('#photoSaveFrame');
if (!frameHelper.length) frameHelper = $('
').attr({
src: 'javascript:false;',
name: 'photoSaveFrame',
id: 'photoSaveFrame'
}).hide().appendTo('body');
}
if (!formHelper) {
formHelper = $('#photoSaveForm');
if (!formHelper.length) formHelper = $('
').attr({
enctype: 'multipart/form-data',
method: 'POST',
action: photoUploadURL,
target: 'photoSaveFrame',
id: 'photoSaveForm'
}).hide().appendTo('body');;
}
return {frame: frameHelper, form: formHelper};
}
function upload_form(key) {
var helpers = getUploadHelpers();
helpers.frame.one('load', function(){
completeUploadFile(key, this.contentDocument || this.contentWindow.document);
});
helpers.form.empty().append(
filesList[key].attr({
name: inputFieldName
}),
createHidden('model', model),
createHidden('id', objectId)
);
$.each(customData, function(name, value) {
createHidden(name, value).appendTo(helpers.form);
});
helpers.form.submit();
}
function createHidden(name, value) {
return $('
').attr({
type: 'hidden',
name: name
}).val(value);
}
function count() {
var count = 0;
for(var tmpKey in filesList) {if (filesList.hasOwnProperty(tmpKey)) ++count;}
return count;
}
// Public methods
return {
setSuccessHandler: function(handler) {
if ($.isFunction(handler)) successHandler = handler;
},
setErrorHandler: function(handler) {
if ($.isFunction(handler)) errorHandler = handler;
},
setDeleteHandler: function(handler) {
if ($.isFunction(handler)) deleteHandler = handler;
},
setAddHandler: function(handler) {
if ($.isFunction(handler)) addHandler = handler;
},
startUpload: function(_model, _objectId, _customData) {
model = _model;
objectId = _objectId;
if($.isPlainObject(_customData)) $.extend(customData, _customData);
return upload();
},
setInputFieldName: function(name) {
inputFieldName = name;
},
reInit: function() {
filesList = {};
preview.html('');
},
getPhotoCount: count,
getUploadResponse: function() {
return uploadResponse;
}
};
}
/* /js/photoUploader.js end */
/* /js/helper.js begin */
InputHelper = {
In : function ( obj, text ) {
if ( $(obj).val() == text ) {
$( obj )
.css ( { color: "#000", fontStyle: "normal" } )
.val ( "" );
}
},
Out : function ( obj, text ){
if ( $(obj).val() == "" || $(obj).val() == text ){
$( obj )
.css ( { color: "#b3b3b3" } )
.val ( text );
}
},
Create : function ( obj, text ) {
$( obj )
.bind ( "focus", function () {
InputHelper.In ( this, text );
} )
.bind ( "blur", function () {
InputHelper.Out ( this, text );
} );
InputHelper.Out ( obj, text );
},
Remove : function ( obj ) {
$( obj ).unbind("focus").unbind("blur");
$( obj ).css ( { color: "#000", fontStyle: "normal" } );
}
}
function get_short_url(url) {
var pattern = /^http:\/\/(www[\d]?.)?[a-zA-Z0-9-_.]*\w{2,4}/,
mass = url.match(pattern),
pattern2 = /^http:\/\/(www[\d]?.)?/;
if (mass && mass.length > 0) {
if(mass[0].length > 20)
return mass[0].replace(pattern2, "");
return mass[0];
}
return url;
}
/* /js/helper.js end */
/* /js/addUserPoiMap.js begin */
var addUserPoiMap = {
mapCanvas: "#placeMapCanvas",
map: null,
activeMarker: null,
initialized: false,
marker: null,
poiId: null,
isDragging: false,
init: function (poiId, obj) {
if (!this.initialized) {
this.poiId = poiId;
$(this.mapCanvas).googleMapV3(
{}, // params
{scrollwheel: true} // mapOptions
);
this.initialized = true;
var myLatLng = new google.maps.LatLng(obj.lat, obj.lng);
//var latlngbounds = new google.maps.LatLngBounds();
//latlngbounds.extend(myLatLng);
$(this.mapCanvas)[0].googleMapObject.setCenter(myLatLng);
$(this.mapCanvas)[0].googleMapObject.setZoom(obj.zoom);
//$(this.mapCanvas)[0].googleMapObject.fitBounds(latlngbounds);
this.map = $(this.mapCanvas)[0].googleMapObject;
this.addItem(myLatLng, obj.icon, obj.icon_hover);
}
},
addItem: function(myLatLng, icon, icon_hover) {
this.marker = new google.maps.Marker({
position: myLatLng,
map: this.map,
draggable: true,
icon: icon,
icon_normal: icon,
icon_hover: icon_hover
});
google.maps.event.addListener(this.marker, 'mouseover', function() { this.setIcon(this.icon_hover); });
google.maps.event.addListener(this.marker, 'mousemove', function() { this.setIcon(this.icon_hover); });
google.maps.event.addListener(this.marker, 'mousedown', function() { this.setIcon(this.icon_hover); addUserPoiMap.isDragging = true; });
google.maps.event.addListener(this.marker, 'mouseup', function() { this.setIcon(this.icon_normal); addUserPoiMap.isDragging = false; });
google.maps.event.addListener(this.marker, 'mouseout', function() { if(!addUserPoiMap.isDragging) this.setIcon(this.icon_normal); });
},
getPoiMarkerInfo: function() {
if(!this.marker)
return false;
var lat = this.marker.getPosition().lat();
var lng = this.marker.getPosition().lng();
return {poiId: this.poiId, lat: lat, lng: lng};
},
scrollToMap: function(id) {
var map_top = Math.round($(this.mapCanvas).offset().top) - 50;
window.scrollTo(0, map_top);
document.location.hash = '';
this.activateDescription(id);
this.makeActive(id);
var items = $(this.mapCanvas)[0].items;
for (var i in items) {
var item = items[i];
if (item.id == id) {
item.scrollTo();
}
}
}
};
/* /js/addUserPoiMap.js end */
/* /js/addUserPoi.js begin */
var addUserPoi = {
randomString : null,
defaultCategory: '',
editTimer : null,
timeLeft : 900, // 15 mins
editAction : false,
poiLink : null,
poiRev : '',
defaultValues : {
address: "Адрес или описание, как найти",
site: "http://",
comment: "Например, лучшее место в городе, где готовят фелафель"
},
poiEditable: false,
pu: new photoUploader('#up_photos_container', '#selectedPhotoAddPoi'),
uploadHandler: null,
checkEditStateTimer: function() {
var _this = this;
if(_this.timeLeft > 0) {
_this.editTimer = setTimeout(function() { _this.checkEditStateTimer(); }, 1000);
} else {
clearTimeout(_this.editTimer);
if( $('#usrPoiMessage') && $('#usrPoiMessage').length != 0 ) {
$('#usrPoiMessage').html('Для этого места пока нет описания. Вы можете
добавить свое описание или поделиться ссылкой на отчет в интернете.');
}
}
_this.timeLeft--;
},
resetForm: function() {
var _this = this;
_this.editAction = false;
$("#addPlaceForm")[0].reset();
InputHelper.Create("#address", _this.defaultValues.address);
InputHelper.Create("#site", _this.defaultValues.site);
InputHelper.Create("#comment", _this.defaultValues.comment);
$('#userPoiCategory option').each(function(){
if($(this).val() == _this.defaultCategory) {
$(this).attr('selected', 'selected');
}
});
$('#msg_bubble').html('Спасибо за новое место.
Я правильно отметил его на карте?');
$('#userPoiTitle').html('Добавление места');
$('#addUserPoiButton').val('Добавить');
},
acceptCoords: function(showAddPhotoForm) {
var _this = this;
var obj = addUserPoiMap.getPoiMarkerInfo();
if(obj) {
$.ajax({
url : '/ajax/poi/confirmPoiCoords',
type : 'POST',
data : $.param(obj),
success: function(data, textStatus, jqXHR){
if(_this.poiLink && !showAddPhotoForm) {
window.location.href = _this.poiLink + '?rev=' + _this.poiRev;
}
$('#addUserPlaceDiv').hide();
_this.resetForm();
$('#addFormDiv').show();
$("#mapDiv").hide();
if(showAddPhotoForm) {
newPoiUrl = _this.poiLink + '?rev=' + _this.poiRev;
$('#photoId').val(data.saveResult.id);
$('#photoModel').val('Poi');
$('.addPhotoLink').click();
}
}
});
}
},
initSubmitFrame: function() {
$('#addPlaceForm').attr('target', 'up_submitFormFrame');
$('#addPlaceForm').attr('action', '/poi/saveUserPoi/' + this.randomString);
},
postedDataProcessed: function(result) {
this.randomString = result.rString;
if(result.status == 'error') {
var error = result.error;
var error_msg = result.error_message;
if(error == 3) {
$('#title').attr('style','border: 1px #ff0000 solid;');
} else {
alert(error_msg);
}
$('#addUserPoiButton').attr('disabled', false);
$('#loader').hide();
$('#addUserPoiButton').show();
} else if (result.status == 'success') {
this.poiLink = result.link;
this.poiRev = result.mod_date;
var _this = this;
var nextStep = function() {
_this.uploadHandler =null;
$('#addUserPoiButton').removeAttr('disabled');
$("#addPlaceForm").hide();
$("#mapDiv").slideDown('fast');
addUserPoiMap.init(
result.poiId, {
lat: result.lat,
lng: result.lng,
zoom: result.zoom,
icon: result.marker_icon,
icon_hover: result.marker_icon_hover
}
);
$('#addUserPoiButton').attr('disabled', false);
$('#loader').hide();
$('#addUserPoiButton').show();
};
if (this.pu.getPhotoCount()) {
this.pu.setSuccessHandler(nextStep);
this.pu.setErrorHandler(function(text, code) {
if (code >= 200) text = 'Что-то не так с сервером. Попробуйте загрузить фотографию ещё раз.';
alert(text);
$('#addUserPoiButton').attr('disabled', false);
$('#loader').hide();
$('#addUserPoiButton').show();
});
this.uploadHandler = function(){
if (_this.pu.getPhotoCount()) {
_this.pu.startUpload('Poi', result.poiId);
} else nextStep();
};
this.uploadHandler();
} else nextStep();
}
},
filterDefaultValues: function() {
var _this = this;
$('#addPlaceForm :input').each(function() {
if(
$(this).val() == _this.defaultValues.address ||
$(this).val() == _this.defaultValues.site ||
$(this).val() == _this.defaultValues.comment
)
$(this).val('');
});
},
setAddAction: function(opts) {
var _this = this;
$('#addUserPoiButton').bind('click', function() {
if ($.isFunction(_this.uploadHandler)) return _this.uploadHandler();
if(!$('#title').val().length) {
$('#title').parent().addClass('ym-error');
$('#title').bind('keyup', function(){
if($(this).val().length > 0)
$(this).parent().removeClass('ym-error');
});
return;
}
$('#addUserPoiButton').attr('disabled', true);
$('#addUserPoiButton').hide();
$('#loader').show();
_this.initSubmitFrame()
_this.filterDefaultValues();
if(_this.editAction == true && opts.poiEditable == '1') {
$('#addPlaceForm').append('
');
if(opts.commentId != '')
$('#addPlaceForm').append('
');
}
$('#addPlaceForm').submit();
if($('#up_Pid').length > 0) $('#up_Pid').remove();
if($('#up_Cid').length > 0) $('#up_Cid').remove();
});
},
setEditAction: function(poi) {
var _this = this;
if(poi != null) {
$('#edit_desc_link').bind('click', function() {
_this.editAction = true;
$("#addPlaceForm")[0].reset();
//$('body').addClass('noscroll');
bodyFreezing.freeze();
$('#addUserPlaceDiv').show();
$('#addPlaceForm').show();
$("#mapDiv").hide();
$('#userPoiTitle').html('Редактирование места');
$('#title').val(poi.title);
$('#site').val(poi.site);
$('#address').val(poi.address);
$('#comment').val(poi.comment);
if($('#site').val().length > 0)
InputHelper.Remove($('#site'));
else
InputHelper.Create("#site", _this.defaultValues.site);
if($('#address').val().length > 0)
InputHelper.Remove($('#address'));
else
InputHelper.Create("#address", _this.defaultValues.address);
if($('#comment').val().length > 0)
InputHelper.Remove($('#comment'));
else
InputHelper.Create("#comment", _this.defaultValues.comment);
$('#msg_bubble').html('Спасибо за уточнения.
Я правильно отметил его на карте?');
$('#userPoiCategory option').each(function(){
if($(this).val() == poi.category) {
$(this).attr('selected', 'selected');
}
});
$('#addUserPoiButton').val('Сохранить');
});
}
},
setSearchAutocomplete: function() {
$('#searchDestination')
.unbind('focus blur')
.bind('focus', function() {
disableLoadingWait = true;
})
.bind( "keydown", function( event ) {
if ( event.keyCode === $.ui.keyCode.TAB &&
$( this ).data("uiAutocomplete").menu.active ) {
event.preventDefault();
}
if ( event.keyCode === $.ui.keyCode.ENTER &&
$( this ).data("uiAutocomplete").menu.active ) {
event.preventDefault();
return false;
}
})
.bind( "keyup", function( event ) {
if ( event.keyCode === $.ui.keyCode.ESCAPE ) {
this.value = lastDestText;
}
})
.bind ('blur', function( event ) {
if($(this).data("uiAutocomplete").menu.active ) {
} else {
$('#destinationPath').show();
$('#destinationChooser').hide();
}
}).autocomplete({
appendTo: $('#searchDestination').data('autocomplete'),
source: function( request, response ) {
$.getJSON('/ajax/destSearch', {
term: request.term
},
function(data) {
response(
$.map( data.result, function( item ) {
return {
icon: item.icon,
label: item.title,
extra: item.extra,
value: new String(item.id),
name: item.name
};
})
);
});
},
search: function() {
var term = this.value;
if ( term.length < 2 ) {
return false;
}
},
focus: function() {
// prevent value inserted on focus
return false;
},
select: function( event, ui ) {
this.value = ui.item.name;
$('#destinationString').html('
' + ui.item.label);
$('#newDestination').val(ui.item.value);
$('#destinationPath').show();
$('#destinationChooser').hide();
return false;
},
close: function( event, ui ) {
$('#destinationPath').show();
$('#destinationChooser').hide();
}
})
.data("uiAutocomplete")._renderItem = function(ul, item ) {
return $( "
" )
.data( "item.autocomplete", item )
.append('
' + item.label + ', ' + item.extra + '' )
.appendTo( ul );
};
},
init: function(opts) {
var _this = this;
_this.randomString = opts.randomString;
_this.defaultCategory = opts.defaultCategory;
_this.resetForm();
var addForm = $('#addUserPlaceDiv');
$('body').append(addForm);
//$('#addPhotoLinkUserPoi').bind('click', function() { _this.acceptCoords(true); });
$('#coordsAcceptButton').bind('click', function() { _this.acceptCoords(false); });
_this.setSearchAutocomplete();
$('#changeDestination').click(function() {
$('#destinationPath').hide();
$('#destinationChooser').show();
$('#searchDestination').focus();
});
addForm.bind('click' , function(e) {
var el = $(e.target);
if(el.attr('id') == $(this).attr('id'))
_this.resetForm();
});
$('#addUserPoiClose').click(function(){
//$('body').removeClass('noscroll');
bodyFreezing.unfreeze();
$('#addUserPlaceDiv').hide();
if(_this.poiLink)
window.location.href = _this.poiLink + '?rev=' + _this.poiRev;
_this.resetForm();
});
// $('.iClose')
// .bind('mouseover', function(){ $(this).prev().show(); })
// .bind('mouseout', function() { $(this).prev().hide(); });
}
};
if(jQuery) {
$.event.trigger('load.addUserPoi.js');
}
$(document).on('auth.complete', function(event, element) {
addUserPoi.setEditAction(null);
});
/* /js/addUserPoi.js end */
/* /js/subscribe.js begin */
$(function(){
var blockAjax = false;
var cEmail = $('#commentSubscribe #needEmail'),
cEmailParent = cEmail.parents('.need-email'),
cCheck = $('#commentSubscribe #emailR'),
cSubscribe = $('#commentSubscribe');
var subscribeTooltip = $('#subscribeTooltip'),
subscribeTooltipClose = $('#subscribeTooltipClose');
function subscribe(el) {
if (blockAjax) return;
var data = {
eid: el.attr('data-eid'),
type: el.is('[data-unsubscribe]') ? 0 : 1
};
if (el.is('[data-noemail]')) data['email'] = el.data('noemail');
blockAjax = true;
$.ajax({
type: 'post',
url: '/ajax/user/subscribe',
data: data,
success: function(data){
var r = data.result;
if (r.e) {
if (r.needEmail) {
$('.subscribeLink').attr('data-noemail', '1');
$('#commentSubscribe #emailR').attr('data-noemail', '1');
return el.trigger('needEmail', [el, r.e]);
} else {
return alert(r.e);
}
}
if (r.s) {
el.text(el.is('[data-unsubscribeText]') ? el.attr('data-unsubscribeText') : 'отписаться');
el.attr('data-unsubscribe', '1');
$('#commentSubscribe').hide();
$('#commentSubscribe #emailR').prop('checked', false);
} else {
el.text(el.is('[data-subscribeText]') ? el.attr('data-subscribeText') : 'подписаться');
el.removeAttr('data-unsubscribe');
$('#commentSubscribe').show();
$('#commentSubscribe #emailR').prop('checked', true);
}
el.trigger('afterSubscribe', [el, r.s]);
if(r.validateEmail)
el.trigger('showMessage');
$('.subscribeLink').removeAttr('data-noemail');
$('#commentSubscribe #emailR').removeAttr('data-noemail');
},
complete: function(){
blockAjax = false;
}
});
}
// ссылка подписаться-отписаться (комментарии + профайл)
$('.subscribeLink').on('click', function(){
var el = $(this);
if(el.is('.not_login_link')){
JSVARS['subscribe'] = 1;
return;
}
subscribe(el);
});
// подписка на комментарии в профайле пользователя
$('.js-user-subscribe').on('needEmail', function(e, el, mess){
$('#email').parent().addClass('ym-error');
$('#email').focus(function(){
$(this).parent().removeClass('ym-error');
});
$(el).parent().append(
$('
').css('left', Math.round($(this).position().left - 87))
);
setTimeout(function(){
var t = Math.round($('#personalData').offset().top);
$(window).scrollTop((t > 0 ? t : 0));
},1500);
setTimeout(function(){
$('.subscribe-tooltip').remove();
},3000);
return;
});
// подписка на комментарии на странице poi/статьи
$('.js-comment-subscribe').on('needEmail', function(e, el){
$('#subscribeForm').data('el', el);
showEmailForm();
});
$('.js-comment-subscribe').on('showMessage', function(){
$('#commentsStart .comments-block_head').after(
'
' +
'
' +
'Подписка на комментарии будет активна после подтверждения email.
Письмо-подтверждение отправлено на указанный электронный адрес.' +
'
'
);
$('#successBoxClose').on('click', function(){
$(this).parent().slideUp();
});
});
var showEmailForm = function(error) {
subscribeTooltip
.show()
.find('#email')
.val('').focus().css('border', error ? '1px solid red' : '');
};
subscribeTooltipClose.on('click', function(){
subscribeTooltip.hide();
});
$('#subscribeForm').submit(function(){
var link = $(this).data('el'),
el = $('[name="email"]', this);
var email = $.trim(el.val());
if (email != '') {
var check = checkEmail(email);
if(check){
el.parent().removeClass('ym-error');
}else{
el.parent().addClass('ym-error');
return false;
}
link.data('noemail', email);
link.click();
link.one('afterSubscribe', function(){
subscribeTooltipClose.click();
$('#unisenderSubscribe').show();
$('#unisender_form').hide();
})
} else {
showEmailForm(true);
}
return false;
})
InputHelper.Create("#subscribe_email","Введите e-mail");
$("#subscribe_email").bind("input", function(){
var check = checkEmail($(this).val());
//emailTest = "^[_.0-9a-z-]+@([0-9a-z][0-9a-z_-]+.)+[a-z]{2,4}$";
//var regex = new RegExp(emailTest);
//if (regex.test($(this).val().toLowerCase()))
if(check)
$("#subscribe_sbt").removeAttr("disabled");
else
$("#subscribe_sbt").attr("disabled", "disabled");
})
$('#unisenderSubscribe').on('needEmail', function(e, el){
$('#unisender_form').show();
$('#unisenderSubscribe').hide();
$('#subscribe_email').focus();
});
$('#unisender_form').submit(function(){
var link = $('#unisenderSubscribe');
var el = $('[name="email"]', this);
var email = $.trim(el.val());
if (email != '') {
link.data('noemail', email);
link.click();
link.one('afterSubscribe', function(){
link.show();
$('#unisender_form').hide();
})
}
return false;
});
// подписаться на комментарии
cCheck.on('click', function(){
if($(this).is(':checked') && $(this).is('[data-noemail]')) {
cEmail.attr('disabled', false).focus();
} else {
cEmail.val('').attr('disabled', true);
cEmailParent.removeClass('ym-error');
}
});
cEmail.on('focusout', function(){
var check = checkEmail($(this).val());
if(!check)
cEmailParent.addClass('ym-error');
else
cEmailParent.removeClass('ym-error');
});
});
/*
function checkEmail(email){
var emailTest = "^[_.0-9a-z-]+@([0-9a-z][0-9a-z_-]+.)+[a-z]{2,4}$";
var regex = new RegExp(emailTest);
if (regex.test(email.toLowerCase()))
return true;
return false;
}
*/
$(document).on('auth.complete', function(event, element) {
if($(element).is('.subscribeLink')) $(element).trigger('click');
if($(element).hasClass('comment_form')) $('.subscribeLink').trigger('click');
});
/* /js/subscribe.js end */
/* /js/map/topMapContainer.js begin */
//
// Данная штука занимается ездилкой карты, которая расположена сверху
// У этого контейнера есть контейнер для фильтров: filterItems
// DIV с самой картой называется topMapCanvas
//
var topMapContainer = {
mapState: 'close',
circleMove: false,
circleMoveDirection: 'none',
circleTop: false,
wrapperMinHeight: false,
wrapperMaxHeight: false,
mapOpenTopMargin: false,
mapCloseTopMargin: false,
centralItem: null,
mapItems: {},
activeMarker: null,
openMap: function() {
this.mapState = 'opening';
$(".antiMouse").hide();
$('#mapWrapper2').animate({
height: this.wrapperMaxHeight
}, 700, function() {
topMapContainer.mapState = 'open';
$("#openCloseMapAction").html(
$("#openCloseMapAction").attr("closeMapAction") + '
'
);
$("#circleOpenCloseMap").addClass('arrowUp');
});
$('#topMapCanvas').animate({
'margin-top': topMapContainer.mapOpenTopMargin
}, 700, function() {});
},
closeMap: function() {
this.mapState = 'closing';
$(".antiMouse").show();
$('#mapWrapper2').animate({
height: this.wrapperMinHeight
}, 700, function() {
$("#openCloseMapAction").html(
$("#openCloseMapAction").attr("openMapAction") + '
'
);
topMapContainer.mapState = 'close';
$("#circleOpenCloseMap").removeClass('arrowUp');
});
$('#topMapCanvas').animate({
'margin-top': topMapContainer.mapCloseTopMargin
}, 700, function() {});
},
toggleOpenCloseMap: function() {
if (this.mapState == 'close') {
this.openMap();
} else if (this.mapState == 'open') {
this.closeMap();
}
},
init: function () {
if (!this.wrapperMinHeight)
this.wrapperMinHeight = Number($("#mapWrapper2").attr("minHeight"));
if (!this.wrapperMaxHeight)
this.wrapperMaxHeight = Number($("#mapWrapper2").attr("maxHeight"));
if (!this.mapOpenTopMargin)
this.mapOpenTopMargin = Number($("#topMapCanvas").attr("openTopMargin"));
if (!this.mapCloseTopMargin)
this.mapCloseTopMargin = Number($("#topMapCanvas").attr("closeTopMargin"));
$('#openCloseMapAction').click(function() {
topMapContainer.toggleOpenCloseMap();
});
$('#circleOpenCloseMap').disableSelection();
$('#circleOpenCloseMap').mousedown(function(event) {
var t = topMapContainer;
if (!t.circleTop) {
t.circleTop = event.pageY;
}
// если на кружочек нажали в момент транзишена - выходим
if (t.mapState != 'open' && t.mapState != 'close') return;
topMapContainer.circleMove = true;
});
$(document).mouseup(function() {
var t = topMapContainer;
if (t.circleMove) {
t.toggleOpenCloseMap();
}
t.circleMove = false;
});
$(document).mousemove(function(event) {
var t = topMapContainer;
// если на кружочек не нажали - выходим
if (!t.circleMove) return;
var y = event.pageY;
var newHeight = t.wrapperMinHeight + (y - t.circleTop);
if (newHeight < t.wrapperMinHeight)
newHeight = t.wrapperMinHeight;
if (newHeight > t.wrapperMaxHeight)
newHeight = t.wrapperMaxHeight;
var dMargin = t.mapCloseTopMargin - t.mapOpenTopMargin;
var newMargin = dMargin - (newHeight / t.wrapperMaxHeight) * dMargin;
if (newMargin < t.mapCloseTopMargin)
newMargin = t.mapCloseTopMargin;
if (newMargin > t.mapOpenTopMargin)
newMargin = t.mapOpenTopMargin;
$("#mapWrapper2").height(newHeight);
$("#topMapCanvas").css("margin-top", newMargin + "px");
});
}
};
$(function() {
topMapContainer.init();
});
/* /js/map/topMapContainer.js end */
/* /js/placesMap.js begin */
var placesMap = {
mapCanvas: "#topMapCanvas",
centerLat: 1,
centerLng: 1,
zoomLevel: 1,
filterContainer: null,
destinationID: null,
fitToBoundsItems: null,
initialized: false,
markerCluster: null,
requestQueue: new Array(),
canLoad: true,
inputEl: '.placeFilter',
markerObject: null,
defaultAjaxUrl: '/ajax/getBubbleContentForPoi',
loadingTimer: null,
loading: true,
init: function() {
$(this.mapCanvas).googleMapV3({
centerLat: this.centerLat,
centerLng: this.centerLng,
zoomLevel: this.zoomLevel,
dragEndEvent: this.mapDragEndEvent,
zoomEndEvent: this.mapZoomEndEvent,
tilesLoadedEvent: this.tilesLoadedEvent
});
if (this.fitToBoundsItems) {
var latlngbounds = new google.maps.LatLngBounds();
for (var i in this.fitToBoundsItems) {
var myLatLng = new google.maps.LatLng(this.fitToBoundsItems[i].lat, this.fitToBoundsItems[i].lng);
latlngbounds.extend(myLatLng);
}
$(this.mapCanvas)[0].googleMapObject.setCenter(latlngbounds.getCenter());
$(this.mapCanvas)[0].googleMapObject.fitBounds(latlngbounds);
}
if (this.filterContainer) {
$(this.filterContainer).remove().appendTo("#filterItems").show();
}
$(this.inputEl).change(function() {
placesMap.redrawItems(); // redraw items and clusters
if (this.checked) {
placesMap.hideMapLoadingSpinner();
placesMap.stopAllAjaxRequests();
placesMap.getAdditionalPlaces();
}
});
},
addPoi: function(poi) {
poi.bubbleAjaxBaseUrl = this.defaultAjaxUrl;
var item;
if (this.markerObject && window[this.markerObject]) {
item = new window[this.markerObject](poi);
} else {
item = new PoiMarker(poi);
}
item.map = $(this.mapCanvas)[0].googleMapObject;
return $(this.mapCanvas)[0].addItem(item);
},
addMarker: function(item) {
this.markerCluster.addMarker(item.marker);
},
mapDragEndEvent: function() {
if(placesMap.initialized) {
placesMap.hideMapLoadingSpinner();
placesMap.stopAllAjaxRequests();
placesMap.getAdditionalPlaces();
}
},
mapZoomEndEvent: function() {
if(placesMap.initialized) {
placesMap.hideMapLoadingSpinner();
placesMap.stopAllAjaxRequests();
placesMap.getAdditionalPlaces();
}
},
tilesLoadedEvent: function() {
if (!placesMap.initialized) {
var mapObject = $(placesMap.mapCanvas)[0].googleMapObject;
var currentZoom = mapObject.getZoom();
if (currentZoom > placesMap.minZoom) {
mapObject.setZoom(placesMap.minZoom);
}
placesMap.initialized = true;
}
},
stopLoading: function() {
this.canLoad = false;
this.hideMapLoadingSpinner();
this.stopAllAjaxRequests();
},
showMapLoadingSpinner: function() {
if($('#loadingDiv').length == 0) {
var map = $(this.mapCanvas);
var loadingDiv = $(
'
' +
'
' +
'
' +
'Загрузка...' +
'
' +
'Остановить' +
'
' +
'
' +
'
'
);
map.before(loadingDiv);
}
$('#loadingDiv').slideDown();
},
hideMapLoadingSpinner: function() {
this.loading = false;
clearTimeout(this.loadingTimer);
$('#loadingDiv').slideUp();
},
redrawItems: function() {
this.markerCluster.clearMarkers();
var preloadedMarkers = $(placesMap.mapCanvas)[0].getMarkersByTagPrefix('preloaded', true);
placesMap.markerCluster.addMarkers(preloadedMarkers);
$(this.inputEl).each(function() {
var uncheck_tags = [];
if (this.checked) {
var items = $(placesMap.mapCanvas)[0].getMarkersByTag(String($(this).val()));
placesMap.markerCluster.addMarkers(items);
} else {
uncheck_tags.push(String($(this).val()));
}
$(placesMap.mapCanvas)[0].hideItems({tags: uncheck_tags});
});
},
stopAllAjaxRequests: function() {
for(i in this.requestQueue) {
var request = this.requestQueue[i];
request.abort();
}
this.requestQueue = new Array();
},
getCheckedBoxes: function() {
var chk = new Array();
$(this.inputEl).each(function() {
if (this.checked) {
chk.push($(this).val());
}
});
return chk;
},
processLoadingTimer: function() {
if(placesMap.loading) {
placesMap.showMapLoadingSpinner();
placesMap.loadingTimer = setTimeout(function() {placesMap.processLoadingTimer();}, 3000);
} else {
placesMap.hideMapLoadingSpinner();
}
},
getAdditionalPlaces: function() {
var chk = this.getCheckedBoxes();
if(chk.length == 0)
return;
var bounds = $(this.mapCanvas)[0].googleMapObject.getBounds();
var sw = bounds.getSouthWest();
var ne = bounds.getNorthEast();
this.canLoad = true;
placesMap.loading = true;
placesMap.loadingTimer = setTimeout(function() {placesMap.processLoadingTimer();}, 3000);
var queryInfo = {
page: "1",
lat1: sw.lat(),
lng1: sw.lng(),
lat2: ne.lat(),
lng2: ne.lng()
};
this.loadPoiPage(queryInfo);
},
loadPoiPage: function(queryInfo) {
if(!this.canLoad) {
placesMap.loading = false;
return;
}
var chk = this.getCheckedBoxes();
var request = $.ajax({
url: '/ajax/getOverlaysForPlacesOnMap/' + this.destinationID + '/' + queryInfo.page,
dataType: 'json',
type : 'GET',
data : {
lat1 : queryInfo.lat1,
lng1 : queryInfo.lng1,
lat2 : queryInfo.lat2,
lng2 : queryInfo.lng2,
categoryIds : chk
},
success: function (data) {
if(data.result && data.result.overlays) {
placesMap.drawOverlays(data.result.overlays);
} else {
placesMap.loading = false;
}
if(data.result && data.result.nextPage != 0) {
placesMap.loading = true;
var newQueryInfo = {
page: data.result.nextPage,
lat1: queryInfo.lat1,
lng1: queryInfo.lng1,
lat2: queryInfo.lat2,
lng2: queryInfo.lng2
};
setTimeout(function() {placesMap.loadPoiPage(newQueryInfo);}, 100);
} else {
placesMap.hideMapLoadingSpinner();
}
if(!placesMap.canLoad)
placesMap.hideMapLoadingSpinner();
},
error: function() {
placesMap.hideMapLoadingSpinner();
}
});
this.requestQueue.push(request);
},
drawOverlays: function(overlays) {
for (i in overlays) {
var place = overlays[i];
place.rawId = String(place.id);
place.id = "poi_" + String(place.id);
place.activeImageURL = place.markerHover;
place.inactiveImageURL = place.markerNormal;
place.width = 420;
var newItem = placesMap.addPoi(place);
if(newItem) {
placesMap.addMarker(newItem);
}
}
},
scrollToMap: function(id, blockId) {
var map_top = Math.round($('#topMapCanvas').offset().top) - 50;
window.scrollTo(0, map_top);
document.location.hash = '';
this.activateDescription(blockId);
this.makeActive(id);
var items = $("#topMapCanvas")[0].items;
for (var i in items) {
var item = items[i];
if (item.id == id) {
item.scrollTo();
$(placesMap.mapCanvas)[0].googleMapObject.setZoom(15);
}
}
},
makeActive: function(id) {
var items = $("#topMapCanvas")[0].items;
for (var i in items) {
var item = items[i];
if (item.id == id) {
item.select();
} else {
item.deselect();
}
}
},
activateDescription: function(id) {
$("#poi_points > div").each(
function() {$(this).removeClass("act");}
);
$("#point" + id).addClass("act");
},
buildClusters: function() {
var items = $(this.mapCanvas)[0].items;
var markers = new Array();
for(i in items) {
markers.push(items[i].marker);
}
if(this.markerCluster)
this.markerCluster.clearMarkers();
var styles = [{
url: '/i/icons/marker_cluster_mid.png',
height: 49,
width: 40,
textColor: '#FFFFFF',
textSize: 13,
fontWeight: 'normal'
},
{
url: '/i/icons/marker_cluster_big.png',
height: 46,
width: 50,
textColor: '#FFFFFF',
textSize: 13,
fontWeight: 'normal'
}];
var opts = {gridSize: 40, maxZoom: 14, styles: styles};
this.markerCluster = new MarkerClusterer($(this.mapCanvas)[0].googleMapObject, markers, opts);
}
};
function showOnMap(id, blockId) {
topMapContainer.openMap();
placesMap.scrollToMap(id, blockId);
}
/* /js/placesMap.js end */
/* /js/map/EventMarker.js begin */
/**
* Created by JetBrains PhpStorm.
* User: IKamenev
* Date: 21.12.11
* Time: 16:05
* To change this template use File | Settings | File Templates.
*/
function EventCustomOverlay(options) {
this._marker = options.marker;
this._marker = options.marker.marker;
this._width = 400;
this._height = 200;
this._div = null;
this._html = options.html;
this._verticalOffset = 50; // расстояние от маркера до нижнего края всплывающего окошка
this._doNotClose = false;
this.setMap(options.map);
}
EventCustomOverlay.prototype = new CustomOverlay();
function EventMarker(item) {
this.id = item.id;
this.type = "country";
this.marker = null;
this.title = item.bubbleContent.title;
this.link = item.link;
this.showBubble = true;
this.bubbleContent = null;
if (item.bubbleContent)
this.bubbleContent = item.bubbleContent;
if (typeof(item.showBubble) != "undefined")
this.showBubble = item.showBubble;
this.countryPath = item.countryPath;
this.inactiveImageURL = '/i/flags/' + this.countryPath + 'MapIconOut.png';
this.activeImageURL = '/i/flags/' + this.countryPath + 'MapIconOver.png';
this.activeImage = null;
this.inactiveImage = null;
this.overlay = null;
this.zIndex = 1;
this.visible = false;
// mouseOut с маркера
this.mouseOver = function (map) {
this.map = map;
if (this.showBubble) {
this.makeActive();
this.openMarker();
}
};
// mouseOver над маркером
this.mouseOut = function (map) {
this.map = map;
if (this.showBubble) {
this.closeMarker();
this.makeInactive();
}
};
// клик по маркеру
this.click = function (map) {
if (this.showBubble && this.link && typeof(this.link) != 'undefined' && this.link != '') {
this.closeMarker();
$("body").hide();
var that = this;
setTimeout(function() {
window.document.location = that.link;
}, 30);
}
// this.map = map;
// this.openMarker();
// this.makeActive();
// если показываем при наведении - то скроллить на балун не надо
//map.setCenter(this.marker.getPosition());
};
this.markerOpened = false;
this.openMarker = function() {
if (this.markerOpened)
return;
if (this.overlay) {
delete(this.overlay);
this.overlay = null;
}
this.overlay = new CountryCustomOverlay({
map: this.map,
html: this.html,
marker: this
});
if (this.map.lastOpenedMarker && this.map.lastOpenedMarker != this) {
this.map.lastOpenedMarker.overlay.hide();
this.map.lastOpenedMarker.markerOpened = false;
this.map.lastOpenedMarker.makeInactive();
}
this.map.lastOpenedMarker = this;
this.markerOpened = true;
};
this.closeMarker = function() {
this.markerOpened = false;
if (this.overlay) {
this.overlay.hide();
if (this.map.lastOpenedMarker) {
this.map.lastOpenedMarker = null;
}
}
};
this.makeActive = function () {
this.marker.setIcon(this.activeImage);
};
this.makeInactive = function () {
this.marker.setIcon(this.inactiveImage);
};
this.inactiveImage = new google.maps.MarkerImage(this.inactiveImageURL);
this.activeImage = new google.maps.MarkerImage(this.activeImageURL);
if (item.zIndex) {
this.zIndex = item.zIndex;
}
this.marker = new google.maps.Marker(
{
position:new google.maps.LatLng(item.lat, item.lng),
map:null,
icon:this.inactiveImage,
title:this.title,
zIndex: this.zIndex
}
);
this.makeHTML = function () {
var img = "/i/noPhoto/175x99.png";
if (this.bubbleContent.media)
img = this.bubbleContent.media;
var parent = "";
if (this.bubbleContent.parentTitle)
parent = ',
' + this.bubbleContent.parentTitle + '';
var additionalPlaceInformation = '';
if (this.bubbleContent.additionalPlaceInformation)
additionalPlaceInformation = this.bubbleContent.additionalPlaceInformation;
return '\
\
';
};
//
//
// this.makeHTML2 = function () {
//
// var img = "/i/noPhoto/175x99.png";
// if (this.image )
// img = this.image;
//
// var flight = "";
//
// if (typeof(this.flight.show) != "undefined" && typeof(this.flight.km) != "undefined" && typeof(this.flight.time) != "undefined" && this.flight.show == 1 && this.flight.km != '' && this.flight.time != '') {
// flight = '
≈ ' + this.flight.km + ' км, ' + this.flight.time + ' (из Москвы)
';
// }
//
// var visa = "";
//
// if (this.visa.necessary_for_russians && this.visa.necessary_for_russians == 'да') {
//
// visa = "
Нужна виза";
//
// if (this.visa.price && this.visa.price != '') {
// visa += ", стоимость ≈ " + this.visa.price + '
';
// }
// } else if (this.visa.necessary_for_russians == 'нет') {
// visa = "Виза не нужна
";
// } else if (this.visa.necessary_for_russians == 'по прибытии') {
//
// visa = "
Виза оформляется по прибытии
";
// }
//
// var touristSeason = "";
// if (this.touristSeason) {
//
// touristSeason = '
Туристический сезон: ' + this.touristSeason + '
';
// }
//
// return '\
//
\
//
\
//
\
//
\
//
' + this.title + '\
//
\
// ' + flight + '\
// ' + touristSeason + '\
// ' + visa + '\
//
\
//
\
//
\
//
\
// ';
// };
this.html = this.makeHTML();
}
/* /js/map/EventMarker.js end */
/* /js/map/CountryMarker.js begin */
/**
* Created by JetBrains PhpStorm.
* User: IKamenev
* Date: 21.12.11
* Time: 16:05
* To change this template use File | Settings | File Templates.
*/
function CountryCustomOverlay(options) {
this._marker = options.marker;
this._marker = options.marker.marker;
this._width = 420;
this._height = 200;
this._div = null;
this._html = options.html;
this._verticalOffset = 50; // расстояние от маркера до нижнего края всплывающего окошка
this._doNotClose = false;
this.setMap(options.map);
}
CountryCustomOverlay.prototype = new CustomOverlay();
function CountryMarker(item) {
this.id = item.id;
this.type = "country";
this.marker = null;
this.title = item.title;
this.link = item.link;
this.showBubble = true;
this.bubbleContent = null;
if (item.bubbleContent)
this.bubbleContent = item.bubbleContent;
if (typeof(item.showBubble) != "undefined")
this.showBubble = item.showBubble;
this.countryPath = item.countryPath;
this.inactiveImageURL = item.flag_inactive;
this.activeImageURL = item.flag_active;
this.activeImage = null;
this.inactiveImage = null;
this.overlay = null;
this.zIndex = 1;
this.visible = false;
// mouseOut с маркера
this.mouseOver = function (map) {
this.map = map;
if (this.showBubble) {
this.makeActive();
this.openMarker();
}
};
// mouseOver над маркером
this.mouseOut = function (map) {
this.map = map;
if (this.showBubble) {
this.closeMarker();
this.makeInactive();
}
};
// клик по маркеру
this.click = function (map) {
if (this.showBubble && this.link && typeof(this.link) != 'undefined' && this.link != '') {
this.closeMarker();
$("body").hide();
var that = this;
setTimeout(function() {
window.document.location = that.link;
}, 30);
}
// this.map = map;
// this.openMarker();
// this.makeActive();
// если показываем при наведении - то скроллить на балун не надо
//map.setCenter(this.marker.getPosition());
};
this.markerOpened = false;
this.openMarker = function() {
if (this.markerOpened)
return;
if (this.overlay) {
delete(this.overlay);
this.overlay = null;
}
this.overlay = new CountryCustomOverlay({
map: this.map,
html: this.html,
marker: this
});
if (this.map.lastOpenedMarker && this.map.lastOpenedMarker != this) {
this.map.lastOpenedMarker.overlay.hide();
this.map.lastOpenedMarker.markerOpened = false;
this.map.lastOpenedMarker.makeInactive();
}
this.map.lastOpenedMarker = this;
this.markerOpened = true;
};
this.closeMarker = function() {
this.markerOpened = false;
if (this.overlay) {
this.overlay.hide();
if (this.map.lastOpenedMarker) {
this.map.lastOpenedMarker = null;
}
}
};
this.makeActive = function () {
this.marker.setIcon(this.activeImage);
};
this.makeInactive = function () {
this.marker.setIcon(this.inactiveImage);
};
this.inactiveImage = new google.maps.MarkerImage(this.inactiveImageURL);
this.activeImage = new google.maps.MarkerImage(this.activeImageURL);
if (item.zIndex) {
this.zIndex = item.zIndex;
}
this.marker = new google.maps.Marker(
{
position:new google.maps.LatLng(item.lat, item.lng),
map:null,
icon:this.inactiveImage,
title:this.title,
zIndex: this.zIndex
}
);
this.makeHTML = function () {
var img = "/i/noPhoto/175x99.png";
var link = "", flight = "", visa = "", parent = "", touristSeason = "", airTemperatures = "";
if(this.bubbleContent){
if(this.bubbleContent.link)
link = this.bubbleContent.link;
if (this.bubbleContent.media)
img = this.bubbleContent.media;
if (typeof(this.bubbleContent.flightFromMoscowShow) != "undefined"
&& typeof(this.bubbleContent.flightFromMoscowKM) != "undefined"
&& typeof(this.bubbleContent.flightFromMoscowHours) != "undefined"
&& this.bubbleContent.flightFromMoscowShow == 1
&& this.bubbleContent.flightFromMoscowKM != ''
&& this.bubbleContent.flightFromMoscowHours != '') {
flight = '
≈ ' + this.bubbleContent.flightFromMoscowKM
+ ' км, ' + this.bubbleContent.flightFromMoscowHours + ' (из Москвы)
';
}
if (this.bubbleContent.visa) {
if (this.bubbleContent.visa.necessary_for_russians && this.bubbleContent.visa.necessary_for_russians == 'да') {
visa = "
Нужна виза";
if (this.bubbleContent.visa.visa_price && this.bubbleContent.visa.visa_price != '') {
visa += ", стоимость ≈ " + this.bubbleContent.visa.visa_price + ' ' + this.bubbleContent.visa.visa_price_currency + '
';
}
visa = '
' + visa + '
';
} else if (this.bubbleContent.visa.necessary_for_russians == 'нет') {
visa = "
Виза не нужна
";
} else if (this.bubbleContent.visa.necessary_for_russians == 'по прибытии') {
visa = "
Виза оформляется по прибытию
";
}
}
if (this.bubbleContent.parentTitle)
parent = ',
' + this.bubbleContent.parentTitle + '';
//if (this.bubbleContent.tourist_season && this.bubbleContent.showSeasons == 1) {
if (this.bubbleContent.tourist_season) {
touristSeason = '
Туристический сезон: ' + this.bubbleContent.tourist_season + '
';
}
if (this.bubbleContent.airTemperatures) {
airTemperatures = 'Средняя температура:
';
var cnt = 0;
for (var i in this.bubbleContent.airTemperatures) {
var temp = this.bubbleContent.airTemperatures[i];
var color = '#119411;';
if (temp.tempEqual != 1)
color = '#ff0000';
if (cnt > 0)
airTemperatures += ', ';
airTemperatures += '
' + temp.month_name + ' ' + temp.temp_text + '°C';
color = '#119411;';
if (temp.waterTempEqual != 1)
color = '#ff0000';
if (temp.water_temp_text) {
airTemperatures += ' (';
airTemperatures += '
вода ' + temp.water_temp_text + '°C';
airTemperatures += ') ';
}
cnt++;
}
}
}
return '\
\
\
\
\
' + this.title + '' + parent + '\
\
' + flight + '\
' + touristSeason + '\
' + visa + '\
' + airTemperatures + '\
\
\
\
\
';
};
this.html = this.makeHTML();
}
/* /js/map/CountryMarker.js end */
/* /js/map/CityMarker.js begin */
var a;
var b;
/**
* Created by JetBrains PhpStorm.
* User: IKamenev
* Date: 21.12.11
* Time: 16:05
* To change this template use File | Settings | File Templates.
*/
function CityCustomOverlay(options) {
this._marker = options.marker;
this._marker = options.marker.marker;
this._width = 420;
this._height = 200;
this._div = null;
this._html = options.html;
this._verticalOffset = 23; // расстояние от маркера до нижнего края всплывающего окошка
this._doNotClose = false;
this.setMap(options.map);
}
CityCustomOverlay.prototype = new CustomOverlay();
function CityMarker(item) {
this.id = item.id;
this.type = "city";
this.continent = item.continent;
this.marker = null;
this.title = item.title;
this.link = item.link;
this.showBubble = true;
this.bubbleContent = null;
if (item.bubbleContent)
this.bubbleContent = item.bubbleContent;
if (typeof(item.showBubble) != "undefined")
this.showBubble = item.showBubble;
this.countryPath = item.countryPath;
this.inactiveImageURL = item.flag_inactive;
this.activeImageURL = item.flag_active;
this.activeImage = null;
this.inactiveImage = null;
this.overlay = null;
this.zIndex = 1;
// mouseOut с маркера
this.mouseOver = function (map) {
this.map = map;
if (this.showBubble) {
this.makeActive();
this.openMarker();
}
return true;
};
// mouseOver над маркером
this.mouseOut = function (map) {
this.map = map;
if (this.showBubble) {
this.closeMarker();
this.makeInactive();
}
};
// клик по маркеру
this.click = function (map) {
if (this.showBubble && this.link && typeof(this.link) != 'undefined' && this.link != '') {
this.closeMarker();
$("body").hide();
var that = this;
setTimeout(function() {
window.document.location = that.link;
}, 30);
}
};
this.markerOpened = false;
this.openMarker = function() {
if (this.markerOpened)
return;
if (this.overlay) {
delete(this.overlay);
this.overlay = null;
}
this.overlay = new CityCustomOverlay({
map: this.map,
html: this.html,
marker: this
});
if (this.map.lastOpenedMarker && this.map.lastOpenedMarker != this) {
this.map.lastOpenedMarker.overlay.hide();
this.map.lastOpenedMarker.markerOpened = false;
this.map.lastOpenedMarker.makeInactive();
}
this.map.lastOpenedMarker = this;
this.markerOpened = true;
};
this.closeMarker = function() {
this.markerOpened = false;
if (this.overlay) {
this.overlay.hide();
if (this.map.lastOpenedMarker) {
this.map.lastOpenedMarker = null;
}
}
};
this.makeActive = function () {
this.marker.setIcon(this.activeImage);
};
this.makeInactive = function () {
this.marker.setIcon(this.inactiveImage);
};
this.inactiveImage = new google.maps.MarkerImage(this.inactiveImageURL);
this.activeImage = new google.maps.MarkerImage(this.activeImageURL);
if (item.zIndex) {
this.zIndex = item.zIndex;
}
this.marker = new google.maps.Marker(
{
position:new google.maps.LatLng(item.lat, item.lng),
map:null,
icon:this.inactiveImage,
title:this.title,
zIndex: this.zIndex
}
);
this.makeHTML = function () {
var img = "/i/noPhoto/175x99.png";
if (this.bubbleContent.media && this.bubbleContent.media.length > 1)
img = this.bubbleContent.media;
var flight = "";
if (typeof(this.bubbleContent.flightFromMoscowShow) != "undefined"
&& typeof(this.bubbleContent.flightFromMoscowKM) != "undefined"
&& typeof(this.bubbleContent.flightFromMoscowHours) != "undefined"
&& this.bubbleContent.flightFromMoscowShow == 1
&& this.bubbleContent.flightFromMoscowKM != ''
&& this.bubbleContent.flightFromMoscowHours != '') {
flight = '
≈ ' + this.bubbleContent.flightFromMoscowKM
+ ' км, ' + this.bubbleContent.flightFromMoscowHours + ' (из Москвы)
';
}
var visa = "";
if (this.bubbleContent.visa) {
if (this.bubbleContent.visa.necessary_for_russians && this.bubbleContent.visa.necessary_for_russians == 'да') {
visa = "
Нужна виза";
if (this.bubbleContent.visa.visa_price && this.bubbleContent.visa.visa_price != '') {
visa += ", стоимость ≈ " + this.bubbleContent.visa.visa_price + ' ' + this.bubbleContent.visa.visa_price_currency + '
';
}
visa = '
' + visa + '
';
} else if (this.bubbleContent.visa.necessary_for_russians == 'нет') {
visa = "
Виза не нужна
";
} else if (this.bubbleContent.visa.necessary_for_russians == 'по прибытии') {
visa = "
Виза оформляется по прибытию
";
}
}
var parent = "";
if (this.bubbleContent.parentTitle)
parent = ',
' + this.bubbleContent.parentTitle + '';
var touristSeason = "";
//if (this.bubbleContent.tourist_season && this.bubbleContent.showSeasons == 1) {
if (this.bubbleContent.tourist_season) {
touristSeason = '
Туристический сезон: ' + this.bubbleContent.tourist_season + '
';
}
var destination = this.title;
if (this.link.length > 1)
destination = '
' + this.title + '';
var airTemperatures = '';
if (this.bubbleContent.airTemperatures) {
airTemperatures = 'Средняя температура:
';
var cnt = 0;
for (var i in this.bubbleContent.airTemperatures) {
var temp = this.bubbleContent.airTemperatures[i];
var color = '#119411;';
if (temp.tempEqual != 1)
color = '#ff0000';
if (cnt > 0)
airTemperatures += ', ';
airTemperatures += '
' + temp.month_name + ' ' + temp.temp_text + '°C';
color = '#119411;';
if (temp.waterTempEqual != 1)
color = '#ff0000';
if (temp.water_temp_text) {
airTemperatures += ' (';
airTemperatures += '
вода ' + temp.water_temp_text + '°C';
airTemperatures += ') ';
}
cnt++;
}
}
return '\
\
\
\
\
' + destination + parent + '
\
\
' + flight + '\
' + touristSeason + '\
' + visa + '\
' + airTemperatures + '\
\
\
\
\
';
};
this.html = this.makeHTML();
}
/* /js/map/CityMarker.js end */
/* /js/map/PoiMarker.js begin */
/**
* Created by JetBrains PhpStorm.
* User: IKamenev
* Date: 21.12.11
* Time: 16:05
* To change this template use File | Settings | File Templates.
*/
function PoiCustomOverlay(options) {
this._markerItem = options.marker;
this._marker = options.marker.marker;
this._width = options.width;
this._height = 200;
this._div = null;
this._html = options.html;
this._verticalOffset = 31; // расстояние от маркера до нижнего края всплывающего окошка
this._doNotClose = false;
this.setMap(options.map);
}
PoiCustomOverlay.prototype = new CustomOverlay();
function PoiMarker(item) {
this.id = item.id;
this.tags = [];
for (i in item.tags) {
this.tags.push(item.tags[i].toString());
}
if(item.rawId)
this.rawId = item.rawId;
this.title = (item.title) ? item.title : '';
this.link = (item.link) ? item.link : '';
this.showBubble = true;
this.bubbleContent = null;
this.bubbleAjaxBaseUrl = null;
this.bubbleLoaded = false;
this.bubbleLoading = false;
this.bubbleWidth = item.width ? item.width : 500;
if(item.bubbleAjaxBaseUrl)
this.bubbleAjaxBaseUrl = item.bubbleAjaxBaseUrl;
if (item.bubbleContent)
this.bubbleContent = item.bubbleContent;
if (typeof(item.showBubble) != "undefined")
this.showBubble = item.showBubble;
this.type = "poi";
this.marker = null;
this.html = "";
if(item.activeImageURL && item.activeImageURL != 'undefined' && item.activeImageURL != '')
this.activeImageURL = item.activeImageURL;
else if(item.markerHover && item.markerHover != 'undefined' && item.markerHover != '')
this.activeImageURL = item.markerHover;
else
this.activeImageURL = '/i/icons/poi_bubble_hover.png';
if(item.inactiveImageURL && item.inactiveImageURL != 'undefined' && item.inactiveImageURL != '')
this.inactiveImageURL = item.inactiveImageURL;
else if(item.markerNormal && item.markerNormal != 'undefined' && item.markerNormal != '')
this.inactiveImageURL = item.markerNormal;
else
this.inactiveImageURL = '/i/icons/poi_bubble.png';
this.activeImage = null;
this.inactiveImage = null;
this.selected = false;
this.overlay = null;
this.zIndex = 1;
// прелоад неактивной картинки, чтобы исключить мигание
this.preloadImage = function (img) {
$("
").attr("src", img);
};
this.preloadImage(this.inactiveImageURL);
this.preloadImage(this.activeImageURL);
// mouseOut с маркера
this.mouseOver = function (map) {
this.map = map;
this.makeActive();
this.openMarker();
};
// mouseOver над маркером
this.mouseOut = function (map) {
this.map = map;
this.closeMarker();
this.makeInactive();
};
// клик по маркеру
this.click = function (map) {
if (this.link && typeof(this.link) != 'undefined' && this.link != '') {
this.closeMarker();
$("body").hide();
var that = this;
setTimeout(function() {
window.document.location = that.link;
}, 30);
}
};
this.markerOpened = false;
this.openMarker = function() {
if (this.markerOpened)
return;
if (this.overlay) {
delete(this.overlay);
this.overlay = null;
}
if(this.bubbleAjaxBaseUrl && !this.bubbleLoaded && !this.bubbleContent) {
this.loadAjaxBubbleHtml();
}
this.overlay = new PoiCustomOverlay({
map: this.map,
html: this.html,
width: this.bubbleWidth,
marker: this
});
if (this.map.lastOpenedMarker && this.map.lastOpenedMarker != this) {
this.map.lastOpenedMarker.overlay.hide();
this.map.lastOpenedMarker.markerOpened = false;
this.map.lastOpenedMarker.makeInactive();
}
this.map.lastOpenedMarker = this;
this.markerOpened = true;
};
this.closeMarker = function() {
this.markerOpened = false;
if (this.overlay) {
this.overlay.hide();
if (this.map.lastOpenedMarker) {
this.map.lastOpenedMarker = null;
}
}
};
this.loadAjaxBubbleHtml = function() {
if(this.bubbleLoading)
return;
this.html = this.makeLoadingHTML();
//this.bubbleWidth = 160;
var url = this.bubbleAjaxBaseUrl + '/' + this.rawId;
var _this = this;
$.ajax({
url: url,
dataType: 'json',
type : 'POST',
success: function (data) {
if(data.result && data.result.content && data.result.success == 'OK') {
var content = data.result.content;
_this.title = content.title;
_this.link = content.link;
_this.bubbleContent = content.bubbleContent;
_this.html = _this.makeHTML();
//_this.bubbleWidth = 550;
_this.bubbleLoaded = true;
if(_this.overlay && _this.markerOpened) {
_this.closeMarker();
_this.openMarker();
}
} else {
_this.html = "";
}
this.bubbleLoading = false;
}
});
this.bubbleLoading = true;
};
this.makeActive = function () {
this.marker.setIcon(this.activeImage);
this.marker.setZIndex(this.zIndex + 1);
};
this.makeInactive = function () {
if (!this.selected)
this.marker.setIcon(this.inactiveImage);
this.marker.setZIndex(this.zIndex);
};
this.select = function() {
this.selected = true;
this.makeActive();
this.openMarker();
};
this.deselect = function() {
this.selected = false;
this.makeInactive();
this.closeMarker();
};
this.scrollTo = function() {
this.map.setCenter(this.marker.getPosition());
};
var baseIcon = "";
this.inactiveImage = new google.maps.MarkerImage(this.inactiveImageURL);
this.activeImage = new google.maps.MarkerImage(this.activeImageURL);
if (item.zIndex) {
this.zIndex = item.zIndex;
}
this.marker = new google.maps.Marker(
{
position:new google.maps.LatLng(item.lat, item.lng),
map:null,
icon:this.inactiveImage,
title:this.title,
zIndex: this.zIndex
}
);
this.makeHTML = function () {
if(!this.bubbleContent)
return "";
var img = "/i/noPhoto/175x99.png";
if (this.bubbleContent.media != '')
img = this.bubbleContent.media;
var editorsChoiceImg = '', blueFlag = '', michelinStars = '';
if (this.bubbleContent.editorsChoice) {
editorsChoiceImg = '
';
}
if (this.bubbleContent.blueFlag) {
blueFlag = '
';
}
if (this.bubbleContent.michelinStars) {
michelinStars = '
';
}
var parent = ',
' + this.bubbleContent.parentTitle + '';
if (typeof(this.bubbleContent.parentLink) == 'undefined' || typeof(this.bubbleContent.parentTitle) == 'undefined' || this.bubbleContent.parentTitle == null) {
parent = '';
}
var categories = '
' + this.bubbleContent.categories + '
';
if (typeof(this.bubbleContent.categories) == "undefined")
categories = '';
var hotelStars = '';
if (this.bubbleContent.hotelStars) {
this.bubbleContent.hotelStars = parseInt(this.bubbleContent.hotelStars);
if (this.bubbleContent.hotelStars > 0) hotelStars = '
';
}
return '\
\
\
\
\
\
' + editorsChoiceImg + blueFlag + ' \
' + this.bubbleContent.shortDescription.replace( /\\'/g, "'").replace(/\\\\/g, "\\") + '
\
\
\
\
';
};
this.makeLoadingHTML = function () {
return '\
\
\
Загрузка...\
\
\
';
};
this.html = this.makeHTML();
}
/* /js/map/PoiMarker.js end */
/* /js/map/RegionMarker.js begin */
/**
* Created by JetBrains PhpStorm.
* User: IKamenev
* Date: 21.12.11
* Time: 16:05
* To change this template use File | Settings | File Templates.
*/
function RegionCustomOverlay(options) {
this._marker = options.marker;
this._marker = options.marker.marker;
this._width = 420;
this._height = 200;
this._div = null;
this._html = options.html;
this._verticalOffset = 23; // расстояние от маркера до нижнего края всплывающего окошка
this._doNotClose = false;
this.setMap(options.map);
}
RegionCustomOverlay.prototype = new CustomOverlay();
function RegionMarker(item) {
this.id = item.id;
this.type = "region";
this.continent = item.continent;
this.marker = null;
this.title = item.title;
this.link = item.link;
this.showBubble = true;
this.bubbleContent = null;
if (item.bubbleContent)
this.bubbleContent = item.bubbleContent;
if (typeof(item.showBubble) != "undefined")
this.showBubble = item.showBubble;
this.countryPath = item.countryPath;
this.activeImageURL = item.flag_active;
this.inactiveImageURL = item.flag_inactive;
this.activeImage = null;
this.inactiveImage = null;
this.overlay = null;
this.zIndex = 1;
// mouseOut с маркера
this.mouseOver = function (map) {
this.map = map;
if (this.showBubble) {
this.makeActive();
this.openMarker();
}
return true;
};
// mouseOver над маркером
this.mouseOut = function (map) {
this.map = map;
if (this.showBubble) {
this.closeMarker();
this.makeInactive();
}
};
// клик по маркеру
this.click = function (map) {
if (this.showBubble && this.link && typeof(this.link) != 'undefined' && this.link != '') {
this.closeMarker();
$("body").hide();
var that = this;
setTimeout(function() {
window.document.location = that.link;
}, 30);
}
};
this.markerOpened = false;
this.openMarker = function() {
if (this.markerOpened)
return;
if (this.overlay) {
delete(this.overlay);
this.overlay = null;
}
this.overlay = new RegionCustomOverlay({
map: this.map,
html: this.html,
marker: this
});
if (this.map.lastOpenedMarker && this.map.lastOpenedMarker != this) {
this.map.lastOpenedMarker.overlay.hide();
this.map.lastOpenedMarker.markerOpened = false;
this.map.lastOpenedMarker.makeInactive();
}
this.map.lastOpenedMarker = this;
this.markerOpened = true;
};
this.closeMarker = function() {
this.markerOpened = false;
if (this.overlay) {
this.overlay.hide();
if (this.map.lastOpenedMarker) {
this.map.lastOpenedMarker = null;
}
}
};
this.makeActive = function () {
this.marker.setIcon(this.activeImage);
};
this.makeInactive = function () {
this.marker.setIcon(this.inactiveImage);
};
this.inactiveImage = new google.maps.MarkerImage(this.inactiveImageURL);
this.activeImage = new google.maps.MarkerImage(this.activeImageURL);
if (item.zIndex) {
this.zIndex = item.zIndex;
}
this.marker = new google.maps.Marker(
{
position:new google.maps.LatLng(item.lat, item.lng),
map:null,
icon:this.inactiveImage,
title:this.title,
zIndex: this.zIndex
}
);
this.makeHTML = function () {
var img = "/i/noPhoto/175x99.png";
if (this.bubbleContent.media && this.bubbleContent.media.length > 1)
img = this.bubbleContent.media;
var flight = "";
if (typeof(this.bubbleContent.flightFromMoscowShow) != "undefined"
&& typeof(this.bubbleContent.flightFromMoscowKM) != "undefined"
&& typeof(this.bubbleContent.flightFromMoscowHours) != "undefined"
&& this.bubbleContent.flightFromMoscowShow == 1
&& this.bubbleContent.flightFromMoscowKM != ''
&& this.bubbleContent.flightFromMoscowHours != '') {
flight = '
≈ ' + this.bubbleContent.flightFromMoscowKM
+ ' км, ' + this.bubbleContent.flightFromMoscowHours + ' (из Москвы)
';
}
var visa = "";
if (this.bubbleContent.visa) {
if (this.bubbleContent.visa.necessary_for_russians && this.bubbleContent.visa.necessary_for_russians == 'да') {
visa = "
Нужна виза";
if (this.bubbleContent.visa.visa_price && this.bubbleContent.visa.visa_price != '') {
visa += ", стоимость ≈ " + this.bubbleContent.visa.visa_price + ' ' + this.bubbleContent.visa.visa_price_currency + '
';
}
visa = '
' + visa + '
';
} else if (this.bubbleContent.visa.necessary_for_russians == 'нет') {
visa = "
Виза не нужна
";
} else if (this.bubbleContent.visa.necessary_for_russians == 'по прибытии') {
visa = "
Виза оформляется по прибытию
";
}
}
var parent = "";
if (this.bubbleContent.parentTitle)
parent = ',
' + this.bubbleContent.parentTitle + '';
var touristSeason = "";
//if (this.bubbleContent.tourist_season && this.bubbleContent.showSeasons == 1) {
if (this.bubbleContent.tourist_season) {
touristSeason = '
Туристический сезон: ' + this.bubbleContent.tourist_season + '
';
}
var destination = this.title;
if (this.link.length > 1)
destination = '
' + this.title + '';
var airTemperatures = '';
if (this.bubbleContent.airTemperatures) {
airTemperatures = 'Средняя температура:
';
var cnt = 0;
for (var i in this.bubbleContent.airTemperatures) {
var temp = this.bubbleContent.airTemperatures[i];
var color = '#119411;';
if (temp.tempEqual != 1)
color = '#ff0000';
if (cnt > 0)
airTemperatures += ', ';
airTemperatures += '
' + temp.month_name + ' ' + temp.temp_text + '°C';
color = '#119411;';
if (temp.waterTempEqual != 1)
color = '#ff0000';
if (temp.water_temp_text) {
airTemperatures += ' (';
airTemperatures += '
вода ' + temp.water_temp_text + '°C';
airTemperatures += ') ';
}
cnt++;
}
}
return '\
\
\
\
\
' + destination + parent + '
\
\
' + flight + '\
' + touristSeason + '\
' + visa + '\
' + airTemperatures + '\
\
\
\
\
';
};
this.html = this.makeHTML();
}
/* /js/map/RegionMarker.js end */
/* /js/markerclusterer.js begin */
/*jslint browser: true, confusion: true, sloppy: true, vars: true, nomen: false, plusplus: false, indent: 2 */
/*global window,google */
/**
* @name MarkerClustererPlus for Google Maps V3
* @version 2.0.9 [February 20, 2012]
* @author Gary Little
* @fileoverview
* The library creates and manages per-zoom-level clusters for large amounts of markers.
*
* This is an enhanced V3 implementation of the
* V2 MarkerClusterer by Xiaoxi Wu. It is based on the
* V3 MarkerClusterer port by Luke Mahe. MarkerClustererPlus was created by Gary Little.
*
* v2.0 release: MarkerClustererPlus v2.0 is backward compatible with MarkerClusterer v1.0. It
* adds support for the ignoreHidden
, title
, printable
,
* batchSizeIE
, and calculator
properties as well as support for
* four more events. It also allows greater control over the styling of the text that appears
* on the cluster marker. The documentation has been significantly improved and the overall
* code has been simplified and polished. Very large numbers of markers can now be managed
* without causing Javascript timeout errors on Internet Explorer. Note that the name of the
* clusterclick
event has been deprecated. The new name is click
,
* so please change your application code now.
*/
/**
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @name ClusterIconStyle
* @class This class represents the object for values in the styles
array passed
* to the {@link MarkerClusterer} constructor. The element in this array that is used to
* style the cluster icon is determined by calling the calculator
function.
*
* @property {string} url The URL of the cluster icon image file. Required.
* @property {number} height The height (in pixels) of the cluster icon. Required.
* @property {number} width The width (in pixels) of the cluster icon. Required.
* @property {Array} [anchor] The anchor position (in pixels) of the label text to be shown on
* the cluster icon, relative to the top left corner of the icon.
* The format is [yoffset, xoffset]
. The yoffset
must be positive
* and less than height
and the xoffset
must be positive and less
* than width
. The default is to anchor the label text so that it is centered
* on the icon.
* @property {Array} [anchorIcon] The anchor position (in pixels) of the cluster icon. This is the
* spot on the cluster icon that is to be aligned with the cluster position. The format is
* [yoffset, xoffset]
where yoffset
increases as you go down and
* xoffset
increases to the right. The default anchor position is the center of the
* cluster icon.
* @property {string} [textColor="black"] The color of the label text shown on the
* cluster icon.
* @property {number} [textSize=11] The size (in pixels) of the label text shown on the
* cluster icon.
* @property {number} [textDecoration="none"] The value of the CSS text-decoration
* property for the label text shown on the cluster icon.
* @property {number} [fontWeight="bold"] The value of the CSS font-weight
* property for the label text shown on the cluster icon.
* @property {number} [fontStyle="normal"] The value of the CSS font-style
* property for the label text shown on the cluster icon.
* @property {number} [fontFamily="Arial,sans-serif"] The value of the CSS font-family
* property for the label text shown on the cluster icon.
* @property {string} [backgroundPosition="0 0"] The position of the cluster icon image
* within the image defined by url
. The format is "xpos ypos"
* (the same format as for the CSS background-position
property). You must set
* this property appropriately when the image defined by url
represents a sprite
* containing multiple images.
*/
/**
* @name ClusterIconInfo
* @class This class is an object containing general information about a cluster icon. This is
* the object that a calculator
function returns.
*
* @property {string} text The text of the label to be shown on the cluster icon.
* @property {number} index The index plus 1 of the element in the styles
* array to be used to style the cluster icon.
*/
/**
* A cluster icon.
*
* @constructor
* @extends google.maps.OverlayView
* @param {Cluster} cluster The cluster with which the icon is to be associated.
* @param {Array} [styles] An array of {@link ClusterIconStyle} defining the cluster icons
* to use for various cluster sizes.
* @private
*/
function ClusterIcon(cluster, styles) {
cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView);
this.cluster_ = cluster;
this.styles_ = styles;
this.center_ = null;
this.div_ = null;
this.sums_ = null;
this.visible_ = false;
this.setMap(cluster.getMap()); // Note: this causes onAdd to be called
}
/**
* Adds the icon to the DOM.
*/
ClusterIcon.prototype.onAdd = function () {
var cClusterIcon = this;
var cMouseDownInCluster;
var cDraggingMapByCluster;
this.div_ = document.createElement("div");
if (this.visible_) {
this.show();
}
this.getPanes().overlayMouseTarget.appendChild(this.div_);
// Fix for Issue 157
google.maps.event.addListener(this.getMap(), "bounds_changed", function () {
cDraggingMapByCluster = cMouseDownInCluster;
});
google.maps.event.addDomListener(this.div_, "mousedown", function () {
cMouseDownInCluster = true;
cDraggingMapByCluster = false;
});
google.maps.event.addDomListener(this.div_, "click", function (e) {
cMouseDownInCluster = false;
if (!cDraggingMapByCluster) {
var mz;
var mc = cClusterIcon.cluster_.getMarkerClusterer();
/**
* This event is fired when a cluster marker is clicked.
* @name MarkerClusterer#click
* @param {Cluster} c The cluster that was clicked.
* @event
*/
google.maps.event.trigger(mc, "click", cClusterIcon.cluster_);
google.maps.event.trigger(mc, "clusterclick", cClusterIcon.cluster_); // deprecated name
// The default click handler follows. Disable it by setting
// the zoomOnClick property to false.
if (mc.getZoomOnClick()) {
// Zoom into the cluster.
mz = mc.getMaxZoom();
mc.getMap().fitBounds(cClusterIcon.cluster_.getBounds());
// Don't zoom beyond the max zoom level
if (mz !== null && (mc.getMap().getZoom() > mz)) {
mc.getMap().setZoom(mz + 1);
}
}
// Prevent event propagation to the map:
e.cancelBubble = true;
if (e.stopPropagation) {
e.stopPropagation();
}
}
});
google.maps.event.addDomListener(this.div_, "mouseover", function () {
var mc = cClusterIcon.cluster_.getMarkerClusterer();
/**
* This event is fired when the mouse moves over a cluster marker.
* @name MarkerClusterer#mouseover
* @param {Cluster} c The cluster that the mouse moved over.
* @event
*/
google.maps.event.trigger(mc, "mouseover", cClusterIcon.cluster_);
});
google.maps.event.addDomListener(this.div_, "mouseout", function () {
var mc = cClusterIcon.cluster_.getMarkerClusterer();
/**
* This event is fired when the mouse moves out of a cluster marker.
* @name MarkerClusterer#mouseout
* @param {Cluster} c The cluster that the mouse moved out of.
* @event
*/
google.maps.event.trigger(mc, "mouseout", cClusterIcon.cluster_);
});
};
/**
* Removes the icon from the DOM.
*/
ClusterIcon.prototype.onRemove = function () {
if (this.div_ && this.div_.parentNode) {
this.hide();
google.maps.event.clearInstanceListeners(this.div_);
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
}
};
/**
* Draws the icon.
*/
ClusterIcon.prototype.draw = function () {
if (this.visible_) {
var pos = this.getPosFromLatLng_(this.center_);
if(this.iconTopMargin)
pos.y = Math.round(pos.y + this.iconTopMargin);
this.div_.style.top = pos.y + "px";
this.div_.style.left = pos.x + "px";
}
};
/**
* Hides the icon.
*/
ClusterIcon.prototype.hide = function () {
if (this.div_) {
this.div_.style.display = "none";
}
this.visible_ = false;
};
/**
* Positions and shows the icon.
*/
ClusterIcon.prototype.show = function () {
if (this.div_) {
var pos = this.getPosFromLatLng_(this.center_);
this.div_.style.cssText = this.createCss(pos);
if (this.cluster_.printable_) {
// (Would like to use "width: inherit;" below, but doesn't work with MSIE)
this.div_.innerHTML = "
" + this.sums_.text + "
";
} else {
this.div_.innerHTML = this.sums_.text;
}
this.div_.title = this.cluster_.getMarkerClusterer().getTitle();
this.div_.style.display = "";
}
this.visible_ = true;
};
/**
* Sets the icon styles to the appropriate element in the styles array.
*
* @param {ClusterIconInfo} sums The icon label text and styles index.
*/
ClusterIcon.prototype.useStyle = function (sums) {
this.sums_ = sums;
var index = Math.max(0, sums.index - 1);
index = Math.min(this.styles_.length - 1, index);
var style = this.styles_[index];
this.url_ = style.url;
this.height_ = style.height;
this.width_ = style.width;
this.anchor_ = style.anchor;
this.anchorIcon_ = style.anchorIcon || [parseInt(this.height_ / 2, 10), parseInt(this.width_ / 2, 10)];
this.textColor_ = style.textColor || "black";
this.textSize_ = style.textSize || 11;
this.textDecoration_ = style.textDecoration || "none";
this.fontWeight_ = style.fontWeight || "bold";
this.fontStyle_ = style.fontStyle || "normal";
this.fontFamily_ = style.fontFamily || "Arial,sans-serif";
this.backgroundPosition_ = style.backgroundPosition || "0 0";
this.iconTopMargin = style.iconTopMargin || 0;
};
/**
* Sets the position at which to center the icon.
*
* @param {google.maps.LatLng} center The latlng to set as the center.
*/
ClusterIcon.prototype.setCenter = function (center) {
this.center_ = center;
};
/**
* Creates the cssText style parameter based on the position of the icon.
*
* @param {google.maps.Point} pos The position of the icon.
* @return {string} The CSS style text.
*/
ClusterIcon.prototype.createCss = function (pos) {
var style = [];
if (!this.cluster_.printable_) {
style.push('background-image:url(' + this.url_ + ');');
style.push('background-position:' + this.backgroundPosition_ + ';');
}
if (typeof this.anchor_ === 'object') {
if (typeof this.anchor_[0] === 'number' && this.anchor_[0] > 0 &&
this.anchor_[0] < this.height_) {
style.push('height:' + (this.height_ - this.anchor_[0]) +
'px; padding-top:' + this.anchor_[0] + 'px;');
} else {
style.push('height:' + this.height_ + 'px; line-height:' + this.height_ +
'px;');
}
if (typeof this.anchor_[1] === 'number' && this.anchor_[1] > 0 &&
this.anchor_[1] < this.width_) {
style.push('width:' + (this.width_ - this.anchor_[1]) +
'px; padding-left:' + this.anchor_[1] + 'px;');
} else {
style.push('width:' + this.width_ + 'px; text-align:center;');
}
} else {
style.push('height:' + this.height_ + 'px; line-height:' +
this.height_ + 'px; width:' + this.width_ + 'px; text-align:center;');
}
if(this.iconTopMargin)
pos.y = Math.round(pos.y + this.iconTopMargin);
style.push('cursor:pointer; top:' + pos.y + 'px; left:' +
pos.x + 'px; color:' + this.textColor_ + '; position:absolute; font-size:' +
this.textSize_ + 'px; font-family:' + this.fontFamily_ + '; font-weight:' +
this.fontWeight_ + '; font-style:' + this.fontStyle_ + '; text-decoration:' +
this.textDecoration_ + ';');
return style.join("");
};
/**
* Returns the position at which to place the DIV depending on the latlng.
*
* @param {google.maps.LatLng} latlng The position in latlng.
* @return {google.maps.Point} The position in pixels.
*/
ClusterIcon.prototype.getPosFromLatLng_ = function (latlng) {
var pos = this.getProjection().fromLatLngToDivPixel(latlng);
pos.x -= this.anchorIcon_[1];
pos.y -= this.anchorIcon_[0];
return pos;
};
/**
* Creates a single cluster that manages a group of proximate markers.
* Used internally, do not call this constructor directly.
* @constructor
* @param {MarkerClusterer} mc The
MarkerClusterer
object with which this
* cluster is associated.
*/
function Cluster(mc) {
this.markerClusterer_ = mc;
this.map_ = mc.getMap();
this.gridSize_ = mc.getGridSize();
this.minClusterSize_ = mc.getMinimumClusterSize();
this.averageCenter_ = mc.getAverageCenter();
this.printable_ = mc.getPrintable();
this.markers_ = [];
this.center_ = null;
this.bounds_ = null;
this.clusterIcon_ = new ClusterIcon(this, mc.getStyles());
}
/**
* Returns the number of markers managed by the cluster. You can call this from
* a
click
,
mouseover
, or
mouseout
event handler
* for the
MarkerClusterer
object.
*
* @return {number} The number of markers in the cluster.
*/
Cluster.prototype.getSize = function () {
return this.markers_.length;
};
/**
* Returns the array of markers managed by the cluster. You can call this from
* a
click
,
mouseover
, or
mouseout
event handler
* for the
MarkerClusterer
object.
*
* @return {Array} The array of markers in the cluster.
*/
Cluster.prototype.getMarkers = function () {
return this.markers_;
};
/**
* Returns the center of the cluster. You can call this from
* a
click
,
mouseover
, or
mouseout
event handler
* for the
MarkerClusterer
object.
*
* @return {google.maps.LatLng} The center of the cluster.
*/
Cluster.prototype.getCenter = function () {
return this.center_;
};
/**
* Returns the map with which the cluster is associated.
*
* @return {google.maps.Map} The map.
* @ignore
*/
Cluster.prototype.getMap = function () {
return this.map_;
};
/**
* Returns the
MarkerClusterer
object with which the cluster is associated.
*
* @return {MarkerClusterer} The associated marker clusterer.
* @ignore
*/
Cluster.prototype.getMarkerClusterer = function () {
return this.markerClusterer_;
};
/**
* Returns the bounds of the cluster.
*
* @return {google.maps.LatLngBounds} the cluster bounds.
* @ignore
*/
Cluster.prototype.getBounds = function () {
var i;
var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
var markers = this.getMarkers();
for (i = 0; i < markers.length; i++) {
bounds.extend(markers[i].getPosition());
}
return bounds;
};
/**
* Removes the cluster from the map.
*
* @ignore
*/
Cluster.prototype.remove = function () {
this.clusterIcon_.setMap(null);
this.markers_ = [];
delete this.markers_;
};
/**
* Adds a marker to the cluster.
*
* @param {google.maps.Marker} marker The marker to be added.
* @return {boolean} True if the marker was added.
* @ignore
*/
Cluster.prototype.addMarker = function (marker) {
var i;
var mCount;
var mz;
if (this.isMarkerAlreadyAdded_(marker)) {
return false;
}
if (!this.center_) {
this.center_ = marker.getPosition();
this.calculateBounds_();
} else {
if (this.averageCenter_) {
var l = this.markers_.length + 1;
var lat = (this.center_.lat() * (l - 1) + marker.getPosition().lat()) / l;
var lng = (this.center_.lng() * (l - 1) + marker.getPosition().lng()) / l;
this.center_ = new google.maps.LatLng(lat, lng);
this.calculateBounds_();
}
}
marker.isAdded = true;
this.markers_.push(marker);
mCount = this.markers_.length;
mz = this.markerClusterer_.getMaxZoom();
if (mz !== null && this.map_.getZoom() > mz) {
// Zoomed in past max zoom, so show the marker.
if (marker.getMap() !== this.map_) {
marker.setMap(this.map_);
}
} else if (mCount < this.minClusterSize_) {
// Min cluster size not reached so show the marker.
if (marker.getMap() !== this.map_) {
marker.setMap(this.map_);
}
} else if (mCount === this.minClusterSize_) {
// Hide the markers that were showing.
for (i = 0; i < mCount; i++) {
this.markers_[i].setMap(null);
}
} else {
marker.setMap(null);
}
this.updateIcon_();
return true;
};
/**
* Determines if a marker lies within the cluster's bounds.
*
* @param {google.maps.Marker} marker The marker to check.
* @return {boolean} True if the marker lies in the bounds.
* @ignore
*/
Cluster.prototype.isMarkerInClusterBounds = function (marker) {
return this.bounds_.contains(marker.getPosition());
};
/**
* Calculates the extended bounds of the cluster with the grid.
*/
Cluster.prototype.calculateBounds_ = function () {
var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds);
};
/**
* Updates the cluster icon.
*/
Cluster.prototype.updateIcon_ = function () {
var mCount = this.markers_.length;
var mz = this.markerClusterer_.getMaxZoom();
if (mz !== null && this.map_.getZoom() > mz) {
this.clusterIcon_.hide();
return;
}
if (mCount < this.minClusterSize_) {
// Min cluster size not yet reached.
this.clusterIcon_.hide();
return;
}
var numStyles = this.markerClusterer_.getStyles().length;
var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles);
this.clusterIcon_.setCenter(this.center_);
this.clusterIcon_.useStyle(sums);
this.clusterIcon_.show();
};
/**
* Determines if a marker has already been added to the cluster.
*
* @param {google.maps.Marker} marker The marker to check.
* @return {boolean} True if the marker has already been added.
*/
Cluster.prototype.isMarkerAlreadyAdded_ = function (marker) {
var i;
if (this.markers_.indexOf) {
return this.markers_.indexOf(marker) !== -1;
} else {
for (i = 0; i < this.markers_.length; i++) {
if (marker === this.markers_[i]) {
return true;
}
}
}
return false;
};
/**
* @name MarkerClustererOptions
* @class This class represents the optional parameter passed to
* the {@link MarkerClusterer} constructor.
* @property {number} [gridSize=60] The grid size of a cluster in pixels. The grid is a square.
* @property {number} [maxZoom=null] The maximum zoom level at which clustering is enabled or
*
null
if clustering is to be enabled at all zoom levels.
* @property {boolean} [zoomOnClick=true] Whether to zoom the map when a cluster marker is
* clicked. You may want to set this to
false
if you have installed a handler
* for the
click
event and it deals with zooming on its own.
* @property {boolean} [averageCenter=false] Whether the position of a cluster marker should be
* the average position of all markers in the cluster. If set to
false
, the
* cluster marker is positioned at the location of the first marker added to the cluster.
* @property {number} [minimumClusterSize=2] The minimum number of markers needed in a cluster
* before the markers are hidden and a cluster marker appears.
* @property {boolean} [ignoreHidden=false] Whether to ignore hidden markers in clusters. You
* may want to set this to
true
to ensure that hidden markers are not included
* in the marker count that appears on a cluster marker (this count is the value of the
*
text
property of the result returned by the default
calculator
).
* If set to
true
and you change the visibility of a marker being clustered, be
* sure to also call
MarkerClusterer.repaint()
.
* @property {boolean} [printable=false] Whether to make the cluster icons printable. Do not
* set to
true
if the
url
fields in the
styles
array
* refer to image sprite files.
* @property {string} [title=""] The tooltip to display when the mouse moves over a cluster
* marker.
* @property {function} [calculator=MarkerClusterer.CALCULATOR] The function used to determine
* the text to be displayed on a cluster marker and the index indicating which style to use
* for the cluster marker. The input parameters for the function are (1) the array of markers
* represented by a cluster marker and (2) the number of cluster icon styles. It returns a
* {@link ClusterIconInfo} object. The default
calculator
returns a
*
text
property which is the number of markers in the cluster and an
*
index
property which is one higher than the lowest integer such that
*
10^i
exceeds the number of markers in the cluster, or the size of the styles
* array, whichever is less. The
styles
array element used has an index of
*
index
minus 1. For example, the default
calculator
returns a
*
text
value of
"125"
and an
index
of
3
* for a cluster icon representing 125 markers so the element used in the
styles
* array is
2
.
* @property {Array} [styles] An array of {@link ClusterIconStyle} elements defining the styles
* of the cluster markers to be used. The element to be used to style a given cluster marker
* is determined by the function defined by the
calculator
property.
* The default is an array of {@link ClusterIconStyle} elements whose properties are derived
* from the values for
imagePath
,
imageExtension
, and
*
imageSizes
.
* @property {number} [batchSize=MarkerClusterer.BATCH_SIZE] Set this property to the
* number of markers to be processed in a single batch when using a browser other than
* Internet Explorer (for Internet Explorer, use the batchSizeIE property instead).
* @property {number} [batchSizeIE=MarkerClusterer.BATCH_SIZE_IE] When Internet Explorer is
* being used, markers are processed in several batches with a small delay inserted between
* each batch in an attempt to avoid Javascript timeout errors. Set this property to the
* number of markers to be processed in a single batch; select as high a number as you can
* without causing a timeout error in the browser. This number might need to be as low as 100
* if 15,000 markers are being managed, for example.
* @property {string} [imagePath=MarkerClusterer.IMAGE_PATH]
* The full URL of the root name of the group of image files to use for cluster icons.
* The complete file name is of the form
imagePath
n.
imageExtension
* where n is the image file number (1, 2, etc.).
* @property {string} [imageExtension=MarkerClusterer.IMAGE_EXTENSION]
* The extension name for the cluster icon image files (e.g.,
"png"
or
*
"jpg"
).
* @property {Array} [imageSizes=MarkerClusterer.IMAGE_SIZES]
* An array of numbers containing the widths of the group of
*
imagePath
n.
imageExtension
image files.
* (The images are assumed to be square.)
*/
/**
* Creates a MarkerClusterer object with the options specified in {@link MarkerClustererOptions}.
* @constructor
* @extends google.maps.OverlayView
* @param {google.maps.Map} map The Google map to attach to.
* @param {Array.
} [opt_markers] The markers to be added to the cluster.
* @param {MarkerClustererOptions} [opt_options] The optional parameters.
*/
function MarkerClusterer(map, opt_markers, opt_options) {
// MarkerClusterer implements google.maps.OverlayView interface. We use the
// extend function to extend MarkerClusterer with google.maps.OverlayView
// because it might not always be available when the code is defined so we
// look for it at the last possible moment. If it doesn't exist now then
// there is no point going ahead :)
this.extend(MarkerClusterer, google.maps.OverlayView);
opt_markers = opt_markers || [];
opt_options = opt_options || {};
this.markers_ = [];
this.clusters_ = [];
this.listeners_ = [];
this.activeMap_ = null;
this.ready_ = false;
this.gridSize_ = opt_options.gridSize || 60;
this.minClusterSize_ = opt_options.minimumClusterSize || 2;
this.maxZoom_ = opt_options.maxZoom || null;
this.styles_ = opt_options.styles || [];
this.title_ = opt_options.title || "";
this.zoomOnClick_ = true;
if (opt_options.zoomOnClick !== undefined) {
this.zoomOnClick_ = opt_options.zoomOnClick;
}
this.averageCenter_ = false;
if (opt_options.averageCenter !== undefined) {
this.averageCenter_ = opt_options.averageCenter;
}
this.ignoreHidden_ = false;
if (opt_options.ignoreHidden !== undefined) {
this.ignoreHidden_ = opt_options.ignoreHidden;
}
this.printable_ = false;
if (opt_options.printable !== undefined) {
this.printable_ = opt_options.printable;
}
this.imagePath_ = opt_options.imagePath || MarkerClusterer.IMAGE_PATH;
this.imageExtension_ = opt_options.imageExtension || MarkerClusterer.IMAGE_EXTENSION;
this.imageSizes_ = opt_options.imageSizes || MarkerClusterer.IMAGE_SIZES;
this.calculator_ = opt_options.calculator || MarkerClusterer.CALCULATOR;
this.batchSize_ = opt_options.batchSize || MarkerClusterer.BATCH_SIZE;
this.batchSizeIE_ = opt_options.batchSizeIE || MarkerClusterer.BATCH_SIZE_IE;
if (navigator.userAgent.toLowerCase().indexOf("msie") !== -1) {
// Try to avoid IE timeout when processing a huge number of markers:
this.batchSize_ = this.batchSizeIE_;
}
this.setupStyles_();
this.addMarkers(opt_markers, true);
this.setMap(map); // Note: this causes onAdd to be called
}
/**
* Implementation of the onAdd interface method.
* @ignore
*/
MarkerClusterer.prototype.onAdd = function () {
var cMarkerClusterer = this;
this.activeMap_ = this.getMap();
this.ready_ = true;
this.repaint();
// Add the map event listeners
this.listeners_ = [
google.maps.event.addListener(this.getMap(), "zoom_changed", function () {
cMarkerClusterer.resetViewport_(false);
// Workaround for this Google bug: when map is at level 0 and "-" of
// zoom slider is clicked, a "zoom_changed" event is fired even though
// the map doesn't zoom out any further. In this situation, no "idle"
// event is triggered so the cluster markers that have been removed
// do not get redrawn.
if (this.getZoom() === 0) {
google.maps.event.trigger(this, "idle");
}
}),
google.maps.event.addListener(this.getMap(), "idle", function () {
cMarkerClusterer.redraw_();
})
];
};
/**
* Implementation of the onRemove interface method.
* Removes map event listeners and all cluster icons from the DOM.
* All managed markers are also put back on the map.
* @ignore
*/
MarkerClusterer.prototype.onRemove = function () {
var i;
// Put all the managed markers back on the map:
for (i = 0; i < this.markers_.length; i++) {
this.markers_[i].setMap(this.activeMap_);
}
// Remove all clusters:
for (i = 0; i < this.clusters_.length; i++) {
this.clusters_[i].remove();
}
this.clusters_ = [];
// Remove map event listeners:
for (i = 0; i < this.listeners_.length; i++) {
google.maps.event.removeListener(this.listeners_[i]);
}
this.listeners_ = [];
this.activeMap_ = null;
this.ready_ = false;
};
/**
* Implementation of the draw interface method.
* @ignore
*/
MarkerClusterer.prototype.draw = function () {};
/**
* Sets up the styles object.
*/
MarkerClusterer.prototype.setupStyles_ = function () {
var i, size;
if (this.styles_.length > 0) {
return;
}
for (i = 0; i < this.imageSizes_.length; i++) {
size = this.imageSizes_[i];
this.styles_.push({
url: this.imagePath_ + (i + 1) + "." + this.imageExtension_,
height: size,
width: size
});
}
};
/**
* Fits the map to the bounds of the markers managed by the clusterer.
*/
MarkerClusterer.prototype.fitMapToMarkers = function () {
var i;
var markers = this.getMarkers();
var bounds = new google.maps.LatLngBounds();
for (i = 0; i < markers.length; i++) {
bounds.extend(markers[i].getPosition());
}
this.getMap().fitBounds(bounds);
};
/**
* Returns the value of the gridSize
property.
*
* @return {number} The grid size.
*/
MarkerClusterer.prototype.getGridSize = function () {
return this.gridSize_;
};
/**
* Sets the value of the gridSize
property.
*
* @param {number} gridSize The grid size.
*/
MarkerClusterer.prototype.setGridSize = function (gridSize) {
this.gridSize_ = gridSize;
};
/**
* Returns the value of the minimumClusterSize
property.
*
* @return {number} The minimum cluster size.
*/
MarkerClusterer.prototype.getMinimumClusterSize = function () {
return this.minClusterSize_;
};
/**
* Sets the value of the minimumClusterSize
property.
*
* @param {number} minimumClusterSize The minimum cluster size.
*/
MarkerClusterer.prototype.setMinimumClusterSize = function (minimumClusterSize) {
this.minClusterSize_ = minimumClusterSize;
};
/**
* Returns the value of the maxZoom
property.
*
* @return {number} The maximum zoom level.
*/
MarkerClusterer.prototype.getMaxZoom = function () {
return this.maxZoom_;
};
/**
* Sets the value of the maxZoom
property.
*
* @param {number} maxZoom The maximum zoom level.
*/
MarkerClusterer.prototype.setMaxZoom = function (maxZoom) {
this.maxZoom_ = maxZoom;
};
/**
* Returns the value of the styles
property.
*
* @return {Array} The array of styles defining the cluster markers to be used.
*/
MarkerClusterer.prototype.getStyles = function () {
return this.styles_;
};
/**
* Sets the value of the styles
property.
*
* @param {Array.} styles The array of styles to use.
*/
MarkerClusterer.prototype.setStyles = function (styles) {
this.styles_ = styles;
};
/**
* Returns the value of the title
property.
*
* @return {string} The content of the title text.
*/
MarkerClusterer.prototype.getTitle = function () {
return this.title_;
};
/**
* Sets the value of the title
property.
*
* @param {string} title The value of the title property.
*/
MarkerClusterer.prototype.setTitle = function (title) {
this.title_ = title;
};
/**
* Returns the value of the zoomOnClick
property.
*
* @return {boolean} True if zoomOnClick property is set.
*/
MarkerClusterer.prototype.getZoomOnClick = function () {
return this.zoomOnClick_;
};
/**
* Sets the value of the zoomOnClick
property.
*
* @param {boolean} zoomOnClick The value of the zoomOnClick property.
*/
MarkerClusterer.prototype.setZoomOnClick = function (zoomOnClick) {
this.zoomOnClick_ = zoomOnClick;
};
/**
* Returns the value of the averageCenter
property.
*
* @return {boolean} True if averageCenter property is set.
*/
MarkerClusterer.prototype.getAverageCenter = function () {
return this.averageCenter_;
};
/**
* Sets the value of the averageCenter
property.
*
* @param {boolean} averageCenter The value of the averageCenter property.
*/
MarkerClusterer.prototype.setAverageCenter = function (averageCenter) {
this.averageCenter_ = averageCenter;
};
/**
* Returns the value of the ignoreHidden
property.
*
* @return {boolean} True if ignoreHidden property is set.
*/
MarkerClusterer.prototype.getIgnoreHidden = function () {
return this.ignoreHidden_;
};
/**
* Sets the value of the ignoreHidden
property.
*
* @param {boolean} ignoreHidden The value of the ignoreHidden property.
*/
MarkerClusterer.prototype.setIgnoreHidden = function (ignoreHidden) {
this.ignoreHidden_ = ignoreHidden;
};
/**
* Returns the value of the imageExtension
property.
*
* @return {string} The value of the imageExtension property.
*/
MarkerClusterer.prototype.getImageExtension = function () {
return this.imageExtension_;
};
/**
* Sets the value of the imageExtension
property.
*
* @param {string} imageExtension The value of the imageExtension property.
*/
MarkerClusterer.prototype.setImageExtension = function (imageExtension) {
this.imageExtension_ = imageExtension;
};
/**
* Returns the value of the imagePath
property.
*
* @return {string} The value of the imagePath property.
*/
MarkerClusterer.prototype.getImagePath = function () {
return this.imagePath_;
};
/**
* Sets the value of the imagePath
property.
*
* @param {string} imagePath The value of the imagePath property.
*/
MarkerClusterer.prototype.setImagePath = function (imagePath) {
this.imagePath_ = imagePath;
};
/**
* Returns the value of the imageSizes
property.
*
* @return {Array} The value of the imageSizes property.
*/
MarkerClusterer.prototype.getImageSizes = function () {
return this.imageSizes_;
};
/**
* Sets the value of the imageSizes
property.
*
* @param {Array} imageSizes The value of the imageSizes property.
*/
MarkerClusterer.prototype.setImageSizes = function (imageSizes) {
this.imageSizes_ = imageSizes;
};
/**
* Returns the value of the calculator
property.
*
* @return {function} the value of the calculator property.
*/
MarkerClusterer.prototype.getCalculator = function () {
return this.calculator_;
};
/**
* Sets the value of the calculator
property.
*
* @param {function(Array., number)} calculator The value
* of the calculator property.
*/
MarkerClusterer.prototype.setCalculator = function (calculator) {
this.calculator_ = calculator;
};
/**
* Returns the value of the printable
property.
*
* @return {boolean} the value of the printable property.
*/
MarkerClusterer.prototype.getPrintable = function () {
return this.printable_;
};
/**
* Sets the value of the printable
property.
*
* @param {boolean} printable The value of the printable property.
*/
MarkerClusterer.prototype.setPrintable = function (printable) {
this.printable_ = printable;
};
/**
* Returns the value of the batchSizeIE
property.
*
* @return {number} the value of the batchSizeIE property.
*/
MarkerClusterer.prototype.getBatchSizeIE = function () {
return this.batchSizeIE_;
};
/**
* Sets the value of the batchSizeIE
property.
*
* @param {number} batchSizeIE The value of the batchSizeIE property.
*/
MarkerClusterer.prototype.setBatchSizeIE = function (batchSizeIE) {
this.batchSizeIE_ = batchSizeIE;
};
/**
* Returns the array of markers managed by the clusterer.
*
* @return {Array} The array of markers managed by the clusterer.
*/
MarkerClusterer.prototype.getMarkers = function () {
return this.markers_;
};
/**
* Returns the number of markers managed by the clusterer.
*
* @return {number} The number of markers.
*/
MarkerClusterer.prototype.getTotalMarkers = function () {
return this.markers_.length;
};
/**
* Returns the current array of clusters formed by the clusterer.
*
* @return {Array} The array of clusters formed by the clusterer.
*/
MarkerClusterer.prototype.getClusters = function () {
return this.clusters_;
};
/**
* Returns the number of clusters formed by the clusterer.
*
* @return {number} The number of clusters formed by the clusterer.
*/
MarkerClusterer.prototype.getTotalClusters = function () {
return this.clusters_.length;
};
/**
* Adds a marker to the clusterer. The clusters are redrawn unless
* opt_nodraw
is set to true
.
*
* @param {google.maps.Marker} marker The marker to add.
* @param {boolean} [opt_nodraw] Set to true
to prevent redrawing.
*/
MarkerClusterer.prototype.addMarker = function (marker, opt_nodraw) {
this.pushMarkerTo_(marker);
if (!opt_nodraw) {
this.redraw_();
}
};
/**
* Adds an array of markers to the clusterer. The clusters are redrawn unless
* opt_nodraw
is set to true
.
*
* @param {Array.} markers The markers to add.
* @param {boolean} [opt_nodraw] Set to true
to prevent redrawing.
*/
MarkerClusterer.prototype.addMarkers = function (markers, opt_nodraw) {
var i;
for (i = 0; i < markers.length; i++) {
this.pushMarkerTo_(markers[i]);
}
if (!opt_nodraw) {
this.redraw_();
}
};
/**
* Pushes a marker to the clusterer.
*
* @param {google.maps.Marker} marker The marker to add.
*/
MarkerClusterer.prototype.pushMarkerTo_ = function (marker) {
// If the marker is draggable add a listener so we can update the clusters on the dragend:
if (marker.getDraggable()) {
var cMarkerClusterer = this;
google.maps.event.addListener(marker, "dragend", function () {
if (cMarkerClusterer.ready_) {
this.isAdded = false;
cMarkerClusterer.repaint();
}
});
}
marker.isAdded = false;
this.markers_.push(marker);
};
/**
* Removes a marker from the cluster. The clusters are redrawn unless
* opt_nodraw
is set to true
. Returns true
if the
* marker was removed from the clusterer.
*
* @param {google.maps.Marker} marker The marker to remove.
* @param {boolean} [opt_nodraw] Set to true
to prevent redrawing.
* @return {boolean} True if the marker was removed from the clusterer.
*/
MarkerClusterer.prototype.removeMarker = function (marker, opt_nodraw) {
var removed = this.removeMarker_(marker);
if (!opt_nodraw && removed) {
this.repaint();
}
return removed;
};
/**
* Removes an array of markers from the cluster. The clusters are redrawn unless
* opt_nodraw
is set to true
. Returns true
if markers
* were removed from the clusterer.
*
* @param {Array.} markers The markers to remove.
* @param {boolean} [opt_nodraw] Set to true
to prevent redrawing.
* @return {boolean} True if markers were removed from the clusterer.
*/
MarkerClusterer.prototype.removeMarkers = function (markers, opt_nodraw) {
var i, r;
var removed = false;
for (i = 0; i < markers.length; i++) {
r = this.removeMarker_(markers[i]);
removed = removed || r;
}
if (!opt_nodraw && removed) {
this.repaint();
}
return removed;
};
/**
* Removes a marker and returns true if removed, false if not.
*
* @param {google.maps.Marker} marker The marker to remove
* @return {boolean} Whether the marker was removed or not
*/
MarkerClusterer.prototype.removeMarker_ = function (marker) {
var i;
var index = -1;
if (this.markers_.indexOf) {
index = this.markers_.indexOf(marker);
} else {
for (i = 0; i < this.markers_.length; i++) {
if (marker === this.markers_[i]) {
index = i;
break;
}
}
}
if (index === -1) {
// Marker is not in our list of markers, so do nothing:
return false;
}
marker.setMap(null);
this.markers_.splice(index, 1); // Remove the marker from the list of managed markers
return true;
};
/**
* Removes all clusters and markers from the map and also removes all markers
* managed by the clusterer.
*/
MarkerClusterer.prototype.clearMarkers = function () {
this.resetViewport_(true);
this.markers_ = [];
};
/**
* Recalculates and redraws all the marker clusters from scratch.
* Call this after changing any properties.
*/
MarkerClusterer.prototype.repaint = function () {
var oldClusters = this.clusters_.slice();
this.clusters_ = [];
this.resetViewport_(false);
this.redraw_();
// Remove the old clusters.
// Do it in a timeout to prevent blinking effect.
setTimeout(function () {
var i;
for (i = 0; i < oldClusters.length; i++) {
oldClusters[i].remove();
}
}, 0);
};
/**
* Returns the current bounds extended by the grid size.
*
* @param {google.maps.LatLngBounds} bounds The bounds to extend.
* @return {google.maps.LatLngBounds} The extended bounds.
* @ignore
*/
MarkerClusterer.prototype.getExtendedBounds = function (bounds) {
var projection = this.getProjection();
// Turn the bounds into latlng.
var tr = new google.maps.LatLng(bounds.getNorthEast().lat(),
bounds.getNorthEast().lng());
var bl = new google.maps.LatLng(bounds.getSouthWest().lat(),
bounds.getSouthWest().lng());
// Convert the points to pixels and the extend out by the grid size.
var trPix = projection.fromLatLngToDivPixel(tr);
trPix.x += this.gridSize_;
trPix.y -= this.gridSize_;
var blPix = projection.fromLatLngToDivPixel(bl);
blPix.x -= this.gridSize_;
blPix.y += this.gridSize_;
// Convert the pixel points back to LatLng
var ne = projection.fromDivPixelToLatLng(trPix);
var sw = projection.fromDivPixelToLatLng(blPix);
// Extend the bounds to contain the new bounds.
bounds.extend(ne);
bounds.extend(sw);
return bounds;
};
/**
* Redraws all the clusters.
*/
MarkerClusterer.prototype.redraw_ = function () {
this.createClusters_(0);
};
/**
* Removes all clusters from the map. The markers are also removed from the map
* if opt_hide
is set to true
.
*
* @param {boolean} [opt_hide] Set to true
to also remove the markers
* from the map.
*/
MarkerClusterer.prototype.resetViewport_ = function (opt_hide) {
var i, marker;
// Remove all the clusters
for (i = 0; i < this.clusters_.length; i++) {
this.clusters_[i].remove();
}
this.clusters_ = [];
// Reset the markers to not be added and to be removed from the map.
for (i = 0; i < this.markers_.length; i++) {
marker = this.markers_[i];
marker.isAdded = false;
if (opt_hide) {
marker.setMap(null);
}
}
};
/**
* Calculates the distance between two latlng locations in km.
*
* @param {google.maps.LatLng} p1 The first lat lng point.
* @param {google.maps.LatLng} p2 The second lat lng point.
* @return {number} The distance between the two points in km.
* @see http://www.movable-type.co.uk/scripts/latlong.html
*/
MarkerClusterer.prototype.distanceBetweenPoints_ = function (p1, p2) {
var R = 6371; // Radius of the Earth in km
var dLat = (p2.lat() - p1.lat()) * Math.PI / 180;
var dLon = (p2.lng() - p1.lng()) * Math.PI / 180;
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c;
return d;
};
/**
* Determines if a marker is contained in a bounds.
*
* @param {google.maps.Marker} marker The marker to check.
* @param {google.maps.LatLngBounds} bounds The bounds to check against.
* @return {boolean} True if the marker is in the bounds.
*/
MarkerClusterer.prototype.isMarkerInBounds_ = function (marker, bounds) {
return bounds.contains(marker.getPosition());
};
/**
* Adds a marker to a cluster, or creates a new cluster.
*
* @param {google.maps.Marker} marker The marker to add.
*/
MarkerClusterer.prototype.addToClosestCluster_ = function (marker) {
var i, d, cluster, center;
var distance = 40000; // Some large number
var clusterToAddTo = null;
for (i = 0; i < this.clusters_.length; i++) {
cluster = this.clusters_[i];
center = cluster.getCenter();
if (center) {
d = this.distanceBetweenPoints_(center, marker.getPosition());
if (d < distance) {
distance = d;
clusterToAddTo = cluster;
}
}
}
if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) {
clusterToAddTo.addMarker(marker);
} else {
cluster = new Cluster(this);
cluster.addMarker(marker);
this.clusters_.push(cluster);
}
};
/**
* Creates the clusters. This is done in batches to avoid timeout errors
* in some browsers when there is a huge number of markers.
*
* @param {number} iFirst The index of the first marker in the batch of
* markers to be added to clusters.
*/
MarkerClusterer.prototype.createClusters_ = function (iFirst) {
var i, marker;
var mapBounds;
var cMarkerClusterer = this;
if (!this.ready_) {
return;
}
// Cancel previous batch processing if we're working on the first batch:
if (iFirst === 0) {
/**
* This event is fired when the MarkerClusterer
begins
* clustering markers.
* @name MarkerClusterer#clusteringbegin
* @param {MarkerClusterer} mc The MarkerClusterer whose markers are being clustered.
* @event
*/
google.maps.event.trigger(this, "clusteringbegin", this);
if (typeof this.timerRefStatic !== "undefined") {
clearTimeout(this.timerRefStatic);
delete this.timerRefStatic;
}
}
// Get our current map view bounds.
// Create a new bounds object so we don't affect the map.
//
// See Comments 9 & 11 on Issue 3651 relating to this workaround for a Google Maps bug:
if (this.getMap().getZoom() > 3) {
mapBounds = new google.maps.LatLngBounds(this.getMap().getBounds().getSouthWest(),
this.getMap().getBounds().getNorthEast());
} else {
mapBounds = new google.maps.LatLngBounds(new google.maps.LatLng(85.02070771743472, -178.48388434375), new google.maps.LatLng(-85.08136444384544, 178.00048865625));
}
var bounds = this.getExtendedBounds(mapBounds);
var iLast = Math.min(iFirst + this.batchSize_, this.markers_.length);
for (i = iFirst; i < iLast; i++) {
marker = this.markers_[i];
if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) {
if (!this.ignoreHidden_ || (this.ignoreHidden_ && marker.getVisible())) {
this.addToClosestCluster_(marker);
}
}
}
if (iLast < this.markers_.length) {
this.timerRefStatic = setTimeout(function () {
cMarkerClusterer.createClusters_(iLast);
}, 0);
} else {
delete this.timerRefStatic;
/**
* This event is fired when the MarkerClusterer
stops
* clustering markers.
* @name MarkerClusterer#clusteringend
* @param {MarkerClusterer} mc The MarkerClusterer whose markers are being clustered.
* @event
*/
google.maps.event.trigger(this, "clusteringend", this);
}
};
/**
* Extends an object's prototype by another's.
*
* @param {Object} obj1 The object to be extended.
* @param {Object} obj2 The object to extend with.
* @return {Object} The new extended object.
* @ignore
*/
MarkerClusterer.prototype.extend = function (obj1, obj2) {
return (function (object) {
var property;
for (property in object.prototype) {
this.prototype[property] = object.prototype[property];
}
return this;
}).apply(obj1, [obj2]);
};
/**
* The default function for determining the label text and style
* for a cluster icon.
*
* @param {Array.} markers The array of markers represented by the cluster.
* @param {number} numStyles The number of marker styles available.
* @return {ClusterIconInfo} The information resource for the cluster.
* @constant
* @ignore
*/
MarkerClusterer.CALCULATOR = function (markers, numStyles) {
var index = 0;
var count = markers.length.toString();
var dv = count;
while (dv !== 0) {
dv = parseInt(dv / 10, 10);
index++;
}
index = Math.min(index, numStyles);
return {
text: count,
index: index
};
};
/**
* The number of markers to process in one batch.
*
* @type {number}
* @constant
*/
MarkerClusterer.BATCH_SIZE = 2000;
/**
* The number of markers to process in one batch (IE only).
*
* @type {number}
* @constant
*/
MarkerClusterer.BATCH_SIZE_IE = 500;
/**
* The default root name for the marker cluster images.
*
* @type {string}
* @constant
*/
MarkerClusterer.IMAGE_PATH = "http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclustererplus/images/m";
/**
* The default extension name for the marker cluster images.
*
* @type {string}
* @constant
*/
MarkerClusterer.IMAGE_EXTENSION = "png";
/**
* The default array of sizes for the marker cluster images.
*
* @type {Array.}
* @constant
*/
MarkerClusterer.IMAGE_SIZES = [53, 56, 66, 78, 90];
/* /js/markerclusterer.js end */
/* /js/smallPhotoGallery.js begin */
$(function(){
var img = $("#BigPhoto"),
source = $("#BigPhotoSource"),
link = img.parent();
$(".smallPhotoGallery .pictures img").mouseover(function(){
var imgObj = $(this),
bigObj = imgSrc[imgObj.attr('id')];
if(imgObj.hasClass("current"))
return false;
$(".pictures .current").removeClass("current");
imgObj.addClass("current");
if(img && bigObj.src != '')
img.attr("src", bigObj.src);
source.html(bigObj.source.html());
link.attr('href', bigObj.link);
});
});
/* /js/smallPhotoGallery.js end */
/* /js/videoGallery.js begin */
function resizeVideo() {
var maxWidth = 600;
var element = $('.video-item_object').children('iframe');
if($('.video-item').width() < maxWidth) {
var width = $('.video-item').width();
var height = Math.round(0.5625 * width);
element.width(width).height(height);
}
};
function initVideo() {
if($('.video-item').width() < 600) {
resizeVideo();
}
};
$(function(){
if($('#videoSlider li').size() > 5){
var currPos = $('#videoSlider li.current').data('pos') - 1;
currPos = currPos < 0 ? 0 : currPos;
$('#videoSlider').ulslide({
effect: {
type: 'carousel', // slide, fade, rotate, scale or carousel
showCount: 5,
distance: 0 // Distance between frames
},
current: currPos,
duration: 500,
width: 175,
height: 99,
nextButton: '#videoSliderPrev',
prevButton: '#videoSliderNext'
});
};
$(document).ready( function(){
initVideo();
});
$(window).resize(function(){
resizeVideo();
});
});
/* /js/videoGallery.js end */
/* /js/socialLikes.js begin */
$(function() {
var cacheStatus = $.ajaxSettings.cache;
$.ajaxSettings.cache = true;
var scripts = [
{ id: 'vkontakte', src: 'http://vk.com/js/api/openapi.js', element: 'vk' },
{ id: 'gplus', src: '//apis.google.com/js/plusone.js', element: 'gl' },
{ id: 'mail', src: 'http://cdn.connect.mail.ru/js/loader.js', element: 'mo' },
{ id: 'twitter', src: 'https://platform.twitter.com/widgets.js', element: 'tw' },
{ id: 'facebook', src: 'http://connect.facebook.net/ru_RU/all.js', element: 'fb' }
];
for(var i in scripts){
var f = function(item) {
var source = item.src;
var element = $('.servicesLike_item.'+ item.element);
if(element && element.attr('data-script-variant')) {
source = element.attr('data-script-variant');
}
$.getScript(source)
.done(function(script, textStatus) {
$('.servicesLike_item.'+ item.element).css('display', 'inline-block');
$.event.trigger(item.id +'ScriptLoad');
});
}; f(scripts[i]);
}
$.ajaxSettings.cache = cacheStatus;
});
/* /js/socialLikes.js end */
/* /js/jquery.tmpl.js begin */
/*!
* jQuery Templates Plugin 1.0.0pre
* http://github.com/jquery/jquery-tmpl
* Requires jQuery 1.4.2
*
* Copyright Software Freedom Conservancy, Inc.
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*/
(function( jQuery, undefined ){
var oldManip = jQuery.fn.domManip, tmplItmAtt = "_tmplitem", htmlExpr = /^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /,
newTmplItems = {}, wrappedItems = {}, appendToTmplItems, topTmplItem = { key: 0, data: {} }, itemKey = 0, cloneIndex = 0, stack = [];
function newTmplItem( options, parentItem, fn, data ) {
// Returns a template item data structure for a new rendered instance of a template (a 'template item').
// The content field is a hierarchical array of strings and nested items (to be
// removed and replaced by nodes field of dom elements, once inserted in DOM).
var newItem = {
data: data || (data === 0 || data === false) ? data : (parentItem ? parentItem.data : {}),
_wrap: parentItem ? parentItem._wrap : null,
tmpl: null,
parent: parentItem || null,
nodes: [],
calls: tiCalls,
nest: tiNest,
wrap: tiWrap,
html: tiHtml,
update: tiUpdate
};
if ( options ) {
jQuery.extend( newItem, options, { nodes: [], parent: parentItem });
}
if ( fn ) {
// Build the hierarchical content to be used during insertion into DOM
newItem.tmpl = fn;
newItem._ctnt = newItem._ctnt || newItem.tmpl( jQuery, newItem );
newItem.key = ++itemKey;
// Keep track of new template item, until it is stored as jQuery Data on DOM element
(stack.length ? wrappedItems : newTmplItems)[itemKey] = newItem;
}
return newItem;
}
// Override appendTo etc., in order to provide support for targeting multiple elements. (This code would disappear if integrated in jquery core).
jQuery.each({
appendTo: "append",
prependTo: "prepend",
insertBefore: "before",
insertAfter: "after",
replaceAll: "replaceWith"
}, function( name, original ) {
jQuery.fn[ name ] = function( selector ) {
var ret = [], insert = jQuery( selector ), elems, i, l, tmplItems,
parent = this.length === 1 && this[0].parentNode;
appendToTmplItems = newTmplItems || {};
if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
insert[ original ]( this[0] );
ret = this;
} else {
for ( i = 0, l = insert.length; i < l; i++ ) {
cloneIndex = i;
elems = (i > 0 ? this.clone(true) : this).get();
jQuery( insert[i] )[ original ]( elems );
ret = ret.concat( elems );
}
cloneIndex = 0;
ret = this.pushStack( ret, name, insert.selector );
}
tmplItems = appendToTmplItems;
appendToTmplItems = null;
jQuery.tmpl.complete( tmplItems );
return ret;
};
});
jQuery.fn.extend({
// Use first wrapped element as template markup.
// Return wrapped set of template items, obtained by rendering template against data.
tmpl: function( data, options, parentItem ) {
return jQuery.tmpl( this[0], data, options, parentItem );
},
// Find which rendered template item the first wrapped DOM element belongs to
tmplItem: function() {
return jQuery.tmplItem( this[0] );
},
// Consider the first wrapped element as a template declaration, and get the compiled template or store it as a named template.
template: function( name ) {
return jQuery.template( name, this[0] );
},
domManip: function( args, table, callback, options ) {
if ( args[0] && jQuery.isArray( args[0] )) {
var dmArgs = jQuery.makeArray( arguments ), elems = args[0], elemsLength = elems.length, i = 0, tmplItem;
while ( i < elemsLength && !(tmplItem = jQuery.data( elems[i++], "tmplItem" ))) {}
if ( tmplItem && cloneIndex ) {
dmArgs[2] = function( fragClone ) {
// Handler called by oldManip when rendered template has been inserted into DOM.
jQuery.tmpl.afterManip( this, fragClone, callback );
};
}
oldManip.apply( this, dmArgs );
} else {
oldManip.apply( this, arguments );
}
cloneIndex = 0;
if ( !appendToTmplItems ) {
jQuery.tmpl.complete( newTmplItems );
}
return this;
}
});
jQuery.extend({
// Return wrapped set of template items, obtained by rendering template against data.
tmpl: function( tmpl, data, options, parentItem ) {
var ret, topLevel = !parentItem;
if ( topLevel ) {
// This is a top-level tmpl call (not from a nested template using {{tmpl}})
parentItem = topTmplItem;
tmpl = jQuery.template[tmpl] || jQuery.template( null, tmpl );
wrappedItems = {}; // Any wrapped items will be rebuilt, since this is top level
} else if ( !tmpl ) {
// The template item is already associated with DOM - this is a refresh.
// Re-evaluate rendered template for the parentItem
tmpl = parentItem.tmpl;
newTmplItems[parentItem.key] = parentItem;
parentItem.nodes = [];
if ( parentItem.wrapped ) {
updateWrapped( parentItem, parentItem.wrapped );
}
// Rebuild, without creating a new template item
return jQuery( build( parentItem, null, parentItem.tmpl( jQuery, parentItem ) ));
}
if ( !tmpl ) {
return []; // Could throw...
}
if ( typeof data === "function" ) {
data = data.call( parentItem || {} );
}
if ( options && options.wrapped ) {
updateWrapped( options, options.wrapped );
}
ret = jQuery.isArray( data ) ?
jQuery.map( data, function( dataItem ) {
return dataItem ? newTmplItem( options, parentItem, tmpl, dataItem ) : null;
}) :
[ newTmplItem( options, parentItem, tmpl, data ) ];
return topLevel ? jQuery( build( parentItem, null, ret ) ) : ret;
},
// Return rendered template item for an element.
tmplItem: function( elem ) {
var tmplItem;
if ( elem instanceof jQuery ) {
elem = elem[0];
}
while ( elem && elem.nodeType === 1 && !(tmplItem = jQuery.data( elem, "tmplItem" )) && (elem = elem.parentNode) ) {}
return tmplItem || topTmplItem;
},
// Set:
// Use $.template( name, tmpl ) to cache a named template,
// where tmpl is a template string, a script element or a jQuery instance wrapping a script element, etc.
// Use $( "selector" ).template( name ) to provide access by name to a script block template declaration.
// Get:
// Use $.template( name ) to access a cached template.
// Also $( selectorToScriptBlock ).template(), or $.template( null, templateString )
// will return the compiled template, without adding a name reference.
// If templateString includes at least one HTML tag, $.template( templateString ) is equivalent
// to $.template( null, templateString )
template: function( name, tmpl ) {
if (tmpl) {
// Compile template and associate with name
if ( typeof tmpl === "string" ) {
// This is an HTML string being passed directly in.
tmpl = buildTmplFn( tmpl )
} else if ( tmpl instanceof jQuery ) {
tmpl = tmpl[0] || {};
}
if ( tmpl.nodeType ) {
// If this is a template block, use cached copy, or generate tmpl function and cache.
tmpl = jQuery.data( tmpl, "tmpl" ) || jQuery.data( tmpl, "tmpl", buildTmplFn( tmpl.innerHTML ));
// Issue: In IE, if the container element is not a script block, the innerHTML will remove quotes from attribute values whenever the value does not include white space.
// This means that foo="${x}" will not work if the value of x includes white space: foo="${x}" -> foo=value of x.
// To correct this, include space in tag: foo="${ x }" -> foo="value of x"
}
return typeof name === "string" ? (jQuery.template[name] = tmpl) : tmpl;
}
// Return named compiled template
return name ? (typeof name !== "string" ? jQuery.template( null, name ):
(jQuery.template[name] ||
// If not in map, and not containing at least on HTML tag, treat as a selector.
// (If integrated with core, use quickExpr.exec)
jQuery.template( null, htmlExpr.test( name ) ? name : jQuery( name )))) : null;
},
encode: function( text ) {
// Do HTML encoding replacing < > & and ' and " by corresponding entities.
return ("" + text).split("<").join("<").split(">").join(">").split('"').join(""").split("'").join("'");
}
});
jQuery.extend( jQuery.tmpl, {
tag: {
"tmpl": {
_default: { $2: "null" },
open: "if($notnull_1){__=__.concat($item.nest($1,$2));}"
// tmpl target parameter can be of type function, so use $1, not $1a (so not auto detection of functions)
// This means that {{tmpl foo}} treats foo as a template (which IS a function).
// Explicit parens can be used if foo is a function that returns a template: {{tmpl foo()}}.
},
"wrap": {
_default: { $2: "null" },
open: "$item.calls(__,$1,$2);__=[];",
close: "call=$item.calls();__=call._.concat($item.wrap(call,__));"
},
"each": {
_default: { $2: "$index, $value" },
open: "if($notnull_1){$.each($1a,function($2){with(this){",
close: "}});}"
},
"if": {
open: "if(($notnull_1) && $1a){",
close: "}"
},
"else": {
_default: { $1: "true" },
open: "}else if(($notnull_1) && $1a){"
},
"html": {
// Unecoded expression evaluation.
open: "if($notnull_1){__.push($1a);}"
},
"=": {
// Encoded expression evaluation. Abbreviated form is ${}.
_default: { $1: "$data" },
open: "if($notnull_1){__.push($.encode($1a));}"
},
"!": {
// Comment tag. Skipped by parser
open: ""
}
},
// This stub can be overridden, e.g. in jquery.tmplPlus for providing rendered events
complete: function( items ) {
newTmplItems = {};
},
// Call this from code which overrides domManip, or equivalent
// Manage cloning/storing template items etc.
afterManip: function afterManip( elem, fragClone, callback ) {
// Provides cloned fragment ready for fixup prior to and after insertion into DOM
var content = fragClone.nodeType === 11 ?
jQuery.makeArray(fragClone.childNodes) :
fragClone.nodeType === 1 ? [fragClone] : [];
// Return fragment to original caller (e.g. append) for DOM insertion
callback.call( elem, fragClone );
// Fragment has been inserted:- Add inserted nodes to tmplItem data structure. Replace inserted element annotations by jQuery.data.
storeTmplItems( content );
cloneIndex++;
}
});
//========================== Private helper functions, used by code above ==========================
function build( tmplItem, nested, content ) {
// Convert hierarchical content into flat string array
// and finally return array of fragments ready for DOM insertion
var frag, ret = content ? jQuery.map( content, function( item ) {
return (typeof item === "string") ?
// Insert template item annotations, to be converted to jQuery.data( "tmplItem" ) when elems are inserted into DOM.
(tmplItem.key ? item.replace( /(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g, "$1 " + tmplItmAtt + "=\"" + tmplItem.key + "\" $2" ) : item) :
// This is a child template item. Build nested template.
build( item, tmplItem, item._ctnt );
}) :
// If content is not defined, insert tmplItem directly. Not a template item. May be a string, or a string array, e.g. from {{html $item.html()}}.
tmplItem;
if ( nested ) {
return ret;
}
// top-level template
ret = ret.join("");
// Support templates which have initial or final text nodes, or consist only of text
// Also support HTML entities within the HTML markup.
ret.replace( /^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/, function( all, before, middle, after) {
frag = jQuery( middle ).get();
storeTmplItems( frag );
if ( before ) {
frag = unencode( before ).concat(frag);
}
if ( after ) {
frag = frag.concat(unencode( after ));
}
});
return frag ? frag : unencode( ret );
}
function unencode( text ) {
// Use createElement, since createTextNode will not render HTML entities correctly
var el = document.createElement( "div" );
el.innerHTML = text;
return jQuery.makeArray(el.childNodes);
}
// Generate a reusable function that will serve to render a template against data
function buildTmplFn( markup ) {
return new Function("jQuery","$item",
// Use the variable __ to hold a string array while building the compiled template. (See https://github.com/jquery/jquery-tmpl/issues#issue/10).
"var $=jQuery,call,__=[],$data=$item.data;" +
// Introduce the data as local variables using with(){}
"with($data){__.push('" +
// Convert the template into pure JavaScript
jQuery.trim(markup)
.replace( /([\\'])/g, "\\$1" )
.replace( /[\r\t\n]/g, " " )
.replace( /\$\{([^\}]*)\}/g, "{{= $1}}" )
.replace( /\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g,
function( all, slash, type, fnargs, target, parens, args ) {
var tag = jQuery.tmpl.tag[ type ], def, expr, exprAutoFnDetect;
if ( !tag ) {
throw "Unknown template tag: " + type;
}
def = tag._default || [];
if ( parens && !/\w$/.test(target)) {
target += parens;
parens = "";
}
if ( target ) {
target = unescape( target );
args = args ? ("," + unescape( args ) + ")") : (parens ? ")" : "");
// Support for target being things like a.toLowerCase();
// In that case don't call with template item as 'this' pointer. Just evaluate...
expr = parens ? (target.indexOf(".") > -1 ? target + unescape( parens ) : ("(" + target + ").call($item" + args)) : target;
exprAutoFnDetect = parens ? expr : "(typeof(" + target + ")==='function'?(" + target + ").call($item):(" + target + "))";
} else {
exprAutoFnDetect = expr = def.$1 || "null";
}
fnargs = unescape( fnargs );
return "');" +
tag[ slash ? "close" : "open" ]
.split( "$notnull_1" ).join( target ? "typeof(" + target + ")!=='undefined' && (" + target + ")!=null" : "true" )
.split( "$1a" ).join( exprAutoFnDetect )
.split( "$1" ).join( expr )
.split( "$2" ).join( fnargs || def.$2 || "" ) +
"__.push('";
}) +
"');}return __;"
);
}
function updateWrapped( options, wrapped ) {
// Build the wrapped content.
options._wrap = build( options, true,
// Suport imperative scenario in which options.wrapped can be set to a selector or an HTML string.
jQuery.isArray( wrapped ) ? wrapped : [htmlExpr.test( wrapped ) ? wrapped : jQuery( wrapped ).html()]
).join("");
}
function unescape( args ) {
return args ? args.replace( /\\'/g, "'").replace(/\\\\/g, "\\" ) : null;
}
function outerHtml( elem ) {
var div = document.createElement("div");
div.appendChild( elem.cloneNode(true) );
return div.innerHTML;
}
// Store template items in jQuery.data(), ensuring a unique tmplItem data data structure for each rendered template instance.
function storeTmplItems( content ) {
var keySuffix = "_" + cloneIndex, elem, elems, newClonedItems = {}, i, l, m;
for ( i = 0, l = content.length; i < l; i++ ) {
if ( (elem = content[i]).nodeType !== 1 ) {
continue;
}
elems = elem.getElementsByTagName("*");
for ( m = elems.length - 1; m >= 0; m-- ) {
processItemKey( elems[m] );
}
processItemKey( elem );
}
function processItemKey( el ) {
var pntKey, pntNode = el, pntItem, tmplItem, key;
// Ensure that each rendered template inserted into the DOM has its own template item,
if ( (key = el.getAttribute( tmplItmAtt ))) {
while ( pntNode.parentNode && (pntNode = pntNode.parentNode).nodeType === 1 && !(pntKey = pntNode.getAttribute( tmplItmAtt ))) { }
if ( pntKey !== key ) {
// The next ancestor with a _tmplitem expando is on a different key than this one.
// So this is a top-level element within this template item
// Set pntNode to the key of the parentNode, or to 0 if pntNode.parentNode is null, or pntNode is a fragment.
pntNode = pntNode.parentNode ? (pntNode.nodeType === 11 ? 0 : (pntNode.getAttribute( tmplItmAtt ) || 0)) : 0;
if ( !(tmplItem = newTmplItems[key]) ) {
// The item is for wrapped content, and was copied from the temporary parent wrappedItem.
tmplItem = wrappedItems[key];
tmplItem = newTmplItem( tmplItem, newTmplItems[pntNode]||wrappedItems[pntNode] );
tmplItem.key = ++itemKey;
newTmplItems[itemKey] = tmplItem;
}
if ( cloneIndex ) {
cloneTmplItem( key );
}
}
el.removeAttribute( tmplItmAtt );
} else if ( cloneIndex && (tmplItem = jQuery.data( el, "tmplItem" )) ) {
// This was a rendered element, cloned during append or appendTo etc.
// TmplItem stored in jQuery data has already been cloned in cloneCopyEvent. We must replace it with a fresh cloned tmplItem.
cloneTmplItem( tmplItem.key );
newTmplItems[tmplItem.key] = tmplItem;
pntNode = jQuery.data( el.parentNode, "tmplItem" );
pntNode = pntNode ? pntNode.key : 0;
}
if ( tmplItem ) {
pntItem = tmplItem;
// Find the template item of the parent element.
// (Using !=, not !==, since pntItem.key is number, and pntNode may be a string)
while ( pntItem && pntItem.key != pntNode ) {
// Add this element as a top-level node for this rendered template item, as well as for any
// ancestor items between this item and the item of its parent element
pntItem.nodes.push( el );
pntItem = pntItem.parent;
}
// Delete content built during rendering - reduce API surface area and memory use, and avoid exposing of stale data after rendering...
delete tmplItem._ctnt;
delete tmplItem._wrap;
// Store template item as jQuery data on the element
jQuery.data( el, "tmplItem", tmplItem );
}
function cloneTmplItem( key ) {
key = key + keySuffix;
tmplItem = newClonedItems[key] =
(newClonedItems[key] || newTmplItem( tmplItem, newTmplItems[tmplItem.parent.key + keySuffix] || tmplItem.parent ));
}
}
}
//---- Helper functions for template item ----
function tiCalls( content, tmpl, data, options ) {
if ( !content ) {
return stack.pop();
}
stack.push({ _: content, tmpl: tmpl, item:this, data: data, options: options });
}
function tiNest( tmpl, data, options ) {
// nested template, using {{tmpl}} tag
return jQuery.tmpl( jQuery.template( tmpl ), data, options, this );
}
function tiWrap( call, wrapped ) {
// nested template, using {{wrap}} tag
var options = call.options || {};
options.wrapped = wrapped;
// Apply the template, which may incorporate wrapped content,
return jQuery.tmpl( jQuery.template( call.tmpl ), call.data, options, call.item );
}
function tiHtml( filter, textOnly ) {
var wrapped = this._wrap;
return jQuery.map(
jQuery( jQuery.isArray( wrapped ) ? wrapped.join("") : wrapped ).filter( filter || "*" ),
function(e) {
return textOnly ?
e.innerText || e.textContent :
e.outerHTML || outerHtml(e);
});
}
function tiUpdate() {
var coll = this.nodes;
jQuery.tmpl( null, null, null, this).insertBefore( coll[0] );
jQuery( coll ).remove();
}
})( jQuery );
/* /js/jquery.tmpl.js end */
/* /js/vwService.js begin */
var loading = false;
var userProfiles = {
skip: 0,
element: '',
id: '',
type: '',
parentDiv: '',
profilesLoaded: [],
hideTimer: null,
loadIndexUsers: function(parentDiv, element, id, type, skip) {
this.loadUsers(parentDiv, element, id, type, skip, true);
},
initTipTip: function() {
$('.withTooltipVW').tipTip({
delay:100,
maxWidth: '213px',
minWidth: '213px',
content: true,
keepAlive: true,
activation: 'click'
});
},
loadUsers: function(parentDiv, element, id, type, skip, index) {
var _this = this;
_this.element = element;
_this.id = id;
_this.type = type;
_this.parentDiv = parentDiv;
$.ajax({
url: '/ajax/visitedwishing/moreusers/' + element + '/' + id + '/' + type + '/' + skip,
type: 'POST',
success: function(data, textStatus, jqXHR){
if(!data.result)
return;
if(data.result.profiles && data.result.profiles.users) {
_this.drawProfiles(data.result.profiles.users, data.result.profiles.skip, index);
} else {
var profileDiv = $('.userProfiles');
profileDiv.empty();
}
}
});
},
drawProfiles : function(profiles, skip, index) {
this.profilesLoaded = [];
var profileDiv = $('.userProfiles');
profileDiv.empty();
$('#userProfileTpl').template('userProfileItem');
for(i in profiles) {
this.profilesLoaded.push(profiles[i]);
profileDiv.append($.tmpl('userProfileItem', profiles[i]));
}
$('#userProfileMoreLinkTpl').template('userProfileMoreLink');
if(skip >= 0) {
var item = {
element : this.element,
id : this.id,
type : this.type,
skip : skip,
index : index
}
profileDiv.append($.tmpl('userProfileMoreLink', item));
}
$('.fullInfo').empty();
},
profileOver : function(userId) {
if(this.profilesLoaded.length <= 0)
return;
var profile = null;
for(i in this.profilesLoaded) {
if(this.profilesLoaded[i].id == userId) {
profile = this.profilesLoaded[i];
break;
}
}
if(profile == null)
return;
$('.vwUsersTooltip_list .userpic').css('opacity','0.5');
$('.upicSm_' + userId).css('opacity','1');
$('#userProfileFullInfoTpl').template('userProfileFullInfo');
$('.fullInfo').empty().append($.tmpl('userProfileFullInfo', profile));
},
resetFullInfo : function() {
}
}
function setVWStatus(element, id, type, sender) {
if(loading) return;
loading = true;
var infoBlockOffsetTop = sender.offset().top + 35;
var infoBlockOffsetLeft = sender.offset().left - 170;
var toggle_after_ajax = element === 'Destination' && sender.attr('id').indexOf('_act') !== -1;
var sender_toggled;
if (sender.attr('id').indexOf('_act') == -1) {
sender_toggled = $('#' + sender.attr('id') + '_act');
} else {
sender_toggled = $('#' + sender.attr('id').replace('_act',''));
}
if (!toggle_after_ajax) {
sender.hide();
sender_toggled.show();
}
$.ajax({
url: '/ajax/setvwstatus',
type: 'get',
data: {type: type, element: element, id: id, ajax: true},
success: function(data) {
loading = false;
var el_id = sender.attr('id').split('_')[0];
if (data.saveResult.status == 'success') {
if (toggle_after_ajax) {
sender.hide();
sender_toggled.show();
}
$(document).trigger('wvChange', [id, sender.parents('[data-id]'), data.saveResult]);
$('.'+ type +'Counter'+id).each(function() {$(this).html(data.saveResult[type+'Counter']);})
}
if (!data.saveResult.html) {
return;
}
if($('#vw_mess')) {
$('#vw_mess').remove();
}
$('body').append(
'' +
''
);
$('#vw_mess').offset({top: infoBlockOffsetTop, left: infoBlockOffsetLeft});
$('#overlayForVWmess, #vw_mess_close').bind('click', function(){
$('#overlayForVWmess').remove();
$('#vw_mess').remove();
});
$(window).bind("resize", function() {
//console.log(currVWEl);
if($('#vw_mess') && !$('#vw_mess').is(':hidden') && currVWEl != ''){
var obj = currVWEl;
$('#vw_mess').offset({left: parseInt(obj.offset().left - 60), top: parseInt(obj.offset().top + 35)});
}
});
}
});
}
$(function(){
userProfiles.initTipTip();
var helpTooltip = $('[data-help-tooltip]');
if(helpTooltip.size()){
var el = helpTooltip.first();
var data = el.data('help-tooltip').split('|');
var l = Math.round(el.position().left + el.width());
el.parent().append(
$('').show()
);
$(document).on('click', '#vwHelpTooltipClose', function(){
$('#vwHelpTooltip').remove();
});
// el.tooltip({
// content: function(){
// return '' + data[1];
// },
// position: {
// my: "center top+13",
// at: "bottom center"
// },
// tooltipClass: 'tooltip tipBottom'
// }).tooltip("open");
$.cookie('_' + data[0], 1, { expires: 365, path: '/' });
// setTimeout(function(){
// el.tooltip("close");
// }, 10000);
}
});
$(document).on('auth.complete', function(event, element) {
if($('.VWContainerItem')) {
$('.VWContainerItem').each(function() {
loadVWElement($(this), $(element).parent(), $(this).data('model'), $(this).data('id'));
});
}
function loadVWElement(container, element, model, id) {
var performClickCheckVisit = ($('.visit:first', container).data('elem') == $(element).data('elem'));
var performClickCheckWish = ($('.wish:first', container).data('elem') == $(element).data('elem'));
$.ajax({
url: '/ajax/login/userVW/'+ model +'/'+ id,
type: 'POST',
success: function(data, textStatus, jqXHR){
var newContainer = $(data);
container.after(newContainer);
container.remove();
var vElem = $($('.visit:first', newContainer).html());
if(performClickCheckVisit && $(element).hasClass('visit') && !$('.visit:first', newContainer).hasClass('act') && vElem.attr('onclick')) {
vElem.trigger('click');
}
var wElem = $($('.wish:first', newContainer).html());
if(performClickCheckWish && $(element).hasClass('wish') && !$('.wish:first', newContainer).hasClass('act') && wElem.attr('onclick')) {
wElem.trigger('click');
}
userProfiles.initTipTip();
}
});
}
});
/* /js/vwService.js end */
/* /tooltip/jquery.tipTip.js begin */
/*
* TipTip
* Copyright 2010 Drew Wilson
* www.drewwilson.com
* code.drewwilson.com/entry/tiptip-jquery-plugin
*
* Version 1.3 - Updated: Mar. 23, 2010
*
* This Plug-In will create a custom tooltip to replace the default
* browser tooltip. It is extremely lightweight and very smart in
* that it detects the edges of the browser window and will make sure
* the tooltip stays within the current window size. As a result the
* tooltip will adjust itself to be displayed above, below, to the left
* or to the right depending on what is necessary to stay within the
* browser window. It is completely customizable as well via CSS.
*
* This TipTip jQuery plug-in is dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*/
(function($){
$.fn.tipTip = function(options) {
var defaults = {
activation: "hover",
keepAlive: false,
maxWidth: "200px",
minWidth: null,
edgeOffset: 3,
defaultPosition: "bottom",
delay: 400,
fadeIn: 200,
fadeOut: 200,
attribute: "title",
cssClass: '',
content: false, // HTML or String to fill TipTIp with
enter: function(){},
exit: function(){}
};
var opts = $.extend(defaults, options);
// Setup tip tip elements and render them to the DOM
if($("#tiptip_holder").length <= 0){
var tiptip_holder = $('');
var tiptip_content = $('');
var tiptip_arrow = $('');
$("body").append(tiptip_holder.html(tiptip_content).prepend(tiptip_arrow.html('')));
} else {
var tiptip_holder = $("#tiptip_holder");
var tiptip_content = $("#tiptip_content");
var tiptip_arrow = $("#tiptip_arrow");
}
return this.each(function(){
var org_elem = $(this);
if(opts.content){
var org_title = $('#' + org_elem.data('tooltip')).html();
//var org_title = opts.content;
} else {
var org_title = org_elem.attr(opts.attribute);
}
if(org_title != ""){
if(!opts.content){
org_elem.removeAttr(opts.attribute); //remove original Attribute
}
var timeout = false,
tiptipHover = false, elemHover = false;
if(opts.activation == "hover"){
org_elem.hover(function(){
active_tiptip();
elemHover = true;
}, function(e){
elemHover = false;
if(!opts.keepAlive){
deactive_tiptip();
}else{
timeout = setTimeout(function(){
if(!tiptipHover)
deactive_tiptip();
}, 200);
}
});
if(opts.keepAlive){
tiptip_holder.hover(function(){
tiptipHover = true;
},function(){
tiptipHover = false;
if(!elemHover)
deactive_tiptip();
});
}
} else if(opts.activation == "focus"){
org_elem.focus(function(){
active_tiptip();
}).blur(function(){
deactive_tiptip();
});
} else if(opts.activation == "click"){
org_elem.click(function(){
active_tiptip();
return false;
});
if(!opts.keepAlive){
org_elem.hover(function(){},function(){
deactive_tiptip();
});
}
}
$(window).on('resize', function(){
deactive_tiptip();
});
function active_tiptip(){
opts.enter.call(this);
if(opts.content)
org_title = $('#' + org_elem.data('tooltip')).html();
tiptip_content.html('');
if(opts.activation == 'click' && opts.keepAlive) {
var elClose = $('');
elClose.bind('click', function(){
deactive_tiptip();
});
tiptip_content.append(elClose);
}
tiptip_content.append(org_title);
tiptip_holder.hide().removeAttr("class").css("margin","0").css('max-width', opts.maxWidth).css('min-width', opts.minWidth);
tiptip_arrow.removeAttr("style");
var top = parseInt(org_elem.offset()['top']);
var left = parseInt(org_elem.offset()['left']);
var org_width = parseInt(org_elem.outerWidth());
var org_height = parseInt(org_elem.outerHeight());
var tip_w = tiptip_holder.outerWidth();
var tip_h = tiptip_holder.outerHeight();
var w_compare = Math.round((org_width - tip_w) / 2);
var h_compare = Math.round((org_height - tip_h) / 2);
var marg_left = Math.round(left + w_compare);
var marg_top = Math.round(top + org_height + opts.edgeOffset);
var t_class = "";
var arrow_top = "";
var arrow_left = Math.round(tip_w - 12) / 2;
if(opts.defaultPosition == "bottom"){
t_class = "_bottom";
} else if(opts.defaultPosition == "top"){
t_class = "_top";
} else if(opts.defaultPosition == "left"){
t_class = "_left";
} else if(opts.defaultPosition == "right"){
t_class = "_right";
}
var right_compare = (w_compare + left) < parseInt($(window).scrollLeft());
var left_compare = (tip_w + left) > parseInt($(window).width());
if((right_compare && w_compare < 0) || (t_class == "_right" && !left_compare) || (t_class == "_left" && left < (tip_w + opts.edgeOffset + 5))){
t_class = "_right";
arrow_top = Math.round(tip_h - 13) / 2;
arrow_left = -12;
marg_left = Math.round(left + org_width + opts.edgeOffset);
marg_top = Math.round(top + h_compare);
} else if((left_compare && w_compare < 0) || (t_class == "_left" && !right_compare)){
t_class = "_left";
arrow_top = Math.round(tip_h - 13) / 2;
arrow_left = Math.round(tip_w);
marg_left = Math.round(left - (tip_w + opts.edgeOffset + 5));
marg_top = Math.round(top + h_compare);
}
var top_compare = (top + org_height + opts.edgeOffset + tip_h + 8) > parseInt($(window).height() + $(window).scrollTop());
var bottom_compare = ((top + org_height) - (opts.edgeOffset + tip_h + 8)) < 0;
if(top_compare || (t_class == "_bottom" && top_compare) || (t_class == "_top" && !bottom_compare)){
if(t_class == "_top" || t_class == "_bottom"){
t_class = "_top";
} /*else {
t_class = t_class+"_top";
}*/
arrow_top = tip_h;
marg_top = Math.round(top - (tip_h + 5 + opts.edgeOffset));
} else if(bottom_compare | (t_class == "_top" && bottom_compare) || (t_class == "_bottom" && !top_compare)){
if(t_class == "_top" || t_class == "_bottom"){
t_class = "_bottom";
} /*else {
t_class = t_class+"_bottom";
}*/
arrow_top = -12;
marg_top = Math.round(top + org_height + opts.edgeOffset);
}
/*
if(t_class == "_right_top" || t_class == "_left_top"){
marg_top = marg_top + 5;
} else if(t_class == "_right_bottom" || t_class == "_left_bottom"){
marg_top = marg_top - 5;
}
if(t_class == "_left_top" || t_class == "_left_bottom"){
marg_left = marg_left + 5;
}
*/
tiptip_arrow.css({"margin-left": arrow_left+"px", "margin-top": arrow_top+"px"});
tiptip_holder.css({"margin-left": marg_left+"px", "margin-top": marg_top+"px"}).addClass("tip" + t_class + ' ' + opts.cssClass);
if (timeout){ clearTimeout(timeout); }
timeout = setTimeout(function(){ tiptip_holder.stop(true,true).fadeIn(opts.fadeIn); }, opts.delay);
}
function deactive_tiptip(){
opts.exit.call(this);
if (timeout){ clearTimeout(timeout); }
//tiptip_holder.fadeOut(opts.fadeOut);
tiptip_holder.hide();
}
}
});
}
})(jQuery);
if(jQuery) {
$.event.trigger('load.tipTip.js');
}
/* /tooltip/jquery.tipTip.js end */
/* /js/jquery.calculateNaturalWidthHeight.js begin */
/*
* NaturalWith calculation function. It has to be async, because sometimes(e.g. for IE) it needs to wait for already cached image to load.
* @param onNaturalWidthHeightDefined callback(img) to be notified when naturalWidth is determined.
*/
jQuery.fn.calculateNaturalWidthHeight = function(onNaturalWidthHeightDefined) {
var img = this[0];
var naturalWidth = img.naturalWidth;
if (naturalWidth) {
onNaturalWidthHeightDefined(img);
} else { //No naturalWidth attribute in IE<9 - calculate it manually.
var newImg = new Image();
newImg.src = img.src;
//Wait for image to load
if (newImg.complete) {
img.naturalWidth = newImg.width;
img.naturalHeight = newImg.height;
onNaturalWidthHeightDefined(img);
} else {
$(newImg).load(function() {
img.naturalWidth = newImg.width;
img.naturalHeight = newImg.height;
onNaturalWidthHeightDefined(img);
});
}
}
};
/* /js/jquery.calculateNaturalWidthHeight.js end */
/* /js/photoLink.js begin */
$(function(){
var pu = new photoUploader('#photoContainer2', '#selectedPhoto');
var loader = $('#addPhotoForm_loader'),
uploadBtn = $('#addUserPhoto .add_btn');
pu.setErrorHandler(function(text, code) {
if (code >= 200) text = 'Что-то не так с сервером. Попробуйте загрузить фотографию ещё раз.';
showError(text);
return false;
});
pu.setSuccessHandler(function(){
window.location.reload();
});
function showError(text) {
$('#photoFormError').html(text).slideDown();
setTimeout(function(){ $('#photoFormError').hide('blind').html(''); }, 4000);
}
$(document).keydown(function(e) {
var els = $('#successUserPhoto, #addUserPhoto')
if (e.keyCode == 27 && ($('#successUserPhoto').is(':visible') || $('#addUserPhoto').is(':visible'))) $('.redigoPopup_close', els).click(); // esc
});
$('#affs_content').append($('#addUserPhoto, #successUserPhoto'));
$('.addPhotoLink').click(function(){
if($(this).is('.not_login_link')) return;
//$('body').addClass('noscroll');
bodyFreezing.freeze();
$('#addUserPhoto').show();
});
$('#addUserPhoto').click(function(e){
var el = $(e.target);
if(el.parents('.redigoPopup_frame').size() < 1)
return false;
});
$('#addUserPhoto .iClose').click(function(){
$('#addUserPhoto').hide();
uploadBtn.show().removeAttr('disabled');
loader.hide();
$('.iDel').show();
bodyFreezing.unfreeze();
});
$('#successUserPhoto .redigoPopup_close, #successUserPhotoClose').click(function(){
$('#successUserPhoto').hide();
bodyFreezing.unfreeze();
});
$("#successUserPhoto input[type='button']").click(function(){
$('.addPhotoLink').click();
$('#successUserPhoto .redigoPopup_close').click();
});
if ($('#successUserPhoto[data-show]').length){
bodyFreezing.freeze();
$('#successUserPhoto').show();
}
uploadBtn.click(function(){
if (pu.getPhotoCount()) {
loader.show();
$(this).hide();
$(this).attr("disabled", "disabled");
$('.iDel').hide();
pu.startUpload(JSVARS.model, JSVARS.objectId, {showSuccess: 1});
} else {
showError("Выберите файл с фотографией");
return false;
}
});
});
$(document).on('auth.complete', function(event, element) {
if($(element).hasClass('addPhotoLink')) {
$(element).trigger('click');
}
});
/* /js/photoLink.js end */
/* /js/footerForDestination.js begin */
$(document).on('load.main.js', function(e) {
equalHeight($(".footer_col"));
});
/* /js/footerForDestination.js end */
/* /js/friendsWidget.js begin */
$(function(){
var timeout;
$('.friendsWidget_list a').hover(
function(){
clearTimeout(timeout);
$('.friendsWidget_list a').not(this).css('opacity', '0.5');
$(this).css('opacity', '1');
}, function(){
clearTimeout(timeout);
timeout = setTimeout(function(){$('.friendsWidget_list a').css('opacity', '1');}, 200);
}
);
$('#friendsWidgetAllLink').click(function(){
$('.friendsWidget_list .none').slideDown();
$(this).slideUp();
});
})
/* /js/friendsWidget.js end */
/* /js/nearbyPlace.js begin */
$(function(){
$('#moreNearbyPoiLink').click(function(){
$('#moreNearbyPoi').slideDown();
$(this).parent().hide();
})
})
/* /js/nearbyPlace.js end */
/* /js/poiDescription.js begin */
$(function(){
if($(".topMap").size() == 0){
$("#address_poi").removeClass('pseudoLink');
}else{
$("#address_poi").click(function(){
var top = Math.round($(".topMap").offset().top);
$('#openCloseMapAction').click();
if($(window).scrollTop() > top)
$(window).scrollTop(top - 30);
})
}
$(".withTooltip").tipTip({
delay: 10,
maxWidth: '400px',
content: true,
keepAlive: true,
activation: 'click'
});
$('.poiDescription').css('min-height', ($('.poiDescription_incut').height() + 30) + 'px');
Calculator.init();
})
/* /js/poiDescription.js end */
/* /js/kiwitaxi.js begin */
$(function() {
$.customTabs = function(el, options) {
var cntx = this;
cntx.$el = $(el);
cntx.$nav = cntx.$el.find('.js-tab');
cntx.$filter = cntx.$el.find('.js-filter');
cntx.$content = cntx.$el.find('.js-content');
cntx.init = function() {
cntx.options = $.extend({},$.customTabs.defaultOptions, options);
cntx.$nav.on('click', 'a', function() {
var curList = cntx.$el.find('.current').attr('href').substring(1),
$newList = $(this),
listID = $newList.attr('href').substring(1);
if ( listID != curList ) {
cntx.$el.find('#'+curList).fadeOut(cntx.options.speed, function() {
cntx.$el.find('#'+listID).fadeIn(cntx.options.speed);
cntx.$el.find('.js-tab a').removeClass('current');
$newList.addClass('current');
});
}
return false;
});
if ( cntx.options.isFilter == true ) {
cntx.$el.find('.js-pop').on('click', function() {
var current = $(this);
cntx.$el.find(cntx.options.filter_elems).not('.' + cntx.options.filter_class).each(function() {
$(this).fadeOut(cntx.options.speed, function() {
cntx.$el.find(cntx.options.filter_class).fadeIn(cntx.options.speed);
current.removeClass('current');
cntx.$el.find('.js-all').addClass('current');
});
});
});
cntx.$el.find('.js-all').on('click', function() {
var current = $(this);
cntx.$el.find(cntx.options.filter_elems).each(function() {
$(this).fadeIn(cntx.options.speed, function() {
current.removeClass('current');
cntx.$el.find('.js-pop').addClass('current');
});
});
});
}
};
cntx.init();
};
$.customTabs.defaultOptions = {
'speed': 200,
'isFilter': false,
'filter_class': 'vis_cl',
'filter_elems': 'li > a'
};
$.fn.customTabs = function(options) {
return this.each(function() {
(new $.customTabs(this, options));
});
};
});
$(function() {
$('.js-context').customTabs({
'filter_class': 'js-popular',
'filter_elems': '.b-transfer-list__tabs-content__row',
'isFilter': true
});
$('.js-wrap-link .js-link').on('click', function(e){
e.preventDefault();
});
$('.js-wrap-link').on('click', function(){
var href = $(this).data('href');
window.open(href);
});
});
/* /js/kiwitaxi.js end */
/* /js/hotelRoomAvailabilityForm.js begin */
$(function () {
var dates = {};
if ($('#tourArrivalCountry').length) {
var co = $('#tourArrivalCountry').val();
$.ajax({
url: '/ajax/tours/dates/' + co,
dataType: 'json',
success: function(data) {
if (data && data.result && data.result.dates) {
dates = data.result.dates;
$('#hotelReservation').show();
}
}
})
} else {
$('#hotelReservation').show();
}
var tabs = $('#hotelReservationTabs li');
tabs.click(function(){
tabs.removeClass('act');
$('.js-tab-content').hide();
var tabContent = $($(this).data('tab'));
if(tabContent){
tabContent.show();
$(this).addClass('act');
}
});
var datepickerParams = {
'dateFormat': 'dd.mm.yy',
'altFormat': 'yy-mm-dd',
'dayNamesMin': ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
'defaultDate': new Date(),
'minDate': new Date(),
'firstDay': 1,
'monthNames': ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
'monthNamesShort': ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'],
'nextText': 'Вперед',
'prevText': 'Назад',
'showOn': 'both',
'buttonImage': '/i/i_calendar.png',
'buttonImageOnly': true,
beforeShowDay: function(date) {
var key = $.datepicker.formatDate("yy-mm-dd", date);
if (dates[key] && dates[key].length) {
return [true, null, null];
} else {
return [false, null, null]
}
},
onSelect: function(dateText, inst) {
var key = $.datepicker.formatDate("yy-mm-dd", $(this).datepicker( "getDate" ));
if (dates[key] && dates[key].length) {
var val = $('#tourNights').val();
$('#tourNights option').hide().prop('disabled', true);
var show = 'option[value="' + dates[key].join('"], option[value="') + '"]';
$(show, '#tourNights').show().prop('disabled', false);
if ($('option[value="'+val+'"]', '#tourNights').is(':disabled')) {
var val = $('#tourNights option:not(:disabled):first').val();
$('#tourNights').val(val);
}
} else {
$('#tourNights option').show();
}
}
};
$('#tourCheckin').datepicker(datepickerParams);
$(document).on('click', '#tourCheckRoomsBtn', function () {
$('#tourReservation .ym-error').removeClass('ym-error');
if ($("#tourCheckin").val() == ''){
$("#tourCheckin").parent().addClass('ym-error').bind('click', function(){ $(this).removeClass('ym-error')});
}else if($("#tourNights").val() == ''){
$("#tourNights").parent().addClass('ym-error').bind('change keyup', function(){ $(this).removeClass('ym-error')});
}else if ($("#tourCheckin").val() && $("#tourNights").val()) {
var checkin = $('#tourCheckin').val(),
nights = $("#tourNights").val(),
adultCount = $('#tourAdultCount input[name="adult"]:checked').val(),
kids = 0,
kids_ages = '';
var hasErrors = false;
$('#tourChildAges input[name="tour_child_ages[]"]').each(function(idx, el){
var val = parseInt($(el).val());
if (!val) {
$(el).parent().addClass('ym-error');
hasErrors = true;
return false;
}
kids++;
kids_ages += '&kids_ages[]=' + val;
});
if (hasErrors) {
return false;
}
var co = $('#tourArrivalCountry').val();
// tours/search?ct=1000&co=12&re=4&adults=2&kids=2&kids_ages[]=1&kids_ages[]=3&kids_ages[]=&df=15.10.2014&nf=7
// параметр - это id страны, куда летим. Параметр надо добавить в xml
//var url = '/tours/search?ct=1000&co='+co+'&re=0&adults=' + adultCount + '&kids=' + kids + kids_ages + '&df=' + checkin + '&nf=' + nights;
var hotelId = $('#tourLTId').val();
var url = '/tours/searchLevelTravelStart?ct=1000&co=' + co + '&adults=' + adultCount + '&kids=' + kids + kids_ages + '&df=' + checkin + '&nf='+nights+'&hid[]='+hotelId+'&_p=&ajax=true';
$('#tourCheckRoomsBtn').prop('disabled', true).text('Ищем тур..');
$('#tourNotFound').hide();
$.ajax({
url: url,
dataType: 'json',
success: function(data) {
(function LevelTravelSearchResult()
{
var rdata = data.param;
rdata.request_id = data.request_id;
rdata.ajax = true;
rdata.onlyFirst = true;
$.ajax({
url: '/tours/searchLevelTravelResult',
data: rdata,
success: function(data2) {
if (data2 === 'in_progress') {
setTimeout(LevelTravelSearchResult, 3000);
return;
}
if (data2 === 'nothing_found') {
$('#tourCheckRoomsBtn').prop('disabled', false).text('Узнать цену');
$('#tourNotFound').show();
return;
}
location.href = data2;
},
error: function() {
$('#tourCheckRoomsBtn').prop('disabled', false).text('Узнать цену');
}
});
})();
},
error: function(){
$('#tourCheckRoomsBtn').prop('disabled', false).text('Узнать цену');
}
});
_gaq.push(['_trackEvent', 'ToursLeads', 'Hotel-POI', 'LevelTravel']);
}
return false;
});
/* выбор кол-ва взрослых и детей */
if($('#roomAdultCount').size() > 0 || $('#tourAdultCount').size() > 0){
$('#roomAdultCount .adult, #roomChildCount .child, #tourAdultCount .adult, #tourChildCount .child').rating();
addChildClick('room', '#roomChildCount', '#roomChildAgesBlock', '#roomChildAges');
addChildClick('tour', '#tourChildCount', '#tourChildAgesBlock', '#tourChildAges');
}
/*************************************/
var checkin = $('#roomCheckin'),
checkout = $('#roomCheckout'),
checkPeriod = $("#checkPeriod"),
datePeriod = '';
function encodeQueryData(data)
{
var ret = [];
for (var d in data)
ret.push(encodeURIComponent(d) + "=" + encodeURIComponent(data[d]));
return ret.join("&");
}
$(document).on('click', '#roomCheckRoomsBtn', function () {
$('#roomReservation .ym-error').removeClass('ym-error');
if (checkPeriod.val() == '' || checkout.val() == '' || checkin.val() == '') {
checkPeriod.parent().addClass('ym-error');
return false;
}
var adultCount = $('#roomAdultCount input[name="adult"]:checked').val();
var _checkin = new Date(checkin.val());
var _checkout = new Date(checkout.val());
var params = {
txtDestino: $('#veturis_title').val(),
list_id: $('#veturis_id').val(),
D1: $.datepicker.formatDate('dd', _checkin),
MA1: $.datepicker.formatDate('mm_yy', _checkin),
D2: $.datepicker.formatDate('dd', _checkout),
MA2: $.datepicker.formatDate('mm_yy', _checkout)
};
var childAges = [];
var hasErrors = false;
$('#roomChildAges *[name="room_child_ages[]"]').each(function(idx, el){
var val = parseInt($(el).val());
if (!val) {
$(el).parent().addClass('ym-error');
hasErrors = true;
return false;
}
params['edadNino' + (idx + 1)] = val;
childAges.push(val);
});
if (hasErrors) {
return false;
}
params['O1'] = adultCount + 'A' + childAges.length + 'N';
var family = {
adults: parseInt(adultCount),
kids: childAges.length,
kids_ages: childAges
};
var oldFamily = $.cookie('family');
if(oldFamily !== JSON.stringify(family)) {
var nowDate = new Date();
var date = nowDate.getDate();
var year = nowDate.getYear();
var month = nowDate.getMonth();
month += 6;
if (month > 12) {
month -= 12;
year += 1;
}
var expiresDate = new Date(year, month, date);
$.cookie('family', JSON.stringify(family), {expires:expiresDate, path:'/'});
}
var url = 'http://hotels.redigo.ru/resultadosBusqueda.php?' + encodeQueryData(params);
var newWindow =window.open(url, "_blank");
newWindow.focus();
_gaq.push(['_trackEvent', 'HotelBookingLeads', 'Hotel-POI']);
return false;
});
$('#checkPeriod').datepicker({
'dateFormat': 'dd.mm.yy',
'altFormat': 'yy-mm-dd',
'dayNamesMin': ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
'defaultDate': new Date(),
'minDate': new Date(),
'firstDay': 1,
'monthNames': ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
'monthNamesShort': ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'],
'nextText': 'Вперед',
'prevText': 'Назад',
'showOn': 'both',
'buttonImage': '/i/i_calendar.png',
'buttonImageOnly': true,
'numberOfMonths': 2,
'selectOtherMonths': true,
onSelect: function(dateText, inst) {
datepickerRange.selectCount++;
datepickerRange.currentDate = datepickerRange.getSelectedDate(inst);
if(datepickerRange.selectCount < 2){
datepickerRange.startDate = datepickerRange.getSelectedDate(inst);
datepickerRange.endDate = null;
datePeriod = dateText;
inst.inline = true;
}else if(datepickerRange.currentDate.getTime() == datepickerRange.startDate.getTime()){
return false;
}else{
datepickerRange.selectCount = 0;
datepickerRange.endDate = datepickerRange.getSelectedDate(inst);
if(datepickerRange.startDate.getTime() > datepickerRange.endDate.getTime()){
datepickerRange.endDate = datepickerRange.startDate;
datepickerRange.startDate = datepickerRange.currentDate;
datePeriod = dateText + ' - ' + datePeriod;
}else{
datePeriod += ' - ' + dateText;
checkout.val(dateText);
}
inst.inline = false;
checkPeriod.parent().removeClass('ym-error');
}
checkPeriod.val(datePeriod);
checkin.val(datepickerRange.startDate);
checkout.val(datepickerRange.endDate);
},
onChangeMonthYear: function(year, month, inst) {
datepickerRange.currentDate = datepickerRange.getSelectedDate(inst);
},
onClose: function(dateText, inst) {
if(inst.inline){
inst.inline = false;
}
}
});
$('#checkPeriod').datepicker('option', 'beforeShowDay', function (currDate) {
if(datepickerRange.startDate && datepickerRange.endDate && currDate.getTime() >= datepickerRange.startDate.getTime() && currDate.getTime() <= datepickerRange.endDate.getTime()){
return [true, "ui-state-active", ""];
} else
return [true, "", ""];
});
$(document).on('mouseover mouseenter', '#ui-datepicker-div *[data-handler = "selectDay"] a.ui-state-default', function(){
var t = $(this),
td = t.parent(),
currDate = new Date(parseInt(td.data("year")), parseInt(td.data("month")), parseInt(t.html())),
startDate = datepickerRange.startDate;
if(startDate && currDate && !datepickerRange.endDate){
$('#ui-datepicker-div *[data-handler = "selectDay"]:not(".ui-datepicker-current-day") a.ui-state-default').each(function(el, pos){
var t = $(this),
td = t.parent();
var d = new Date(parseInt(td.data("year")), parseInt(td.data("month")), parseInt(t.html()));
if((d > startDate && d < currDate) || (d < startDate && d > currDate))
t.removeClass('ui-state-hover').addClass('ui-state-active');
else
t.removeClass('ui-state-active');
});
}
});
});
function addChildClick(idx, childCnt, agesCnt, ages ){
var childCnt = $(childCnt);
var childInps = $('.child', childCnt);
var ages = $(ages),
agesCnt = $(agesCnt);
childInps.click(function(){
var i = 0,
fields = '',
currInp = $('input[name="child"]:checked', childCnt);
ages.html('');
if(currInp.val() == 1 && currInp.data('act') == 1){
currInp.data('act', 0);
currInp.prop('checked', false);
$('.rating-cancel', childCnt).trigger('click');
agesCnt.slideUp();
}else{
var count = parseInt(currInp.val());
currInp.data('act', 1);
if(count > 0){
agesCnt.show();
while(i < count){
fields += '';
i++;
}
ages.append(fields).slideDown();
}
}
});
}
var datepickerRange = {
startDate:null,
endDate:null,
currentDate:new Date(),
selectCount:0,
getSelectedDate: function(inst){
return new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay);
}
};
/* /js/hotelRoomAvailabilityForm.js end */
/* /js/jquery.form.js begin */
/*!
* jQuery Form Plugin
* version: 3.43.0-2013.09.03
* Requires jQuery v1.5 or later
* Copyright (c) 2013 M. Alsup
* Examples and documentation at: http://malsup.com/jquery/form/
* Project repository: https://github.com/malsup/form
* Dual licensed under the MIT and GPL licenses.
* https://github.com/malsup/form#copyright-and-license
*/
/*global ActiveXObject */
;(function($) {
"use strict";
/*
Usage Note:
-----------
Do not use both ajaxSubmit and ajaxForm on the same form. These
functions are mutually exclusive. Use ajaxSubmit if you want
to bind your own submit handler to the form. For example,
$(document).ready(function() {
$('#myForm').on('submit', function(e) {
e.preventDefault(); // <-- important
$(this).ajaxSubmit({
target: '#output'
});
});
});
Use ajaxForm when you want the plugin to manage all the event binding
for you. For example,
$(document).ready(function() {
$('#myForm').ajaxForm({
target: '#output'
});
});
You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
form does not have to exist when you invoke ajaxForm:
$('#myForm').ajaxForm({
delegation: true,
target: '#output'
});
When using ajaxForm, the ajaxSubmit function will be invoked for you
at the appropriate time.
*/
/**
* Feature detection
*/
var feature = {};
feature.fileapi = $("").get(0).files !== undefined;
feature.formdata = window.FormData !== undefined;
var hasProp = !!$.fn.prop;
// attr2 uses prop when it can but checks the return type for
// an expected string. this accounts for the case where a form
// contains inputs with names like "action" or "method"; in those
// cases "prop" returns the element
$.fn.attr2 = function() {
if ( ! hasProp )
return this.attr.apply(this, arguments);
var val = this.prop.apply(this, arguments);
if ( ( val && val.jquery ) || typeof val === 'string' )
return val;
return this.attr.apply(this, arguments);
};
/**
* ajaxSubmit() provides a mechanism for immediately submitting
* an HTML form using AJAX.
*/
$.fn.ajaxSubmit = function(options) {
/*jshint scripturl:true */
// fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
if (!this.length) {
log('ajaxSubmit: skipping submit process - no element selected');
return this;
}
var method, action, url, $form = this;
if (typeof options == 'function') {
options = { success: options };
}
else if ( options === undefined ) {
options = {};
}
method = options.type || this.attr2('method');
action = options.url || this.attr2('action');
url = (typeof action === 'string') ? $.trim(action) : '';
url = url || window.location.href || '';
if (url) {
// clean url (don't include hash vaue)
url = (url.match(/^([^#]+)/)||[])[1];
}
options = $.extend(true, {
url: url,
success: $.ajaxSettings.success,
type: method || $.ajaxSettings.type,
iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
}, options);
// hook for manipulating the form data before it is extracted;
// convenient for use with rich editors like tinyMCE or FCKEditor
var veto = {};
this.trigger('form-pre-serialize', [this, options, veto]);
if (veto.veto) {
log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
return this;
}
// provide opportunity to alter form data before it is serialized
if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
log('ajaxSubmit: submit aborted via beforeSerialize callback');
return this;
}
var traditional = options.traditional;
if ( traditional === undefined ) {
traditional = $.ajaxSettings.traditional;
}
var elements = [];
var qx, a = this.formToArray(options.semantic, elements);
if (options.data) {
options.extraData = options.data;
qx = $.param(options.data, traditional);
}
// give pre-submit callback an opportunity to abort the submit
if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
log('ajaxSubmit: submit aborted via beforeSubmit callback');
return this;
}
// fire vetoable 'validate' event
this.trigger('form-submit-validate', [a, this, options, veto]);
if (veto.veto) {
log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
return this;
}
var q = $.param(a, traditional);
if (qx) {
q = ( q ? (q + '&' + qx) : qx );
}
if (options.type.toUpperCase() == 'GET') {
options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
options.data = null; // data is null for 'get'
}
else {
options.data = q; // data is the query string for 'post'
}
var callbacks = [];
if (options.resetForm) {
callbacks.push(function() { $form.resetForm(); });
}
if (options.clearForm) {
callbacks.push(function() { $form.clearForm(options.includeHidden); });
}
// perform a load on the target only if dataType is not provided
if (!options.dataType && options.target) {
var oldSuccess = options.success || function(){};
callbacks.push(function(data) {
var fn = options.replaceTarget ? 'replaceWith' : 'html';
$(options.target)[fn](data).each(oldSuccess, arguments);
});
}
else if (options.success) {
callbacks.push(options.success);
}
options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
var context = options.context || this ; // jQuery 1.4+ supports scope context
for (var i=0, max=callbacks.length; i < max; i++) {
callbacks[i].apply(context, [data, status, xhr || $form, $form]);
}
};
if (options.error) {
var oldError = options.error;
options.error = function(xhr, status, error) {
var context = options.context || this;
oldError.apply(context, [xhr, status, error, $form]);
};
}
if (options.complete) {
var oldComplete = options.complete;
options.complete = function(xhr, status) {
var context = options.context || this;
oldComplete.apply(context, [xhr, status, $form]);
};
}
// are there files to upload?
// [value] (issue #113), also see comment:
// https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219
var fileInputs = $('input[type=file]:enabled', this).filter(function() { return $(this).val() != ''; });
var hasFileInputs = fileInputs.length > 0;
var mp = 'multipart/form-data';
var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
var fileAPI = feature.fileapi && feature.formdata;
log("fileAPI :" + fileAPI);
var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
var jqxhr;
// options.iframe allows user to force iframe mode
// 06-NOV-09: now defaulting to iframe mode if file input is detected
if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
// hack to fix Safari hang (thanks to Tim Molendijk for this)
// see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
if (options.closeKeepAlive) {
$.get(options.closeKeepAlive, function() {
jqxhr = fileUploadIframe(a);
});
}
else {
jqxhr = fileUploadIframe(a);
}
}
else if ((hasFileInputs || multipart) && fileAPI) {
jqxhr = fileUploadXhr(a);
}
else {
jqxhr = $.ajax(options);
}
$form.removeData('jqxhr').data('jqxhr', jqxhr);
// clear element array
for (var k=0; k < elements.length; k++)
elements[k] = null;
// fire 'notify' event
this.trigger('form-submit-notify', [this, options]);
return this;
// utility fn for deep serialization
function deepSerialize(extraData){
var serialized = $.param(extraData, options.traditional).split('&');
var len = serialized.length;
var result = [];
var i, part;
for (i=0; i < len; i++) {
// #252; undo param space replacement
serialized[i] = serialized[i].replace(/\+/g,' ');
part = serialized[i].split('=');
// #278; use array instead of object storage, favoring array serializations
result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]);
}
return result;
}
// XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
function fileUploadXhr(a) {
var formdata = new FormData();
for (var i=0; i < a.length; i++) {
formdata.append(a[i].name, a[i].value);
}
if (options.extraData) {
var serializedData = deepSerialize(options.extraData);
for (i=0; i < serializedData.length; i++)
if (serializedData[i])
formdata.append(serializedData[i][0], serializedData[i][1]);
}
options.data = null;
var s = $.extend(true, {}, $.ajaxSettings, options, {
contentType: false,
processData: false,
cache: false,
type: method || 'POST'
});
if (options.uploadProgress) {
// workaround because jqXHR does not expose upload property
s.xhr = function() {
var xhr = $.ajaxSettings.xhr();
if (xhr.upload) {
xhr.upload.addEventListener('progress', function(event) {
var percent = 0;
var position = event.loaded || event.position; /*event.position is deprecated*/
var total = event.total;
if (event.lengthComputable) {
percent = Math.ceil(position / total * 100);
}
options.uploadProgress(event, position, total, percent);
}, false);
}
return xhr;
};
}
s.data = null;
var beforeSend = s.beforeSend;
s.beforeSend = function(xhr, o) {
o.data = formdata;
if(beforeSend)
beforeSend.call(this, xhr, o);
};
return $.ajax(s);
}
// private function for handling file uploads (hat tip to YAHOO!)
function fileUploadIframe(a) {
var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
var deferred = $.Deferred();
// #341
deferred.abort = function(status) {
xhr.abort(status);
};
if (a) {
// ensure that every serialized input is still enabled
for (i=0; i < elements.length; i++) {
el = $(elements[i]);
if ( hasProp )
el.prop('disabled', false);
else
el.removeAttr('disabled');
}
}
s = $.extend(true, {}, $.ajaxSettings, options);
s.context = s.context || s;
id = 'jqFormIO' + (new Date().getTime());
if (s.iframeTarget) {
$io = $(s.iframeTarget);
n = $io.attr2('name');
if (!n)
$io.attr2('name', id);
else
id = n;
}
else {
$io = $('');
$io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
}
io = $io[0];
xhr = { // mock object
aborted: 0,
responseText: null,
responseXML: null,
status: 0,
statusText: 'n/a',
getAllResponseHeaders: function() {},
getResponseHeader: function() {},
setRequestHeader: function() {},
abort: function(status) {
var e = (status === 'timeout' ? 'timeout' : 'aborted');
log('aborting upload... ' + e);
this.aborted = 1;
try { // #214, #257
if (io.contentWindow.document.execCommand) {
io.contentWindow.document.execCommand('Stop');
}
}
catch(ignore) {}
$io.attr('src', s.iframeSrc); // abort op in progress
xhr.error = e;
if (s.error)
s.error.call(s.context, xhr, e, status);
if (g)
$.event.trigger("ajaxError", [xhr, s, e]);
if (s.complete)
s.complete.call(s.context, xhr, e);
}
};
g = s.global;
// trigger ajax global events so that activity/block indicators work like normal
if (g && 0 === $.active++) {
$.event.trigger("ajaxStart");
}
if (g) {
$.event.trigger("ajaxSend", [xhr, s]);
}
if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
if (s.global) {
$.active--;
}
deferred.reject();
return deferred;
}
if (xhr.aborted) {
deferred.reject();
return deferred;
}
// add submitting element to data if we know it
sub = form.clk;
if (sub) {
n = sub.name;
if (n && !sub.disabled) {
s.extraData = s.extraData || {};
s.extraData[n] = sub.value;
if (sub.type == "image") {
s.extraData[n+'.x'] = form.clk_x;
s.extraData[n+'.y'] = form.clk_y;
}
}
}
var CLIENT_TIMEOUT_ABORT = 1;
var SERVER_ABORT = 2;
function getDoc(frame) {
/* it looks like contentWindow or contentDocument do not
* carry the protocol property in ie8, when running under ssl
* frame.document is the only valid response document, since
* the protocol is know but not on the other two objects. strange?
* "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy
*/
var doc = null;
// IE8 cascading access check
try {
if (frame.contentWindow) {
doc = frame.contentWindow.document;
}
} catch(err) {
// IE8 access denied under ssl & missing protocol
log('cannot get iframe.contentWindow document: ' + err);
}
if (doc) { // successful getting content
return doc;
}
try { // simply checking may throw in ie8 under ssl or mismatched protocol
doc = frame.contentDocument ? frame.contentDocument : frame.document;
} catch(err) {
// last attempt
log('cannot get iframe.contentDocument: ' + err);
doc = frame.document;
}
return doc;
}
// Rails CSRF hack (thanks to Yvan Barthelemy)
var csrf_token = $('meta[name=csrf-token]').attr('content');
var csrf_param = $('meta[name=csrf-param]').attr('content');
if (csrf_param && csrf_token) {
s.extraData = s.extraData || {};
s.extraData[csrf_param] = csrf_token;
}
// take a breath so that pending repaints get some cpu time before the upload starts
function doSubmit() {
// make sure form attrs are set
var t = $form.attr2('target'), a = $form.attr2('action');
// update form attrs in IE friendly way
form.setAttribute('target',id);
if (!method || /post/i.test(method) ) {
form.setAttribute('method', 'POST');
}
if (a != s.url) {
form.setAttribute('action', s.url);
}
// ie borks in some cases when setting encoding
if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
$form.attr({
encoding: 'multipart/form-data',
enctype: 'multipart/form-data'
});
}
// support timout
if (s.timeout) {
timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
}
// look for server aborts
function checkState() {
try {
var state = getDoc(io).readyState;
log('state = ' + state);
if (state && state.toLowerCase() == 'uninitialized')
setTimeout(checkState,50);
}
catch(e) {
log('Server abort: ' , e, ' (', e.name, ')');
cb(SERVER_ABORT);
if (timeoutHandle)
clearTimeout(timeoutHandle);
timeoutHandle = undefined;
}
}
// add "extra" data to form if provided in options
var extraInputs = [];
try {
if (s.extraData) {
for (var n in s.extraData) {
if (s.extraData.hasOwnProperty(n)) {
// if using the $.param format that allows for multiple values with the same name
if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) {
extraInputs.push(
$('').val(s.extraData[n].value)
.appendTo(form)[0]);
} else {
extraInputs.push(
$('').val(s.extraData[n])
.appendTo(form)[0]);
}
}
}
}
if (!s.iframeTarget) {
// add iframe to doc and submit the form
$io.appendTo('body');
}
if (io.attachEvent)
io.attachEvent('onload', cb);
else
io.addEventListener('load', cb, false);
setTimeout(checkState,15);
try {
form.submit();
} catch(err) {
// just in case form has element with name/id of 'submit'
var submitFn = document.createElement('form').submit;
submitFn.apply(form);
}
}
finally {
// reset attrs and remove "extra" input elements
form.setAttribute('action',a);
if(t) {
form.setAttribute('target', t);
} else {
$form.removeAttr('target');
}
$(extraInputs).remove();
}
}
if (s.forceSync) {
doSubmit();
}
else {
setTimeout(doSubmit, 10); // this lets dom updates render
}
var data, doc, domCheckCount = 50, callbackProcessed;
function cb(e) {
if (xhr.aborted || callbackProcessed) {
return;
}
doc = getDoc(io);
if(!doc) {
log('cannot access response document');
e = SERVER_ABORT;
}
if (e === CLIENT_TIMEOUT_ABORT && xhr) {
xhr.abort('timeout');
deferred.reject(xhr, 'timeout');
return;
}
else if (e == SERVER_ABORT && xhr) {
xhr.abort('server abort');
deferred.reject(xhr, 'error', 'server abort');
return;
}
if (!doc || doc.location.href == s.iframeSrc) {
// response not received yet
if (!timedOut)
return;
}
if (io.detachEvent)
io.detachEvent('onload', cb);
else
io.removeEventListener('load', cb, false);
var status = 'success', errMsg;
try {
if (timedOut) {
throw 'timeout';
}
var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
log('isXml='+isXml);
if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
if (--domCheckCount) {
// in some browsers (Opera) the iframe DOM is not always traversable when
// the onload callback fires, so we loop a bit to accommodate
log('requeing onLoad callback, DOM not available');
setTimeout(cb, 250);
return;
}
// let this fall through because server response could be an empty document
//log('Could not access iframe DOM after mutiple tries.');
//throw 'DOMException: not available';
}
//log('response detected');
var docRoot = doc.body ? doc.body : doc.documentElement;
xhr.responseText = docRoot ? docRoot.innerHTML : null;
xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
if (isXml)
s.dataType = 'xml';
xhr.getResponseHeader = function(header){
var headers = {'content-type': s.dataType};
return headers[header.toLowerCase()];
};
// support for XHR 'status' & 'statusText' emulation :
if (docRoot) {
xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
}
var dt = (s.dataType || '').toLowerCase();
var scr = /(json|script|text)/.test(dt);
if (scr || s.textarea) {
// see if user embedded response in textarea
var ta = doc.getElementsByTagName('textarea')[0];
if (ta) {
xhr.responseText = ta.value;
// support for XHR 'status' & 'statusText' emulation :
xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
}
else if (scr) {
// account for browsers injecting pre around json response
var pre = doc.getElementsByTagName('pre')[0];
var b = doc.getElementsByTagName('body')[0];
if (pre) {
xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
}
else if (b) {
xhr.responseText = b.textContent ? b.textContent : b.innerText;
}
}
}
else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
xhr.responseXML = toXml(xhr.responseText);
}
try {
data = httpData(xhr, dt, s);
}
catch (err) {
status = 'parsererror';
xhr.error = errMsg = (err || status);
}
}
catch (err) {
log('error caught: ',err);
status = 'error';
xhr.error = errMsg = (err || status);
}
if (xhr.aborted) {
log('upload aborted');
status = null;
}
if (xhr.status) { // we've set xhr.status
status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
}
// ordering of these callbacks/triggers is odd, but that's how $.ajax does it
if (status === 'success') {
if (s.success)
s.success.call(s.context, data, 'success', xhr);
deferred.resolve(xhr.responseText, 'success', xhr);
if (g)
$.event.trigger("ajaxSuccess", [xhr, s]);
}
else if (status) {
if (errMsg === undefined)
errMsg = xhr.statusText;
if (s.error)
s.error.call(s.context, xhr, status, errMsg);
deferred.reject(xhr, 'error', errMsg);
if (g)
$.event.trigger("ajaxError", [xhr, s, errMsg]);
}
if (g)
$.event.trigger("ajaxComplete", [xhr, s]);
if (g && ! --$.active) {
$.event.trigger("ajaxStop");
}
if (s.complete)
s.complete.call(s.context, xhr, status);
callbackProcessed = true;
if (s.timeout)
clearTimeout(timeoutHandle);
// clean up
setTimeout(function() {
if (!s.iframeTarget)
$io.remove();
else //adding else to clean up existing iframe response.
$io.attr('src', s.iframeSrc);
xhr.responseXML = null;
}, 100);
}
var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
if (window.ActiveXObject) {
doc = new ActiveXObject('Microsoft.XMLDOM');
doc.async = 'false';
doc.loadXML(s);
}
else {
doc = (new DOMParser()).parseFromString(s, 'text/xml');
}
return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
};
var parseJSON = $.parseJSON || function(s) {
/*jslint evil:true */
return window['eval']('(' + s + ')');
};
var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
var ct = xhr.getResponseHeader('content-type') || '',
xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
data = xml ? xhr.responseXML : xhr.responseText;
if (xml && data.documentElement.nodeName === 'parsererror') {
if ($.error)
$.error('parsererror');
}
if (s && s.dataFilter) {
data = s.dataFilter(data, type);
}
if (typeof data === 'string') {
if (type === 'json' || !type && ct.indexOf('json') >= 0) {
data = parseJSON(data);
} else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
$.globalEval(data);
}
}
return data;
};
return deferred;
}
};
/**
* ajaxForm() provides a mechanism for fully automating form submission.
*
* The advantages of using this method instead of ajaxSubmit() are:
*
* 1: This method will include coordinates for elements (if the element
* is used to submit the form).
* 2. This method will include the submit element's name/value data (for the element that was
* used to submit the form).
* 3. This method binds the submit() method to the form for you.
*
* The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
* passes the options argument along after properly binding events for submit elements and
* the form itself.
*/
$.fn.ajaxForm = function(options) {
options = options || {};
options.delegation = options.delegation && $.isFunction($.fn.on);
// in jQuery 1.3+ we can fix mistakes with the ready state
if (!options.delegation && this.length === 0) {
var o = { s: this.selector, c: this.context };
if (!$.isReady && o.s) {
log('DOM not ready, queuing ajaxForm');
$(function() {
$(o.s,o.c).ajaxForm(options);
});
return this;
}
// is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
return this;
}
if ( options.delegation ) {
$(document)
.off('submit.form-plugin', this.selector, doAjaxSubmit)
.off('click.form-plugin', this.selector, captureSubmittingElement)
.on('submit.form-plugin', this.selector, options, doAjaxSubmit)
.on('click.form-plugin', this.selector, options, captureSubmittingElement);
return this;
}
return this.ajaxFormUnbind()
.bind('submit.form-plugin', options, doAjaxSubmit)
.bind('click.form-plugin', options, captureSubmittingElement);
};
// private event handlers
function doAjaxSubmit(e) {
/*jshint validthis:true */
var options = e.data;
if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
e.preventDefault();
$(this).ajaxSubmit(options);
}
}
function captureSubmittingElement(e) {
/*jshint validthis:true */
var target = e.target;
var $el = $(target);
if (!($el.is("[type=submit],[type=image]"))) {
// is this a child element of the submit el? (ex: a span within a button)
var t = $el.closest('[type=submit]');
if (t.length === 0) {
return;
}
target = t[0];
}
var form = this;
form.clk = target;
if (target.type == 'image') {
if (e.offsetX !== undefined) {
form.clk_x = e.offsetX;
form.clk_y = e.offsetY;
} else if (typeof $.fn.offset == 'function') {
var offset = $el.offset();
form.clk_x = e.pageX - offset.left;
form.clk_y = e.pageY - offset.top;
} else {
form.clk_x = e.pageX - target.offsetLeft;
form.clk_y = e.pageY - target.offsetTop;
}
}
// clear form vars
setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
}
// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
$.fn.ajaxFormUnbind = function() {
return this.unbind('submit.form-plugin click.form-plugin');
};
/**
* formToArray() gathers form element data into an array of objects that can
* be passed to any of the following ajax functions: $.get, $.post, or load.
* Each object in the array has both a 'name' and 'value' property. An example of
* an array for a simple login form might be:
*
* [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
*
* It is this array that is passed to pre-submit callback functions provided to the
* ajaxSubmit() and ajaxForm() methods.
*/
$.fn.formToArray = function(semantic, elements) {
var a = [];
if (this.length === 0) {
return a;
}
var form = this[0];
var els = semantic ? form.getElementsByTagName('*') : form.elements;
if (!els) {
return a;
}
var i,j,n,v,el,max,jmax;
for(i=0, max=els.length; i < max; i++) {
el = els[i];
n = el.name;
if (!n || el.disabled) {
continue;
}
if (semantic && form.clk && el.type == "image") {
// handle image inputs on the fly when semantic == true
if(form.clk == el) {
a.push({name: n, value: $(el).val(), type: el.type });
a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
}
continue;
}
v = $.fieldValue(el, true);
if (v && v.constructor == Array) {
if (elements)
elements.push(el);
for(j=0, jmax=v.length; j < jmax; j++) {
a.push({name: n, value: v[j]});
}
}
else if (feature.fileapi && el.type == 'file') {
if (elements)
elements.push(el);
var files = el.files;
if (files.length) {
for (j=0; j < files.length; j++) {
a.push({name: n, value: files[j], type: el.type});
}
}
else {
// #180
a.push({ name: n, value: '', type: el.type });
}
}
else if (v !== null && typeof v != 'undefined') {
if (elements)
elements.push(el);
a.push({name: n, value: v, type: el.type, required: el.required});
}
}
if (!semantic && form.clk) {
// input type=='image' are not found in elements array! handle it here
var $input = $(form.clk), input = $input[0];
n = input.name;
if (n && !input.disabled && input.type == 'image') {
a.push({name: n, value: $input.val()});
a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
}
}
return a;
};
/**
* Serializes form data into a 'submittable' string. This method will return a string
* in the format: name1=value1&name2=value2
*/
$.fn.formSerialize = function(semantic) {
//hand off to jQuery.param for proper encoding
return $.param(this.formToArray(semantic));
};
/**
* Serializes all field elements in the jQuery object into a query string.
* This method will return a string in the format: name1=value1&name2=value2
*/
$.fn.fieldSerialize = function(successful) {
var a = [];
this.each(function() {
var n = this.name;
if (!n) {
return;
}
var v = $.fieldValue(this, successful);
if (v && v.constructor == Array) {
for (var i=0,max=v.length; i < max; i++) {
a.push({name: n, value: v[i]});
}
}
else if (v !== null && typeof v != 'undefined') {
a.push({name: this.name, value: v});
}
});
//hand off to jQuery.param for proper encoding
return $.param(a);
};
/**
* Returns the value(s) of the element in the matched set. For example, consider the following form:
*
*
*
* var v = $('input[type=text]').fieldValue();
* // if no values are entered into the text inputs
* v == ['','']
* // if values entered into the text inputs are 'foo' and 'bar'
* v == ['foo','bar']
*
* var v = $('input[type=checkbox]').fieldValue();
* // if neither checkbox is checked
* v === undefined
* // if both checkboxes are checked
* v == ['B1', 'B2']
*
* var v = $('input[type=radio]').fieldValue();
* // if neither radio is checked
* v === undefined
* // if first radio is checked
* v == ['C1']
*
* The successful argument controls whether or not the field element must be 'successful'
* (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
* The default value of the successful argument is true. If this value is false the value(s)
* for each element is returned.
*
* Note: This method *always* returns an array. If no valid value can be determined the
* array will be empty, otherwise it will contain one or more values.
*/
$.fn.fieldValue = function(successful) {
for (var val=[], i=0, max=this.length; i < max; i++) {
var el = this[i];
var v = $.fieldValue(el, successful);
if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
continue;
}
if (v.constructor == Array)
$.merge(val, v);
else
val.push(v);
}
return val;
};
/**
* Returns the value of the field element.
*/
$.fieldValue = function(el, successful) {
var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
if (successful === undefined) {
successful = true;
}
if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
(t == 'checkbox' || t == 'radio') && !el.checked ||
(t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
tag == 'select' && el.selectedIndex == -1)) {
return null;
}
if (tag == 'select') {
var index = el.selectedIndex;
if (index < 0) {
return null;
}
var a = [], ops = el.options;
var one = (t == 'select-one');
var max = (one ? index+1 : ops.length);
for(var i=(one ? index : 0); i < max; i++) {
var op = ops[i];
if (op.selected) {
var v = op.value;
if (!v) { // extra pain for IE...
v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
}
if (one) {
return v;
}
a.push(v);
}
}
return a;
}
return $(el).val();
};
/**
* Clears the form data. Takes the following actions on the form's input fields:
* - input text fields will have their 'value' property set to the empty string
* - select elements will have their 'selectedIndex' property set to -1
* - checkbox and radio inputs will have their 'checked' property set to false
* - inputs of type submit, button, reset, and hidden will *not* be effected
* - button elements will *not* be effected
*/
$.fn.clearForm = function(includeHidden) {
return this.each(function() {
$('input,select,textarea', this).clearFields(includeHidden);
});
};
/**
* Clears the selected form elements.
*/
$.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
return this.each(function() {
var t = this.type, tag = this.tagName.toLowerCase();
if (re.test(t) || tag == 'textarea') {
this.value = '';
}
else if (t == 'checkbox' || t == 'radio') {
this.checked = false;
}
else if (tag == 'select') {
this.selectedIndex = -1;
}
else if (t == "file") {
if (/MSIE/.test(navigator.userAgent)) {
$(this).replaceWith($(this).clone(true));
} else {
$(this).val('');
}
}
else if (includeHidden) {
// includeHidden can be the value true, or it can be a selector string
// indicating a special test; for example:
// $('#myForm').clearForm('.special:hidden')
// the above would clean hidden inputs that have the class of 'special'
if ( (includeHidden === true && /hidden/.test(t)) ||
(typeof includeHidden == 'string' && $(this).is(includeHidden)) )
this.value = '';
}
});
};
/**
* Resets the form data. Causes all form elements to be reset to their original value.
*/
$.fn.resetForm = function() {
return this.each(function() {
// guard against an input with the name of 'reset'
// note that IE reports the reset function as an 'object'
if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
this.reset();
}
});
};
/**
* Enables or disables any matching elements.
*/
$.fn.enable = function(b) {
if (b === undefined) {
b = true;
}
return this.each(function() {
this.disabled = !b;
});
};
/**
* Checks/unchecks any matching checkboxes or radio buttons and
* selects/deselects and matching option elements.
*/
$.fn.selected = function(select) {
if (select === undefined) {
select = true;
}
return this.each(function() {
var t = this.type;
if (t == 'checkbox' || t == 'radio') {
this.checked = select;
}
else if (this.tagName.toLowerCase() == 'option') {
var $sel = $(this).parent('select');
if (select && $sel[0] && $sel[0].type == 'select-one') {
// deselect all other options
$sel.find('option').selected(false);
}
this.selected = select;
}
});
};
// expose debug var
$.fn.ajaxSubmit.debug = false;
// helper fn for console logging
function log() {
if (!$.fn.ajaxSubmit.debug)
return;
var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
if (window.console && window.console.log) {
window.console.log(msg);
}
else if (window.opera && window.opera.postError) {
window.opera.postError(msg);
}
}
})( (typeof(jQuery) != 'undefined') ? jQuery : window.Zepto );
/* /js/jquery.form.js end */
/* /js/clickVoyageTour.js begin */
$(function(){
$('.clickVoyage_request').click(function(){
$('#clickVoyage_request, #clickVoyage_form').show();
$('#clickVoyage_successBox, #clickVoyage_errorBox').hide();
var data = $(this).data();
$('#clickVoyage_name, #clickVoyage_email, #clickVoyage_phone, #clickVoyage_comment').val('');
$('#clickVoyage_hotelTitle').text(data.title);
$('#clickVoyage_hotelStars').removeClass().addClass('hotelStars hotel' + data.stars +'star');
$('#clickVoyage_tour').val(data.tour);
});
$('#clickVoyage_request .iClose').click(function(){
$('#clickVoyage_request').hide();
bodyFreezing.unfreeze();
});
var options = {
url: '/ajax/tour/request',
dataType: 'json',
beforeSubmit: function(data) {
$('#clickVoyage_errorBox').hide();
},
success: function (data) {
if (data.e) return showError(data.m);
if (data.error) return showError('Ошибка на сервере. Попробуйте ещё раз.');
$('#clickVoyage_successBox').show();
$('#clickVoyage_form').hide();
},
error: function() {
showError('Ошибка на сервере. Попробуйте ещё раз.');
}
};
$('#clickVoyage_form').ajaxForm(options);
function showError(text) {
$('#clickVoyage_errorBox').html(text).slideDown();
setTimeout(function(){ $('#clickVoyage_errorBox').html('').slideUp(); },2000);
return false;
}
$('.clickVoyage_pansion-tooltip').tipTip({
delay:100,
minWidth: '213px',
content: true,
keepAlive: true
});
});
/* /js/clickVoyageTour.js end */