Skip to content
This repository has been archived by the owner on Oct 8, 2024. It is now read-only.

Commit

Permalink
updates to support lint/test/build + slight refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
ecaroth committed Sep 23, 2016
1 parent 0f20679 commit 6f85d41
Show file tree
Hide file tree
Showing 12 changed files with 475 additions and 22 deletions.
91 changes: 91 additions & 0 deletions .jshintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
{
// JSHint Default Configuration File (as on JSHint website)
// See http://jshint.com/docs/ for more details

"maxerr" : 50, // {int} Maximum error before stopping

// Enforcing
"bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
"camelcase" : false, // true: Identifiers must be in camelCase
"curly" : false, // true: Require {} for every new block or scope
"eqeqeq" : true, // true: Require triple equals (===) for comparison
"forin" : false, // true: Require filtering for..in loops with obj.hasOwnProperty()
"freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc.
"immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
"latedef" : false, // true: Require variables/functions to be defined before being used
"newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()`
"noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
"noempty" : true, // true: Prohibit use of empty blocks
"nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters.
"nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
"plusplus" : false, // true: Prohibit use of `++` and `--`
"quotmark" : false, // Quotation mark consistency:
// false : do nothing (default)
// true : ensure whatever is used is consistent
// "single" : require single quotes
// "double" : require double quotes
"undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
"unused" : true, // Unused variables:
// true : all variables, last function parameter
// "vars" : all variables only
// "strict" : all variables, all function parameters
"strict" : true, // true: Requires all functions run in ES5 Strict Mode
"maxparams" : false, // {int} Max number of formal params allowed per function
"maxdepth" : false, // {int} Max depth of nested blocks (within functions)
"maxstatements" : false, // {int} Max number statements per function
"maxcomplexity" : false, // {int} Max cyclomatic complexity per function
"maxlen" : false, // {int} Max number of characters per line
"varstmt" : false, // true: Disallow any var statements. Only `let` and `const` are allowed.

// Relaxing
"asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
"boss" : false, // true: Tolerate assignments where comparisons would be expected
"debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
"eqnull" : false, // true: Tolerate use of `== null`
"esversion" : 5, // {int} Specify the ECMAScript version to which the code must adhere.
"moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
// (ex: `for each`, multiple try/catch, function expression…)
"evil" : false, // true: Tolerate use of `eval` and `new Function()`
"expr" : false, // true: Tolerate `ExpressionStatement` as Programs
"funcscope" : false, // true: Tolerate defining variables inside control statements
"globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
"iterator" : false, // true: Tolerate using the `__iterator__` property
"lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
"laxbreak" : false, // true: Tolerate possibly unsafe line breakings
"laxcomma" : false, // true: Tolerate comma-first style coding
"loopfunc" : true, // true: Tolerate functions being defined in loops
"multistr" : false, // true: Tolerate multi-line strings
"noyield" : false, // true: Tolerate generator functions with no yield statement in them.
"notypeof" : false, // true: Tolerate invalid typeof operator values
"proto" : false, // true: Tolerate using the `__proto__` property
"scripturl" : false, // true: Tolerate script-targeted URLs
"shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
"sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
"supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
"validthis" : false, // true: Tolerate using this in a non-constructor function

// Environments
"browser" : true, // Web Browser (window, document, etc)
"browserify" : false, // Browserify (node.js code in the browser)
"couch" : false, // CouchDB
"devel" : true, // Development/debugging (alert, confirm, etc)
"dojo" : false, // Dojo Toolkit
"jasmine" : false, // Jasmine
"jquery" : false, // jQuery
"mocha" : true, // Mocha
"mootools" : false, // MooTools
"node" : true, // Node.js
"nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
"phantom" : false, // PhantomJS
"prototypejs" : false, // Prototype and Scriptaculous
"qunit" : false, // QUnit
"rhino" : false, // Rhino
"shelljs" : false, // ShellJS
"typed" : false, // Globals for typed array constructions
"worker" : false, // Web Workers
"wsh" : false, // Windows Scripting Host
"yui" : false, // Yahoo User Interface

// Custom Globals
"globals" : { "_LOG": true} // additional predefined global variables
}
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Usage
Simply include the script on any page where it's needed, create a new instance of xDomainCookie, and leverage the get/set functions:

```html
<script src="//my.s3bucket.com/xdomain_cookie.js"></script>
<script src="//my.s3bucket.com/xdomain_cookie.min.js"></script>
<script>
var xd_cookie = xDomainCookie( '//my.s3bucket.com' );
xd_cookie.get( 'cookie_name', function(cookie_val){
Expand All @@ -31,7 +31,7 @@ Simply include the script on any page where it's needed, create a new instance o
Usage Notes
------

_Please Note_ that it's important for the `xdomain_cookie.js` file to be served from the same domain _and_ protocol as the path passed in for the iframe creation (when creating `xDomainCookie`). You can setup the script to use whichever page the protocol of the main window is using by specifying `//` as the protocol prefix (instead of explicit `https://` or `http://`, assuming the webserver hosting the `xdomain_cookie.html` file supports that procolol). It's also OK to serve both the script and iframe path over HTTPS in all instances, regardless of if the main page is loaded over HTTPS.
_Please Note_ that it's important for the `xdomain_cookie.min.js` file to be served from the same domain _and_ protocol as the path passed in for the iframe creation (when creating `xDomainCookie`). You can setup the script to use whichever page the protocol of the main window is using by specifying `//` as the protocol prefix (instead of explicit `https://` or `http://`, assuming the webserver hosting the `xdomain_cookie.html` file supports that procolol). It's also OK to serve both the script and iframe path over HTTPS in all instances, regardless of if the main page is loaded over HTTPS.

