/*! * 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 basic widget * * Copyright 2011, dmitry.petrov@sup.com * * http://docs.jquery.com/UI * * Depends: * jquery.ui.core.js * jquery.ui.widget.js * * @overview Base widget for all livejournal widgets. * * Basic widget adds pub/sub system to the widget hierarchy. By convention widgets add prefix equal * to the widget name of the most parent element who fires it. E.g. if someWidget and subSomeWidget * that extends someWidget do fire open event, it should be prefixed with someWidget - someWidget/open */ ( function( $ ) { var __callbacks = {}, //these events we set once and for all widget instances globalEvents = { documentClick: false } $.widget( 'lj.basicWidget', { options: { /** * Object contains strings with class names that are used within widget. */ classNames: {}, /** * Object contains strings with selectors that are used to find nodes within widget. */ selectors: {}, /** * Object contains translation strings for a widget. Widget should not contain hardcoded strings. */ ml: {}, /** * Object contains strings with templates, that are used to build content within widget. */ tmpl: {} }, _create: function() { /** * {Object} Contains all events that should not trigger events on next fire for this widget. */ this.__suppressedEvents = {}; }, /** * Bind common events for the widget */ _bindControls: function() { var widget = this; /** * documentClick */ if( !globalEvents.documentClick ) { $( document ).click( function( ev ) { widget._fire( 'documentClick', [], true ); } ); globalEvents.documentClick = true; } }, _setOption: function(name, val) { switch (name) { case 'selectors': case 'classNames': case 'tmpl': case 'templates': this.options[name] = $.extend(this.options[name], val); return; break; } $.Widget.prototype._setOption.apply(this, arguments); }, /** * Subscribe to the event with the callback. * * @param {String} type Event type. * @param {Function} callback Function that should be fired on the event. */ _on: function( type, callback ) { if( !( type in __callbacks ) ) { __callbacks[ type ] = []; } __callbacks[ type ].push( { fn: callback, owner: this } ); }, /** * Remove subscription on the event. * * @param {String} type Event type. * @param {Function=} callback Callback function. If parameter is omitted, function will remove all * callbacks of this instance from the subscription on this type of event. */ _off: function( type, callback ) { if( !( type in __callbacks ) ) { return; } var cbs = __callbacks[ type ]; for( var i = 0; i < cbs.length; ++i ) { if( ( callback && cbs[ i ].fn === callback ) || ( cbs[i].owner === this ) ) { cbs.splice( i, 1 ); } } }, /** * Dispatch event. * * @param {String} type Event type. * @param {Array=[]} args array with arguments that will be passed to the callback functions. * @param {Boolean=False} includeOwner If false the message is not recieved by * the object that dispatched it. */ _fire: function( type, args, includeOwner ) { args = args || []; includeOwner = includeOwner || false; if( type in __callbacks ) { var cbs = __callbacks[ type ], i = -1; while( cbs[ ++i ] ) { if( !includeOwner && cbs[ i ].owner === this ) { continue; } if( type in cbs[ i ].owner.__suppressedEvents ) { continue; } cbs[ i ].fn.apply( null, args ); } //we delete supressed event flag only after firing event because //wedget can subscribe more than one callback while(cbs[ --i ]) { if( type in cbs[ i ].owner.__suppressedEvents ) { delete cbs[ i ].owner.__suppressedEvents[ type ]; } } } }, /** * Prevent event from being trigger on this widget instance on next fire. * An event after next will be processed as normal */ _suppressNextEvent: function( eventName ) { this.__suppressedEvents[ eventName ] = true; }, /** * Remove all subscriptions on widget distruction. If overriden, this method should be * also caled. */ _destroy: function() { var cbs; for( var type in __callbacks ) { cbs = __callbacks[ type ]; for( var i = 0; i < cbs.length; ++i ) { if( cbs[i].owner === this ) { cbs.splice( i, 1 ) } } } } } ); } )( 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, showDelay: 0, 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 showEffect: '', //can be fade 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; $.lj.basicWidget.prototype._create.apply(this); $.lj.basicWidget.prototype._bindControls.apply(this); //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(); this._on('documentClick', function() { if (options.closeOnDocumentClick && !ljBubble.blockDocumentClick) { ljBubble.hide(); } else { ljBubble.blockDocumentClick = false; } }); // 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(); ljBubble.blockDocumentClick = true; 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); options.hoverTimer = setTimeout(function () { ljBubble.show(); }, options.showDelay); }) .bind('mouseleave' + eventNamespace, function () { clearTimeout(options.hoverTimer); 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 '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(), body = $('body'), viewportScrollLeft = body.prop('scrollLeft'), 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 + viewportScrollLeft) { 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; //prevent delayed mouseout event clearTimeout(this.options.hoverTimer); target = (target) ? $(target) : options.target; $( ':lj-bubble' ).not( this.element ).bubble( "hide" ); if (!ljBubble.bubbleNode.is(':visible')) { ljBubble.option('currentTarget', target); position = ljBubble._updatePosition(); if (this.options.showEffect === 'fade') { this.bubbleNode.fadeIn(200); } else { this.bubbleNode.show(); } } ljBubble._trigger( 'show', null, [ { position: position } ] ); return this; }, hide: function () { //prevent delayed mouseout event clearTimeout(this.options.hoverTimer); if (!this.bubbleNode.is(':visible')) { //do not fire events if bubble is already hidden return; } if (this.options.showEffect === 'fade') { this.bubbleNode.fadeOut(200); } else { 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', $.lj.basicWidget, 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+)/, class_nopopup = 'noctxpopup', 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)) && !(parent.className.indexOf(class_nopopup) >= 0)) { 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.className.indexOf(class_nopopup) >= 0)) { 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); }, cleanCache: function(keys) { var self = this; keys = keys || []; if (typeof keys === 'string') { keys = [ keys ]; } keys.forEach(function(key) { if (self.cachedResults[key]) { delete self.cachedResults[key]; } }); }, // 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); /*! * jQuery storage plugin. * @author Dmitry Petrov , 2011 * * Plugin uses local storage if availible and falls back to IE userData mechanism. * * @TODO: add cookie and simple ( non-persistent ) storage providers. * * Plugin supports IE5.5+, Firefox 3.5+, Opera 10.5+, Google Chrome, Apple Safari 4+ */ ( function( $ ) { var JSON = window.JSON || LiveJournal.JSON; function stringify(val) { return JSON.stringify(val); } function parse(val) { var ret; try { ret = JSON.parse(val); } catch(e) { ret = val; } return ret; } /** * @namespace $.storage */ $.storage = function() { /** * Every provider should have the same interface and provide isSupported function to * test if it's applicable in current browser */ var userDataProvider = function() { var storageId = 'jQueryStorageProvider', storageName = '__jQueryStorage__', node; function _create() { node = $( '' ).attr( 'id', storageId ).css( 'display', 'none' ).appendTo( $( 'head' ) ).get(0); node.addBehavior( '#default#userdata' ); node.load( storageName ); } _create(); return { /** * Set item in the storage * * @param {String} name * @param {String} val */ setItem: function( name, val ) { node.setAttribute( name, stringify(val) ); node.save( storageName ); }, /** * Get item from the storage. * * @param {String} Item name. * @return {Object|String} Item value or null it it does not exist. */ getItem: function( name ) { return parse(node.getAttribute(name)); }, /** * Remove item from the storage * * @param @{String} Item name. */ removeItem: function( name ) { node.removeAttribute( name ); node.save( storageName ); } } }; userDataProvider.isSupported = function() { return $.browser.msie && ( +$.browser.version ) > 5; }; var localStorageProvider = function() { function _create() { } _create(); return { setItem: function(name, val) { localStorage.setItem(name, stringify(val)); }, getItem: function(name) { return parse(localStorage.getItem(name)); }, removeItem: function( name ) { localStorage.removeItem(name); } } }; localStorageProvider.isSupported = function() { return !!window.localStorage; }; var simpleProvider = function() { var store; function _create() { store = {}; } _create(); return { setItem: function( name, val ) { store.name = val; }, getItem: function( name ) { return store.name || null; }, removeItem: function( name ) { delete store.name; } } }; simpleProvider.isSupported = function() { return true; }; //currently all transports should be implemented inside plugin. var providers = [ localStorageProvider, userDataProvider, simpleProvider ]; for( var i = 0; i < providers.length; ++i ) { if( providers[ i ].isSupported() ) { return providers[ i ](); } } //We shoud never reach this point. }(); } ) ( jQuery ); /* * jQuery Hotkeys Plugin * Copyright 2010, John Resig * Dual licensed under the MIT or GPL Version 2 licenses. * * Based upon the plugin by Tzury Bar Yochay: * http://github.com/tzuryby/hotkeys * * Original idea by: * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/ */ (function(jQuery){ jQuery.hotkeys = { version: "0.8", specialKeys: { 8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause", 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home", 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del", 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7", 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/", 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8", 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 187: "+", 189: "-", 191: "/", 224: "meta" }, shiftNums: { "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&", "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<", ".": ">", "/": "?", "\\": "|" } }; function keyHandler( handleObj ) { // Only care when a possible input has been specified if ( typeof handleObj.data !== "string" ) { return; } var origHandler = handleObj.handler, keys = handleObj.data.toLowerCase().split(" "); handleObj.handler = function( event ) { // Don't fire in text-accepting inputs that we didn't directly bind to if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) || /text|password|search|tel|url|email|number/.test( event.target.type ) ) ) { return; } // Keypress represents characters, not special keys var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[ event.which ], character = String.fromCharCode( event.which ).toLowerCase(), key, modif = "", possible = {}; // check combinations (alt|ctrl|shift+anything) if ( event.altKey && special !== "alt" ) { modif += "alt+"; } if ( event.ctrlKey && special !== "ctrl" ) { modif += "ctrl+"; } // TODO: Need to make sure this works consistently across platforms if ( event.metaKey && !event.ctrlKey && special !== "meta" ) { modif += "meta+"; } if ( event.shiftKey && special !== "shift" ) { modif += "shift+"; } if ( special ) { possible[ modif + special ] = true; } else { possible[ modif + character ] = true; possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true; // "$" can be triggered as "Shift+4" or "Shift+$" or just "$" if ( modif === "shift+" ) { possible[ jQuery.hotkeys.shiftNums[ character ] ] = true; } } for ( var i = 0, l = keys.length; i < l; i++ ) { if ( possible[ keys[i] ] ) { return origHandler.apply( this, arguments ); } } }; } jQuery.each([ "keydown", "keyup", "keypress" ], function() { jQuery.event.special[ this ] = { add: keyHandler }; }); })( jQuery ); /** * Utility functions to navigate through comments tree in s1 */ (function($) { $.comments = $.comments || { options: { selectors: { leaf: '.b-leaf', levelTwig: '.b-tree-twig-{level}', twig: '.b-tree-twig' }, classNames: { levelTwig: 'b-tree-twig-{level}' } }, _selector: function(name) { return this.options.selectors[name] }, _className: function(name) { return this.options.classNames[name] } }; var __sel = $.comments._selector.bind($.comments), __class = $.comments._className.bind($.comments); $.extend($.comments, { /** * Get commentLevel * * @param {jQuery} node Leaf or twig node * @return {Number} Comment's level. */ level: function(node) { var twig = (node.is(__sel('twig'))) ? node : node.closest(__sel('twig')), match = RegExp(__class('levelTwig').supplant({ level: '(\\d+)'})) .exec(twig.prop('className')); return match && parseInt(match[1], 10) || 1; }, /** * @param {jQuery} node Comment node. * @param {String} ditemid Id of comment to find. * * @return {jQuery|Boolean} Returns comment node or false if such parent was not found. */ parent: function(node, ditemid) { var twig = node.closest(__sel('twig')), level = $.comments.level(twig), prev = twig; while (level > 1) { level--; prev = prev.prevAll(__sel('levelTwig').supplant({ level: level}) + ':first'); if (prev.length === 0) { return false; } if (prev.data('tid') === ditemid) { return prev.find(__sel('leaf')); } } }, /** * Check if comment has children. * * @param {jQuery} node Leaf or twig node * @return {Boolean} True if coment has child comments. */ hasChildren: function(node) { var twig = (node.is(__sel('twig'))) ? node : node.closest(__sel('twig')), level = $.comments.level(twig); return $.comments.level(twig.next()) > level; }, /** * Check if on comment is child of another * * @param {jQuery} child Child comment or twig. * @param {jQuery} parent Parent comment or twig. * * @return {Boolean} */ isChild: function(child, parent) { var childTwig = (child.is(__sel('twig'))) ? child : child.closest(__sel('twig')), parentTwig = (parent.is(__sel('twig'))) ? parent : parent.closest(__sel('twig')), parentLevel = $.comments.level(parent), childLevel = $.comments.level(child); if (childLevel === 1 || childLevel <= parentLevel) { return false; } //check obvious cases var realParent = childTwig.prevAll(__sel('levelTwig').supplant({ level: parentLevel }) + ':first'); return realParent.get(0) === parentTwig.get(0); }, /** * Get all comments in the thread including the param comment/ * * @param {jQuery} node Root node. * @return {jQuery} jQuery collection containing all leaves of the thread. */ getThread: function(node) { var twig = node.closest(__sel('twig')), level = $.comments.level(twig), children = jQuery(), next = twig, nextLevel; children = children.add(twig.find(__sel('leaf'))); while(true) { next = next.next(); if (next.length === 0) { break; } nextLevel = $.comments.level(next); if (nextLevel <= level) { break; }; children = children.add(next.find(__sel('leaf'))); } return children; }, /** * Filter twigs collection and drop leave only topmost comments in colection. * * @param {jQuery} twigs Collections of twigs to filter. * @return {jQuery} Returns filtered collection. */ topComments: function(twigs) { } }); })(jQuery); /*! * LiveJournal Commentator * * Copyright 2011, dmitry.petrov@sup.com * * http://docs.jquery.com/UI * * Depends: * jquery.ui.core.js * jquery.ui.widget.js * jquery.lj.authtype.js * * Usage: *
* ... *
* * * FIXME: this widget should be merged with commentform */ (function($, window) { var Commentator = { options: { publicKey: '', ajax: true, needCaptcha: false, captchaContainerId: '', selectors: { comments: '#comments', errorWrapper: '.b-watering-errorbox', errorBlock: '.b-postform-alert-ajax', blockingErrorBlocks: '.b-bubble-warning', preloaderElem: '.b-postform-preload', controls: ':button, :submit', ajaxField: 'input[name="json"]', previewControl: 'input[name="submitpreview"]', inputParentTalkid: '#parenttalkid', submitControl: 'button[name=submitpost]', form: 'form', captchaBox: '.b-postform-captchabox', //if user is anonymous and chooses auth as livejournal we should refresh page on comment //only for s1 anonLoginSubmit: '.b-watering-authtype-user.b-watering-user-notreg' }, classNames: { idle: 'b-postform-preload-active', captchaActive: 'b-postform-captchabox-active', errorWrapperShow: 'b-watering-errorbox-show', replyPage: 'b-postform' //'b-watering-replypage' //old form can bee seen only on reply page }, templates: { frame: '{caret}'.supplant({ text: text }); } else if (text.match(/^(http:\/\/(www\.)?)?youtube\.com/)) { text = 'http://www.youtube.com/embed/' + LiveJournal.parseGetArgs(text).v; text = '{caret}'.supplant({ text: text }); } else if (text.match(/^(http:\/\/(www\.)?)?rutube\.ru/)) { text = 'http://video.rutube.ru/' + LiveJournal.parseGetArgs(text).v; text = (' ' + '' + '' + '{caret}').supplant({ text: text }); } else if (text.match(/^(http:\/\/(www\.)?)?vimeo\.com/)) { videoid = (text.split('?')[0]).replace(/\/+$/,'').split('/').pop(); text = 'http://player.vimeo.com/video/' + videoid; text = ('{caret}').supplant({ text: text }); } else { if (!text.match(/^[a-z+]+:(\/\/)?/)) { defaultSelection = text; text = 'http://' + text; } text = '{caret}'.supplant({ text: text }); } break; } text = text.supplant({ type: type }); this.element.trigger('insertText', { text: text, defaultSelection: defaultSelection }); } }); }(jQuery, window)); (function($, window) { var Commentform = { options: { classNames: { active: 'active', startAuth: 'b-watering-trueauth-', buttonDisabled: 'b-ljbutton-disabled', editComment: 'b-leaf-editing', formEditComment: 'b-watering-editing', formEditMyComment: 'b-watering-editing-mycomment', authtypeAnonymous: 'b-watering-authtype-anonymous', notfriend: 'b-watering-user-notafriend', notreg: 'b-watering-user-notreg', regonly: 'b-watering-user-regonly', friendonly: 'b-watering-user-friendonly', errorWrapperShow: 'b-watering-errorbox-show', commentFrozen: 'b-leaf-frozen', commentScreened: 'b-leaf-screened', commentSpammed: 'b-leaf-spammed', firstLevelParent: 'b-xylem', showSpam: 'b-grove-showspam' }, selectors: { form: '#postform', errorWrapper: '.b-watering-errorbox', tail: '.b-watering-arrows', toolbar: '.b-watering-bar', submitControl: '.b-ljbutton-submit', submitButton: '.b-ljbutton-submit [type=submit]', comment: '#body', subject: '[name=subject]', editid: 'input[name=editid]', inputReplyto: '#replyto', inputParentTalkid: '#parenttalkid', commentsRoot: '.b-grove', addFirstLevelControl: '.b-addcomment', firstLevelParent: '.b-xylem', firstLevelCommentContainer: '.b-xylem-cell-add', addControl: '.b-leaf-actions-reply', addControlLink: '.b-pseudo', commentContainer: '.b-leaf', editLink: '.b-controls-edit', commentText: '.b-leaf-article', commentSubject: '.b-leaf-subject', closeControl: '.b-watering-close' }, disabledRegex: /b-watering-user-(deleted|suspended|banned|notmail)/ }, // private methods _create: function() { this._findNodes(); this._bindEvents(); this._disabled = false; this._authToken = this._commentsRoot.data('authtoken'); this._updateFormState(); this._state; //commentform state, may be add or edit this._node; //node the form is attached to now; }, _findNodes: function() { var selectors = this.options.selectors; this._inputs = { editid: this.element.find(selectors.editid), replyTo: this.element.find(selectors.inputReplyto), parentTalkid: this.element.find(selectors.inputParentTalkid) }; this._tail = this.element.find(selectors.tail); this._tailHeight = parseInt(this._tail.css('height'),10); this._commentsRoot = $(selectors.commentsRoot); this._firstLevelParent = $(selectors.firstLevelParent); this._firstLevelCommentContainer = $(selectors.firstLevelCommentContainer); this._submitControl = this.element.find(selectors.submitControl); this._submitButton = this.element.find(selectors.submitButton); this._commentArea = this.element.find(selectors.comment); this._subject = this.element.find(selectors.subject); this._errorsWrapper = this.element.find(selectors.errorWrapper); this._form = this.element.find(selectors.form); this._toolbar = this.element .find(selectors.toolbar) .commentsFormToolbar(); }, _bindEvents: function() { var commentform = this, selectors = commentform.options.selectors, classNames = commentform.options.classNames, commentContainerSelector = selectors.commentContainer; this._commentArea.input(this._updateFormState.bind(this)); //add root comment buttons this._commentsRoot.delegate(selectors.addFirstLevelControl + ':first', 'click', function(event) { commentform.add(); event.preventDefault(); }); this._commentsRoot.delegate(selectors.addFirstLevelControl + ':gt(0)', 'click', function(event) { commentform._scrollToForm(); if (!commentform.element.is(':visible') || commentform._node.get(0) !== commentform._firstLevelCommentContainer.get(0)) { commentform.add(); } event.preventDefault(); }); this._commentsRoot.delegate(selectors.addControl, 'click', function(event) { commentform.add($(this)); event.preventDefault(); }); this.element .delegate(selectors.closeControl, 'click', function(event) { commentform._closeForm(); event.preventDefault(); }); this._form.bind('submit', function() { //we need to disable form after all submit actions setTimeout(function() { commentform._setDisabled.bind(commentform, true); commentform._commentArea.attr('disabled', true); }, 0); }); LiveJournal.register_hook('commentator/submit', function(message) { if (message.hidden && !Site.remoteUser) { commentform._closeForm(); } commentform._updateFormState(); commentform._commentArea.attr('disabled', false); }); this._toolbar .bind("insertText", function(ev, obj) { commentform._insertText(obj.text, obj.defaultSelection); }) .bind('terminateAction', function(ev) { commentform._currentSelection = null; }); //internet explorer looses selection on blur event, so we poll other events this._commentArea.bind( jQuery.browser.msie ? 'keyup mousedown mouseup' : 'blur', function(ev) { commentform._currentSelection = DOM.getSelectedRange(this); }); this.element .bind('newComment', function() { jQuery.fx.off = true; //we disable animation to collapse form immediately commentform._node && commentform._node.find(selectors.editLink).remove(); commentform._closeForm(); jQuery.fx.off = false; }) .bind('authtypechange', function(ev, data) { //blockers should disable form only if user selected current authentenication var className = classNames.startAuth + data.authtype; commentform._currentAuthSelected = commentform.element.hasClass(className); commentform._updateFormState(); }); $(document) .bind('commentsUpdated', function(ev, ditemid) { var node = commentform._node; if (node && ( node.parent().length === 0 || node.attr('id') === ditemid || $.comments.parent(node, ditemid) !== false ) ) { commentform._closeForm(); } }) .bind('editComment', function(ev, node) { commentform.edit(node); }); }, _updateFormState: function() { var classNames = this.options.classNames; //we do not want to post empty comment disabled = this._commentArea.val().trim().length === 0 || //condition for current auth (this._currentAuthSelected && //user is banned/suspended etc (this.options.disabledRegex.test(this.element.prop('className')) || //user is not a friend, but only friends' comment are allowed (this.element.hasClass(classNames.friendonly) && this.element.hasClass(classNames.notfriend) || //user didn't activate his email and he is like anonymous now (this.element.hasClass(classNames.notreg) && (this.element.hasClass(classNames.regonly) || this.element.hasClass(classNames.friendonly))) ))) || // OR //user is anonymous or didn't verified his email (this.element.hasClass(classNames.authtypeAnonymous) && //end the post is only for registered users (this.element.hasClass(classNames.regonly) || this.element.hasClass(classNames.friendonly))) this._setDisabled(disabled) }, _setDisabled: function(disabled) { disabled = disabled || false; this._submitControl.toggleClass(this.options.classNames.buttonDisabled, disabled); this._submitButton.attr('disabled', disabled); this._disabled = disabled; this.element.trigger('commentformdisable', this._disabled) //pass information to the commentator }, _insertText: function(text, defaultSelection) { var self = this, val = this._commentArea.val(), newval, selection = this._currentSelection; defaultSelection = defaultSelection || ''; if (!this._currentSelection) { newval = text.supplant({ caret: defaultSelection }) this._commentArea.val(val + newval); } else { var selected = val.substring(selection.start, selection.end), start = val.substring(0, selection.start), end = val.substring(selection.end); if (selected.length === 0) { selected = defaultSelection; } newval = text.supplant({ caret: selected }); this._commentArea.val( start + newval + end); } var txt = this._commentArea.get(0), pos = selection ? selection.start + newval.length : this._commentArea.val().length; DOM.setSelectedRange(txt, pos, pos); this._commentArea .focus(); this._updateFormState(); }, _scrollToForm: function() { var top = this._commentsRoot.offset().top - parseInt(this._commentsRoot.css('margin-top'), 10); jQuery('html, body').animate({ scrollTop: top }); return this; }, _setOption: function(option, value) { var commentform = this, options = this.options, classNames = options.classNames, selectors = options.selectors, activeClassName = classNames.active, anchor; switch (option) { case 'activeControl': if (options.activeControl) { options.activeControl.removeClass(activeClassName); } if (value) { value.addClass(activeClassName); } break; } options[option] = value; }, //public methods show: function(oncomplete) { var self = this, options = this.options; this.element.css({ display: 'block', height: '' }); var elementHeight = this.element.height(); this._tail .css({ height: '', top: '' }); this._tailHeight = parseInt(this._tail.css('height'), 10); this._tail .css({ height: '-=' + this._tailHeight, top: '+=' + this._tailHeight }) .animate({ height: '+=' + this._tailHeight, top: '-=' + this._tailHeight }, 250); this.element .css({ opacity: 0, height: '0' }); var formOffset = this.element.offset().top, doc = jQuery('html, body'), win = jQuery(window), winH = win.height(), winOffset = win.scrollTop(), edgeOffset = winOffset + winH - elementHeight - jQuery('#ljtime').height(), dOffset, doScroll = false; if (edgeOffset < winOffset) { edgeOffset = winOffset; } dOffset = - edgeOffset + formOffset; if (edgeOffset < formOffset) { doScroll = true; } this.element.animate({ height: elementHeight, opacity: 1 }, { duration: 450, easing: 'easeOutExpo', step: function(now, fx) { if (doScroll && fx.prop === 'opacity') { doc.scrollTop(winOffset + dOffset * now); } }, complete: function() { self.element.css({ height: '', opacity: '' }); oncomplete && oncomplete(); } }); }, hide: function(oncomplete, anchor) { this.element.trigger('commentformclose'); anchor = anchor || this.options.activeControl; if (anchor.parent().length === 0) { //only a comment can disappear anchor = jQuery('#' + this._node.attr('id')); } else if (!anchor.hasClass(this.options.classNames.firstLevelParent)) { anchor = anchor.closest(this.options.selectors.commentContainer); } var screenTop = $(document).scrollTop(); var screenBottom = screenTop + $(window).height(); var elTop = this.element.offset().top; var elBottom = elTop + this.element.outerHeight(); var self = this, options = this.options, $document = jQuery(document); if (this.element.is(':visible')) { var anchorTop = anchor.offset().top - $(document).scrollTop(); // if the animated element is out of viewport if (elBottom < screenTop || elTop > screenBottom) { this.element.attr('style', ''); $document.scrollTop(anchor.offset().top - anchorTop); oncomplete && oncomplete(); return; } else { var h = this.element.height(); this._tail.animate({ height: '-=' + this._tailHeight, top: '+=' + this._tailHeight }, 200); this.element .animate({ height: 0, opacity: 0 }, { duration: 500, easing: 'easeOutExpo', step: function() { $document.scrollTop(anchor.offset().top - anchorTop); }, complete: function() { $document.scrollTop(anchor.offset().top - anchorTop); self.element.css('display', ''); oncomplete && oncomplete(); } }); } } else { oncomplete && oncomplete(); } }, /** * Add a comment. * * @param {jQuery=} control reply control node, if we reply to some one. */ add: function(control) { var options = this.options, willBeShown = this.comment('add', control); options.activeControl && !options.activeControl.hasClass(options.classNames.firstLevelParent) && options.activeControl.find(options.selectors.addControlLink).html(Site.ml_text['talk.replytothis']); if (willBeShown) { this._commentArea.val(''); this._subject.val(''); this._inputs.editid.val(''); this._submitButton.html(Site.ml_text['talk.postcomment']); control && control.find(options.selectors.addControlLink).html(Site.ml_text['talk.answer']); } this._updateFormState(); }, /** * Edit a comment. * * @param {jQuery} node Comment node. */ edit: function(node) { if (!node) { return }; //we can only reply to something var commentform = this, control = node.find(this.options.selectors.editLink), itemid = node.attr('id').substr(1); this.options.activeControl && this.options.activeControl.find(this.options.selectors.addControlLink).html(Site.ml_text['talk.replytothis']); function toggleEdit(show, text, userpic, subject) { commentform.comment('edit', control); node.toggleClass(commentform.options.classNames.editComment, show); commentform.element.toggleClass(commentform.options.classNames.formEditComment, show); if (Site.remoteUser !== Site.currentJournal) { commentform.element.toggleClass(commentform.options.classNames.formEditMyComment, show); } if (show) { commentform._inputs.editid.val(itemid); commentform._commentArea .val(text); commentform._subject .val(subject); commentform._submitButton.html(Site.ml_text['talk.editcomment']); commentform.element.trigger('userpic', [userpic]); commentform._updateFormState(); } } function loadComment(cb) { commentform._setCommentPending(node, true); $.getJSON(LiveJournal.getAjaxUrl('get_comment_source', { thread: itemid, journal: Site.current_journal.username }) ).success(function(result) { commentform._setCommentPending(node, false); if (result.status === 'ok') { cb && cb(result.message, result.userpic_keyword); } else { alert(result.message) } }) .error(function() { setTimeout(loadComment.bind(null, cb), 200); }) } if (!this._isInState('edit', node)) { var subject = node.find(this.options.selectors.commentSubject).text(); loadComment(function(text, userpic) { toggleEdit(true, text, userpic, subject); }); } else { toggleEdit(false); } }, _setCommentPending: function(node, pending) { //we trigger event and commentsOperations widget gets it on some parent node. this.element.trigger('commentpending', [node, pending]); }, /** * Toggle comment form visibility * * @returns {Boolean} Returns true if form will be eventually show, and false otherwise */ comment: function(action, control) { var commentform = this, selectors = this.options.selectors, classNames = this.options.classNames, node, moveForm = function(node) { commentform.element.insertAfter(node); }, showFunc = function(commentNode, control) { commentform._setOption('activeControl', control); commentform._inputs.replyTo.val(ditemid); commentform._inputs.parentTalkid.val(ditemid); commentform.element.data('parenttalkid', commentNode.attr('id') || null); //we need to set null if we open form at the moveForm(commentNode); commentform.show(); commentform._state = action; commentform._node = node; commentform._commentArea.focus(); commentform._updateFormState(); }; if (!control) { //add new root comment node = this._firstLevelCommentContainer; control = this._firstLevelParent; ditemid = '0'; moveForm = function(node) { commentform.element.appendTo(node); }; } else { node = control.closest(selectors.commentContainer); ditemid = Math.floor(parseInt(node.prop('id').substr(1), 10 ) / 256); if (node.hasClass(classNames.commentFrozen) || (node.hasClass(classNames.commentScreened) && action !== 'edit') || (node.hasClass(classNames.commentSpammed) && !this._commentsRoot.hasClass(classNames.showSpam))) { return false; //do nothing for frozen comments } } if (this._isInState(action, node)) { this._closeForm(); //we just need to close the form return false; } if (this._node) { this._node.removeClass(classNames.editComment); } this.hide(showFunc.bind(null, node, control), control); this.element .removeClass(classNames.formEditComment) .removeClass(classNames.formEditMyComment); return true; }, _isInState: function(action, node) { return ((this._state === action) && this._node && node.get(0) === this._node.get(0)); }, _closeForm: function() { var commentform = this, options = this.options, classNames = this.options.classNames, ditemid = '0', node = this._node, collapseFunc = function() { commentform._inputs.replyTo.val('0'); commentform._inputs.parentTalkid.val('0'); commentform._inputs.editid.val('0'); commentform.element.data('parenttalkid', null); commentform._setOption('activeControl', null); commentform._state = null; commentform._node = null; commentform._errorsWrapper.removeClass(classNames.errorWrapperShow); }; options.activeControl && !options.activeControl.hasClass(options.classNames.firstLevelParent) && options.activeControl.find(options.selectors.addControlLink).html(Site.ml_text['talk.replytothis']); node.removeClass(classNames.editComment); this.element .removeClass(classNames.formEditComment) .removeClass(classNames.formEditMyComment); this.hide(collapseFunc); } }; $.widget('lj.commentform', Commentform); })(jQuery, this); (function( window, $ ) { /** * * Livejournal sharing script. * * Usage: * * .. Somewhere in the head .. * * * .. Somewhere on the page .. * share * * * You can attach single links: * LJShare.entry( { url: "http://some.url.com/", title: "Post title", description: "Post description" } ) * .attach( '#link_selector', 'service_name' ) * .attach( jQuery( '#another_selector' ), 'service_name2' ) //we can pass nodes or jquery collections * .link( '#selector', [ "twitter", "vkontakte", "moimir"] ); //also we can attach popup * */ function preload( srcArr ) { for( var i = 0; i < srcArr.length; ++i ) { ( new Image() ).src = Site.imgprefix + srcArr[ i ] + '?v=1'; } } function prepareOptions( opts ) { var defaults = { title: '', description: '', url: '' }; var options = jQuery.extend( {}, defaults, opts ); //we encode strings two times, because they are decoded once on the livejournal endpoint options.url = encodeURIComponent( encodeURIComponent( options.url ) ); options.title = encodeURIComponent( encodeURIComponent( options.title ) ); options.description = encodeURIComponent( encodeURIComponent( options.description ) ); return options; } preload( [ '/popup-cls.gif', '/popup-arr.gif', '/icons/sharethis.gif' ] ); function supplant(str, o) { return str.replace(/{([^{}]*)}/g, function (a, b) { var r = o[b]; return typeof r === 'string' || typeof r === 'number' ? r : a; } ); } var selectors = { close: ".i-popup-close", links: ".b-sharethis-services a", arrow: ".i-popup-arr" }; // four arrow positions availible var arrow_opts = { className: "i-popup-arr", position: { tl: "i-popup-arrtl", tr: "i-popup-arrtr", bl: "i-popup-arrbl", br: "i-popup-arrbr" } }; var template = { //here we take values from global_options.ml object start: '
' + '
{title}
' + '
', //here we take values from an object made from service object. Availible vars: name, url, title. item: '{title}', //here we take values from global_options.ml object end: '
' + '
' }; //buildLink takes values passed to the url with link method ( title, post url, description ) var default_options = { ml: { close: "Close", title: "Share" }, services: { livejournal: { title: 'LiveJournal', bindLink: 'http://www.livejournal.com/update.bml?repost_type=c&repost={url}', openInTab: true }, facebook: { title: 'Facebook', bindLink: 'http://www.facebook.com/sharer.php?u={url}' }, twitter: { title: 'Twitter', bindLink: 'http://twitter.com/share?url={url}&text={title}' }, vkontakte: { title: 'Vkontakte', bindLink: 'http://vkontakte.ru/share.php?url={url}' }, moimir: { title: 'Moi Mir', bindLink: 'http://connect.mail.ru/share?url={url}' }, stumbleupon: { title: 'Stumbleupon', bindLink: 'http://www.stumbleupon.com/submit?url={url}', openInTab: true }, digg: { title: 'Digg', bindLink: 'http://digg.com/submit?url={url}', openInTab: true }, email: { title: 'E-mail', bindLink: 'http://api.addthis.com/oexchange/0.8/forward/email/offer?username=internal&url={url}&title={title}', height: 600 }, tumblr: { title: 'Tumblr', bindLink: 'http://www.tumblr.com/share?v=3&u={url}' }, odnoklassniki: { title: 'Odnoklassniki', bindLink: 'http://www.odnoklassniki.ru/dk?st.cmd=addShare&st.s=1&st._surl={url}' } }, //list of links wich will be shown, when user will click on share link. Can be overriden in init and link methods. links: [ 'livejournal', 'facebook', 'twitter', 'vkontakte', 'odnoklassniki', 'moimir', 'email', 'digg', 'tumblr', 'stumbleupon' ], showOn: 'click' }; var global_options = $.extend( true, {}, default_options ); window.LJShare = {}; /** * Overrides default options for current page. * * @param Object opts Options object, may contain the following fields: * ml - translation strings to use; * services - An Object, that contains configuration fields for services links; * links - array of links that will be shown to the user in popup. */ window.LJShare.init = function( opts ) { if( opts ) { global_options = $.extend( true, {}, default_options, opts ); global_options.links = opts.links || global_options.links; } }; /** * Bind share popup to the latest link found on the page * * @param Object opts Options object, may contain the following fields: * title, description, url - parameters of the page you want to share; * links - array of links that will be shown to the user in popup. * @param String|Node|Jquery collection Node the popup has to be attached to. Default id a:last */ window.LJShare.link = function( opts, node ) { if( opts && opts.share_uniq_id ) { var id = opts.share_uniq_id; delete opts.share_uniq_id; LJShare.link( opts, $( '#' + id ) ); return; } var a = node || $( 'a:last' ), linkImg = a.find( 'img' ), link = (linkImg.length) ? linkImg : a, url = a.attr( 'href' ), options = prepareOptions( $.extend( {}, { url: url } , opts ) ), dom; a.attr( 'href', 'javascript:void(0)' ); var links = ( opts.links ) ? opts.links : global_options.links; function buildDom( initHidden ) { initHidden = initHidden || false; var str = [ supplant( template.start, global_options.ml ) ], serviceName, serviceObj; for( var i = 0; i < links.length; ++i ) { serviceName = links[i]; serviceObj = global_options.services[ serviceName ]; str.push( supplant( template.item, { name: serviceName, title: serviceObj.title, url: supplant( serviceObj.bindLink, options ) } ) ); } str.push( supplant( template.end, global_options.ml ) ); bubbleOptions = { target: link, showOn: options.showOn || global_options.showOn }; if( options.showOn === "hover" ) { bubbleOptions.closeControl = false; } dom = $( str.join( ' ' ) ) .hide() .bubble( bubbleOptions ); if( !initHidden ) { dom .bubble( 'show' ); } } function bindControls() { dom.find( selectors.links ).click( function( ev ) { dom.bubble('hide'); var service = $( this ).attr( 'data-service' ), width, height; if( global_options.services[ service ].openInTab ) { if( $.browser.msie ) { ev.preventDefault(); width = $( window ).width(); height = $( window ).height(); window.open( this.href, null, 'toolbar=yes,menubar=yes,status=1,location=yes,scrollbars=yes,resizable=yes,width=' + width + ',height=' + height ); } else { //other browsers just open link in new tab this.target = "_blank"; } } else { ev.preventDefault(); width = global_options.services[ service ].width || 640; height = global_options.services[ service ].height || 480; window.open(this.href, 'sharer', 'toolbar=0,status=0,width=' + width + ',height=' + height + ',scrollbars=yes,resizable=yes'); } } ); } if( options.showOn === "hover" ) { if( !dom ) { buildDom( true ); bindControls(); } } link.one( 'click', function( ev ) { ev.stopPropagation(); if( !dom ) { buildDom(); bindControls(); } } ); return this; }; window.LJShare.entry = function( opts ) { var defaults = { title: '', description: '', url: '' }; var options = prepareOptions( opts ); return { attach: function( node, service ) { var link = jQuery( node ), serviceObj = global_options.services[ service ]; if( service in global_options.services ) { link.each( function() { var url = supplant( serviceObj.bindLink, options ); if ( service.openInTab ) { this.url = url; this.target = "_blank"; } else { $( this ).click( function( ev ) { var width = service.width || 640; var height = service.height || 480; window.open( url, 'sharer', 'toolbar=0,status=0,width=' + width + ',height=' + height + ',scrollbars=yes,resizable=yes'); ev.preventDefault(); } ); } } ); } return this; }, link: function( node, links ) { var opts = jQuery.extend( {}, options, links ? { links: links } : null ); LJShare.link( opts, ( node ) ? jQuery( node ) : null ); return this; } }; }; } )( window, jQuery ); /*! * LiveJournal loader for vkontakte like buttons. * * Copyright 2011, dmitry.petrov@sup.com * * VK script is often loaded with notable delay, so * plugin just loads it after the page rendering and * allows to display page faster. * */ ( function( $ ) { if( $.VK ) { return; } $.VK = {}; var onloads = []; buttons = [], onloadPassed = false, scriptLoaded = false, scriptLoading = false; /** * Public API * * @namespase $.VK */ $.VK = { /** * Init VK object after the script load. * Function passes all option to the VK.init * @param {Object} options */ init: function( options ) { onloads.push( function() { VK.init( options ); } ) }, /** * Add button to init after script load. * If this method was called after the page load, and script wasn't downloaded yet, * it will trigger downloading. */ addButton: function( elementId, options ) { buttons.push( { id: elementId, options: options } ); if( onloadPassed && !scriptLoading ) { if( scriptLoaded ) { initButtons(); } else { loadScript( initButtons ); } } } } function initButtons() { for( var i = 0; i < buttons.length; ++i ) { VK.Widgets.Like( buttons[ i ].id, buttons[ i ].options ); } buttons = []; } function loadScript( onload ) { onload = onload || $.noop; scriptLoading = true; $.getScript( 'http://userapi.com/js/api/openapi.js?31', function() { scriptLoading = false; scriptLoaded = true; for( var i = 0; i < onloads.length; ++i ) { onloads[ i ](); } onloads = []; onload(); } ); } jQuery( function() { //Do not download the script if the widgets were not added yet. if( buttons.length ) { //Do not load the script directly after the page load. //We don't want to delay other onload functions somehow. setTimeout( function() { loadScript( initButtons ); }, 500 ); } onloadPassed = true; } ); } ) ( jQuery ); function ellipsis(node) { var s = document.documentElement.style, w = node.offsetWidth, clon = node.cloneNode(true); clon.style.position = "absolute"; clon.style.width = "auto"; clon.style.overflow = "visible"; clon.style.top = "-10000px"; node.parentNode.insertBefore(clon, node); if (clon.offsetWidth > w) { node.title = node[/*@cc_on"innerText"||@*/"textContent"]; // FF2, FF3 if (!("textOverflow" in s || "OTextOverflow" in s)) { var searcher = function(el, orig) { if (el.nodeType === 1 && el.tagName === "IMG") { orig.parentNode.removeChild(orig); el.parentNode.removeChild(el); } if (el.nodeType === 3) { var text = el.nodeValue; while (text.length > 0 && clon.offsetWidth > w) { text = text.substr(0, text.length - 1); el.textContent = text + "..."; } if (text.length) { orig.textContent = el.textContent // replace space on end .replace(/[\s\.\,\-]+\.\.\.$/, '...'); return true; } orig.textContent = el.textContent = ""; } var i; for (i = el.childNodes.length - 1; i >= 0; i--) { if (searcher(el.childNodes[i], orig.childNodes[i])) { return true; } } }; searcher(clon, node); } } else { node.title = ""; } clon.parentNode.removeChild(clon); return node; } LJLive = { is_full: false, getStateCode: function(state) { if (~navigator.userAgent.indexOf('iPad')) { return ''; } var normalState = !Cookie('ljlive-is-min') && state !== false, code = '
' +'' +'' +'' +'' +''; if (!normalState) { jQuery('body').addClass('ljtimes-minimized'); } var height = 21; if (normalState) { var param; if (Site.currentEntry) { param = {entry: Site.currentEntry}; } code += ''; height += 24; } code += '
'; return code; }, tagMousedown: function(e) { e = jQuery.event.fix(e); if (e.which !== 1) { return; } LJLive.writePostHide(); if (!$('ljtime_iframe')) { jQuery('' +'').appendTo('body'); jQuery('#ljtime').addClass('b-ljtimes-opening'); cont.animate({ height: win.height() }, 1000, function(){ // don't use overflow: hidden for Quirks mode html.css('overflow-x', 'hidden').css('overflow-y', 'hidden'); node.css('left', 0); jQuery('#ljtime').removeClass('b-ljtimes-opening'); node.find('.i-ljtimes-btn') .animate({ top: 0 }, 100) .click(function(){ LJLive.is_full = false; // no jump win.scrollTop(last_scroll_top).scrollLeft(last_scroll_left); node .height(node.height()) .animate({ height: 24 }, function(){ node.remove(); html.css('overflow-x', html_overflow_x) .css('overflow-y', html_overflow_y); full.show(); win.scrollTop(last_scroll_top).scrollLeft(last_scroll_left); }); }); cont.height('auto'); }).css('overflow', 'visible'); }, messagesIsShow: false, messagesShow: function(target) { LJLive.messagesIsShow = true; var node = jQuery(target).parents('.b-inbox'), show_node = jQuery('
'+LJLive.ml.loading+'
') .appendTo('body'), right = target.parentNode.offsetWidth/2 - 40; // arrow of center show_node .css('right', Math.max(right, 4)) .mousedown(function(e){ e.stopPropagation(); }); var user_url = ''; if (/^(community|users)\./.test(location.host)) { user_url = '/'+Site.currentJournal; } jQuery.getJSON( user_url + '/__alerts/get.html', function(data) { if (!data.messages.length) { node.find('.arrow').html(0); show_node.remove(); } var rec_ids = [], html = '', i; data.messages = data.messages.splice(0, 5); for (i = -1; data.messages[++i];) { rec_ids.push(data.messages[i].rec_id); html += ''+data.messages[i].message+''; } show_node .html('
'+html+'
') .css('right', Math.max(right, 4)); LJLive.messagesHide = function() { LJLive.messagesIsShow = false; show_node.remove(); }; jQuery.post( user_url + '/__alerts/mark_readed.html', {rec_id: rec_ids.join(',')}, function(data){ +data.unread_count ? node.find('a').text(data.unread_count) : node.find('.arrow').html(0); }, 'json'); } ); jQuery(document).one('mousedown', function(e, iframe_target) { if (target !== iframe_target) { LJLive.messagesHide(); } }); }, messagesHide: jQuery.noop, _loginNode: null, _loginShow: function(target, method) { if (!LJLive._loginNode) { LJLive._loginNode = jQuery('
' + LJLive.ml.html_login_form + '
').appendTo('body'); } var show_node = LJLive._loginNode; LJLive[method + 'Show'] = function(target) { LJLive[method + 'IsShow'] = true; show_node.show(); var right = jQuery(window).width() - jQuery(target.parentNode).offset().left - target.parentNode.offsetWidth; right += target.parentNode.offsetWidth/2 - 40; // arrow of center show_node.css('right', Math.max(right, 4)); }; LJLive[method + 'Show'](target); LJLive[method + 'Hide'] = function(){ LJLive[method + 'IsShow'] = false; show_node.hide(); }; jQuery(document).mousedown(function(e, iframe_target) { if (target !== iframe_target) { LJLive[method + 'Hide'](); } }); show_node.find('.b-ljtimes-login').mousedown(function(e){ e.stopPropagation(); }); show_node.find('.i-popup-close').click(LJLive[method + 'Hide']); }, writePostIsShow: false, writePostShow: function(target) { if (!Site.has_remote) { LJLive._loginShow(target, 'writePost'); return; } var auth_token, right, html = '
' + LJLive.ml.html_submit_form + '
', show_node = jQuery(html).appendTo('body'), form_node = show_node.find('form'); LJLive.writePostShow = function(target) { LJLive.writePostIsShow = true; show_node.show(); right = jQuery(window).width() - jQuery(target.parentNode).offset().left - target.parentNode.offsetWidth; right += target.parentNode.offsetWidth/2 - 40; // arrow of center show_node.css('right', right); jQuery.post(LiveJournal.getAjaxUrl('quick_post'), {get_auth: 1}, function(data){ auth_token = data.auth; }, 'json'); }; LJLive.writePostHide = function(){ LJLive.writePostIsShow = false; show_node.hide(); }; LJLive.writePostShow(target); form_node.find('input, textarea').placeholder(); form_node .submit(function(e){ jQuery.post(LiveJournal.getAjaxUrl('quick_post'), form_node.serialize() + '&auth_token=' + auth_token, function(data){ LJLive.writePostHide(); var html; if (data.error) { html = '
' +''+data.error+'' +'' +'
'; } else { html = '
' +''+LJLive.ml.post_created_title+'' +''+LJLive.ml.post_created_body.replace('[[url]]', data.entry_url)+'' +'' +'
'; form_node[0].reset(); form_node[0].send.disabled = true; } var node = jQuery(html) .appendTo('body') .css('right', right); node.find('a.ljtimes_again').click(function(e){ jQuery(document).mousedown(); // close LJLive.writePostShow(target); e.preventDefault(); }); node.find('.i-popup-close').click(function() { jQuery(document).mousedown(); // close }); node.mousedown(function(e){ e.stopPropagation(); }); jQuery(document) .add(node.find('.i-ljtimes-logged-close')) .one('mousedown', function(){ node.remove(); }); }, 'json'); e.preventDefault(); // Google Analytics LJLive.window._gaq.push(['_trackEvent', 'Mini LJTimes', 'post', 'Write form - posted']); }); form_node.find('textarea').input(function(){ form_node[0].send.disabled = !this.value.length; }); show_node.find('.i-popup-close').click(LJLive.writePostHide); }, writePostHide: jQuery.noop, suggestBubble: null, suggestBubbleRemove: function() { if (LJLive.suggestBubble) { LJLive.suggestBubble.remove(); } }, suggestIsShow: false, suggestShow: function(target) { if (!Site.has_remote) { LJLive._loginShow(target, 'suggest'); return; } var show_node = jQuery(LJLive.ml.html_suggest_form) .mousedown(function(e) { e.stopPropagation(); }) .appendTo('body'), auth_token; var form_node = show_node.find('form'); jQuery(form_node[0].url) .placeholder() .input(function(){ form_node[0].send.disabled = !this.value || this.value === this.getAttribute('placeholder'); }); LJLive.suggestShow = function(target) { if (Site.currentEntry) { jQuery(form_node[0].url).val(Site.currentEntry).placeholder().input(); } jQuery.post(LiveJournal.getAjaxUrl('lj_times_recommend'), {get_auth: 1}, function(data){ auth_token = data.auth; }, 'json'); // arrow of center var right = jQuery(window).width() - jQuery(target).offset().left - jQuery(target).width()/2 - 40; show_node.css('right', right); LJLive.suggestIsShow = true; show_node.show(); }; LJLive.suggestShow(target); form_node.submit(function(e){ form_node[0].send.disabled = true; jQuery.post(LiveJournal.getAjaxUrl('lj_times_recommend'), form_node.serialize() + '&auth_token=' + auth_token, function(data){ LJLive.suggestHide(); if (Site.currentEntry && Site.currentEntry == form_node[0].url.value) { jQuery('span', target).html( LJLive.ml.suggest_already.replace('[[num]]', data.currentEntryRecommendations) ); } var right = jQuery(window).width() - jQuery(target).offset().left - jQuery(target).width()/2 - 40, html = '
' +''+(data.ret || data.error)+'' +'' +'
'; var node = jQuery(html) .appendTo('body') .css('right', right); node.find('a.ljtimes_again').click(function(e){ jQuery(document).mousedown(); // close LJLive.writePostShow(target); e.preventDefault(); }); node.find('.i-popup-close').click(function() { jQuery(document).mousedown(); // close }); node.mousedown(function(e){ e.stopPropagation(); }); jQuery(document) .add(node.find('.i-ljtimes-logged-close')) .one('mousedown', function(){ node.remove(); }); form_node[0].reset(); LJLive.window._gaq.push(['_trackEvent', 'Mini LJTimes', 'post', 'Suggest form - posted']); }, 'json'); e.preventDefault(); }); LJLive.suggestHide = function() { LJLive.suggestIsShow = false; show_node.hide(); }; show_node.find('.i-popup-close').click(LJLive.suggestHide); jQuery(document).mousedown(function(e, iframe_target) { if (target !== iframe_target) { LJLive.suggestHide(); } }); }, suggestHide: jQuery.noop, calcTime: function(ts) { if (typeof ts === 'number') { var minutes = Math.ceil((LJLive.now - ts) / 60); ts = minutes + ' ' + LJLive.ml.timeText(minutes); } return ts; }, frameInit: function(frame) { LJLive.frame_body = frame.document.body; LJLive.ml = frame.ML_ljtimes; LJLive.now = frame.now; LJLive.window = frame; // fire event for main document jQuery(LJLive.frame_body) .mousedown(function(e){ jQuery(document).trigger('mousedown', e.target); }) // don't use stopPropagation // write post don't hide if mousedown on document .delegate('.b-update a', 'mousedown', function() { LJLive.suggestBubbleRemove(); !LJLive.writePostIsShow ? LJLive.writePostShow(this) : LJLive.writePostHide(); LJLive.window._gaq.push(['_trackEvent', 'Mini LJTimes', 'click', 'Write form - clicked']); }) .delegate('.b-inbox a', 'mousedown', function() { LJLive.writePostHide(); LJLive.suggestBubbleRemove(); !LJLive.messagesIsShow ? LJLive.messagesShow(this) : LJLive.messagesHide(); LJLive.window._gaq.push(['_trackEvent', 'Mini LJTimes', 'click', 'Messages']); }) .delegate('.b-suggest a', 'mousedown', function() { LJLive.writePostHide(); LJLive.suggestBubbleRemove(); !LJLive.suggestIsShow ? LJLive.suggestShow(this) : LJLive.suggestHide(); LJLive.window._gaq.push(['_trackEvent', 'Mini LJTimes', 'click', 'Suggest clicked']); }); jQuery('.b-update, .b-inbox', LJLive.frame_body).click(function(e){ e.preventDefault(); }); // Google Analytics jQuery('.b-random', LJLive.frame_body).click(function(){ LJLive.window._gaq.push(['_trackEvent', 'Mini LJTimes', 'click', 'Random journal']); }); // TODO after 14 remove with branding if (LJLive.feb14 === true) { // place hearts in right place var suggest_link = jQuery('.b-suggest a', LJLive.frame_body), love_pic = jQuery('').appendTo('#ljtime'); setTimeout(function() { var right = jQuery(window).width() - suggest_link.offset().left; //place upper part of picture right above bottom one love_pic.css('right', right + 'px').show(); }, 100); } }, insertAdditionalHTML: function() { jQuery('#ljtime').append( LJLive.ml.html_additional ); }, helpBubbleInit: function() { if (Cookie('ljlive-bubble') === '2') { return; } var $win = jQuery(window), $doc = jQuery(document); function show_bubble(text) { $win.unbind('scroll', check_to_show_bubble); $doc.unbind('mousemove', check_to_show_bubble); jQuery('#ljtime').unbind('mouseover', check_to_show_bubble); var bubble = jQuery(LJLive.ml.html_bubble).appendTo('#ljtime'); bubble .find('.b-ljtimes-bubble-p') .html(text); bubble .css('top', -bubble.height() - 27) .find('.b-ljtimes-bubble-close') .click(function(){ bubble.remove(); Cookie('ljlive-bubble', 1, { expires: 355, domain: '.'+location.host.match(/[^.]+\.\w+$/)[0], path: '/' }); // suggest bubble show_suggest_bubble(); }); } function show_suggest_bubble() { var bubble = jQuery(LJLive.ml.html_suggest_bubble), suggestLink = jQuery(LJLive.frame_body).find('.b-suggest a'), // arrow of center right = $win.width() - suggestLink.offset().left - suggestLink.width()/2 - 40; bubble .css('right', right) .find('.i-popup-close') .click(function(){ bubble.remove(); Cookie('ljlive-bubble', 2, { expires: 355, domain: '.'+location.host.match(/[^.]+\.\w+$/)[0], path: '/' }); }) .end() .appendTo('#ljtime'); LJLive.suggestBubble = bubble; } var timeout; function check_to_show_bubble(e) { clearTimeout(timeout); switch(e.type) { case 'mousemove': // user rests timeout = setTimeout(function(){ show_bubble(LJLive.ml.bubble_rests); }, 1000*60); break; case 'scroll': // bottom page if ($win.scrollTop() + $win.height() === $doc.height()) { show_bubble(LJLive.ml.bubble_scroll); } break; case 'mouseover': show_bubble(LJLive.ml.bubble_mouseover); break; } } if (!Cookie('ljlive-bubble')) { $win.scroll(check_to_show_bubble); $doc.mousemove(check_to_show_bubble); jQuery('#ljtime').mouseover(check_to_show_bubble); } else if (Cookie('ljlive-bubble') === '1') { show_suggest_bubble(); } }, dataInit: function(data) { if (data.mode === 'night') { jQuery('.b-ttiny', LJLive.frame_body).addClass('b-ttiny-night'); } var i; for (i = -1; data.live[++i];) { data.live[i].text = data.live[i].text + ', ' + LJLive.calcTime(data.live[i].ts); } // TODO: delete if (LJLive.window.additional_data) { var i = -1; for (; LJLive.window.additional_data[++i]; ) { data.live.splice(Math.round(i*2.3), 0, LJLive.window.additional_data[i]) } } for (i = -1; data.themes[++i];) { data.themes[i].text = data.themes[i].text + ', ' + LJLive.calcTime(data.themes[i].ts); } var text_ary = data .live.concat(data.themes) .sort(function(){ return Math.round(Math.random())-0.5; }); if (text_ary.length) { i = 0; var last_node, before_last_node, post_container = jQuery('.b-posts', LJLive.frame_body), logo_width = jQuery('.b-logo', LJLive.frame_body).outerWidth(), interval, anim_complete = true, fade_nodes = function( in_node, out_node ) { var anim_count = 2, fadeFinshed = function() { anim_count--; anim_complete = anim_count === 0; }; anim_complete = false; out_node.fadeTo(800, 0, function() { jQuery(this).remove(); fadeFinshed(); } ); in_node.fadeTo(800, 1, function() { fadeFinshed(); } ); }, interval_func = function() { if( !anim_complete ) { return; } if (!text_ary[i+1]) { i = -1; } var post_data = text_ary[++i], node = jQuery('
  • '+post_data.text+'
  • ', LJLive.frame_body.ownerDocument), width = jQuery(window).width() - logo_width - jQuery('.b-quick', LJLive.frame_body).outerWidth() - jQuery('.b-inbox', LJLive.frame_body).width(); node .mouseenter(function(){ clearInterval(interval); }) .mouseleave(function(){ clearInterval(interval); interval = setInterval(interval_func, 5000); }) .css('width', width); if (last_node) { node.css('opacity', 0); } node.appendTo(post_container); ellipsis(node[0]); if (last_node) { fade_nodes( node, last_node ); } //Google Analytics var click_node = node.find('a:first'); if (!click_node[0].onclick) { click_node.click(function(e) { LJLive.window._gaq.push(['_trackEvent', 'Live - mini ljtimes', 'click', this.href]); // if no new tab if (this.target !== '_blank' && !(e.metaKey || e.altKey || e.shiftKey || e.ctrlKey) && e.which === 1) { setTimeout('top.location="' + this.href + '"', 100); e.preventDefault(); } }); } last_node = node; }; interval = setInterval(interval_func, 5000); interval_func(); } LJLive.helpBubbleInit(); LJLive.insertAdditionalHTML(); } }; (function( top ) { var icoBase = 'http://wh.lj.ru/iepinned'; function updateJumpList( dict, inboxNumber ) { inboxNumber = inboxNumber || 0; window.external.msSiteModeCreateJumplist( 'LiveJournal'); window.external.msSiteModeAddJumpListItem( dict.journal, Site.remoteJournalBase, icoBase + '/recent.ico' ); window.external.msSiteModeAddJumpListItem( dict.friends, Site.remoteJournalBase + '/friends', icoBase + '/friends.ico' ); window.external.msSiteModeAddJumpListItem( dict.ljtimes, Site.siteroot + '/ljtimes/', icoBase + '/ljtimes.ico' ); window.external.msSiteModeAddJumpListItem( dict.ratings_posts, Site.siteroot + '/ratings/posts/', icoBase + '/top.ico' ); window.external.msSiteModeAddJumpListItem( dict.random_journal, Site.siteroot + '/random.bml', icoBase + '/surprise.ico' ); if( inboxNumber > 0 ) { window.external.msSiteModeAddJumpListItem( dict.inbox + '(' + inboxNumber + ')', Site.siteroot + '/inbox/', icoBase + '/inbox.ico' ); window.external.msSiteModeSetIconOverlay( icoBase + '/inbox.ico', dict.inbox + '(' + inboxNumber + ')' ); } else { window.external.msSiteModeAddJumpListItem( dict.inbox, Site.siteroot + '/inbox/', icoBase + '/inbox_empty.ico' ); window.external.msSiteModeClearIconOverlay(); } window.external.msSiteModeShowJumplist(); } function fetchInbox() { var url = Site.siteroot + LiveJournal.getAjaxUrl( 'inbox_count' ); $.getJSON( url, function( data ) { updateJumpList( data.number ); } ); } function setMeta( name, content ) { var meta = document.createElement( 'meta' ) meta.name = name; meta.content = content; document.getElementsByTagName( 'head' )[0].appendChild( meta ); } function injectPinnedMeta( dict ) { if( Site.has_remote ) { setMeta( "msapplication-task", "name=" + dict.update_journal + ";action-uri=" + Site.siteroot + "/update.bml;icon-uri=" + icoBase + "/post.ico" ); } setMeta( "application-name", dict.app_name ); setMeta( "msapplication-tooltip", dict.app_tooltip ); setMeta( "msapplication-starturl", Site.siteroot ); } var defaultDict = { app_name: 'LiveJournal', app_tooltip: 'Livejournal.com', inbox: 'Inbox', update_journal: 'Post an entry', journal: 'Journal', friends: 'Friends', ljtimes: 'LJTimes', ratings_posts: 'Ratings', random_journal: 'Surprise me!' } top.ie9InitPinnedMode = function( dict ) { dict = jQuery.extend( {}, defaultDict, dict ); try { injectPinnedMeta( dict ); if(window.external.msIsSiteMode()) { if( Site.has_remote > 0 ) { //here we should pass the number of current unread notifications var unread_count = parseInt( Site.inbox_unread_count, 10) || 0; updateJumpList( dict, unread_count ); /* setInterval( function() { //fetchInbox(); //ajax requests emulation //updateJumpList( dict, Math.floor( Math.random() * 10 ) ); }, 3000 ); */ } else { window.external.msSiteModeClearJumplist(); window.external.msSiteModeClearIconOverlay(); } } else {} } catch(e) { } } ie9InitPinnedMode(); }( window ));