/*! * jQuery UI 1.8.14 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI */ (function(c,j){function k(a,b){var d=a.nodeName.toLowerCase();if("area"===d){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&l(a)}return(/input|select|textarea|button|object/.test(d)?!a.disabled:"a"==d?a.href||b:b)&&l(a)}function l(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.14", keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus(); b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this, "overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection", function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,m,n){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(m)g-=parseFloat(c.curCSS(f,"border"+this+"Width",true))||0;if(n)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth, outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){return k(a,!isNaN(c.attr(a,"tabindex")))},tabbable:function(a){var b=c.attr(a,"tabindex"),d=isNaN(b); return(d||b>=0)&&k(a,!d)}});c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e= 0;e0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a)[^>]*$|\{\{\! /,b={},f={},e,p={key:0,data:{}},i=0,c=0,l=[];function g(g,d,h,e){var c={data:e||(e===0||e===false)?e:d?d.data:{},_wrap:d?d._wrap:null,tmpl:null,parent:d||null,nodes:[],calls:u,nest:w,wrap:x,html:v,update:t};g&&a.extend(c,g,{nodes:[],parent:d});if(h){c.tmpl=h;c._ctnt=c._ctnt||c.tmpl(a,c);c.key=++i;(l.length?f:b)[i]=c}return c}a.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(f,d){a.fn[f]=function(n){var g=[],i=a(n),k,h,m,l,j=this.length===1&&this[0].parentNode;e=b||{};if(j&&j.nodeType===11&&j.childNodes.length===1&&i.length===1){i[d](this[0]);g=this}else{for(h=0,m=i.length;h0?this.clone(true):this).get();a(i[h])[d](k);g=g.concat(k)}c=0;g=this.pushStack(g,f,i.selector)}l=e;e=null;a.tmpl.complete(l);return g}});a.fn.extend({tmpl:function(d,c,b){return a.tmpl(this[0],d,c,b)},tmplItem:function(){return a.tmplItem(this[0])},template:function(b){return a.template(b,this[0])},domManip:function(d,m,k){if(d[0]&&a.isArray(d[0])){var g=a.makeArray(arguments),h=d[0],j=h.length,i=0,f;while(i").join(">").split('"').join(""").split("'").join("'")}});a.extend(a.tmpl,{tag:{tmpl:{_default:{$2:"null"},open:"if($notnull_1){__=__.concat($item.nest($1,$2));}"},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:{open:"if($notnull_1){__.push($1a);}"},"=":{_default:{$1:"$data"},open:"if($notnull_1){__.push($.encode($1a));}"},"!":{open:""}},complete:function(){b={}},afterManip:function(f,b,d){var e=b.nodeType===11?a.makeArray(b.childNodes):b.nodeType===1?[b]:[];d.call(f,b);m(e);c++}});function j(e,g,f){var b,c=f?a.map(f,function(a){return typeof a==="string"?e.key?a.replace(/(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g,"$1 "+d+'="'+e.key+'" $2'):a:j(a,e,a._ctnt)}):e;if(g)return c;c=c.join("");c.replace(/^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/,function(f,c,e,d){b=a(e).get();m(b);if(c)b=k(c).concat(b);if(d)b=b.concat(k(d))});return b?b:k(c)}function k(c){var b=document.createElement("div");b.innerHTML=c;return a.makeArray(b.childNodes)}function o(b){return new Function("jQuery","$item","var $=jQuery,call,__=[],$data=$item.data;with($data){__.push('"+a.trim(b).replace(/([\\'])/g,"\\$1").replace(/[\r\t\n]/g," ").replace(/\$\{([^\}]*)\}/g,"{{= $1}}").replace(/\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g,function(m,l,k,g,b,c,d){var j=a.tmpl.tag[k],i,e,f;if(!j)throw"Unknown template tag: "+k;i=j._default||[];if(c&&!/\w$/.test(b)){b+=c;c=""}if(b){b=h(b);d=d?","+h(d)+")":c?")":"";e=c?b.indexOf(".")>-1?b+h(c):"("+b+").call($item"+d:b;f=c?e:"(typeof("+b+")==='function'?("+b+").call($item):("+b+"))"}else f=e=i.$1||"null";g=h(g);return"');"+j[l?"close":"open"].split("$notnull_1").join(b?"typeof("+b+")!=='undefined' && ("+b+")!=null":"true").split("$1a").join(f).split("$1").join(e).split("$2").join(g||i.$2||"")+"__.push('"})+"');}return __;")}function n(c,b){c._wrap=j(c,true,a.isArray(b)?b:[q.test(b)?b:a(b).html()]).join("")}function h(a){return a?a.replace(/\\'/g,"'").replace(/\\\\/g,"\\"):null}function s(b){var a=document.createElement("div");a.appendChild(b.cloneNode(true));return a.innerHTML}function m(o){var n="_"+c,k,j,l={},e,p,h;for(e=0,p=o.length;e=0;h--)m(j[h]);m(k)}function m(j){var p,h=j,k,e,m;if(m=j.getAttribute(d)){while(h.parentNode&&(h=h.parentNode).nodeType===1&&!(p=h.getAttribute(d)));if(p!==m){h=h.parentNode?h.nodeType===11?0:h.getAttribute(d)||0:0;if(!(e=b[m])){e=f[m];e=g(e,b[h]||f[h]);e.key=++i;b[i]=e}c&&o(m)}j.removeAttribute(d)}else if(c&&(e=a.data(j,"tmplItem"))){o(e.key);b[e.key]=e;h=a.data(j.parentNode,"tmplItem");h=h?h.key:0}if(e){k=e;while(k&&k.key!=h){k.nodes.push(j);k=k.parent}delete e._ctnt;delete e._wrap;a.data(j,"tmplItem",e)}function o(a){a=a+n;e=l[a]=l[a]||g(e,b[e.parent.key+n]||e.parent)}}}function u(a,d,c,b){if(!a)return l.pop();l.push({_:a,tmpl:d,item:this,data:c,options:b})}function w(d,c,b){return a.tmpl(a.template(d),c,b,this)}function x(b,d){var c=b.options||{};c.wrapped=d;return a.tmpl(a.template(b.tmpl),b.data,c,b.item)}function v(d,c){var b=this._wrap;return a.map(a(a.isArray(b)?b.join(""):b).filter(d||"*"),function(a){return c?a.innerText||a.textContent:a.outerHTML||s(a)})}function t(){var b=this.nodes;a.tmpl(null,null,null,this).insertBefore(b[0]);a(b).remove()}})(jQuery); /*! * LiveJournal Bubble * use it to wrap some content with pop-up "bubble" - * it'll be positioned relative to "target" param (also can be passed with public method "show" invocation) * * Copyright 2011, sergey.zhirkov@sup.com * * http://docs.jquery.com/UI * * Depends: * jquery.ui.core.js * jquery.ui.widget.js * * Usage: * */ (function ($, window) { var LJBubble = { options: { target: null, currentTarget: null, hoverTimer: null, hoverDelay: 600, position: { x: 0, y: 0 }, /** * offset object can contain directly fields x and y or fields l,r,t,b,tl,tr,bl,br * that contain offset object for the bubble in the exact position. * Priority order: x,y -> tl,bl,tr,br -> t,b -> l,r */ offset: {}, // horizontal align relative to target elem // TODO "right" align align: 'center', // left || center // always show under target elem (even if bubble node does not fit screen height) alwaysShowUnderTarget: false, closeControl: true, closeOnContentClick: false, closeOnDocumentClick: true, closeOnEscape: true, // show on special event triggered by target (no action by default - "false") showOn: false, // 'click' || ('hover' || 'mouseover') || 'focus' || false preventDefaultTargetClick: true, outerHtml: '' + '', classNames: { containerAddClass: '', //if this value is set it will add this class to the top node positionPrefix: 'i-popup-arr', arrowDefault: 'i-popup-arr', withCloseControl: 'b-popup-withclosecontrol', noCloseControl: 'b-popup-noclosecontrol' }, selectors: { bubbleNode: 'div.bubble-node', bubbleArrow: 'i.i-popup-arr', bubbleInner: 'div.b-popup-inner', closeControl: 'i.i-popup-close' } }, // private methods _create: function () { var ljBubble = this, options = ljBubble.options, selectors = options.selectors; //this flag is needed because we cannot simply top propogation //on content click - user won't be able to open links. this.blockDocumentClick = false; // wrap bubble content with bubble outer html ljBubble._makeNode(); // set default options ljBubble._setOptions(options); }, _setOption: function (option, value) { var ljBubble = this, options = ljBubble.options, classNames = options.classNames, eventNamespace = '.' + ljBubble.widgetName + '-' + option, currentShowOn = options.showOn, newValue; switch (option) { case 'target': newValue = $(value); if(options.target && options.target[0] === newValue[0]) { break; } //if target changes we should rebind all events from the old one. //we don't if the old one is a string or an ordinary node, because //it can happen only on init if (options.target && !(typeof options.target === "string") && ('length' in options.target)) { this._setOption('showOn', false); options.target = newValue; this._setOption('showOn', currentShowOn); } else { options.target = $(value); } return; //return from the function, because we modified value break; case 'closeControl': if (value) { ljBubble.bubbleNode .delegate(options.selectors.closeControl, 'click' + eventNamespace, function (event) { ljBubble.hide(); }) .removeClass(classNames.noCloseControl) .addClass(classNames.withCloseControl); } else { ljBubble.bubbleNode .undelegate(options.selectors.closeControl, 'click' + eventNamespace) .removeClass(classNames.withCloseControl) .addClass(classNames.noCloseControl); } break; case 'position': ljBubble.bubbleNode.css({ left: value.x, top: value.y }); break; case 'showOn': value = (value == 'mouseover') ? 'hover' : value; if (value == 'click') { options.target.bind('click' + eventNamespace, function (event) { var target = $(this); event.preventDefault(); event.stopPropagation(); if (ljBubble.bubbleNode.is(':visible')) { ljBubble.hide(); } else { ljBubble.show(target); } }); } else { options.target.unbind('click' + eventNamespace); } if (value == 'hover') { options.target .add(ljBubble.bubbleNode) .bind('touchstart' + eventNamespace + ' mouseenter' + eventNamespace, function () { clearTimeout(options.hoverTimer); ljBubble.show(); }) .bind('mouseleave' + eventNamespace, function () { options.hoverTimer = setTimeout(function () { ljBubble.hide(); }, options.hoverDelay); }); } else { options.target .add(ljBubble.bubbleNode) .unbind('touchstart' + eventNamespace) .unbind('mouseenter' + eventNamespace) .unbind('mouseleave' + eventNamespace); } if (value == 'focus') { options.target .bind('focus' + eventNamespace, function (event) { var target = $(this); event.preventDefault(); event.stopPropagation(); ljBubble.show(target); }); // @BUG: this was commented out because click on the bubble // content triggers blur event. // .bind('blur' + eventNamespace, function (event) { // ljBubble.hide(); // }); } else { options.target .unbind('focus' + eventNamespace) .unbind('blur' + eventNamespace); } break; case 'preventDefaultTargetClick': if (value) { options.target.bind('click' + eventNamespace, function (event) { event.preventDefault(); }); } else { options.target.unbind('click' + eventNamespace); } break; case 'closeOnDocumentClick': if (value) { $(document).bind('click' + eventNamespace, function (event) { if (options.currentTarget && event.target !== options.currentTarget[0] && !ljBubble.blockDocumentClick ) { ljBubble.hide(); } ljBubble.blockDocumentClick = false; }); } else { $(document).unbind('click' + eventNamespace); } break; case 'closeOnEscape': if (value) { $(document).bind('keydown' + eventNamespace, function (event) { // escape if (event.keyCode == 27) { ljBubble.hide(); } }); } else { $(document).unbind('keydown' + eventNamespace); } break; case 'closeOnContentClick': if (!value) { ljBubble.bubbleNode.bind('mousedown' + eventNamespace + ' click' + eventNamespace, function (event) { ljBubble.blockDocumentClick = true; }); } else { ljBubble.bubbleNode.unbind('mousedown' + eventNamespace + ' click' + eventNamespace); } break; }; options[option] = value; }, _makeNode: function () { var bubbleNode = $(this.options.outerHtml), bubbleArrow = bubbleNode.find(this.options.selectors.bubbleArrow), bubbleInner = bubbleNode.find(this.options.selectors.bubbleInner); // this.element - with bubble content this.element .css('display', 'block') .prependTo(bubbleInner); this.bubbleNode = bubbleNode.prependTo('body'); this.bubbleArrow = bubbleArrow; // store arrow elem position bubbleNode.css({ visibility: 'hidden', display: 'block' }); var containerAddClass = this.options.classNames.containerAddClass; //additional class is needed to customize look and behavior of bubble if needed if (containerAddClass && containerAddClass.length > 0) { bubbleNode.addClass(containerAddClass); } bubbleArrow.data({ 'left': bubbleArrow.position().left, 'top': bubbleArrow.position().top }); bubbleNode.css({ visibility: 'visible', display: 'none' }); }, _getPosition: function (targetControl) { targetControl = targetControl || this.options.currentTarget; // if there is image in target (like this: ) - bubble will be positioned relative to image if (targetControl.find('img').length) { targetControl = targetControl.find('img'); } var ljBubble = this, options = ljBubble.options, align = options.align, alwaysShowUnderTarget = options.alwaysShowUnderTarget, viewport = $(window), viewportWidth = viewport.width(), viewportHeight = viewport.height(), elem = ljBubble.bubbleNode, elemWidth = elem.width(), elemHeight = elem.height(), popupArrow = ljBubble.bubbleArrow, popupArrowLeft = popupArrow.data('left'), popupArrowTop = popupArrow.data('top'), popupArrowWidth = 13, // popup arrow drawn with borders (6px at left and right side) targetOffset = targetControl.offset(), targetLeft = Math.round(targetOffset.left), targetTop = Math.round(targetOffset.top), targetWidth = targetControl.width(), targetHeight = targetControl.height(), scrollOffset = viewport.scrollTop(), leftPositionX = (align == 'center') ? // center align (arrow relative to target elem) Math.floor( targetLeft + (targetWidth / 2) - popupArrowLeft - (popupArrowWidth / 2) ) : // left align targetLeft, rightPositionX = targetLeft + Math.floor( (targetWidth / 2) - (elemWidth - popupArrowLeft - popupArrowWidth / 2) ), topPositionY = targetTop - popupArrowTop + targetHeight, bottomPositionY = targetTop + popupArrowTop - elemHeight, arrowPositionType = { x: 'l', // left y: 't' // top }, arrowPositionTypes = { 'tl': { x: leftPositionX, y: topPositionY }, 'tr': { x: rightPositionX, y: topPositionY }, 'bl': { x: leftPositionX, y: bottomPositionY }, 'br': { x: rightPositionX, y: bottomPositionY } }, position, checkAngle = { x: leftPositionX + elemWidth, y: topPositionY + elemHeight }; if (checkAngle.x > viewportWidth) { arrowPositionType.x = 'r'; // right } if (!alwaysShowUnderTarget && checkAngle.y > viewportHeight + viewport.scrollTop() && bottomPositionY > 0) { arrowPositionType.y = 'b'; // bottom } arrowPositionType = arrowPositionType.y + arrowPositionType.x; popupArrow .removeClass() .addClass(options.classNames.arrowDefault) .addClass(options.classNames.positionPrefix + arrowPositionType); position = arrowPositionTypes[arrowPositionType]; position = this._applyOffset( position, arrowPositionType ); return { position: position, bubblePosition: arrowPositionType }; }, _updatePosition: function () { var newPosition = this._getPosition(); this.option('position', newPosition.position); return newPosition; }, _applyOffset: function( position, bubblePosition ) { var offset = this.options.offset, offsetObj; if( 'x' in offset ) { offsetObj = offset; } else { offsetObj = offset[ bubblePosition ] || offset[ bubblePosition.charAt( 0 ) ] || offset[ bubblePosition.charAt( 1 ) ]; } if( offsetObj ) { position.x += offsetObj.x; position.y += offsetObj.y; } return position; }, // public methods show: function (target) { var ljBubble = this, options = ljBubble.options, position; target = (target) ? $(target) : options.target; $( ':lj-bubble' ).not( this.element ).bubble( "hide" ); if (!ljBubble.bubbleNode.is(':visible')) { ljBubble.option('currentTarget', target); position = ljBubble._updatePosition(); ljBubble.bubbleNode.show(); } ljBubble._trigger( 'show', null, [ { position: position } ] ); return this; }, hide: function () { //prevent delayed mouseout event clearTimeout(this.options.hoverTimer); this.bubbleNode.hide(); this._trigger('hide'); return this; }, /** * Reposition bubble on the page. The method is needed to reposition bubble * in case when it's content is changed and it remains visible at the same time. */ updatePosition: function() { this._updatePosition(); } }; $.widget('lj.bubble', LJBubble); })(jQuery, this); /** * Contextual popup is displayed on mouse hover near * every userpic and userhead */ /** * Widget shows the dialog to edit current user note. */ LJWidgetIPPU_AddAlias = new Class(LJWidgetIPPU, { init: function (opts, params) { opts.widgetClass = "IPPU::AddAlias"; this.width = opts.width; // Use for resizing later this.height = opts.height; // Use for resizing later this.alias = opts.alias; LJWidgetIPPU_AddAlias.superClass.init.apply(this, arguments); }, changeAlias: function (evt, form) { this.doPost({ alias: form["Widget[IPPU_AddAlias]_alias"].value + "", foruser: form["Widget[IPPU_AddAlias]_foruser"].value + "" }); evt.preventDefault(); }, onData: function (data) { if (!data.res || !data.res.success) { return; } this.close(); //Changing button. Only on profile page var edit_node = jQuery('.profile_addalias'); if (edit_node.length) { if (data.res.alias) { edit_node[0].style.display = 'none'; edit_node[1].style.display = 'block'; edit_node[1].firstChild.alias = data.res.alias; } else { edit_node[0].style.display = 'block'; edit_node[1].style.display = 'none'; } } var username = data.res.username, alias = data.res.alias; if(ContextualPopup.cachedResults[username]) { ContextualPopup.cachedResults[username].alias_title = alias ? 'Edit Note' : 'Add Note'; ContextualPopup.cachedResults[username].alias = alias; } if (ContextualPopup.currentId === username) { ContextualPopup.renderPopup(ContextualPopup.currentId); } }, onError: function (msg) { LJ_IPPU.showErrorNote("Error: " + msg); }, onRefresh: function () { var form = jQuery('#addalias_form').get(0), input = jQuery(form['Widget[IPPU_AddAlias]_alias']), delete_btn = jQuery(form['Widget[IPPU_AddAlias]_aliasdelete']), widget = this; input.focus(); if (delete_btn.length) { delete_btn.click(function(){ input.val(''); }); input.input(function() { // save button disabled form['Widget[IPPU_AddAlias]_aliaschange'].disabled = !this.value; }); } jQuery(form).submit(function(e) { widget.changeAlias(e, form) }); }, cancel: function (e) { this.close(); } }); //this object contains only authToken Aliases = {} function addAlias(target, ptitle, ljusername, oldalias, callback) { if (! ptitle) return true; new LJWidgetIPPU_AddAlias({ title: ptitle, width: 440, height: 180, authToken: Aliases.authToken, callback: callback }, { alias: target.alias||oldalias, foruser: ljusername }); return false; } (function($) { /** * Object contains methods to build and display user popup. */ var popup = { popupDelay: 500, popupTimer: null, adriverImages : { anonymous: 'http://ad.adriver.ru/cgi-bin/rle.cgi?sid=1&ad=186396&bt=21&pid=482107&bid=893162&bn=893162&rnd={random}', guest: 'http://ad.adriver.ru/cgi-bin/rle.cgi?sid=1&ad=186396&bt=21&pid=482107&bid=893165&bn=893165&rnd={random}', self: 'http://ad.adriver.ru/cgi-bin/rle.cgi?sid=1&ad=186396&bt=21&pid=482107&bid=893167&bn=893167&rnd={random}' }, classNames: { popup: 'b-popup-contextual' }, selectors: { wrapper: '.b-contextualhover', bubble: '.b-popup', popup: '.contextualPopup' }, templates: { wrapper: '
', loading: 'Loading...', content: '{{if userpic }}' + '
' + '
' + '' + '
' + '
' + '{{/if}}' + '
' + '
' + '

{{html title.title}}

' + '{{each headLinks}}' + '

{{if $value.url}}${$value.text}' + '{{else}}{{html $value}}{{/if}}

' + '{{/each}}' + '
' + '{{each(i, group) linkGroups}}' + '{{if group.length }}' + '
    ' + '{{each group}}
  • ' + '{{if $value.url}}${$value.text}' + '{{else}}{{html $value}}{{/if}}' + '
  • {{/each}}' + '
' + '{{/if}}' + '{{/each}}' + '{{if showBanOptions }}' + '
    ' + '{{if reportBot}}
  • ${reportBot.text}
  • {{/if}}' + '
  • ' + '

    ${banUsersLink.text}:

    ' + '{{if banCheckboxes}}
    ' + '{{each banCheckboxes}}' + '

    ' + '{{/each}}' + '
    {{/if}}' + '
  • ' + '
' + '{{/if}}' + '
' }, init: function() { var wrapper = jQuery(this.templates.wrapper), self = this; this.element = jQuery(wrapper).bubble({ // showDelay: 500, closeControl: false, // showOn: 'hover', hide: function() { ContextualPopup.hideHourglass(); }, classNames: { containerAddClass: this.classNames.popup } }); this.bindShowHideEvents(this.element.closest(this.selectors.bubble)); }, bindShowHideEvents: function(el) { var self = this; el = jQuery(el); el.bind('mouseenter', function(ev) { self.show(); }); el.bind('mouseleave', function(ev) { self.hide(); }); }, show: function(force) { this.setVisibile(true, force); }, hide: function(force) { this.setVisibile(false, force); }, setVisibile: function(isVisible, force) { var action = isVisible ? "show" : "hide", self = this; force = force || false; clearTimeout(this.popupTimer); if (force) { this.element.bubble(action); } else { this.popupTimer = setTimeout(function() { self.element.bubble(action); }, this.popupDelay); } }, /** * Constructs object, passes it to the template, * inserts it in the bubble and binds events. * * @param {Object} data Object returned from the endpoint. * @param {String} ctxPopupId The id of the user. */ render: function(data, ctxPopupId) { if (!data) { this.element.empty().append(this.templates.loading); return; } else if (!data.username || !data.success || data.noshow) { this.hide(true); return; } var buildObject = { headLinks: [], linkGroups: [] }; if (data.url_userpic && data.url_userpic != ctxPopupId) { buildObject.userpic = { allpics: data.url_allpics, pic: data.url_userpic }; } // relation var label, username = '' + data.display_username + ' '; if (data.is_comm) { if (data.is_member) label = data.ml_you_member.replace('[[username]]', username); else if (data.is_friend) label = data.ml_you_watching.replace('[[username]]', username); else label = username; } else if (data.is_syndicated) { if (data.is_friend) label = data.ml_you_subscribed.replace('[[username]]', username); else label = username; } else { if (data.is_requester) { label = data.ml_this_is_you; } else { label = username; if (data.is_friend_of) { if (data.is_friend) label += data.ml_mutual_friend; else label += data.ml_lists_as_friend; } else if (data.is_friend) { label += data.ml_your_friend; } } } buildObject.title = { title: label }; // aliases if (!data.is_requester && data.is_logged_in) { if (data.alias_enable) { if (data.alias) { buildObject.headLinks.push('' + data.alias.encodeHTML() + ''); } buildObject.headLinks.push({ url: Site.siteroot + '/manage/notes.bml', click: function(e) { e.preventDefault(); addAlias(this, data.alias_title, data.username, data.alias || ''); }, text: data.alias_title }); } else { buildObject.headLinks.push( ''+ ''+ ''+ ' '+ ''+data.alias_title+''+ ''); } } // add/remove friend link if (data.is_logged_in && !data.is_requester) { buildObject.headLinks.push({ selector: 'a[href="{url}"]:first', url: data.url_addfriend, click: function(e) { e.preventDefault(); e.stopPropagation(); ContextualPopup.changeRelation(data, ctxPopupId, data.is_friend ? 'removeFriend' : 'addFriend', e); }, text: function() { if (data.is_comm) return data.is_friend ? data.ml_stop_community : data.ml_watch_community; else if (data.is_syndicated) return data.is_friend ? data.ml_unsubscribe_feed : data.ml_subscribe_feed; else return data.is_friend ? data.ml_remove_friend : data.ml_add_friend; }() }); if (data.is_friend && !data.is_identity) { buildObject.headLinks.push({ url: data.url_addfriend, text: data.ml_edit_friend_tags }); } } var linkGroup = []; // member of community if (data.is_logged_in && data.is_comm) { linkGroup.push({ selector: 'a[href="{url}"]', url: data.is_member ? data.url_leavecomm : data.url_joincomm, text: data.is_member ? data.ml_leave : data.ml_join_community, click: function(e) { e.preventDefault(); ContextualPopup.changeRelation(data, ctxPopupId, data.is_member ? 'leave' : 'join', e); } }); } //filter community if( ( !data.is_comm && Site.current_journal && ( "is_comm" in Site.current_journal ) && Site.current_journal.is_comm === "1" ) || data.posted_in ) { linkGroup.push({ url: ( ( data.posted_in ) ? data.posted_in : Site.current_journal.url_journal ) + '/?poster=' + data.username, text: ( Site.remoteUser === data.username && !data.posted_in ) ? ( data.ml_filter_by_poster_me || 'Filter community by me' ) : ( data.ml_filter_by_poster || 'Filter community by poster' ) }); } buildObject.linkGroups.push(linkGroup); linkGroup = []; // send message if (data.is_logged_in && data.is_person && ! data.is_requester && data.url_message) { linkGroup.push({ url: data.url_message, text: data.ml_send_message }); } // vgift if ((data.is_person || data.is_comm) && !data.is_requester && data.can_receive_vgifts) { linkGroup.push({ url: Site.siteroot + '/shop/vgift.bml?to=' + data.username, text: data.ml_send_gift }); } // wishlist if ((data.is_person || data.is_comm) && !data.is_requester && data.wishlist_url) { linkGroup.push({ url: data.wishlist_url, text: data.ml_view_wishlist }); } // buy the same userhead if (data.is_logged_in && data.is_person && ! data.is_requester && data.is_custom_userhead) { linkGroup.push((data.is_app_userhead) ? { url: data.url_userhead_install, text: data.ml_userhead_install } : { url: data.url_buy_userhead, text: data.ml_buy_same_userhead } ); } // identity if (data.is_identity && data.is_requester) { linkGroup.push({ url: Site.siteroot + '/identity/convert.bml', text: data.ml_upgrade_account }); } // add site-specific content here var extraContent = LiveJournal.run_hook('ctxpopup_extrainfo', data); if (extraContent) { linkGroup.push(extraContent); } buildObject.linkGroups.push(linkGroup); if (data.is_logged_in && !data.is_requester && !data.is_comm && !data.is_syndicated) { buildObject.showBanOptions = true; buildObject.banUsersLink = { url: Site.siteroot + '/manage/banusers.bml', text: data.ml_ban }; // ban/unban buildObject.banCheckboxes = []; buildObject.banCheckboxes.push({ selector: '.ban_user', className: 'ban_user', label: data.ml_ban_in_my, checked: data.is_banned, change: function(e) { e.preventDefault(); ContextualPopup.changeRelation(data, ctxPopupId, data.is_banned ? 'setUnban' : 'setBan', e); } }); // report a bot if (!Site.remote_is_suspended) { buildObject.reportBot = { url: Site.siteroot + '/abuse/bots.bml?user=' + data.username, text: data.ml_report }; } // ban user from all maintained communities if (!data.is_requester && !data.is_comm && !data.is_syndicated && data.have_communities) { buildObject.banCheckboxes.push({ selector: '.ban_everywhere', className: 'ban_everywhere', label: data.ban_everywhere_title, checked: data.is_banned_everywhere, change: function(e) { e.preventDefault(); var action = data.is_banned_everywhere ? 'unbanEverywhere' : 'banEverywhere'; ContextualPopup.changeRelation(data, ctxPopupId, action, e); } }); } } var userType = 'guest'; if (!data.is_logged_in) { // anonymous userType = 'anonymous'; } else if (data.is_requester) { // self userType = 'self'; } new Image().src = this.adriverImages[userType].supplant({ random: Math.random()}); this.element .empty() .append(jQuery.tmpl(this.templates.content, buildObject)); if (this.element.is(':visible')) { //show method forces bubble to reposition with respect to the new content this.element.bubble('updatePosition'); } this.setPopupEvents(buildObject); }, /** * Go through all build objects and find all callbacks that should be bound * to the node events. * * @param {Object} buildObject Template object. */ setPopupEvents: function(buildObject) { var element = this.element; element.undelegate(); function walkObject(obj) { $.each(obj, function(key, value) { var selector; if (value.click) { //default handler is by url var selector = value.selector || '[href="' + value.url + '"]'; selector = selector.supplant(value); element.delegate(selector, 'click', value.click); } if (value.change) { //for checkboxes selector should present anyway var selector = value.selector; selector = selector.supplant(value); element.delegate(selector, 'change', value.change); } //maybe this object has children with events to be set if(typeof value === "object") { walkObject(value); } }); } walkObject(buildObject); } }; window.ContextualPopup = { cachedResults : {}, currentRequests: {}, currentId : null, currentElement : null, hourglass : null, setup: function() { // don't do anything if no remote if (!Site.ctx_popup) return; popup.init(); jQuery(document.body) .mouseover(ContextualPopup.mouseOver) .ljAddContextualPopup(); }, /** * Search child nodes and bind hover events on them if needed. */ searchAndAdd: function(node) { if (!Site.ctx_popup) return; // attach to all ljuser head icons var rex_userid = /\?userid=(\d+)/, rex_userpic = /(userpic\..+\/\d+\/\d+)|(\/userpic\/\d+\/\d+)/, ljusers = jQuery('span.ljuser>a>img', node), i = -1, userid, ljuser, parent; // use while for speed while (ljusers[++i]) { ljuser = ljusers[i]; parent = ljuser.parentNode; if (parent.href && (userid = parent.href.match(rex_userid))) { ljuser.userid = userid[1]; } else if (parent.parentNode.getAttribute('lj:user')) { ljuser.username = parent.parentNode.getAttribute('lj:user'); } else { continue; } if (parent.parentNode.getAttribute('data-journal')) { ljuser.posted_in = parent.parentNode.getAttribute('data-journal'); } ljuser.className += ' ContextualPopup'; } ljusers = node.getElementsByTagName('img'); i = -1; while (ljusers[++i]) { ljuser = ljusers[i]; if (ljuser.src.match(rex_userpic)) { ljuser.up_url = ljuser.src; if (ljuser.parentNode.getAttribute('data-journal')) { ljuser.posted_in = ljuser.parentNode.getAttribute('data-journal'); } ljuser.className += ' ContextualPopup'; } } }, mouseOver: function(e) { var target = e.target, ctxPopupId = target.username || target.userid || target.up_url, t = ContextualPopup; if (target.tagName == 'IMG' && ctxPopupId) { // if we don't have cached data background request it if (!t.cachedResults[ctxPopupId]) { t.getInfo(target, ctxPopupId); } // doesn't display alt as tooltip if (jQuery.browser.msie && target.title !== undefined) { target.title = ''; } // show other popup if (t.currentElement != target) { t.showPopup(ctxPopupId, target); } else { popup.show(); } } }, showPopup: function(ctxPopupId, ele) { var showNow = popup.element.is(':visible'); jQuery(this.currentElement) .unbind('mouseenter mouseleave'); this.currentId = ctxPopupId; var data = this.cachedResults[ctxPopupId]; if (data && data.noshow) return; if (this.currentElement && this.currentElement !== ele) { popup.hide(true); } if (data && data.error) { popup.hide(true); ContextualPopup.showNote(data.error, ele); return; } popup.render(data, ctxPopupId); popup.element.bubble('option', 'target', jQuery(ele)); popup.bindShowHideEvents(ele); popup.show(showNow); this.currentElement = ele; }, renderPopup: function(ctxPopupId) { popup.render(this.cachedResults[ctxPopupId], ctxPopupId) }, // ajax request to change relation changeRelation: function (info, ctxPopupId, action, e) { var changedRelation = function(data) { if (data.error) { return ContextualPopup.showNote(data.error); } if (ContextualPopup.cachedResults[ctxPopupId]) { jQuery.extend(ContextualPopup.cachedResults[ctxPopupId], data); } // if the popup is up, reload it ContextualPopup.renderPopup(ctxPopupId); } var xhr = jQuery.post(LiveJournal.getAjaxUrl('changerelation'), { target: info.username, action: action, auth_token: info[action + '_authtoken'] }, function(data) { ContextualPopup.hourglass = null; changedRelation(data); }, 'json' ); ContextualPopup.hideHourglass(); ContextualPopup.hourglass = jQuery(e).hourglass(xhr)[0]; //entering mouse on the hourglass should no close popup jQuery(ContextualPopup.hourglass.ele).bind('mouseenter', function(ev) { popup.element.trigger('mouseenter'); }); // so mousing over hourglass doesn't make ctxpopup think mouse is outside ContextualPopup.hourglass.add_class_name('lj_hourglass'); return false; }, // create a little popup to notify the user of something showNote: function (note, ele) { ele = ele || popup.element[0]; LJ_IPPU.showNote(note, ele); }, // do ajax request of user info getInfo: function(target, popup_id) { var t = this; if (t.currentRequests[popup_id]) { return; } t.currentRequests[popup_id] = 1; var reqParams = { user: target.username || '' }; jQuery.ajax({ url: LiveJournal.getAjaxUrl('ctxpopup'), data: Object.extend( reqParams, { userid: target.userid || 0, userpic_url: target.up_url || '', mode: 'getinfo' }), dataType: 'json', success: function(data) { if (data.error) { data.username = reqParams.user; t.cachedResults[data.username] = data; popup.hide(true); t.showNote(data.error, target); return; } if( target.posted_in ) { data.posted_in = target.posted_in; } t.cachedResults[String(data.userid)] = t.cachedResults[data.username] = t.cachedResults[data.url_userpic] = data; // non default userpic if (target.up_url) { t.cachedResults[target.up_url] = data; } t.currentRequests[popup_id] = null; if (t.currentId == popup_id) { t.renderPopup(popup_id); } }, error: function() { t.currentRequests[popup_id] = null; } }); }, hideHourglass: function () { if (this.hourglass) { this.hourglass.hide(); this.hourglass = null; } } }; })(jQuery); // when page loads, set up contextual popups jQuery(ContextualPopup.setup); function AdFox_SetLayerVis(spritename,state){ document.getElementById(spritename).style.visibility=state; } function AdFox_Open(AF_id){ AdFox_SetLayerVis('AdFox_DivBaseFlash_'+AF_id, "hidden"); AdFox_SetLayerVis('AdFox_DivOverFlash_'+AF_id, "visible"); } function AdFox_Close(AF_id){ AdFox_SetLayerVis('AdFox_DivOverFlash_'+AF_id, "hidden"); AdFox_SetLayerVis('AdFox_DivBaseFlash_'+AF_id, "visible"); } function AdFox_getCodeScript(AF_n,AF_id,AF_src){ var AF_doc; if(AF_n<10){ try{ if(document.all && !window.opera){ AF_doc = window.frames['AdFox_iframe_'+AF_id].document; }else if(document.getElementById){ AF_doc = document.getElementById('AdFox_iframe_'+AF_id).contentDocument; } }catch(e){} if(AF_doc){ AF_doc.write('<\/scr'+'ipt>'); }else{ setTimeout('AdFox_getCodeScript('+(++AF_n)+','+AF_id+',"'+AF_src+'");', 100); } } } function adfoxSdvigContent(banID, flashWidth, flashHeight){ var obj = document.getElementById('adfoxBanner'+banID).style; if (flashWidth == '100%') obj.width = flashWidth; else obj.width = flashWidth + "px"; if (flashHeight == '100%') obj.height = flashHeight; else obj.height = flashHeight + "px"; } function adfoxVisibilityFlash(banName, flashWidth, flashHeight){ var obj = document.getElementById(banName).style; if (flashWidth == '100%') obj.width = flashWidth; else obj.width = flashWidth + "px"; if (flashHeight == '100%') obj.height = flashHeight; else obj.height = flashHeight + "px"; } function adfoxStart(banID, FirShowFlNum, constVisFlashFir, sdvigContent, flash1Width, flash1Height, flash2Width, flash2Height){ if (FirShowFlNum == 1) adfoxVisibilityFlash('adfoxFlash1'+banID, flash1Width, flash1Height); else if (FirShowFlNum == 2) { adfoxVisibilityFlash('adfoxFlash2'+banID, flash2Width, flash2Height); if (constVisFlashFir == 'yes') adfoxVisibilityFlash('adfoxFlash1'+banID, flash1Width, flash1Height); if (sdvigContent == 'yes') adfoxSdvigContent(banID, flash2Width, flash2Height); else adfoxSdvigContent(banID, flash1Width, flash1Height); } } function adfoxOpen(banID, constVisFlashFir, sdvigContent, flash2Width, flash2Height){ var aEventOpenClose = new Image(); var obj = document.getElementById("aEventOpen"+banID); if (obj) aEventOpenClose.src = obj.title+'&rand='+Math.random()*1000000+'&prb='+Math.random()*1000000; adfoxVisibilityFlash('adfoxFlash2'+banID, flash2Width, flash2Height); if (constVisFlashFir != 'yes') adfoxVisibilityFlash('adfoxFlash1'+banID, 1, 1); if (sdvigContent == 'yes') adfoxSdvigContent(banID, flash2Width, flash2Height); } function adfoxClose(banID, constVisFlashFir, sdvigContent, flash1Width, flash1Height){ var aEventOpenClose = new Image(); var obj = document.getElementById("aEventClose"+banID); if (obj) aEventOpenClose.src = obj.title+'&rand='+Math.random()*1000000+'&prb='+Math.random()*1000000; adfoxVisibilityFlash('adfoxFlash2'+banID, 1, 1); if (constVisFlashFir != 'yes') adfoxVisibilityFlash('adfoxFlash1'+banID, flash1Width, flash1Height); if (sdvigContent == 'yes') adfoxSdvigContent(banID, flash1Width, flash1Height); } /*! * LiveJournal Inline calendar * * Copyright 2011, dmitry.petrov@sup.com * * http://docs.jquery.com/UI * * Depends: * jquery.ui.core.js * jquery.ui.widget.js * * @overview Inline calendar widget. * * Widget can be attached to any existant markup. * * Date wildcards used: * - %D - day ( 01 - 31 ) * - %M - month ( 01 - 02 ) * - %Y - year ( yyyy, e.g. 2002 ) * - %s - unix timestamp in ms * * Options: * - dayRef: Format of the url that will be attached to each day in the calendar. * - allRefs: Wether to attach links to days in the calendar. * and override currentDate on success. * - activeFrom: Days before this will be inactive in calendar. * - actoveUntil: Days after this willbe inactive incalendar. * - startMonth: Widget will not allow to switch calendar pane to the month before this. * - endMonth: Widget will not allow to switch calendar pane to the month after this. * - startAtSunday: Wether to count sunday as the start of the week. * - events: Object, containing events to show in the calendar. They will be rendered as links. Structure of the object: * { "yyyy": { "mm1" : [ d1, d2, d3, d4 ], "mm2": [ d5, d6, d7 ] } } * * Events: * - daySelected: Event is triggered when user selects a day in the calendar. The second parameter passed to the * function is a Date object. * - dateChange Event is triggered when user click on next or prev month/year button. * - currentDateChange: Events is triggered when a new date is set in calendar as current. * * Consistent options ( setting these options is guaranteed to work correctly ): * - currentDate, date - Set/get current date. * - activeFrom, date - Set/get earliest active date. * - activeUntil, date - Set/get last active date. * - title, title - set calendar title. * - events, obj - override current events object * * @TODO: move all service functions to the widget object and merge it with the view. * */ (function( $, window ) { var defaultOptions = { dayRef: '/%Y/%M/%D', monthRef: '', //the same, but for the months and year. Calendar will render link, if options are set yearRef: '', allRefs: false, currentDate: new Date(), //allow user to select dates in this range activeUntil: null, activeFrom: null, //allow user to switch months between these dates startMonth: new Date( 1900, 0, 1 ), endMonth: new Date( 2050, 0, 1 ), startAtSunday: false, dateFormat: "%Y-%M-%D", defaultTitle: "Calendar", events: null, //object with events to show in the calendar displayedMonth: null, //month displayed on the calendar. If not specified at //startup currentDate is used instead. dateChange: null, selectors: { table: 'table', title: 'h5', tbody: 'tbody', month: '.cal-nav-month', year: '.cal-nav-year', prevMonth: '.cal-nav-month .cal-nav-prev', nextMonth: '.cal-nav-month .cal-nav-next', prevYear: '.cal-nav-year .cal-nav-prev', nextYear: '.cal-nav-year .cal-nav-next', monthLabel: '.cal-nav-month .cal-month', yearLabel: '.cal-nav-year .cal-year' }, classNames: { container: '', inactive : 'other', future : 'other', current : 'current', nextDisabled : 'cal-nav-next-dis', prevDisabled : 'cal-nav-prev-dis', cellHover : 'hover' }, ml: { monthNamesLong: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], monthNamesShort: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], caption: "Calendar" } }; function getDateNumber( d, dropDays ) { dropDays = dropDays || false; var day = d.getDate().toString(); if( day.length === 1 ) { day = "0" + day; } if( dropDays ) { day = ""; } var month = d.getMonth().toString(); if( month.length === 1 ) { month = "0" + month; } return parseInt( d.getFullYear().toString() + month + day, 10); } function insideTimeRange( range, iDate ) { return getDateNumber( iDate, true ) >= getDateNumber( range[0], true ) && getDateNumber( iDate, true ) <= getDateNumber( range[1], true ); } function View(nodes, styles, o) { this.initialize = function () { this.tbody = this.catchTableStructure(); }; this.modelChanged = function (monthDate, events, switcherStates) { //we have a 30% speedup when we temporary remove tbody from dom this.tbody.detach(); this.fillDates(monthDate, events); for (var sws in switcherStates) { nodes[sws][ (!switcherStates[sws]) ? 'addClass' : 'removeClass']( this.disabledStyle(sws) ); } var monthText = o.monthRef ? $( '', { href: Calendar._formatDate( monthDate, o.monthRef ), text: o.ml.monthNamesShort[ monthDate.getMonth() ] } ) : o.ml.monthNamesShort[ monthDate.getMonth() ]; var yearText = o.yearRef ? $( '', { href: Calendar._formatDate( monthDate, o.yearRef ), text: monthDate.getFullYear() } ) : monthDate.getFullYear(); nodes.monthLabel.empty().append( monthText ); nodes.yearLabel.empty().append( yearText ); this.tbody.appendTo( nodes.table ); }; this.catchTableStructure = function() { var tbody = nodes.tbody[0]; nodes.daysCells = []; nodes.daysSpans = []; var row, rowsCount = tbody.rows.length, cell, cellsCount; var toAdd = 6 - rowsCount; var rowStr = ''; for( var i = 0; i < 7; ++i ) { rowStr += ''; } rowStr += ''; while( toAdd-- > 0 ) { //add missing rows if server has rendered not enough markup $( rowStr ).hide().appendTo( nodes.tbody ); } rowsCount = 6; nodes.lastRow = jQuery( tbody.rows[ tbody.rows.length - 1 ] ); for( row = 0; row < rowsCount; ++row ) { for( cell = 0, cellsCount = tbody.rows[ row ].cells.length; cell < cellsCount; ++cell ) { // take into account span inside td var node = jQuery( tbody.rows[ row ].cells[ cell ] ); nodes.daysCells.push( node ); nodes.daysSpans.push( node.children() ); } } return jQuery( tbody ); }; this.fillDates = function (monthDate, events) { function hasEvents( date ) { var year = date.getFullYear(), month = date.getMonth(), day = date.getDate(); return( events && events[ year ] && events[ year ][ month ] && events[ year ][ month ][ day ] ); } var d = new Date( monthDate ); d.setDate( 1 ); var offset; if( o.startAtSunday ) { offset = d.getDay(); } else { offset = ( d.getDay() === 0 ? 6 : d.getDay() - 1 ); } d.setDate( 1 - offset ); for( var i = 0, l = nodes.daysCells.length; i < l; ++i ) { var cell = nodes.daysCells[ i ], span = nodes.daysSpans[ i ]; this.formDayString( d, cell, span, hasEvents( d ), this.isActiveDate( d, monthDate ) ); d.setDate( d.getDate() + 1 ); } d.setDate( d.getDate() - 1 ); //get the date from the last cell //we do not use show and hide methods, because show method sets display: block; if( d.getDate() < 7 ) { nodes.lastRow.css('display', ''); } else { nodes.lastRow.css('display', 'none'); } }; this.isActiveDate = function( date, currentMonth ) { var isActive = true; isActive = ( currentMonth.getFullYear() === date.getFullYear() && currentMonth.getMonth() === date.getMonth() ); if( isActive && ( o.activeFrom || o.activeUntil ) ) { isActive = ( o.activeFrom && getDateNumber( o.activeFrom ) <= getDateNumber( date ) ) || ( o.activeUntil && getDateNumber( o.activeUntil ) >= getDateNumber( date ) ); } return isActive; }; this.formDayString = function( d, cell, span, hasEvents, isActive ) { d = new Date( d ); var oldDay = cell.data( 'day' ), oldHasEvents = cell.data( 'hasEvents' ), oldIsActive = cell.data( 'isActive' ); var isCurrentDay = ( getDateNumber( d ) === getDateNumber( o.currentDate ) ); cell.data( 'day', d ); cell.data( 'isActive', isActive ); cell.data( 'hasEvents', hasEvents ); cell[isCurrentDay ? 'addClass' : 'removeClass']( styles.current ); cell.removeClass( styles.cellHover ); if( !isActive ) { cell.addClass( styles.inactive ); span.html(d.getDate()); } else if( hasEvents || o.allRefs ) { cell.removeClass( styles.inactive ) span.html( jQuery( '', { html: d.getDate(), href: Calendar._formatDate( d, o.dayRef ) } ) ); } else { cell.removeClass( styles.inactive ); span.html(d.getDate()); } }; this.disabledStyle = function (sws) { if(sws === 'prevMonth' || sws === 'prevYear') { return styles.prevDisabled; } else { return styles.nextDisabled; } }; } var Calendar = { options: {}, //all options were move to the default options object _create: function() { this._preInit(); this._initialize(); this._postInit(); }, _preInit: function() { var def = $[ this.namespace ][ this.widgetName ].getDefaults(); this.options = jQuery.extend( true, {}, def, this.options ); }, // @TODO: need to change the structure of initialization code to remove this method _initialize: function() { if( !this.options.displayedMonth ) { this.options.displayedMonth = new Date( this.options.currentDate ); } this._events = this.options.events; this._hideTimer = null; this._nodes = this._nodes || { container: this.element, root: this.element }; this._invalidateTimer = null; this._bindNodes(); this.options.startMonth.setDate( 1 ); this._view = new (this._getView())( this._nodes, this.options.classNames, this.options ); this._view.initialize(); if( this._nodes.table.hasClass( "monday" ) ) { this._setOption( "startAtSunday", false ); } this._bindEvents(); }, _postInit: function() { }, _getView: function() { return View; }, _bindNodes: function() { for( var i in this.options.selectors ) { if( !( i in this._nodes ) ) { this._nodes[ i ] = this._nodes.container.find( this.options.selectors[ i ] ); } } var displayedMonth; if( displayedMonth = this._parseDate( this._nodes.table.attr( "data-date" ) ) ) { this.options.displayedMonth = displayedMonth; } }, destroy: function() { $.Widget.prototype.destroy.apply(this, arguments); }, _bindEvents: function() { var self = this; var switcherStates = this._getSwitcherStates( this.options.currentDate ), switcherMouseDown = function( item ) { return function (ev) { ev.preventDefault(); ev.stopPropagation(); var switcherStates = self._getSwitcherStates( self.options.currentDate ); if( switcherStates[item] ) { self["_" + item](); } }; }; for (var sws in switcherStates) { this._nodes[sws].mousedown( switcherMouseDown(sws) ); } this._nodes.tbody .delegate( 'td', 'mousedown', function( ev ) { self._cellSelectedEvent( $( this ), ev ); } ); }, _switchMonth: function ( go ) { var event = jQuery.Event( "dateChange" ); event.moveForward = go > 0; event.switchType = Math.abs( go ) === 12 ? "year" : ( Math.abs( go ) === 1 ? "month" : null ); event.date = new Date( this.options.displayedMonth ); event.date.setMonth( event.date.getMonth() + go ); this._nodes.root.trigger( event ); this._setOption( 'displayedMonth', event.date ); }, _prevMonth: function () { this._switchMonth( -1 ); }, _nextMonth: function () { this._switchMonth( 1 ); }, _prevYear : function () { this._switchMonth( -12 ); }, _nextYear : function () { this._switchMonth( 12 ); }, _cellSelectedEvent: function( cell, ev ) { //if cell is inactive or user controls it's behavior we do not pass event to the link if( !cell.data('isActive' ) || this._cellSelected( cell.data( 'day' ) ) ) { ev.stopPropagation(); ev.preventDefault(); } }, /** * @return {Boolean} returns true if user prevents default behaviour */ _cellSelected: function( date ) { var event = jQuery.Event( "daySelected" ); this._nodes.root.trigger( event, [ date, this._formatDate(date, this.options.dateFormat) ] ); if( !event.isDefaultPrevented() ) { this._setOption( 'currentDate', date ); } return !event.isDefaultPrevented(); }, _fitDate: function( date ) { date = new Date( date ); var enabledMonthsRange = [ this.options.startMonth, this.options.endMonth ]; if( !insideTimeRange( enabledMonthsRange, date ) ) { if( getDateNumber( date, true ) < getDateNumber( enabledMonthsRange[ 0 ], true ) ) { date = new Date( enabledMonthsRange[ 0 ] ); } else { date = new Date( enabledMonthsRange[ 1 ] ); } } return date; }, _getSwitcherStates: function () { var monthDate = this.options.displayedMonth, yearStart = new Date( monthDate.getFullYear(), 0, 1 ), yearEnd = new Date( monthDate.getFullYear(), 11, 1 ); return { prevMonth: this._isActivePrev( monthDate ) !== false, prevYear: this._isActivePrev( yearStart ) !== false, nextMonth: this._isActiveNext( monthDate ) !== false, nextYear: this._isActiveNext( yearEnd ) !== false }; }, _isActiveNext: function( date ) { return this._isActiveDate( date, 1 ); }, _isActivePrev: function( date ) { return this._isActiveDate( date, -1 ); }, _isActiveDate: function( date, dir ) { var d = new Date( date ); d.setMonth( d.getMonth() + dir ); d.setDate( 1 ); return insideTimeRange( [ this.options.startMonth, this.options.endMonth ], d ); }, _invalidateDisplay: function() { var self = this; clearTimeout( this._invalidateTimer ); setTimeout( function() { self._view.modelChanged( self.options.displayedMonth, self._events, self._getSwitcherStates() ); }, 50 ); }, _setOption: function( name, value ) { switch( name ) { case 'currentDate': this.options.currentDate = this._fitDate( value ); var event = jQuery.Event("currentDateChange"), date = new Date(this.options.currentDate); this._nodes.root.trigger( event, [ date, this._formatDate(date, this.options.dateFormat) ] ); this._setOption( 'displayedMonth', value ); break; case 'activeFrom': this.options.activeFrom = new Date( value ); this._invalidateDisplay(); break; case 'activeUntil': this.options.activeUntil = new Date( value ); this._invalidateDisplay(); break; case 'title': this._title = value; this._nodes.title.html( value ); break; case 'events': this._events = value; this._invalidateDisplay(); break; case 'displayedMonth': var newDate = this._fitDate( new Date( value ) ), isCurrentMonth = ( getDateNumber( newDate, true ) === getDateNumber( this.options.displayedMonth ) ); if( !isCurrentMonth ) { this.options.displayedMonth = this._fitDate( new Date( value ) ); this._invalidateDisplay(); } break; case 'startMonth': this.options.startMonth = new Date( value ); this._invalidateDisplay(); break; case 'endMonth': this.options.endMonth = new Date( value ); this._invalidateDisplay(); break; case 'startAtSunday': this.options.startAtSunday = !!value; break; } }, getElement: function( name ) { if( name in this._nodes ) { return this._nodes[ name ]; } else { return null; } }, /** * Serialize date to string according the format string. * We suppose that every token takes place in the string only once. * * @param string Date Date object. * @param string format Date format. * @return Date|null Returns new Date object or null on parse failure. */ _formatDate: function( d, format ) { format = format || "%Y-%M-%D"; var str = format; var subs = { '%Y' : d.getFullYear(), '%M' : ( "0" + ( d.getMonth() + 1 ) ).slice( -2 ), '%D' : ( "0" + d.getDate() ).slice( -2 ), '%S' : +d }; for( var k in subs ) { if( !subs.hasOwnProperty(k) ) { continue; } str = str.replace( k, subs[k] ); } return str; }, /** * Parse date from string according following format. * We suppose that every token takes place in the string only once. * * @param string str Date string. * @param string format Date format. * * @return Date|null Returns new Date object or null on parse failure. */ _parseDate: function( str, format ) { var testStr = format || "%Y-%M-%D", positions = [ null ], pos = 0, token, regs = { '%Y' : '(\\d{4})', '%M' : '(\\d{2})', '%D' : '(\\d{2})', '%S' : '(\\d{13})' }; while( ( pos = testStr.indexOf( '%', pos ) ) !== -1 ) { token = testStr.substr( pos, 2 ); if( token in regs ) { testStr = testStr.replace( token, regs[ token ] ); positions.push( token ); } else { positions.push( null ); } } var r = new RegExp( testStr ), arr = r.exec( str ); if( !arr ) { return null; } else { var d = new Date(); for( var i = 1; i < arr.length; ++i ) { if( positions[ i ] ) { switch( positions[ i ] ) { case '%D': d.setDate( arr[ i ] ); break; case '%M': d.setMonth( parseInt( arr[ i ], 10 ) - 1 ); break; case '%Y': d.setFullYear( arr[ i ] ); break; } } } return d; } } }; $.widget('lj.inlineCalendar', Calendar ); jQuery.extend( $.lj.inlineCalendar, { getDefaults: function() { return defaultOptions; }, setDefaults: function ( opts ) { if( opts ) { jQuery.extend( defaultOptions, opts ); } } } ); } ( jQuery, window ) ); /*! * LiveJournal Calendar * * Copyright 2011, dmitry.petrov@sup.com * * http://docs.jquery.com/UI * * Depends: * jquery.ui.core.js * jquery.ui.widget.js * jquery.lj.bubble.js * jquery.lj.inlineCalendar.js * * input plugin ( jquery_fn.js ) * mask plugin ( jquery/jquery.mask.js ) * * @overview Calendar widget. * * Widget can be attached either directly to input or to any other element. * If selected element is not input then input element can be set through options.input variable. * * Date wildcards used: * - %D - day ( 01 - 31 ) * - %M - month ( 01 - 02 ) * - %Y - year ( yyyy, e.g. 2002 ) * - %s - unix timestamp in ms * * Options: * - dayRef: Format of the url that will be attached to each day in the calendar. * - allRefs: Wether to attach links to days in the calendar. * - currentDate: initialy selected date. If input is not empty, than widget will try to parse it's contents * and override currentDate on success. * - activeFrom: Days before this will be inactive in calendar. * - actoveUntil: Days after this willbe inactive incalendar. * - startMonth: Widget will not allow to switch calendar pane to the month before this. * - endMonth: Widget will not allow to switch calendar pane to the month after this. * - startAtSunday: Wether to count sunday as the start of the week. * - dateFormat: Format of date string that will be inserted in the input after user selected value. * - showOn: When to display calendat widget ( click, hover, focus ) * - hoverDelay: When showOn === 'hover', this option represents the delay in ms after which calendar will be hidden. * - events: Object, containing events to show in the calendar. They will be rendered as links. Structure of the object: * { "yyyy": { "mm1" : [ d1, d2, d3, d4 ], "mm2": [ d5, d6, d7 ] } } * * Events: * - daySelected: Event is triggered when user selects a day in the calendar. The second parameter passed to the * function is a Date object. * - dateChange * * Consistent options ( setting these options is guaranteed to work correctly ): * - currentDate, date - Set/get current date. Input is also updated on set. * - activeFrom, date - Set/get earliest active date. * - activeUntil, date - Set/get last active date. * - title, title - set calendar title. * - events, obj - override current events object * * */ (function( $, window ) { var tmpl; var defaultOptions = { showOn: 'click', closeControl: true, showCellHovers: false, hoverDelay: 400, align: 'center', events: null, //object with events to show in the calendar displayedMonth: null, //month displayed on the calendar. If not specified at //startup currentDate is used instead. selectors: { tmpl: '.appwidget-calendar .calendar' }, classNames: { showCellHovers: 'all-days' }, tmpl: { cell: '', row: '{cells}', body: '{rows}', footer: '', header: '