Committer: dpetrov
LJSUP-9016: LJ-like - add donate buttonA trunk/htdocs/js/jquery/ A trunk/htdocs/js/jquery/jquery.postmessage.js A trunk/htdocs/js/jquery/jquery.storage.js A trunk/htdocs/misc/xdr.html
Added: trunk/htdocs/js/jquery/jquery.postmessage.js =================================================================== --- trunk/htdocs/js/jquery/jquery.postmessage.js (rev 0) +++ trunk/htdocs/js/jquery/jquery.postmessage.js 2011-06-16 04:26:48 UTC (rev 10653) @@ -0,0 +1,352 @@ +/*! + * jQuery rpc plugin. + * @author Dmitry Petrov <Dmitry.Petrov@sup.com>, 2011 + * + * Plugin is used to allow crossdomain communication between container window and child + * popups. To make communication work in older browsers, user has to put html (xfr.html) file in the + * root of container page domain. + * + * To allow communication either container should initiate communication or child popup + * should know the origin of the container ( protocol://host:port ). + * + * Contents of xdr.html: + * <!DOCTYPE HTML> + * <html lang="en"> + * <head> + * <meta charset="UTF-8"> + * <script type="text/javascript"> + * function sendData() { + * if ( location.hash && location.hash.length > 1 ) { + * window.parent.opener.rpcMessage = location.hash; + * location.hash = '#'; + * } + * } + * + * setInterval( sendData, 50 ); + * </script> + * </head> + * <body> + * </body> + * </html> + * + * Plugin was tested to work in IE7+, Firefox 3.5+, Opera 10.10+, Google Chrome 12 + * + * @TODO: Plugin doesn't check origin of the messages for now, though it's not possible for old + * browser because of technology used to pass data ( hash polling ). + * + */ +(function( window, $ ){ + var cache_bust = 1, + interval_id, + + /** + * has_postMessage shows if browser supports postMessage communication. IE8 is disabled because it + * doesn't allow to use postMessage from popup. + */ + has_postMessage = !!window.postMessage && ( !$.browser.msie || parseInt( $.browser.version, 10 ) > 8 ), + /** + * senderUrl stores the address of container window + */ + senderUrl, + /** + * iframeUrl stores the address of iframe in the domain of container window + */ + iframeUrl, + receiveCallbacks = [], + senderDomainFrame, + /* + * pollDelay is a delay for checking it hash changed + */ + pollDelay = 100; + + /** + * targets is a collection of targets to exhange messages with. Container can pass messages to the multiple domains + */ + var targets = function() { + var container = []; + + var collection = { + add: function( w, ref ) { + var pos; + if( ( pos = collection.indexOf( w ) === -1 ) ){ + container.push( { win: w, href: ref } ); + } else { + collection[ pos ].href = ref; + } + }, + + remove: function( w ) { + var pos; + if( ( pos = collection( w ) === -1 ) ){ + return; + } else { + collection.splice( pos, 1 ); + collection[ pos ].href = ref; + } + }, + + indexOf: function( w ) { + for( var i = 0; i < collection.length; ++i ) { + if( w === collection[ i ].top ) { + return i; + } + } + + return -1; + }, + + getTarget: function( win ) { + var pos = collection.indexOf( win ); + if( pos === -1 ) { + return; + } else { + container[ pos ].href; + } + } + }; + + return collection; + }(); + + /** + * Converts message object to the hash string ready to pass to other window + * + * @param {Object} message + * @return {String} + */ + function toHash( message ) { + message = typeof message === 'string' ? message : $.param( message ); + return '#' + (+new Date) + (cache_bust++) + ':rpc&' + message; + } + + /** + * Converts param string like a=b&c=d to the object { a: 'b', c: 'd' } + * + * @param {String} hasStr + * + * @return {Object} + */ + function toObject( hashStr ) { + var obj = {}, pair; + var pairs = hashStr.split( "&" ); + for( var arg = 0; arg < pairs.length; arg++ ) { + pair = pairs[ arg ].split( "=" ); + + //if user passes simple string, we return it as an response + if( pair.length === 1 && pairs.length === 1 ) { + return pair[0]; + } + + obj[ pair[ 0 ] ] = decodeURIComponent( pair[ 1 ] ); + } + + return obj; + } + + /** + * Checks if hash contains correct rpc call + */ + function isHash( str ) { + return /#\d{14}:rpc(.*)&/.test( str ); + } + + function extractBaseUrl( url ) { + return url.replace( /([^:]+:\/\/[^\/]+).*/, '$1' ) + } + + /** + * Function tries to find rpc call through different communication channels ( hash or window rpcMessage property ) + * + * @return {Boolean} + */ + function catchEvent() { + var channel = ''; + if( !has_postMessage && location.hash.length > 0 && /^#\d+:rpc&.*$/.test( location.hash ) ) { + channel = location.hash; + location.hash = ''; + } else if( window.rpcMessage && window.rpcMessage.length > 0 && /^#\d+:rpc&.*$/.test( window.rpcMessage ) ) { + channel = window.rpcMessage; + window.rpcMessage = ''; + } else { + return; + } + + return toObject( channel.replace( /#\d+:rpc&/, '' ) ); + } + + /** + * Polling loop + */ + function startPolling() { + interval_id && clearInterval( interval_id ); + interval_id = null; + + interval_id = setInterval( function() { + var data = catchEvent(); + + if( data ) { + for( var i = 0; i < receiveCallbacks.length; ++i ) { + var cb = receiveCallbacks[ i ].pmcb || receiveCallbacks[ i ].cb; + cb( { data: data } ); + } + } + }, pollDelay ); + } + + /** + * @namespace $.rpc + */ + $.rpc = $.rpc || {}; + + /** + * Send message to the other window. If senderUrl us set the code acts like child window. + * + * @param {Object|String} message + * @param {Object} target Window object of the window to send message to + */ + $.rpc.postMessage = function( message, target ) { + target = target || parent; + + var target_url, useFrame = false; + if( !(target_url = targets.getTarget( target ) ) ) { + if( has_postMessage ) { + target_url = "*"; + } else { + if( !senderUrl ) { + throw "Transport channel has not been init for communication"; + } else { + target_url = senderUrl; + useFrame = true; + } + } + } + + if ( has_postMessage ) { + window.setTimeout( function() { + target.postMessage( typeof message === 'string' ? message : $.param( message ), extractBaseUrl( target_url ) ); + }, 0 ); + + } else if ( target_url ) { + if( useFrame ) { + // window.open( iframeUrl + toHash( message ), 'xdr' ); + $( senderDomainFrame ).attr( 'src', iframeUrl + toHash( message ) ); + } else { + target.location = target_url.replace( /#.*$/, '' ) + toHash( message ); + } + } + }; + + /** + * Bind callback to listen messages + * + * @param {Function} callback + */ + $.rpc.bind = function( callback ) { + if ( has_postMessage ) { + var rm_callback = function(e) { + e = e || window.event; + callback( e ); + }; + + if ( window.addEventListener ) { + window.addEventListener( 'message', rm_callback, false ); + } else { + window.attachEvent( 'onmessage', rm_callback ); + } + receiveCallbacks.push( { cb: callback, pmcb: rm_callback } ); + } else { + receiveCallbacks.push( { cb: callback } ); + } + }; + + /** + * Stop listening messages with callback + */ + $.rpc.unbind = function( callback ) { + for( var i = 0; i < receiveCallbacks.length; ++i ) { + if( receiveCallbacks[ i ].cb === callback ) { + if ( has_postMessage ) { + if ( window.addEventListener ) { + window.removeEventListener( 'message', receiveCallbacks[ i ] ); + } else { + windowdetachEvent( 'onmessage', receiveCallbacks[ i ] ); + } + } + receiveCallbacks.splice( i, 1 ); + return; + } + } + } + + /** + * Init communication with child window from container. Function makes sense only for browsers + * that do not support postMessage. + * + * @param {Object} w window object of child window. + * @param {String} recipientUrl url if the child window + * @param {String} senderUrl url of the container window + */ + $.rpc.initRecipient = function( w, recipientUrl, senderUrl ) { + //if only fallback method is used, we have to pass parent window url with query string to enable communication + if( !has_postMessage ) { + w.location = recipientUrl.replace( /#.*$/, '' ) + toHash( { transport_url: senderUrl } ); + } else { + w.location = recipientUrl; + } + + targets.add( w, recipientUrl ); + }; + + //plugin listens messages in case if parent window will send it's url. + if( !has_postMessage ) { + var bootstrap = function( e ) { + if( e.data && e.data.transport_url ) { + $.rpc.setSender( e.data.transport_url ); + $.rpc.unbind( bootstrap ); + } + }; + $.rpc.bind( bootstrap ); + } + + /** + * Set Url of the parent window in the child. Function also initiates communication channel, + * if it has not been done yet. + * + * @param {String} url + */ + $.rpc.setSender = function( url ) { + senderUrl = url; + + //setting senderUrl means that we're in child window, so we need + //to create the page in the domain of parent window + if( !has_postMessage ) { + iframeUrl = extractBaseUrl( url ) + '/xdr.html'; + + if( !senderDomainFrame ) { + senderDomainFrame = document.createElement( 'iframe' ); + senderDomainFrame.id = 'xdr'; + senderDomainFrame.src = iframeUrl; + senderDomainFrame.style.display = 'none'; + + document.body.insertBefore( senderDomainFrame, document.body.firstChild ); + } else { + $( senderDomainFrame ).attr( 'src', iframeUrl ); + } + + } + }; + + /** + * Return current url of the parent window. + * + * @return {String} + */ + $.rpc.getSender = function() { + return senderUrl; + }; + + $.rpc.hasPostMessage = has_postMessage; + + startPolling(); + +})( window, jQuery ); Added: trunk/htdocs/js/jquery/jquery.storage.js =================================================================== --- trunk/htdocs/js/jquery/jquery.storage.js (rev 0) +++ trunk/htdocs/js/jquery/jquery.storage.js 2011-06-16 04:26:48 UTC (rev 10653) @@ -0,0 +1,123 @@ +/*! + * jQuery storage plugin. + * @author Dmitry Petrov <Dmitry.Petrov@sup.com>, 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( $ ) { + /** + * @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 = $( '<link>' ).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, 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 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, val ); + }, + getItem: function( name ) { + return 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 ); Added: trunk/htdocs/misc/xdr.html =================================================================== --- trunk/htdocs/misc/xdr.html (rev 0) +++ trunk/htdocs/misc/xdr.html 2011-06-16 04:26:48 UTC (rev 10653) @@ -0,0 +1,18 @@ +<!DOCTYPE HTML> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <script type="text/javascript"> + function sendData() { + if ( location.hash && location.hash.length > 1 ) { + window.parent.opener.rpcMessage = location.hash; + location.hash = '#'; + } + } + + setInterval( sendData, 50 ); + </script> +</head> +<body> +</body> +</html>