Compare commits

...

8 Commits
v0.4 ... master

5 changed files with 586 additions and 167 deletions

View File

@ -1,15 +1,27 @@
# bt
manage communication between browser tabs
Manage commnunication between browser tabs.
this js lib can perform several actions on browser tabs like :
- (*new) varset - set a stringifiable object on tabs
- (*new) varsync - synchro a object previously set with varset on an other tab
- (*new) prepend node
- (*new) synchro node attributes
- define custom before and after command functions
- search and kill zombies tabs (enable onload by default)
- append/rewrite/synchro node on all (other) tabs or a specific tab (and possibly on specific frame context) eventually with callback.
- reload all tabs or a specific tab with specified url or tab 's current url
- perform your custom actions on all tabs or specific tab
### require
html5 localStorage
html5 localStorage svan (pluie.org small vanilla jquery-like lib)
### Initialize
$v(document).ready(function() {
$bt.init();
$(document).ready(function() {
// $bt.zkillonload = false // disable zombkill cmd at statup
$bt.init(optionalCallback);
}
@ -17,21 +29,43 @@ manage communication between browser tabs
// append data on node to other browser tabs
$bt.append('#test', "<b>it's cool to append</b>");
// rewrite content on node to other browser tabs
$bt.html('#test', "<b>it's cool to rewrite</b>");
// rewrite content to specific browser tabs
$bt.html('#test', "<b>it's cool to rewrite</b>", '1449974562012');
// append content to specific browser tab
$bt.append('#test', "<b>it's cool to rewrite</b>", null, null, '1449974562012');
// rewrite content to specified browser tab with callback
// callback must be define before command with method $bt.setCallback('callbackname', callback);
$bt.html('#test', "<b>it's cool to rewrite</b>", null, 'callbackname', '1449974562012');
// append content to specified browser tab on specific frame
$bt.append('#test', "<b>it's cool to rewrite</b>", 'frameName', null, '1449974562012');
// perform a node synchro to other browser tabs
$bt.sync('#test');
// perform a node synchro to specified browser tab on specific frame with callback
$bt.sync('#test', 'frameName', 'callbackname', '1449974562012');
// perform a node attr synchro to all browser tab
$bt.attr('#test', ['class', 'title']);
// perform a varset to all tabs
$bt.varset('myVar', { toto : tutu : { tata : "titi" }});
// perform a varsync from specific tab (for example after calling tab will reload)
$bt.varsync('myVar', '1449974562012');
// reload other browser tabs
$bt.reload();
// reload other browser tabs to specific url
$bt.reload(window.location.path+"?reloaded=1");
// reload specific browser tab to specific url
$bt.reload(window.location.path+"?reloaded=1", '1449974562012');
// search and kill zombies tabs
$bt.zombkill(callback, timeout);
// get browser tab list
$bt.list;
@ -54,10 +88,27 @@ manage communication between browser tabs
}
}
// it's also possible to define a before and after function :
$bt.before = function(cmd) {
$bt.log("i'm fired before every command");
}
$bt.after = function(cmd) {
$bt.log("i'm fired after every command");
}
// send a custom command to other browser tabs
$bt.send({ name : $bt.CMD_CUSTOM, customKey : 'customValue' });
### Bonus
// alias localStorage : clear|rem|get|set
$l
// alias json : str|obj
$j
### Demo
on chromium/chrome browser make sure to test on a web server (not directly file://)

147
demo.html
View File

@ -1,38 +1,57 @@
<!doctype>
<html>
<head>
<title>pluie.org bt demo</title>
<title>pluie.org bt demo - v0.7</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style>
body { background-color:#888; color:white; font-family:arial; font-size:1.3rem; }
#test { border: 1px dashed #ccc; }
body > div { border-radius:20px; margin:40px; padding:20px; background-color: #ccc; color:black; }
body > span { font-size:0.8rem; }
button { padding:5px 15px; }
h3 { margin:35px 0 10px 0; }
h2 { margin:5px 0 0 0; }
textarea { padding:8px 5px; border-radius:10px; border-style:solid; width:100%; height:120px; }
#test { margin:20px; background-color:white; padding:20px; border: 1px dashed #aaa; }
body { background-color:#888; color:white; font-family:arial; font-size:1.3rem; }
#test { border: 1px dashed #ccc; }
body > div { border-radius:20px; margin:40px; padding:20px; background-color: #ccc; color:black; }
body > span { font-size:0.8rem; }
button { padding:5px 15px; }
h3 { margin:35px 0 10px 0; }
h2 { margin:5px 0 0 0; }
textarea { padding:8px 5px; border-radius:10px; border-style:solid; width:100%; height:120px; font-size:1.2rem; }
#test { margin:20px; background-color:white; padding:20px; border: 1px dashed #aaa; }
.syncAttrName { color:#990000; }
</style>
</head>
<body>
<h2>pluie.org bt demo : </h2>
<span>author : a-sansara</span>
<span>author : a-sansara - version : 0.7</span>
<div>
<p>
this js lib can perform several actions on browser tabs.
<p> Manage commnunication between browser tabs.</p>
<p> this js lib can perform several actions on browser tabs like :<br/>
<ul><li>append/rewrite/synchro node content on all (other) tabs or a specific tab (and possibly on specific frame context) eventually with callback.</li>
<li>reload all tabs or a specific tab with specified url or tab 's current url</li>
<li>perform your custom actions on all tabs or specific tab</li>
<li>kill zombies tabs onload</li>
<li>define custom before and after command functions</li>
<li>(*new) sync node attributes</li>
<li>(*new) prepend node content</li>
<li>(*new) varset - set a stringifiable object on tabs</li>
<li>(*new) varsync - synchro a object previously set with varset on an other tab</li>
</ul>
</p>
<h3>Static actions</h3>
<button class="cmd-sta-prepend" title="$bt.prepend('#test', '<b>it\'s cool to prepend</b><br/>');">static dom prepend</button>
<button class="cmd-sta-append" title="$bt.append('#test', '<b>it\'s cool to append</b><br/>');">static dom append</button>
<button class="cmd-sta-html" title="$bt.html('#test', '<b>it\'s cool to rewrite</b><br/>')">static dom html</button>
<button class="cmd-zombkill" title="">cmd zombkill</button>
<button class="cmd-varset">cmd varset</button>
<button class="cmd-varget">varget</button>
<button class="cmd-varsync">varsync</button>
<h3>Dynamic actions</h3>
enter txt or html : <br/>
<textarea id="data"></textarea>
<br/>select your action<br/>
<button class="cmd-append">dom append</button>
<button class="cmd-append-cb">dom append with callback</button>
<button class="cmd-html">dom html</button>
<button class="cmd-html">dom html with before</button>
<button class="cmd-sync">dom sync</button>
<button class="cmd-attr">dom attr sync</button>
<button class="cmd-tarsync">target dom sync</button>
<button class="cmd-reload">tab reload</button>
<button class="cmd-custom">custom cmd</button>
@ -41,6 +60,7 @@ enter txt or html : <br/>
<h3>Target output</h3>
<div id="test"></div>
</div>
<script type="text/javascript" src="src/svan-min.js"></script>
<script type="text/javascript" src="src/bt.js"></script>
<script type="text/javascript">
// define a new custom cmd
@ -55,45 +75,120 @@ $bt.on = function(cmd) {
break;
}
}
$bt.vars['myVar'] = "start";
//
$v(document).ready(function() {
$bt.init();
$(document).ready(function() {
$bt.init(function() {
$bt.setCallback('mycallback', function(cmd) {
alert("i'm the callback of cmd "+cmd.name);
});
$bt.before = function(cmd) {
console.log('before cmd '+cmd.name);
}
$bt.after = function(cmd) {
console.log('after cmd '+cmd.name);
}
});
var bind = function(call) {
var html = $v('#data').val();
var html = $('#data').val();
if (html.length > 0) call(html)
else $v('#data').attr('placeholder', 'insert data first !');
else $('#data').attr('placeholder', 'insert data first !');
}
$v('.cmd-append').on('click', function(){
$('.cmd-append').on('click', function(){
bind(function(data) {
$bt.append('#test', data);
});
});
$v('.cmd-html').on('click', function(){
$('.cmd-append-cb').on('click', function(){
bind(function(data) {
$bt.append('#test', data, null, 'mycallback');
});
});
$('.cmd-html').on('click', function(){
bind(function(data) {
$bt.html('#test', data);
});
});
$v('.cmd-custom').on('click', function(){
$('.cmd-custom').on('click', function(){
bind(function(data) {
$bt.send({ name : $bt.CMD_CUSTOM, customKey : data });
});
});
$v('.cmd-sync').on('click', function(){
$v('h2').html('pluie.org bt demo (sync on '+(new Date).getTime()+') :');
$('.cmd-sync').on('click', function(){
$('h2').html('pluie.org bt demo (sync on '+(new Date).getTime()+') :');
$bt.sync('h2');
});
$v('.cmd-reload').on('click', function(){
$('.cmd-reload').on('click', function(){
$bt.reload(window.location.pathname+"?reload="+(new Date).getTime());
});
$v('.cmd-tarsync').on('click', function(){
$('.cmd-tarsync').on('click', function(){
$bt.sync('#test');
});
$v('.cmd-sta-append').on('click', function(){
$('.cmd-attr').on('click', function(){
$(this).toggle('syncAttrName');
$(this).attr('title', 'i have '+($(this).hasClass('syncAttrName') ? '' : 'not ')+' the class syncAttrName');
$bt.attr('.cmd-attr', ['class', 'title']);
});
$('.cmd-sta-append').on('click', function(){
eval(this.title);
});
$v('.cmd-sta-html').on('click', function(){
$('.cmd-sta-prepend').on('click', function(){
eval(this.title);
});
$('.cmd-varset').on('click', function(){
var obj = {
toto : {
titi : {
tutu : 'cool',
tata : ['one', 2, 'three']
}
}
}
console.log(obj);
$('#test').html("varset : myVar<br/>json value : <br/>"+$j.str(obj));
$bt.varset('myVar', obj);
});
$('.cmd-varget').on('click', function(){
if (!$.isNone($bt.vars['myVar'])) {
$('#test').html("varget : myVar<br/>json value : <br/>"+$j.str($bt.vars['myVar']));
}
else {
$('#test').html("varget : myVar<br/>undefined");
$('#test').html("clic on varsync to sync myVar");
}
});
$('.cmd-varsync').on('click', function(){
if ($bt.list.length > 1) {
$bt.varsync('myVar', null, $bt.list[0] == $bt.id ? $bt.list[1] : $bt.list[0]);
if (!$.isNone($bt.vars['myVar'])) {
$('#test').html("varget : myVar<br/>json value : <br/>"+$j.str($bt.vars['myVar']));
}
else {
$('#test').html("varget : myVar<br/>undefined");
}
}
else {
$('#test').html("need one more tab to perform a bt varsync");
}
});
$('.cmd-sta-html').on('click', function(){
eval(this.title);
});
$('.cmd-zombkill').on('click', function(){
$bt.list = $j.obj($l.get($bt.LS_TABS));
for (var i=4; i > 0; i--) {
var zombi = (new Date).getTime()+i;
$bt.list.push(zombi);
}
$l.set($bt.LS_TABS, $j.str($bt.list));
$bt._broadcast();
$bt.log($bt.list);
var l = $bt.list.length;
alert('zombies tabs created, so, check for zombies');
$bt.zombkill(function(){
alert('killing '+(l-$bt.list.length)+' zombies');
});
});
});
</script>
</body>

1
src/bt-min.js vendored Normal file

File diff suppressed because one or more lines are too long

535
src/bt.js
View File

@ -3,26 +3,72 @@
* @contributors :
* @copyright : pluie.org
* @date : 2015-12-10 22:22:34
* @version : 0.2
* @version : 0.8
* @license : MIT
* @require : html5 localStorage
* @desc :
* @require : html5 localStorage svan (small vanilla jquery-like lib)
* @desc : manage communication between browser tabs
*
* BrowserTab USAGE :
* USAGE :
*
*
* Initialize
*
*
* $(document).ready(function() {
* $bt.init();
* }
* // $bt.zkillonload = false // disable zombkill cmd at statup
* $bt.init(optionalCallback);
* });
*
*
*
* Internal Commands
*
* // append data on node to other browser tabs
* $bt.append('#test', '<b>it's cool to append</b>');
* $bt.append('#test', "<b>it's cool to append</b>");
*
* // rewrite content on node to other browser tabs
* $bt.html('#test', '<b>it's cool to rewrite</b>');
* $bt.html('#test', "<b>it's cool to rewrite</b>");
*
* // append content to specific browser tab
* $bt.append('#test', "<b>it's cool to rewrite</b>", null, null, '1449974562012');
*
* // rewrite content to specified browser tab with callback
* // callback must be define before command with method $bt.setCallback('callbackname', callback);
* $bt.html('#test', "<b>it's cool to rewrite</b>", null, 'callbackname', '1449974562012');
*
* // append content to specified browser tab on specific frame
* $bt.append('#test', "<b>it's cool to rewrite</b>", 'frameName', null, '1449974562012');
*
* // perform a node synchro to other browser tabs
* $bt.sync('#test');
*
* // perform a node synchro to specified browser tab on specific frame with callback
* $bt.sync('#test', 'frameName', 'callbackname', '1449974562012');
*
* // perform a node attr synchro to all browser tab
* $bt.attr('#test', ['class', 'title']);
*
* // perform a varset to all tabs
* $bt.varset('myVar', { toto : tutu : { tata : "titi" }});
*
* // perform a varsync from specific tab (for example after calling tab will reload)
* $bt.varsync('myVar', '1449974562012');
*
* // reload other browser tabs
* $bt.reload();
*
* // reload specific browser tab to specific url
* $bt.reload(window.location.path+"?reloaded=1", '1449974562012');
*
* // check and kill zombies tabs
* $bt.zombkill();
*
* // get browser tab list
* $bt.list;
*
* // current browser tab id
* $bt.id;
*
*
* Custom Commands
*
@ -38,53 +84,112 @@
* }
* }
*
* // it's also possible to define a before and after function :
* $bt.before = function(cmd) {
* $bt.log("i'm fired before every command");
* }
*
* $bt.after = function(cmd) {
* $bt.log("i'm fired after every command");
* }
*
* // send a custom command to other browser tabs
* $bt.send({ name : $bt.CMD_CUSTOM, customKey : 'customValue' });
*
*
* Bonus
*
* // alias localStorage : clear|rem|get|set
* $l;
* // alias json : str|obj
* $j
*
* enjoy !
*/
var $l = (function alias() {
var a = localStorage;
return {
clear : function() { return a.clear(); },
get : function(k) { return a.getItem(k); },
rem : function(k) { return a.removeItem(k); },
set : function(k, v) { return a.setItem(k, v); }
};
}());
var $j = (function alias() {
var a = JSON;
return {
str : function(o) { return a.stringify(o); },
obj : function(s) { return a.parse(s); }
};
}());
var $bt = {
TRACE : true && typeof console != "undefined",
/*! @constant LS_TABS localStorage key for browsertabs list */
LS_TABS : 'bt.tabs',
/*! @constant LS_CURTAB localStorage key for current browsertab */
LS_CURTAB : 'bt.ctab',
/*! @constant LS_CMD localStorage key command to interact with other tabs */
LS_CMD : 'bt.cmd',
/*! @constant CMD_SYNC internal command to perform a browser tab synchro */
CMD_SYNC : 'bt.sync',
/*! @constant CMD_APPEND internal command to perform a dom append */
CMD_APPEND : 'dom.append',
/*! @constant CMD_HTML internal command to perform a dom html */
CMD_HTML : 'dom.html',
/*! @constant CMD_RELOAD internal command to perform a browser tab reload */
CMD_RELOAD : 'bt.reload',
VERSION : 0.8,
UID : null,
HASH : null,
TRACE : true && !$.isNone(console),
setConstant : function(value, name) {
if (!name) {
if (this.UID == null) throw Error('UID is not define, you need to define $bt.UID or provide the "name" parameter');
else if (this.HASH == null) {
this.HASH = this._getHash(this.UID);
}
return this.HASH+'.'+value;
}
return _getHash(name)+'.'+value;
},
/*! @var vars */
vars : [],
/*! @var callbacks */
callbacks : [],
/*! @var zomblist */
zomblist : [],
/*! @var zkillonload */
zkillonload : true,
/*! @var zombTimeout in ms */
zombTimeout : 250,
/*!
* @desc initialize on dom ready
* @public
* @method init
* @param string fn a function to call on initializing on dom ready
*/
init : function(fn) {
init : function(uid, fn) {
if (!fn && $.isFunc(uid) || arguments.length==0) {
if (this.UID == null) throw Error('UID is not define, you need to define $bt.UID or provide the "uid" parameter');
if (arguments.length > 0) fn = uid;
uid = this.UID;
}
this._initConstant(uid);
this._init(fn);
},
/*!
* @desc custom method to implements before custom command
* @public
* @method before
* @param string cmd a cmd to treat
*/
/*!
* @desc custom method to implements after custom command
* @public
* @method after
* @param string cmd a cmd to treat
*/
/*!
* @desc custom method to implements custom command
* @public
* @method on
* @param string cmd a cmd to treat
*/
on : function(cmd) {
// custom
this.log(cmd);
},
/*!
* @desc console log if trace is enabled
* @public
* @method on
* @param mix data data to log
*/
log : function(data) {
log : function(data) {
if (this.TRACE) console.log(data);
},
/*!
@ -93,75 +198,234 @@ var $bt = {
* @method send
* @param object cmd the cmd to send
*/
send : function(cmd) {
send : function(cmd) {
cmd.uid = this.id+Math.random();
cmd.from = this.id;
if (typeof cmd.to == "undefined") cmd.to = "*";
$l.set(this.LS_CMD, $j.str(cmd));
if ($.isNone(cmd.to)) cmd.to = "*";
cmd = $j.str(cmd);
$bt.log('sending cmd : '+this.LS_CMD+' : '+cmd);
$l.set(this.LS_CMD, cmd);
$l.rem(this.LS_CMD);
},
/*!
* @desc perform an dom append command on other tabs
* @desc perform a dom append command on other tabs
* @public
* @method append
* @param string selector the selector wich target the node(s)
* @param string data the data to append
* @param string ctx context name of selector (frame name relative to document wich match specified selector) or if not defined or null current document
* @param string callback callback name to fire on command
* @param int btid target browser tab id (if not defined all target all tabs)
*/
append : function(selector, data, btid) {
this._dom(this.CMD_APPEND, selector, data, btid);
append : function(selector, data, ctx, callback, btid) {
this._dom(this.CMD_APPEND, ctx, selector, data, callback, btid);
},
/*!
* @desc perform an dom html command on other tabs
* @desc perform a dom prepend command on other tabs
* @public
* @method append
* @param string selector the selector wich target the node(s)
* @param string data the data to append
* @param string ctx context name of selector (frame name relative to document wich match specified selector) or if not defined or null current document
* @param string callback callback name to fire on command
* @param int btid target browser tab id (if not defined all target all tabs)
*/
html : function(selector, data, btid) {
this._dom(this.CMD_HTML, selector, data, btid);
prepend : function(selector, data, ctx, callback, btid) {
this._dom(this.CMD_PREPEND, ctx, selector, data, callback, btid);
},
/*!
* @desc perform an dom synchro command on other tabs
* @desc perform a dom html command on other tabs
* @public
* @method append
* @param string selector the selector wich target the node(s)
* @param string data the data to append
* @param string ctx context name of selector (frame name relative to document wich match specified selector) or if not defined or null current document
* @param string callback callback name to fire on command
* @param int btid target browser tab id (if not defined all target all tabs)
*/
html : function(selector, data, ctx, callback, btid) {
this._dom(this.CMD_HTML, ctx, selector, data, callback, btid);
},
/*!
* @desc perform a dom synchro command on other tabs
* @public
* @method sync
* @param string selector the selector wich target the node(s) to synchro
* @param string ctx context name of selector (frame name relative to document wich match specified selector) or if not defined or null current document
* @param string callback callback name to fire on command
* @param int btid target browser tab id (if not defined all target all tabs)
*/
sync : function(selector, btid) {
this._dom(this.CMD_HTML, selector, $v(selector).html(), btid);
sync : function(selector, ctx, callback, btid) {
var c = !$.isNone(ctx) && ctx != null ? parent.frames[ctx].document : document;
this._dom(this.CMD_HTML, ctx, selector, $(selector, c).html(), callback, btid);
},
/*!
* @desc perform an reload command on other tabs with specified url
* @desc perform a reload command on other tabs with specified url
* @public
* @method reload
* @param string url the url to load (if not defined load current page)
* @param int btid target browser tab id (if not defined all target all tabs)
*/
reload : function(url, btid) {
reload : function(url, btid) {
$bt.send({ name : $bt.CMD_RELOAD, url : url, to : !btid ? '*' : btid });
},
/*!
* @desc define a callback. callback are fire on desire command
* @public
* @method setCallback
* @param string name the callback name (for command binding)
* @param function callback the callback
*/
setCallback : function(name, callback) {
if ($.isNone($bt.callbacks[name])) {
$bt.callbacks[name] = callback;
}
else {
console.log("BT ERROR : callback name already exist");
}
},
/*!
* @desc kill all zombi tabs
* @public
* @method zombkill
* @param int timeout timeout ins ms for killing zombies (no ping reply)
*/
zombkill : function(callback, timeout) {
var askid = (new Date).getTime();
$bt.zomblist[''+askid] = [];
$bt.list.forEach(function(id) {
if (id != $bt.id) {
$bt.zomblist[''+askid]['ping'+id] = '';
}
});
$bt.send({ name : $bt.CMD_ZOMBKILL, askid : askid, to : '*' });
var tid = setTimeout(function() {
$bt._refresh();
for(var k in $bt.zomblist[''+askid]) {
if ($bt.zomblist[''+askid][k] != 'pong') {
var i = $bt.list.indexOf(parseInt(k.substring(4)));
if (i > -1) $bt.list.splice(i, 1);
}
}
$l.set($bt.LS_TABS, $j.str($bt.list));
$bt._broadcast();
$bt.log($bt.list);
clearTimeout(tid);
if ($.isFunc(callback)) callback();
}, !timeout ? $bt.zombTimeout : timeout);
},
/*!
* @desc perform a var set command on other tabs
* @public
* @method varset
* @param string varName the var identifier
* @param object data the data object (wich need to be stringifiable via json)
* @param string callback callback name to fire on command
* @param int btid target browser tab id (if not defined all target all tabs)
*/
varset : function(varName, data, callback, btid) {
try {
var djson = $j.str(data);
$bt.vars[varName] = data;
$bt.send({ name : $bt.CMD_VAR_SET, varName : varName, data : djson, callback : callback, to : !btid ? '*' : btid });
}
catch(e) {
console.log(e);
}
},
/*!
* @desc perform a var sync command on specified tab
* @public
* @method varset
* @param string varName the var identifier
* @param string callback callback name to fire on command
* @param int fromId target browser tab id (must be defined and uniq)
*/
varsync : function(varName, callback, toId) {
$bt.send({ name : $bt.CMD_VAR_SYNC, varName : varName, data : '', callback : callback, to : toId });
},
/*!
* @desc perform a dom attribute synchro command on other tabs
* @public
* @method sync
* @param string selector the selector wich target the node(s) to synchro
* @param string|[string] attrName attribute name to sync
* @param string ctx context name of selector (frame name relative to document wich match specified selector) or if not defined or null current document
* @param string callback callback name to fire on command
* @param int btid target browser tab id (if not defined all target all tabs)
*/
attr : function(selector, attrName, ctx, callback, btid) {
var context = !ctx ? document : window.parent.frames[ctx].document;
if ($.isStr(attrName)) attrName = [ attrName ];
var data = [];
attrName.forEach(function (attr, index) {
data.push(attr != "disabled" ? $(selector, context).attr(attr) : $(selector, context).first().disabled);
});
$bt.send({ name : $bt.CMD_ATTR_SYNC, attr : attrName, selector : selector, data : data, context : ctx, callback : callback, to : !btid ? '*' : btid });
},
_dontkill : function(askid, id) {
$bt.send({ name : $bt.CMD_DONTKILL, askid : askid, to : id });
},
/*! @private */
_refresh : function() {
_refresh : function() {
$bt.list = $j.obj($l.get($bt.LS_TABS));
},
/*! @private */
_broadcast : function() {
_broadcast : function() {
$bt.send({ name : $bt.CMD_SYNC });
},
/*! @private */
_remove : function(id) {
_remove : function(id) {
if (!id) id = $bt.id;
var i = $bt.list.indexOf(id);
if (i > -1) $bt.list.splice(i, 1);
},
/*! @private */
_init : function(fn) {
$v(window).on('beforeunload', $bt._unload);
$v(window).on('storage', $bt._cmd);
$v(window).on('focus', $bt._focus);
//~ $l.clear();
_getHash : function(name) {
name = window.location.host + '#' + name;
var uid = 0;
for (var i = 0, c = 0, lim = name.length; i < lim; i++) {
uid = ((uid<<5)-uid)+name.charCodeAt(i);
uid = uid & uid; // Convert to 32bit integer
}
return Math.abs(uid);
},
/*! @private */
_initConstant : function(name) {
var uid = this.HASH != null ? this.HASH : this._getHash(name);
/*! @constant LS_TABS localStorage key for browsertabs list */
this.LS_TABS = uid+'.bt.list',
/*! @constant LS_CURTAB localStorage key for current browsertab */
this.LS_CURTAB = uid+'.bt.current',
/*! @constant LS_CMD localStorage key command to interact with other tabs */
this.LS_CMD = uid+'.bt.event',
/*! @constant CMD_SYNC internal command to perform a browser tab synchro */
this.CMD_SYNC = uid+'.bt.sync',
/*! @constant CMD_VAR_SET internal command to perform a browser tab var set */
this.CMD_VAR_SET = uid+'.bt.varset',
/*! @constant CMD_VAR_SYNC internal command to perform a browser tab var sync */
this.CMD_VAR_SYNC = uid+'.bt.varsync',
/*! @constant CMD_ATTR_SYNC internal command to perform a dom sync attribute */
this.CMD_ATTR_SYNC = uid+'.bt.attr',
/*! @constant CMD_APPEND internal command to perform a dom append */
this.CMD_APPEND = uid+'.bt.dom.append',
/*! @constant CMD_PREPEND internal command to perform a dom append */
this.CMD_PREPEND = uid+'.bt.dom.prepend',
/*! @constant CMD_HTML internal command to perform a dom html */
this.CMD_HTML = uid+'.bt.dom.rewrite',
/*! @constant CMD_RELOAD internal command to perform a browser tab reload */
this.CMD_RELOAD = uid+'.bt.reload',
/*! @constant CMD_ZOMBKILL internal command to perform a browser tab zombies kill */
this.CMD_ZOMBKILL = uid+'.bt.zombkill',
/*! @constant CMD_DONTKILL internal command to perform a dontkill browser tab (CMD_ZOMBKILL reply) */
this.CMD_DONTKILL = uid+'.bt.dontkill'
},
/*! @private */
_init : function(fn) {
$(window).on('beforeunload', $bt._unload);
$(window).on('storage', $bt._cmd);
$bt._defHandlerCurrentTab();
// $(window).on('focus', $bt._focus); replace by _defHandlerCurrentTab to fix frame context
$bt.id = (new Date).getTime();
var t = $l.get($bt.LS_TABS);
$bt.list = t==null ? [] : $j.obj(t);
@ -169,14 +433,38 @@ var $bt = {
$l.set($bt.LS_TABS, $j.str($bt.list));
$bt._broadcast();
$bt.log($bt.list);
if (typeof fn == "function") fn();
if ($bt.zkillonload) $bt.zombkill(fn);
else if ($.isFunc(fn)) {
fn();
}
},
/*! @private */
_dom : function(n, s, d, id) {
$bt.send({ name : n, selector : s, data : d, to : !id ? '*' : id });
_defHandlerCurrentTab : function() {
var evcVal = "hidden";
var evcType = "visibilitychange";
if (!$.isNone(document.mozHidden)) {
evcVal = "mozHidden";
evcType = "mozvisibilitychange";
} else if (!$.isNone(document.msHidden)) {
evcVal = "msHidden";
evcType = "msvisibilitychange";
} else if (!$.isNone(document.webkitHidden)) {
evcVal = "webkitHidden";
evcType = "webkitvisibilitychange";
}
$(window).on(evcType, function() {
if (!document[evcVal]) {
$bt.log('SET CURRENT TAB : '+$bt.id);
$l.set($bt.LS_CURTAB, $bt.id);
}
}, false);
},
/*! @private */
_unload : function(e) {
_dom : function(n, c, s, d, cb, id) {
$bt.send({ name : n, context : c, selector : s, data : d, callback : cb, to : !id ? '*' : id });
},
/*! @private */
_unload : function(e) {
$bt._refresh();
$bt._remove();
$l.set($bt.LS_TABS, $j.str($bt.list));
@ -184,17 +472,28 @@ var $bt = {
return null;
},
/*! @private */
_focus : function(e) {
$l.set($bt.LS_CURTAB, $bt.id);
},
//~ _focus : function(e) {
//~ $l.set($bt.LS_CURTAB, $bt.id);
//~ },
/*! @private */
_cmd : function(e) {
_cmd : function(e) {
if (!$.isNone(e.originalEvent)) e = e.originalEvent;
if (e.key!=$bt.LS_CMD) return;
var cmd = $j.obj(e.newValue);
if (!cmd) return;
if (cmd.to == "*" || cmd.to == $bt.id) {
$bt.log("do "+cmd.name);
$bt.log(cmd);
console.log('RECEIVING cmd '+cmd.name+' : ');
console.log(cmd);
try {
if (!$.isNone(cmd.context) && cmd.context!=null && !$.isNone(window.parent.frames[cmd.context])) {
cmd.context = window.parent.frames[cmd.context].document;
}
else cmd.context = document;
}
catch(e) {
$bt.log("bad context "+cmd.context+" : "+e.message);
}
if ($.isFunc($bt.before)) $bt.before(cmd);
switch(cmd.name) {
case $bt.CMD_SYNC :
@ -203,88 +502,60 @@ var $bt = {
break;
case $bt.CMD_APPEND :
$v(cmd.selector).append(cmd.data);
$(cmd.selector, cmd.context).append(cmd.data);
break;
case $bt.CMD_HTML :
$v(cmd.selector).html(cmd.data);
$(cmd.selector, cmd.context).html(cmd.data);
break;
case $bt.CMD_PREPEND :
$(cmd.selector, cmd.context).prepend(cmd.data);
break;
case $bt.CMD_RELOAD :
window.location = typeof cmd.url != "undefined" ? cmd.url : window.location;
window.location = !$.isNone(cmd.url) ? cmd.url : window.location;
break;
// emit response for zombkill
case $bt.CMD_ZOMBKILL :
$bt._dontkill(cmd.askid, cmd.from);
break;
// receiv response for zombkill
case $bt.CMD_DONTKILL :
$bt.zomblist[''+cmd.askid]['ping'+cmd.from] = 'pong';
break;
case $bt.CMD_VAR_SET :
$bt.vars[cmd.varName] = $j.obj(cmd.data);
break;
case $bt.CMD_VAR_SYNC :
if (!$.isNone($bt.vars[cmd.varName])) $bt.varset(cmd.varName, $bt.vars[cmd.varName]);
break;
case $bt.CMD_ATTR_SYNC :
cmd.attr.forEach(function(attr, index) {
if (cmd.attr != "disabled") {
$(cmd.selector, cmd.context).attr(attr, cmd.data[index]);
}
else {
$(cmd.selector, cmd.context).first().disabled = cmd.data[index];
}
});
break;
default :
// do your stuff here
if (typeof $bt.on == "function") $bt.on(cmd);
if ($.isFunc($bt.on)) $bt.on(cmd);
break;
}
if ($.isStr(cmd.callback) && cmd.callback.length>0 && !$.isNone($bt.callbacks[cmd.callback]) && $.isFunc($bt.callbacks[cmd.callback])) {
$bt.log(cmd.callback);
$bt.callbacks[cmd.callback].call({}, cmd);
}
if ($.isFunc($bt.after)) $bt.after(cmd);
}
}
}
// vanilla minimal jquery style
var $v = function(p) {
var s = typeof p == "string";
var z = "cool";
var c = s ? document.querySelectorAll(p) : null;
var a = s ? [].slice.call(c) : [p];
delete(s);
// alias
if (p==localStorage) {
return {
clear : function() { return p.clear(); },
get : function(k) { return p.getItem(k); },
rem : function(k) { return p.removeItem(k); },
set : function(k, v) { return p.setItem(k, v); }
};
}
// alias
else if (p==JSON) {
// alias JSON
return {
str : function(o) { return p.stringify(o); },
obj : function(s) { return p.parse(s); }
};
}
else {
this.foreach = function(f) {
[].forEach.call(c, f);
};
// assume uniq selector
// Living Standard cf https://w3c.github.io/DOM-Parsing/#innerhtml
this.html = function(d) {
if (arguments.length == 0) return a[0].innerHTML;
else a[0].innerHTML = d;
};
this.append = function(d) {
this.foreach(function(n) {
n.innerHTML += d;
});
};
this.on = function(t, f, u) {
if (c != null) {
this.foreach(function(n) {
n.addEventListener(t, f, u===true);
});
}
else a[0].addEventListener(t, f, u===true);
};
// assume uniq selector
this.val = function(d) {
if (arguments.length == 0) return a[0].value;
else a[0].value = d;
};
// assume uniq selector
this.attr = function(k, v) {
if (arguments.length == 1) return a[0].getAttribute(k);
else a[0].setAttribute(k, v);
};
this.ready = function(f) {
document.addEventListener('DOMContentLoaded', f);
};
return this;
}
}
// alias localStorage
var $l = $v(localStorage);
// alias json
var $j = $v(JSON);

1
src/svan-min.js vendored Normal file
View File

@ -0,0 +1 @@
/* by a-sansara - v 0.5 - https://github.com/pluie-org/svan */!function(){var n=function(n,i){return typeof n==i},i=function(n,o){return new i.init(n,o)},o=i.isNone=function(i){return n(i,"undefined")},l=i.isStr=function(i){return n(i,"string")},r=i.isFunc=function(i){return n(i,"function")},c=i.isObj=function(i){return n(i,"object")},u=i.isNode=function(n){return c(n)&&!o(n.nodeType)},f=i.isWin=function(n){return c(n)&&!o(n.window)&&n.window==n};i.prototype={regsan:function(n){return n.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")},first:function(){return this.found?this.list[0]:null},last:function(){return this.found?this.list[this.list.length-1]:b},index:function(n){return this.found&&n>0&&n<this.list.length?this.list[n]:b},all:function(){return this.list},find:function(s){return this.found?new i.init(s,this.list[0]):new i.init("")},each:function(n){this.found&&this.list.forEach(n)},html:function(n){return n?void this.each(function(i){i.innerHTML=n}):this.found?this.list[0].innerHTML:""},append:function(n){this.each(function(i){i.innerHTML+=n})},prepend:function(n){if(this.found)if(l(n)){var i=document.createElement("div");for(i.innerHTML=n;null!=i.lastChild;)this.first().insertBefore(i.lastChild,this.first().firstChild)}else{var i=l(n)?document.createElement("div"):n;this.first().insertBefore(n,this.first().firstChild)}},on:function(n,i,o){this.each(function(l){l.addEventListener(n,i,o===!0)})},val:function(n){return n?void this.each(function(i){if("select"==i.nodeName.toLowerCase()){for(var o=0,l=i.options.length;l>o;o++)if(i.options[o].value==n){i.options[o].selected=!0;break}}else i.value=n}):this.found?"select"==this.list[0].nodeName.toLowerCase()?this.list[0].options[this.list[0].selectedIndex].value:this.list[0].value:null},attr:function(n,i){return 1==arguments.length?this.found?"disabled"==n?this.list[0].disabled:this.list[0].getAttribute(n):null:void this.each(function(o){"disabled"==n?o.disabled=i:o.setAttribute(n,i)})},toggle:function(n){this.each(function(i){i.classList.toggle(n)})},hasClass:function(n){return this.found?this.list[0].classList.contains(n):this.found},removeClass:function(n){this.each(function(i){i.classList.contains(n)&&i.classList.toggle(n)})},addClass:function(n){this.each(function(i){i.classList.contains(n)||i.classList.toggle(n)})},fadeIn:function(n,i,o){if(this.found){n||(n=this.FADE_DURATION);var l=parseFloat(1/n*20),r=this.first();r.style.opacity=0,r.style.display=o||"block",function n(){var o=parseFloat(r.style.opacity);(o+=l)<1?(r.style.opacity=o,requestAnimationFrame(n)):"function"==typeof i&&i.call(r)}(n)}},fadeOut:function(n,i){if(this.found){n||(n=this.FADE_DURATION);var o=parseFloat(1/n*20),l=this.first();l.style.opacity=1,function n(){var r=parseFloat(l.style.opacity);(r-=o)<0?(l.style.display="none","function"==typeof i&&i.call(l)):(l.style.opacity=r,requestAnimationFrame(n))}(n)}},ready:function(n){this.context.addEventListener("DOMContentLoaded",n)}},i.eachObj=function(n,i,o){for(var l in n)n.hasOwnProperty(l)&&i.call(o,l,n[l])},i.ajax=function(n){this.eachObj(n,function(n,i){console.log(n),console.log(i)});var i=new XMLHttpRequest;i.onreadystatechange=function(){4==this.readyState&&(200==this.status?r(n.done)&&n.done.call(n.context,this.responseText,this.status):r(n.fail)&&n.fail.call(n.context,this.responseText,this.status),r(n.always)&&n.always.call(n.context,this.responseText,this.status))},!o(n.timeout)&&n.async&&(i.timeout=n.timeout),r(n.before)&&n.before.call(i),i.open(n.method,n.url,n.async),i.setRequestHeader("Content-Type","application/x-www-form-urlencoded");var l="";c(n.data)&&this.eachObj(n.data,function(n,i){l+=n+"="+encodeURIComponent(i)}),i.send(l)};var h=i.init=function(n,i){return this.FADE_DURATION=700,this.VERSION=.5,this.context=o(i)?document:i,this.list=l(n)?[].slice.call(this.context.querySelectorAll(n)):u(n)||f(n)?[n]:[],this.found=this.list.length>0,this};h.prototype=i.prototype,window.Svan=i,o(window.$)&&(window.$=i)}();