This script should work in all modern desktop and mobile browsers that support the postMessage API (IE 8+, Chrome, FF, etc).

Expand Down Expand Up @@ -102,12 +102,14 @@ There's a full test suite that leverages zombie/connect to mock & test the libra
npm test
```
##### Dev Server
The dev server runs on localhost:3001. Once running you can map whatever hosts to 127.0.0.1 and load the JS in running local pages from {{myhost}}:3001/xdomain_cookie.js.
##### Dev Server & Development
The dev server runs on localhost:3001. Once running you can map whatever hosts to 127.0.0.1 and load the JS in running local pages from {{myhost}}:3001/xdomain_cookie.dev.js.
```
npm run dev
```

It is also pre-configurd with an example scenario that shows usage across domains. It requires you to map the following domains to 127.0.0.1 in your hosts file: shared_cookie_test.com, shared_cookie_test2.com, and shared_cookie_iframe.com

You can then see the example working by visiting http://shared_cookie_test.com:3001/test_page.html and shared_cookie_test2.com/test_page.html - take a look at the console output to see cookie behavior.

When developing locally you can lint/test/build the library by running `gulp build`
209 changes: 209 additions & 0 deletions dev/xdomain_cookie.dev.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
(function(exports) {
"use strict";

var xDomainCookie = function( iframe_path, namespace, xdomain_only, iframe_load_timeout_ms ){
//iframe_path = full TLD (and optional path) to location where iframe_shared_cookie.html is served from, and domain cookie will be set on
//namespace = namespace to use when identifying that postMessage calls incoming are for our use

if( iframe_path.substr(0,2)==='//' ) iframe_path = (window.location.protocol==='https:'?'https:':'http:')+iframe_path; //verify protocol is present & used

var _namespace = namespace || 'xdsc', //namespace for the shared cookie in case there are multiple instances on one page - prevents postMessage collision
_load_wait_ms = iframe_load_timeout_ms || (1000*6), //wait 6 seconds if no other overloaded wait time specified
_iframe_ready = false, //has the iframe posted back as ready?
_iframe_load_error = false, //was there an error loading the iframe from specified iframe_path in designated iframe_load_timeout_ms?
_callbacks = [], //list of pending callbacks to ping when iframe is ready or err occurs
_xdomain_cookie_data = {}, //shared cookie data set by the iframe after load/ready
_id = new Date().getTime(), //identifier to use for iframe in case there are multiple on the page
_default_expires_days = 30, //default expiration days for cookies when re-uppded
_xdomain_only = !!xdomain_only; //should we ONLY use xdomain cookies (and avoid local cache)

//function called on inbound post message - filter/verify that message is for our consumption, then set ready data an fire callbacks
function _inbound_postmessage( event ){

var origin = event.origin || event.originalEvent.origin; // For Chrome, the origin property is in the event.originalEvent object.
if (origin !== iframe_path) return; //incoming message not from iframe

if(typeof event.data !== 'string') return; //expected json string encoded payload
var data = null;
try{
data = JSON.parse(event.data);
}catch(e){}

if(typeof data !== 'object' || (data instanceof Array)) return; //data is not a non-array object
if(!('msg_type' in data) || data.msg_type !== 'xdsc_read') return; //data is not a xdomainc-cookie payload
if(!('namespace' in data) || data.namespace !== _namespace) return; //wrong namespace for msg

//NOTE - the only thing iframe postMessages to us is when it's initially loaded, and it includes payload of all cookies set on iframe domain
_xdomain_cookie_data = data.cookies;
_iframe_ready = true;
_fire_pending_callbacks();
}

//an error occured loading the iframe from specified source (based on timeout)
function _iframe_load_error_occured(){
_iframe_load_error = true;
_fire_pending_callbacks();
}

//wait until iframe is loaded & ready, or an error occurs, then execute callbakcfunction
function _on_iframe_ready_or_error( cb ){
_callbacks.push( cb );
_fire_pending_callbacks();
}

//run all pending callbacks that are registered
function _fire_pending_callbacks(){
if( !_iframe_load_error && !_iframe_ready ) return; //not yet ready to fire callbacks, still waiting on error or ready
while(_callbacks.length>0){
_callbacks.pop()( _iframe_load_error );
}
}

//set a cookie in the iframe @ iframe_path
function _set_cookie_in_iframe( cookie_name, cookie_value, expires_days ){
//NOTE - this function is only called from within _on_iframe_ready_or_err function when there is NOT an error
//so we can safely assume iframe is present, ready, and callable at this point

//postMessage to Iframe w/ info
var data = {
namespace: _namespace,
msg_type: 'xdsc_write',
cookie_name: cookie_name,
cookie_val: cookie_value,
expires_days: expires_days
};

document.getElementById('xdomain_cookie_'+_id).contentWindow.postMessage(JSON.stringify(data), iframe_path );
}

//basic local cookie getter function
function _get_local_cookie( cookie_name ){
var name = cookie_name + "=";
var ca = document.cookie.split(';');
for(var i=0; i<ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return decodeURIComponent( c.substring(name.length,c.length) );
}
return "";
}

//basic local cookie setter function
function _set_local_cookie( cookie_name, cookie_value, expires_days ){
var d = new Date();
d.setTime(d.getTime() + ( expires_days*1000*60*60*24) );
document.cookie = cookie_name + "=" + cookie_value + "; expires="+d.toUTCString();
}

//function to set the value for both cookies (local & xdomain)
function _set_xdomain_cookie_value( cookie_name, cookie_value, expires_days ){

//if iframe isn't ready, wait for it to be ready
if(!_iframe_ready && !_iframe_load_error){
return _callbacks.push(function(){
_set_xdomain_cookie_value( cookie_name, cookie_value, expires_days);
});
}

expires_days = expires_days || _default_expires_days;
//if cookie is empty (null or undefined) delete the cookie
expires_days = (cookie_value===null || cookie_value===undefined) ? -100 : expires_days;

if(!_xdomain_only) _set_local_cookie( cookie_name, cookie_value, expires_days );

if(!_iframe_load_error){
_set_cookie_in_iframe( cookie_name, cookie_value, expires_days );
}

//set local cached value
_xdomain_cookie_data[cookie_name] = cookie_value;
}

//function to call after instantiation to sync a cookie, supplying a cookie name, value to write if it does NOT exist, expires
//time (in ms from now), and a callback for completion (which includes the resolved cookie value as the only argument)
function _get_xdomain_cookie_value( cookie_name, callback, expires_days ){

expires_days = expires_days || _default_expires_days;

//cb function to create closure for pending user callback
function _cb( xdomain_success, cookie_val, callback ){

//re-up the cookie
_set_xdomain_cookie_value( cookie_name, cookie_val, expires_days );

if(typeof callback === 'function') callback( cookie_val );
}

if(!_xdomain_only){
//see if local cookie is set - if so, no need to wait for iframe to fetch cookie
var _existing_local_cookie_val = _get_local_cookie( cookie_name );
if(_existing_local_cookie_val){
//set onready call to write-through cookie once iframe is ready, then call callback directly
_on_iframe_ready_or_error( function( is_err ){
_cb( !is_err, _existing_local_cookie_val );
});
return callback( _existing_local_cookie_val );
}
}

//no local cookie is set/present, so bind CB to iframe ready/error callback so it's pinged a soon as we hit a ready state from iframe
_on_iframe_ready_or_error(function( is_err ){

//if an error occurs loading the iframe, return appropriate response w/ callback
if(is_err) return _cb( false, null, callback );

var _current_cookie_val = cookie_name in _xdomain_cookie_data ? _xdomain_cookie_data[cookie_name] : null;
_cb( !is_err, _current_cookie_val, callback );

});
}

//bind postmessage listeners for incoming messages from iframe
window.addEventListener('message', _inbound_postmessage );

//create hidden iframe on the page that loads from same domain as this script and is used for communication / cookie setting
var ifr = document.createElement('iframe');
ifr.style.display = 'none';
ifr.id = 'xdomain_cookie_'+_id;
var data = {
namespace: _namespace,
window_origin: window.location.origin,
iframe_origin: iframe_path
};
ifr.src = iframe_path+'/xdomain_cookie.html#'+encodeURIComponent(JSON.stringify(data));
document.body.appendChild( ifr );

//set timeout to specify load error if iframe doesn't load in _load_wait_ms
setTimeout(
function(){
if(!_iframe_ready) _iframe_load_error_occured();
},
_load_wait_ms
);

return {
get: _get_xdomain_cookie_value,
set: _set_xdomain_cookie_value
};
};

exports.xDomainCookie = xDomainCookie;
})(this);

/*
//EXAMPLE OF USAGE
var shared_cookie = xDomainSharedCookie( 'https://shared.contently.com', 'contently.ifsc' );
var _temp_id = generate_new_user_id_for_use_if_cookie_not_set();
shared_cookie.create(
'whatever_cookie_name', //cookie name
_temp_id, //cookie value to use if not set
(1000*60*60*24*30), // expiration future milliseconds for cookei (30 days here)
function( cookie_val ){
//callback for cookie fetch finalization
//NOTE - cookie will be read/set locally regardless, but success of setting cross-domain version is first param for callback
_set_user_cookie_id_for_etl_pipeline( cookie_val );
}
);
*/
Loading

0 comments on commit 6f85d41

Please sign in to comment.