[ljcom] r10653: LJSUP-9016: LJ-like - add donate button
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>
